commit c90c389a8a8d9d8661e9772ec4144c5cf2039f23 Author: toma Date: Wed Nov 25 17:56:58 2009 +0000 Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegames@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..a3b0c210 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,4 @@ + +Look in the subdirs to get info about the authors. + +The package is maintained by Stephan Kulow diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..8900e10b --- /dev/null +++ b/COPYING @@ -0,0 +1,347 @@ +NOTE! The GPL below is copyrighted by the Free Software Foundation, but +the instance of code that it refers to (the kde programs) are copyrighted +by the authors who actually wrote it. + +--------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/COPYING-DOCS b/COPYING-DOCS new file mode 100644 index 00000000..4a0fe1c8 --- /dev/null +++ b/COPYING-DOCS @@ -0,0 +1,397 @@ + GNU Free Documentation License + Version 1.2, November 2002 + + + Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + +0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document "free" in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The "Document", below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as "you". You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall subject +(or to related matters) and contains nothing that could fall directly +within that overall subject. (Thus, if the Document is in part a +textbook of mathematics, a Secondary Section may not explain any +mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +A section "Entitled XYZ" means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as "Acknowledgements", +"Dedications", "Endorsements", or "History".) To "Preserve the Title" +of such a section when you modify the Document means that it remains a +section "Entitled XYZ" according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + + +2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +3. COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + + +4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +A. Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. +B. List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has fewer than five), + unless they release you from this requirement. +C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. +D. Preserve all the copyright notices of the Document. +E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. +F. Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. +G. Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. +H. Include an unaltered copy of this License. +I. Preserve the section Entitled "History", Preserve its Title, and add + to it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section Entitled "History" in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. +J. Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the "History" section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. +K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section all + the substance and tone of each of the contributor acknowledgements + and/or dedications given therein. +L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. +M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. +N. Do not retitle any existing section to be Entitled "Endorsements" + or to conflict in title with any Invariant Section. +O. Preserve any Warranty Disclaimers. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled "History" +in the various original documents, forming one section Entitled +"History"; likewise combine any sections Entitled "Acknowledgements", +and any sections Entitled "Dedications". You must delete all sections +Entitled "Endorsements". + + +6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + + +7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an "aggregate" if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + + +8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled "Acknowledgements", +"Dedications", or "History", the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + + +9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document is void, and will +automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + + +10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. + + +ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled "GNU + Free Documentation License". + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 00000000..01148ab6 --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,486 @@ +NOTE! The LGPL below is copyrighted by the Free Software Foundation, but +the instance of code that it refers to (the kde libraries) are copyrighted +by the authors who actually wrote it. + +--------------------------------------------------------------------------- + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..59959f64 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,16 @@ +1999-03-11 Mario Weilguni + + * fixed (hopefully) all problems with Qt-2.0 + +1999-02-17 David Faure + + * Added call to AC_CHECK_RANDOM, and converted all apps to use + random() and srandom() (and to avoid RAND_MAX). + If random() is not defined, acconfig.h defines the header for the + fake functions in kdecore. + +1998-12-05 Alex Zepeda + + * autmoc: Moved to admin/ + * Makefile.cvs: Copied over from kdenetwork verbatim. + * README: Updated to "require" Qt 1.41. diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..f8bad0c1 --- /dev/null +++ b/INSTALL @@ -0,0 +1,176 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes a while. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/kde/bin', `/usr/local/kde/lib', etc. You can specify an +installation prefix other than `/usr/local/kde' by giving `configure' +the option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff --git a/Mainpage.dox b/Mainpage.dox new file mode 100644 index 00000000..061abb9a --- /dev/null +++ b/Mainpage.dox @@ -0,0 +1,8 @@ +/** \mainpage The KDE Games API Reference + +This section contains the KDE online class reference for the current development version of KDE Games. + +- libkdegames (classes) + + +*/ diff --git a/Makefile.am.in b/Makefile.am.in new file mode 100644 index 00000000..2aa4523a --- /dev/null +++ b/Makefile.am.in @@ -0,0 +1,13 @@ +AUTOMAKE_OPTIONS = foreign 1.6.1 +COMPILE_FIRST = libkdegames +COMPILE_AFTER_libksirtet = ksirtet kfouleggs klickety +DISTCLEANFILES = inst-apps + +MAINTAINERCLEANFILES = subdirs configure.in acinclude.m4 SUBDIRS +include admin/deps.am + +include admin/Doxyfile.am + + + + diff --git a/Makefile.cvs b/Makefile.cvs new file mode 100644 index 00000000..b4752bd8 --- /dev/null +++ b/Makefile.cvs @@ -0,0 +1,16 @@ + +all: + @echo "This Makefile is only for the CVS repository" + @echo "This will be deleted before making the distribution" + @echo "" + @if test ! -d admin; then \ + echo "Please recheckout this module!" ;\ + echo "for cvs: use checkout once and after that update again" ;\ + echo "for cvsup: checkout kde-common from cvsup and" ;\ + echo " link kde-common/admin to ./admin" ;\ + exit 1 ;\ + fi + $(MAKE) -f admin/Makefile.common cvs + +.SILENT: + diff --git a/README b/README new file mode 100644 index 00000000..76d4581b --- /dev/null +++ b/README @@ -0,0 +1,135 @@ +In this file: + +* About kdegames +* Common Mistakes +* Debugging +* More Info + + +About kdegames +-------------- +This is a compilation of various games + +* atlantik + Monopoly-like board games + +* debian + Files necessary to create Debian packages. + +* doc + XML based documentation for the programs. + +* kasteroids + Shoot at those nasty asteroids. + +* katomic + Build complex atoms with a minimal amount of moves. + +* kbackgammon + Play backgammon against a local human player, via a game server or + against GNU Backgammon (not included) + +* kbattleship + Sink battleship of your opponents, with built-in game server. + +* kblackbox + Find atoms in a grid by shooting electrons. + +* kbounce + Claim areas and don't get disturbed. + +* keneloba + Push pieces of your opponent out of the board (an Abalone type game). + +* kfouleggs + A famous japanese game known as puyo-puyo. + +* kjumpingcube + A tactical game for number-crunchers. + +* klickety + A tetris like game. + +* klines + Place 5 equal pieces together, but wait, there are 3 new ones. + +* mahjongg + A tile laying patience. + +* kmines + The classical mine sweeper. + +* kolf + A mini golf game. + +* konquest + Conquer the planets of your enemy. + +* kpat + Several patience card games. + +* kpoker + The game of poker. + +* kreversi + The old reversi board game, also known as Othello. + +* ksame + Collect pieces of the same color. + +* kshisen + Patience game where you take away all pieces. + +* ksirtet + Very known if spelt this backwards. + +* ksmiletris + Another Tetris-like game. + +* ksnake + Don't bite yourself, eat apples! + +* ksokoban + Move all storage boxes into the cabinet. + +* kspaceduel + Two player game with shooting spaceships flying around a sun. + +* ktron + Like ksnake, but without fruits. + +* ktuberling + Kids game: make your own potato (NO french fries!) + +* kwin4 + Place 4 pieces in a row. + +* libkdegames + KDE game library used by many of these programs, contains also images + of card decks. + +* lskat + Lieutnant skat. + + +Common Mistakes +--------------- +If configure claims Qt cannot be found, have a look at http://www.trolltech.com +to get a copy of latest Qt 3.3.x version. + + +Debugging +--------- +You can use --enable-debug with the configure script, if you want to have +debug code in your KDE apps and libs. I recommend to do this, since this is +alpha software and this makes debugging things a whole lot easier. + + +More Info +--------- +Please direct any bug reports to our bug list by visiting +http://bugs.kde.org. + +General KDE discussions should go to the KDE mailing list (kde@kde.org). + + diff --git a/atlantik/AUTHORS b/atlantik/AUTHORS new file mode 100644 index 00000000..5ef9d50f --- /dev/null +++ b/atlantik/AUTHORS @@ -0,0 +1 @@ +Rob Kaper diff --git a/atlantik/COPYING b/atlantik/COPYING new file mode 100644 index 00000000..c13faf0d --- /dev/null +++ b/atlantik/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/atlantik/COPYING.LIB b/atlantik/COPYING.LIB new file mode 100644 index 00000000..ae23fcfd --- /dev/null +++ b/atlantik/COPYING.LIB @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/atlantik/ChangeLog b/atlantik/ChangeLog new file mode 100644 index 00000000..8f889261 --- /dev/null +++ b/atlantik/ChangeLog @@ -0,0 +1,633 @@ +0.7.5 (KDE 3.4) +----- + +- Show mortgage and house prices/values on estate views. +- Make token image a saved configuration setting, use KIconDialog. +- Avoid scrollbar by using KWrappedListViewItem. + +0.7.2 (KDE 3.3.2) +----- + +- Proper use of host and port in kio_atlantik: fixes problems with + invitations sent by newer clients. +- bugfix: scroll player views when they get too big. (#69043) +- bugfix: show correct amount of players in trade widget. + +0.7.1 (KDE 3.3.1) +----- + +- Don't show warning dialog when exiting a game that ended. (#88617) + +0.7.0 (KDE 3.3) +----- + +- Event log +- Use of KStatusBar +- Game master can boot other players to lounge during game setup (#52631) +- Support gameupdate tag, deprecate updategamelist (requires monopd >= 0.9.0) +- Support individual configupdate tags +- Show estate names on board (#61858) +- Properly withdraw from a game prior to intended client exits, to avoid + triggering monopd's reconnection timeout. + +0.6.3 +----- + +- bugfix: avoid crash when unresolvable hosts are in the meta server list +- bugfix: fix crash when network core is reset while readbuffer isn't empty +- bugfix: chat view can be cleared (#69044) + +0.6.1 +----- + +- hide development servers identifying themselves with "-dev" + +0.6.0 +----- + +- support for custom tokens +- don't connect to meta server unless user has configured to do so +- support for KNotify events +- option to hide development servers (on by default) +- gui to connect to custom server +- klatencytimer: support for server ping times +- option to show timestamp in chat messages + +0.5.5 +----- + +- bugfix: fix wild pointer when removing trade items (#68589) +- bugfix: set proper palette color for EstateDetails so Keramik buttons + don't look out of place + +0.5.4 +----- + +- bugfix: don't interrupt token movement when player leaves jail on doubles +- bugfix: add m_atlanticCore checks in Board, fixes some Atlantik Designer + crashes +- bugfix: don't show players from other games in auction and trade widgets + on monopd 0.9.0 servers +- bugfix: valgrind found two crash conditions when leaving game + configuration widget or auction widget (#66498) + +0.5.3 +----- + +- fix problem where sometimes the select game widget appears + after starting a game +- removed buggy trademap in network code for decent ptrlist in core +- memory management: reset complete core when going to select server page +- store reconnection cookie +- hide token when player goes bankrupt +- prevent player from building/unmortgaging when in debt +- disable auction/usecard menu item when not available (requires monopd >= 0.8.1) +- make use of trade revisions (monopd >= 0.8.2 recommended) +- don't tell game has started when it has not +- sort user column in server list by number, not alphabetically +- show non-game users when not in a game +- prevent double close buttons on estate details + +0.5.2 +----- + +- correctly handle utf-8 over the network +- only show localhost server when it is available +- leaving a game and starting a new game correctly reinits atlanticCore +- use disability to join game when size == MAX in gameupdate (games in + updategamelist now have canbejoined boolean attribute) +- using playerupdate for player list in SelectConfiguration +- use portfolioview instead of klistview for players during config +- better handling of display messages from server +- fixed token animation + +0.5.1 +----- + +- fixed potential double initialization of gameboard +- don't crash when removing money from a trade +- properly quote arguments given to KProcess in kio_atlantik +- valgrind leak and unitialised memory access fixes + +0.5.0 +----- + +- fixed dangling pointer for removeGUI(Trade *) in AtlanticCore +- player name change doesn't make it to trade money items +- fixed token positioning on game start +- fixes to work with monopd 0.6.0 API + +0.4.0 +----- + +- token geometry is now aware of estate colour captions +- queue display widgets + - fix buttons, they have specific targets, not just the current widget + - delete buttons in EstateDetails::newUpdate() +- commandline parameters host, port and game supported (for auto-join) +- kio_atlantik allows for easy connecting from other applications + (kopete/kmail) +- leave game and leave server options +- pre-game configuration + +2002-07-08 (kaper) +---------- + +- support for auto-connect and auto-join + +2002-07-07 (kaper) +---------- + +- request full update when getting invalid data from the network +- gui bugfixes + +2002-07-04 (kaper) (atlantik-0.3.0) +---------- + +- clear display on + +2002-07-03 (kaper) +---------- + +- removing trade money works again, fixed possible crashes in trade and + auction deletion within network API + +2002-06-30 (kaper) +---------- + +- monopd no longer sends gamelist on connect, so request it +- more intelligent EstateDetails, shows no useless info anymore and added + groupname, price and isMortgaged +- 10% or $200 taxes finally implemented! (requires monopd CVS >= 20020701) +- removed
from messagewindow, apparently Qt autowraps (again??) + +2002-06-27 (kaper) +---------- + +- recently committed some changes to be up-to-date with some API monopd + changes +- generalised BoardDisplay::displayCard() into displayText() so I can fix + the missing jail notification bug + +2002-04-29 (kaper) +---------- + +- dropped QSocket for KExtendedSocket + +2002-03-30 (kaper) +---------- + +- monopd API: support for estategroupupdate +- build portfolioview upon game start for better layouting + +2002-02-19 (kaper) +---------- + +- Trading done except for small esthetic TODOs. + +2002-02-13 (kaper) +---------- + +- Nicer button layouts (QSpacerItems, KIcon::SizeSmall, margin/spacingHints) + +2002-02-11 (kaper) +---------- + +- Gametypes no longer hardcoded but fetched from monopd server +- Trading money, rejecting trades! + +2002-01-31 (kaper) +---------- + +- Bugfixes +- Trades! (ok, just the estates and no way to accept, but still ;) + +2002-01-08 (kaper) +---------- + +- Auctions! +- Board resize updates tokens again + +2001-12-22 (katz) +--------- + +- Add support for setting AtlantikBoard's size in its ctor; + simply tell it maxEstates. Right now for Atlantik's board + it simply uses a hard-coded 40 +- in Designer, implement 'smaller' nad 'larger' menu entries, + now able to save and load boards with any size!!! +- add boardinfo.[h/cpp] that has a BoardInfo class that has board information, + and a class to View/Edit it +- designer uses it, doesn't read/write the info yet tho + +2001-12-20 (katz) +--------- + +- fix using user's colors for openNew in designer + +2001-12-20 (katz) +--------- + +Designer: +- gets its first entry in ChangeLog, because designer has been + on the whole useless before this +- supports chance and cc cards; loads and saves properly +- doesn't crash in random places +- loads and saves in Cap's new format +- fix probs with the swallowed dialogs +- don't use defaultcity.conf in openNew(); make board with + user's KDE colors! pretty :-) doesn'twork tho, neil will fix +- use KComboBox now QComboBox +- maybe some other things? + +2001-12-20 (kaper) +---------- + +- Moved network stuff to libatlantiknetwork + +2001-12-19 (kaper) +---------- + +- Better toolbar disable/enable code (API changes in monopd) +- Bugfixes for m_playerSelf +- preparations to use KExtendedSocket instead of QSocket +- using KPushButton instead of QPushButton + +2001-12-18 (kaper) +---------- + +- moved Trade to libatlantic +- libatlantic now includes AtlanticCore wrapper + +2001-12-17 (kaper) +---------- + +- moved object management to Network class +- moved Estate and Player to libatlantic + +2001-12-16 (kaper) +---------- + +- monopd no longer 'includes' estates in trades, but has a proper + targetplayer for them. added support for this API update, as well as the + update regarding money trades + +2001-12-14 (kaper) +---------- + +- better looking portfolios +- support for displaying game types in game list +- ability to choose between city or atlantic gametype +- GUI to create a trade + +2001-11-29 (kaper) +---------- + +- Portfolios are _truly_ dynamic now. :-) + +2001-11-27 (kaper) +---------- + +- Set version to 0.1.2 CVS. + +2001-11-27 (kaper) (0.1.1 release) +---------- + +- PortfolioViews and PortfolioEstates work again! +- Tagged 0.1.1 release. + +2001-11-23 (kaper) +---------- + +- Trading skeleton +- Better implementation of connection between Player and PortfolioView +- Various code cleanups + +2001-11-21 (kaper) +---------- + +- Very basic skeleton for atlanticd (monopd-compatible server) + +2001-11-15 (kaper) +---------- + +- Even more dynamic thinking: estateview actions now completely depend on + server data instead of own checks. + +2001-11-14 (kaper) +---------- + +- Tokens are correctly positioned at startup and token animation is working + again. +- Chance/community cards are displayed in board center. + +2001-11-12 (kaper) +---------- + +- EstateViews have correct orientation again. +- Fixed weird Quartz behavior on large resolutions. + +2001-11-11 (kaper) +---------- + +- First changes to replace KMessageBox with in-window widget. +- Starting games is possible again. +- Connected Estate::changed to EstateView::estateChanged. +- Added informational message at game startup regarding current Atlantik + state (buggy, at least). +- No longer using fixed geometry. +- Dynamic (server guided) colour support for estates. +- Board configuration settings update properly again. +- Starting Player / Token relationship as we did for Estate / EstateView +- Tokens move again (animation not in operation yet). +- Right Mouse Button actions on estates are properly working again. + +2001-11-05 (kaper) +---------- + +- SelectGame and SelectConfiguration KWizard replacement widgets done. +- Better icon loading. +- Better error checking when connecting to a server. + +2001-10-19 (kaper) +---------- + +- Rewriting new game wizard into regular widgets, SelectServer done. +- Internal changes. + +2001-10-10 (kaper) +---------- + +- Estates are created dynamically now! +- Lots of internal changes regarding player and estateupdates. +- monopd API change: mortgages are now a toggle. + +2001-10-09 (kaper) +---------- + +- Portfolioviews are now generated dynamically. The Atlantik class manages + the creation and updates of the content are done by the (new) Player + class. Atlantik does act as intermediate here, though, eventually being + responsible for both player and widget management. +- Player objects/views are only created when playerupdate contains init=1 +- Estate class created, simplified grid layout code for board. +- Temporarily removed gameboard spacer code. + +2001-10-05 (kaper) +---------- + +- Qt3 updates. +- Small monopd API updates. + +2001-09-04 (kaper) +---------- + +- Been a while since the last update, due to the renaming to Atlantik (now + mostly taken care of, thus this entry), new game concept (in progress) and + my vacation to San Francisco (unfortunately no longer in progress). + +2001-08-06 (kaper) +---------- + +- Monopigator works! :-) + +2001-07-30 (kaper) +---------- + +- Estateupdate visual update fixes. +- Using can_be_mortgaged and can_be_unmortgaged attributes of monopd's + estateupdate. +- New app icons by Bart Szyszka :-) +- Token confirmation disabled for jumpToken when resizing gameboard or after + directmove instrution from server. + +2001-07-19 (kaper) +---------- + +- Network interface for trades completed, all commands and signals are in + place. + +2001-07-17 (kaper) +---------- + +- Encapsulated actual monopd API commands in gameNetwork +- Extended gameNetwork to support trading API commands +- RMB actions on estates only available when owned by player respresented by + this client + +2001-07-16 (kaper) +---------- + +- Small bugfix connecting standard roll action to correct slot. +- Code documentation! (at least for the KMonop class) +- Quartz effects! (configurable) + +2001-07-13 (neil) +---------- + +- UI: make the SelectGame widgets respond as the user may expect them to + +2001-07-01 (kaper) +---------- + +- Using KStdGameAction more and more (requires kdenonbeta version for roll + action) +- Updated to be compatible with recent monopd API changes + +2001-06-29 (kaper) +---------- + +- Bugfix: when owner=-1 in estateupdate, KMonop now clears the + portfolio/board estateviews. +- Added icon for Go. +- New config option to highlite unowned properties. +- Seperated server messages from chat. + +2001-06-27 (kaper) +---------- + +- Updated TODO (some wishlist items, changed version roadmap) +- Added a lot of i18n strings! + +2001-06-26 (kaper) +---------- + +- New config option: mortgaged properties can be grayed out on the gameboard +- Slightly increased size of portfolio estates +- Added RMB popup to estates with mortgage/unmortgage and build/sell house + actions +- Added luxury tax and community chest icons. + +2001-06-24 (kaper) +---------- + +- Server port no longer hardcoded, added extra default server (running + monopd CVS). +- Game board visualization of houses and hotels! + +2001-06-21 (kaper) +---------- + +- Network code parses attribs houses and mortgaged. + +2001-06-19 (kaper) +---------- + +- Added "end turn" button. +- Added "pay to leave jail" button. +- Added playername to config dialog. +- Updated parsing of changes in monopd. + +2001-06-17 (kaper) +---------- + +- Upgraded version to pre-0.2.0 + +2001-06-17 (kaper) (0.1.0 release) +---------- + +- Happy birthday Katy. Love, Rob. +- Integrated recent monopd updates. +- Some internal code changes. +- Added support for +- Added support for which replaced +- Tagged 0.1.0 release + +2001-06-10 +---------- + +- Portfolioestate/board recognize utilities as ownable estates. +- Prepared utilities for icons. + +2001-06-09 +---------- + +- Configuration works! +- Location confirmation upon jumpToken. +- Unowned indication also for railroads. + +2001-06-05 +---------- + +- More PlayerUpdate* changes. + +2001-06-05 +---------- + +- Added parsing of message which is new in monopd. + Chatting can thus be done through the console (lineedit field) now. +- Some changes to configure dialog, none that influence behavior though. +- Got tired of passing netw through all classes, it's pretty general anyway + so I made it a general variable. +- PlayerUpdate* changes (movetoken was removed from monopd API) + +2001-06-01 +---------- + +- Added parsing of command which is new in monopd +- Sending .t# command to monopd which is now required during token movement + +2001-05-30 +---------- + +- Token is now a nice icon (not yet transparent though, unfortunately I + couldn't get that to work right) + +2001-05-28 +---------- + +- Added board icons for train and chance estates +- Token which has turn is raised to make sure it's on top + +2001-05-27 +---------- + +- Moved some of the XML parsing code back to GameNetwork::processNode +- Changed version (back.. ssht!) to pre-0.0.2 +- Internal improvements to network/newgamewizard code (more accurate slots + and button validation) + +2001-05-27 (0.0.1 release) +---------- + +- Visual feedback showing who's turn it is +- Roll/buy buttons only enabled during turn +- Tagged 0.0.1 release + +2001-05-20 +---------- + +- Small cosmetic changes. +- New application icons, icons installdir changed. +- Configure dialog (looks nice, doesn't load/save yet) + +2001-05-16 +---------- + +- Message view autoscrolls. +- Small (cosmetic) portfolioview updates. +- Visual display on board showing which properties are still for sale + +2001-05-15 +---------- + +- New game dialog improvements (select game page checks network status and + gamelist availability +- Tokens are actually moving over the board! (instead of jumping) + +2001-05-08 +---------- + +- Portfolios built upon "playerupdate" message instead of final playerlist, + since we'll get plenty more updates anyway. +- Token placed on location hinted by playerupdate. +- Input box at bottom left corner can be used to send messages to the server + to compensate for any commands not yet implemented. Such as .n to set your + name and .r to roll. +- Cash and estates in portfolio get updated after purchase/rent. + +2001-05-07 +---------- + +- Wizard notifies game server we're starting the game upon finish. Bugfix in + KMonop::slotStartNewGame which checks whether wizard still exists before + trying to hide it. +- Portfolio overviews are built upon game start, requires monopd which sends + final attribute along with playerlist. +- Framework for token class. +- Informational messages from server are shown in output textbox. +- Only sending start game command when wizard is finished, not when it is + cancelled. + +2001-05-03 +---------- + +- Various code cleanups, keeping things neat. +- Playerlist and gamelist are automatically sent by server, manual request + no longer required. + +2001-05-02 +---------- + +- Playerlist is fetched from server. +- Playerlist is interpreted and updated when someone enters. +- Option to finish setup and launch game, closes all wizards. +- Network object moved to KMonop, where it belongs. Wizard and its pages use + pointers. + +2001-04-30 +---------- + +- NewGameDialog is now a wizard. A connection to the server is made and a + list of available games to join fetched, using Qt's XML parsing + capabilities. + +2001-04-27 +---------- + +- Turned NewGameDialog into modal dialog. + +2001-04-26 +---------- + +- Initial ChangeLog entry. diff --git a/atlantik/INSTALL b/atlantik/INSTALL new file mode 100644 index 00000000..34523708 --- /dev/null +++ b/atlantik/INSTALL @@ -0,0 +1,33 @@ +Contents +-------- + + 1. Compiling the CVS version + 2. Compiling a release tarball + 3. Installing a finished build + 4. Starting the application + +1. Compiling the CVS version +---------------------------- + +cd kdegames && make -f Makefile.cvs && ./configure && cd atlantik && make + +2. Compiling a release tarball +------------------------------ + +cd atlantik-x.y.z && ./configure && make + +3. Installing a finished build +------------------------------ + +As root; + +make install + +4. Starting the application +--------------------------- + +Click on K->Games->Board->Atlantik or run atlantik from konsole or minicli. + +Please note that Atlantik is only a client! You will need to connect to a +monopd server on the Internet or run one locally! See the README file for +more details on the Atlantik/monopd client/server concept. diff --git a/atlantik/Makefile.am b/atlantik/Makefile.am new file mode 100644 index 00000000..d4389577 --- /dev/null +++ b/atlantik/Makefile.am @@ -0,0 +1,13 @@ +SUBDIRS = libatlantic libatlantikclient libatlantikui client \ + kio_atlantik pics themes + +EXTRA_DIST = atlantik.desktop + +xdg_apps_DATA = atlantik.desktop + +rcdir = $(kde_datadir)/atlantik +rc_DATA = atlantikui.rc eventsrc + +messages: rc.cpp + $(XGETTEXT) `find . -name '*.cpp'` -o $(podir)/atlantik.pot + diff --git a/atlantik/README b/atlantik/README new file mode 100644 index 00000000..c51d2859 --- /dev/null +++ b/atlantik/README @@ -0,0 +1,94 @@ +Contents +-------- + +1. Introduction +2. Download +3. Mailinglists +4. Roadmap +5. Designer +6. Legal issues + +1. Introduction +--------------- + +Atlantik is a KDE client for playing Monopoly-like games on the monopd +network. + +Purpose of the Atlantic board game is to acquire land in major cities in +North America and Europe while being a transatlantic traveller. One of the +game modes plays like the popular real estate board game based on Atlantic +City street names. + +Atlantik was previously known as KMonop and might still be referred to as +such in some documentation. + +2. Download +----------- + +Atlantik can be downloaded from + +http://unixcode.org/atlantik/ + +and monopd can be downloaded from + +http://unixcode.org/monopd/ + +However, the latest versions of both projects are found in CVS. + +Atlantik is located in the CVS repository of the KDE project in the module +kdegames. See http://www.kde.org/anoncvs.html for instructions how to +download KDE modules from CVS. Atlantik is included in the kdegames package +since the KDE 3.1 release. + +For more information on monopd CVS see the monopd pages on +http://sourceforge.net/projects/monopd/ + +Both monopd and Atlantik are in heavy development and it is important to match +versions when connecting to a game server. One can choose a suitable server +through the Monopigator interface. + +3. Mailinglists +--------------= + +There are mailinglists available for discussion of Atlantik development. The +atlantik-devel list is for general discussion and development of the +codebase, the atlantik-cvs list has all the CVS commits in it and the +atlantik-artists list is for the development of GFX/SFX for Atlantik. + +See http://mail.kde.org/mailman/listinfo/atlantik-devel +See http://mail.kde.org/mailman/listinfo/atlantik-cvs +and http://mail.kde.org/mailman/listinfo/atlantik-artists + +on information on subscribing, posting and list archives. + +There is a seperate mailinglist for monopd related discussion: + +To subscribe, send e-mail to: monopd-devel-subscribe@lists.capsi.com +To post, send e-mail to: monopd-devel@lists.capsi.com + +4. Roadmap +---------- + +The TODO file gives a nice approximation of desired functionality and +priorities. It can be used as roadmap or checklist. + +5. Designer +----------- + +Atlantik Designer is a game board designer that will create game config +files for monopd. You can find it in KDE CVS, module kdeaddons. + +6. Legal issues +--------------- + +Many people have expressed their concerns about possible copyrights, +trademarks and patents applicable to Monopoly® and the possible implications +for Atlantik (and monopd) development and distribution. + +I believe that Atlantik and monopd are completely clear of violating any +copyrights, trademarks or patents and that there are no legal issues that +might affect development or distibution of either application. + +For more information, please read + +http://unixcode.org/atlantik/legal.html diff --git a/atlantik/README.KDE-3.0 b/atlantik/README.KDE-3.0 new file mode 100644 index 00000000..c941aaa9 --- /dev/null +++ b/atlantik/README.KDE-3.0 @@ -0,0 +1,7 @@ +Atlantik uses KExtendedSocket to connect to monopd servers. Due to some bugs +in KBufferedIO and KExtendedSocket in kdelibs, you might experience +unexplained crashes when using Atlantik with KDE 3.0, 3.0.1 or 3.0.2. + +It is recommended to run Atlantik with at least KDE 3.0.3 or 3.1 Beta1, or a +KDE CVS checkout of the HEAD branch recent enough to contain revision 1.6 of +kdelibs/kdecore/kbufferedio.cpp and revision 1.39 of kextsock.cpp. diff --git a/atlantik/README.packaging b/atlantik/README.packaging new file mode 100644 index 00000000..90608a7c --- /dev/null +++ b/atlantik/README.packaging @@ -0,0 +1,25 @@ +Notes for packaging stand-alone releases that will work with all of KDE 3.x: + +kdegames/configure.in.in +------------------------ + +#MIN_CONFIG(3.0) + +doc/ +---- + +Remove da/ and "da" SUBDIRS entry from Makefile.in, it has a non-backward +compatible entity. + +Ensure + + +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include + +#include "atlanticclient.h" +#include "atlanticclient.moc" + +AtlanticClient::AtlanticClient(QObject *parent, const char *name) : QSocket(parent, name) +{ + connect(this, SIGNAL(readyRead()), this, SLOT(readData())); +} + +void AtlanticClient::sendData(const QString &data) +{ + writeBlock(data.latin1(), data.length()); +} + +void AtlanticClient::readData() +{ + if (canReadLine()) + { + emit clientInput(this, readLine()); + + // There might be more data + QTimer::singleShot(0, this, SLOT(readData())); + } + else + { + // Maximum message size. Messages won't get bigger than 32k anyway, so + // if we didn't receive a newline by now, we probably won't anyway. + if (bytesAvailable() > (1024 * 32)) + flush(); + } +} diff --git a/atlantik/atlanticd/atlanticclient.h b/atlantik/atlanticd/atlanticclient.h new file mode 100644 index 00000000..7b47b0f3 --- /dev/null +++ b/atlantik/atlanticd/atlanticclient.h @@ -0,0 +1,36 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef CLIENT_H +#define CLIENT_H + +#include + +class AtlanticClient : public QSocket +{ +Q_OBJECT + +public: + AtlanticClient(QObject *parent = 0, const char *name = 0); + void sendData(const QString &data); + +private slots: + void readData(); + +signals: + void clientInput(AtlanticClient *client, const QString &data); +}; +#endif diff --git a/atlantik/atlanticd/atlanticdaemon.cpp b/atlantik/atlanticd/atlanticdaemon.cpp new file mode 100644 index 00000000..3fb80cf0 --- /dev/null +++ b/atlantik/atlanticd/atlanticdaemon.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2002 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include +#include + +#include + +#include "atlanticclient.h" +#include "atlanticdaemon.h" +#include "atlanticdaemon.moc" +#include "serversocket.h" + +AtlanticDaemon::AtlanticDaemon() +{ + m_serverSocket = new ServerSocket(1234, 100); + connect(m_serverSocket, SIGNAL(newClient(AtlanticClient *)), this, SLOT(newClient(AtlanticClient *))); + + m_atlanticCore = new AtlanticCore(this, "atlanticCore"); + + // Create socket for Monopigator + m_monopigatorSocket = new QSocket(); + connect(m_monopigatorSocket, SIGNAL(connected()), this, SLOT(monopigatorConnected())); + + // Register server + monopigatorRegister(); +} + +AtlanticDaemon::~AtlanticDaemon() +{ + delete m_monopigatorSocket; +} + +void AtlanticDaemon::monopigatorRegister() +{ + m_monopigatorSocket->connectToHost("gator.monopd.net", 80); +} + +void AtlanticDaemon::monopigatorConnected() +{ + QString get = "GET /register.php?host=capsi.com&port=1234&version=atlanticd-prototype HTTP/1.1\nHost: gator.monopd.net\n\n"; + m_monopigatorSocket->writeBlock(get.latin1(), get.length()); + m_monopigatorSocket->close(); + + // Monopigator clears old entries, so keep registering every 180s + QTimer::singleShot(180000, this, SLOT(monopigatorRegister())); +} + +void AtlanticDaemon::newClient(AtlanticClient *client) +{ + m_clients.append(client); + + connect(client, SIGNAL(clientInput(AtlanticClient *, const QString &)), this, SLOT(clientInput(AtlanticClient *, const QString &))); +} + +void AtlanticDaemon::clientInput(AtlanticClient *client, const QString &data) +{ +} diff --git a/atlantik/atlanticd/atlanticdaemon.h b/atlantik/atlanticd/atlanticdaemon.h new file mode 100644 index 00000000..729a960e --- /dev/null +++ b/atlantik/atlanticd/atlanticdaemon.h @@ -0,0 +1,48 @@ +// Copyright (c) 2002 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIC_ATLANTICDAEMON_H +#define ATLANTIC_ATLANTICDAEMON_H + +#include + +class QSocket; + +class AtlanticCore; +class AtlanticClient; +class ServerSocket; + +class AtlanticDaemon : public QObject +{ +Q_OBJECT + +public: + AtlanticDaemon(); + ~AtlanticDaemon(); +private slots: + void monopigatorRegister(); + void monopigatorConnected(); + void newClient(AtlanticClient *client); + void clientInput(AtlanticClient *client, const QString &data); + +private: + QSocket *m_monopigatorSocket; + ServerSocket *m_serverSocket; + AtlanticCore *m_atlanticCore; + QPtrList m_clients; +}; + +#endif diff --git a/atlantik/atlanticd/main.cpp b/atlantik/atlanticd/main.cpp new file mode 100644 index 00000000..235dcd00 --- /dev/null +++ b/atlantik/atlanticd/main.cpp @@ -0,0 +1,27 @@ +// Copyright (c) 2002 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include "atlanticdaemon.h" + +int main(int argc, char *argv[]) +{ + new AtlanticDaemon(); + + QApplication qapplication(argc, argv); + qapplication.exec(); +} diff --git a/atlantik/atlanticd/serversocket.cpp b/atlantik/atlanticd/serversocket.cpp new file mode 100644 index 00000000..2056a754 --- /dev/null +++ b/atlantik/atlanticd/serversocket.cpp @@ -0,0 +1,32 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include "atlanticclient.h" +#include "serversocket.h" + +ServerSocket::ServerSocket(int port, int backlog) : QServerSocket(port, backlog) +{ +} + +void ServerSocket::newConnection(int socket) +{ + AtlanticClient *client = new AtlanticClient(this, "socket"); + client->setSocket(socket); + + emit newClient(client); +} + +#include "serversocket.moc" diff --git a/atlantik/atlanticd/serversocket.h b/atlantik/atlanticd/serversocket.h new file mode 100644 index 00000000..fce347e9 --- /dev/null +++ b/atlantik/atlanticd/serversocket.h @@ -0,0 +1,35 @@ +// Copyright (c) 2002 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef SERVERSOCKET_H +#define SERVERSOCKET_H + +#include + +class AtlanticClient; + +class ServerSocket : public QServerSocket +{ +Q_OBJECT + +public: + ServerSocket(int port, int backlog); + void newConnection(int socket); + +signals: + void newClient(AtlanticClient *client); +}; +#endif diff --git a/atlantik/atlantik.desktop b/atlantik/atlantik.desktop new file mode 100644 index 00000000..d5191956 --- /dev/null +++ b/atlantik/atlantik.desktop @@ -0,0 +1,84 @@ +[Desktop Entry] +Exec=atlantik -caption "%c" %i %m +Name=Atlantik +Name[ar]=لعبة الرقعة (Atlantik) +Name[be]=Ðтлантыка +Name[bn]=আটলানà§à¦Ÿà¦¿à¦• +Name[eo]=Atlantiko +Name[fi]=Monopoli +Name[hi]=अटलांटिक +Name[lv]=Atlantija +Name[ne]=à¤à¤Ÿà¤²à¤¾à¤¨à¥à¤Ÿà¤¿à¤• +Name[pa]=à¨à¨Ÿà¨²à¨¾à¨‚ਟਿਕ +Name[ta]=அடà¯à®²à®¾à®©à¯à®Ÿà®¿à®•à¯ +Name[tg]=Ðтлантик +Name[th]=à¹à¸­à¸•à¹à¸¥à¸™à¸•à¸´à¸„ +Name[wa]=Atlantike +Name[zh_TW]=Atlantik å¤§å¯Œç¿ +Name[zu]=I-Atlantik +GenericName=Monopoly®-like Board Games +GenericName[ar]=ألعاب الرقعة الشبيهة بمونوبولي +GenericName[be]=ÐаÑÑ‚Ð¾Ð»ÑŒÐ½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ Ñ‚Ñ‹Ð¿Ñƒ ÐœÐ°Ð½Ð°Ð¿Ð¾Ð»Ñ–Ñ +GenericName[bg]=Монополи® +GenericName[bn]=মনোপলি®-জাতীয় ছকভিতà§à¦¤à¦¿à¦• খেলা +GenericName[br]=Ur c'hoari a seurt gant Monopoly® +GenericName[bs]=Igre nalik na Monopol® +GenericName[ca]=Jocs de taula semblants al Monopoly® +GenericName[cs]=Deskové hry podobné Monopoly® +GenericName[cy]=Gêmau Bwrdd tebyg i Monopoly® +GenericName[da]=Matador®-lignende brætspil +GenericName[de]=Monopoly®-ähnliche Brettspiele +GenericName[el]=ΕπιτÏαπέζια παιχνίδια παÏόμοια με το Monopoly® +GenericName[eo]=Monopoly-similaj bretludoj +GenericName[es]=Juegos de tablero estilo Monopoly® +GenericName[et]=Monopoli stiilis mängud +GenericName[eu]=Monopoly® bezalako mahai jokuak +GenericName[fa]=بازیهای شبیه تک قطبی تخته +GenericName[fi]=Monopoli®-tyyliset lautapelit +GenericName[fr]=Jeux de plateau dans le style du Monopoly® +GenericName[he]=משחקי לוח נוסח Monopoly® +GenericName[hi]=मोनोपॉली-®-जैसे बिसात के खेल +GenericName[hr]=Igre poput Monopola® +GenericName[hu]=Monopoly-szerű játék +GenericName[is]=Monopoly®- Borðleikir +GenericName[it]=Gioco da tavolo simile a Monopoly® +GenericName[ja]=モノãƒãƒªãƒ¼ã®ã‚ˆã†ãªãƒœãƒ¼ãƒ‰ã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​ក្ដារ​ដូច Monopoly® +GenericName[lt]=Monopolio tipo stalo žaidimas +GenericName[lv]=Monopoly® lÄ«dzÄ«ga spÄ“le +GenericName[mk]=Игри на табла во Ñтилот на Монопол® +GenericName[nb]=Monopol-aktige brettspill +GenericName[nds]=Monopoly-liek Spelen +GenericName[ne]=à¤à¤•à¤¾à¤§à¤¿à¤•à¤¾à¤°Â® जसà¥à¤¤à¥‹ बोरà¥à¤¡ खेल +GenericName[nl]=Monopoly®-achtige bordspellen +GenericName[nn]=Monopol®-liknande brettspel +GenericName[pa]=Monopoly®-ਵਰਗੀ ਬੋਰਡ ਖੇਡ +GenericName[pl]=Gra planszowa podobna do Monopoly® +GenericName[pt]=Jogo de Tabuleiro tipo Monopoly® +GenericName[pt_BR]=Jogo parecido com Banco Imobiliário® +GenericName[ro]=Un joc de tip Monopoly® +GenericName[ru]=Ðтлантик +GenericName[se]=Monopol®-lágan duolbbášspeallu +GenericName[sk]=Hry typu Monopoly® +GenericName[sl]=PloÅ¡Äadna igra, podobna Monopoliju® +GenericName[sr]=Игре на табли налик на Монопол +GenericName[sr@Latn]=Igre na tabli nalik na Monopol +GenericName[sv]=Monopol®-liknande brädspel +GenericName[ta]=Monopoly போல போரà¯à®Ÿà¯ விளையாடà¯à®Ÿà¯à®•à®³à¯ +GenericName[tg]=Бозии Рӯимизӣ ба монанди МонополиÑ® +GenericName[tr]=Monopoly®-benzeri tahta oyunları +GenericName[uk]=Ігри на дошці Ñхожі на гру МонополіÑ® +GenericName[uz]=Monopoly® oÊ»yiniga oÊ»xshagan stol oÊ»yini +GenericName[uz@cyrillic]=Monopoly® ўйинига ўхшаган Ñтол ўйини +GenericName[ven]=Mutambo wa kha Bodo unanga Monopoly® +GenericName[wa]=Djeus di platea djinre monopoly +GenericName[zh_CN]=类似 Monopoly® çš„æ£‹ç±»æ¸¸æˆ +GenericName[zh_TW]=é¡žä¼¼Monopoly®大富ç¿æ£‹ç›¤éŠæˆ² +GenericName[zu]=Imidlalo efana ne-Monopoly yebhodi +Type=Application +DocPath=atlantik/index.html +Terminal=false +Icon=atlantik +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;BoardGame; diff --git a/atlantik/atlantikui.rc b/atlantik/atlantikui.rc new file mode 100644 index 00000000..1f37d2f9 --- /dev/null +++ b/atlantik/atlantikui.rc @@ -0,0 +1,24 @@ + + + + &Game + + + &Move + + + + + + + + + + + + + + + + + diff --git a/atlantik/client/Makefile.am b/atlantik/client/Makefile.am new file mode 100644 index 00000000..25eb24e1 --- /dev/null +++ b/atlantik/client/Makefile.am @@ -0,0 +1,11 @@ +bin_PROGRAMS = atlantik +INCLUDES = -I$(top_srcdir)/libkdegames -I$(srcdir)/../libatlantic -I$(srcdir)/../libatlantikclient -I$(srcdir)/../libatlantikui $(all_includes) +atlantik_LDFLAGS = $(all_libraries) $(KDE_RPATH) +atlantik_LDADD = ../libatlantikui/libatlantikui.la ../libatlantikclient/libatlantikclient.la $(LIB_KDEGAMES) $(LIB_KIO) +atlantik_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +atlantik_SOURCES = atlantik.cpp configdlg.cpp event.cpp eventlogwidget.cpp \ + main.cpp monopigator.cpp selectconfiguration_widget.cpp \ + selectgame_widget.cpp selectserver_widget.cpp + +METASOURCES = AUTO diff --git a/atlantik/client/atlantik.cpp b/atlantik/client/atlantik.cpp new file mode 100644 index 00000000..56bae64b --- /dev/null +++ b/atlantik/client/atlantik.cpp @@ -0,0 +1,853 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#undef KDE_3_2_FEATURES +#if defined(KDE_MAKE_VERSION) +#if KDE_VERSION >= KDE_MAKE_VERSION(3,2,0) + #define KDE_3_2_FEATURES +#endif +#endif + +#include + +#include +#include +#include +#include +#include +#include "atlantik.moc" + +#include + +#include +#include + +#include "eventlogwidget.h" +#include "main.h" +#include "selectserver_widget.h" +#include "selectgame_widget.h" +#include "selectconfiguration_widget.h" + +LogTextEdit::LogTextEdit( QWidget *parent, const char *name ) : QTextEdit( parent, name ) +{ +#ifdef KDE_3_2_FEATURES + m_clear = KStdAction::clear( this, SLOT( clear() ), 0 ); +#else + m_clear = new KAction( i18n("Clear"), "clear", NULL, this, SLOT( clear() ), static_cast(0), "clear" ); +#endif + m_selectAll = KStdAction::selectAll( this, SLOT( selectAll() ), 0 ); + m_copy = KStdAction::copy( this, SLOT( copy() ), 0 ); +} + +LogTextEdit::~LogTextEdit() +{ + delete m_clear; + delete m_selectAll; + delete m_copy; +} + +QPopupMenu *LogTextEdit::createPopupMenu( const QPoint & ) +{ + QPopupMenu *rmbMenu = new QPopupMenu( this ); + m_clear->plug( rmbMenu ); + rmbMenu->insertSeparator(); + m_copy->setEnabled( hasSelectedText() ); + m_copy->plug( rmbMenu ); + m_selectAll->plug( rmbMenu ); + + return rmbMenu; +} + +Atlantik::Atlantik () + : KMainWindow (), + m_runningGame( false ) +{ + // Read application configuration + readConfig(); + + // Toolbar: Game +// KStdGameAction::gameNew(this, SLOT(slotNewGame()), actionCollection(), "game_new"); + m_showEventLog = new KAction(i18n("Show Event &Log")/*, "atlantik_showeventlog"*/, CTRL+Key_L, this, SLOT(showEventLog()), actionCollection(), "showeventlog"); + KStdGameAction::quit(kapp, SLOT(closeAllWindows()), actionCollection(), "game_quit"); + + // Toolbar: Settings + KStdAction::preferences(this, SLOT(slotConfigure()), actionCollection()); + KStdAction::configureNotifications(this, SLOT(configureNotifications()), actionCollection()); + + // Initialize pointers to 0L + m_configDialog = 0; + m_board = 0; + m_eventLogWidget = 0; + m_selectServer = 0; + m_selectGame = 0; + m_selectConfiguration = 0; + m_atlantikNetwork = 0; + + // Game and network core + m_atlanticCore = new AtlanticCore(this, "atlanticCore"); + connect(m_atlanticCore, SIGNAL(createGUI(Player *)), this, SLOT(newPlayer(Player *))); + connect(m_atlanticCore, SIGNAL(removeGUI(Player *)), this, SLOT(removeGUI(Player *))); + connect(m_atlanticCore, SIGNAL(createGUI(Trade *)), this, SLOT(newTrade(Trade *))); + connect(m_atlanticCore, SIGNAL(removeGUI(Trade *)), this, SLOT(removeGUI(Trade *))); + + initEventLog(); + initNetworkObject(); + + // Menu,toolbar: Move + m_roll = KStdGameAction::roll(this, SIGNAL(rollDice()), actionCollection()); + m_roll->setEnabled(false); + m_buyEstate = new KAction(i18n("&Buy"), "atlantik_buy_estate", CTRL+Key_B, this, SIGNAL(buyEstate()), actionCollection(), "buy_estate"); + m_buyEstate->setEnabled(false); + m_auctionEstate = new KAction(i18n("&Auction"), "auction", CTRL+Key_A, this, SIGNAL(auctionEstate()), actionCollection(), "auction"); + m_auctionEstate->setEnabled(false); + m_endTurn = KStdGameAction::endTurn(this, SIGNAL(endTurn()), actionCollection()); + m_endTurn->setEnabled(false); + m_jailCard = new KAction(i18n("Use Card to Leave Jail")/*, "atlantik_move_jail_card"*/, 0, this, SIGNAL(jailCard()), actionCollection(), "move_jailcard"); + m_jailCard->setEnabled(false); + m_jailPay = new KAction(i18n("&Pay to Leave Jail"), "jail_pay", CTRL+Key_P, this, SIGNAL(jailPay()), actionCollection(), "move_jailpay"); + m_jailPay->setEnabled(false); + m_jailRoll = new KAction(i18n("Roll to Leave &Jail")/*, "atlantik_move_jail_roll"*/, CTRL+Key_J, this, SIGNAL(jailRoll()), actionCollection(), "move_jailroll"); + m_jailRoll->setEnabled(false); + + // Mix code and XML into GUI + KMainWindow::createGUI(); + applyMainWindowSettings( KGlobal::config(), "AtlantikMainWindow" ); + KMainWindow::statusBar()->insertItem("Atlantik " ATLANTIK_VERSION_STRING, 0); + KMainWindow::statusBar()->insertItem(QString::null, 1); + connect(statusBar(), SIGNAL(released(int)), this, SLOT(statusBarClick(int))); + + // Main widget, containing all others + m_mainWidget = new QWidget(this, "main"); + m_mainWidget->show(); + m_mainLayout = new QGridLayout(m_mainWidget, 3, 2); + setCentralWidget(m_mainWidget); + + // Vertical view area for portfolios. + m_portfolioScroll = new QScrollView(m_mainWidget, "pfScroll"); + m_mainLayout->addWidget( m_portfolioScroll, 0, 0 ); + m_portfolioScroll->setHScrollBarMode( QScrollView::AlwaysOff ); + m_portfolioScroll->setResizePolicy( QScrollView::AutoOneFit ); + m_portfolioScroll->setFixedHeight( 200 ); + m_portfolioScroll->hide(); + + m_portfolioWidget = new QWidget( m_portfolioScroll->viewport(), "pfWidget" ); + m_portfolioScroll->addChild( m_portfolioWidget ); + m_portfolioWidget->show(); + + m_portfolioLayout = new QVBoxLayout(m_portfolioWidget); + m_portfolioViews.setAutoDelete(true); + + // Nice label +// m_portfolioLabel = new QLabel(i18n("Players"), m_portfolioWidget, "pfLabel"); +// m_portfolioLayout->addWidget(m_portfolioLabel); +// m_portfolioLabel->show(); + + // Text view for chat and status messages from server. + m_serverMsgs = new LogTextEdit(m_mainWidget, "serverMsgs"); + m_serverMsgs->setTextFormat(QTextEdit::PlainText); + m_serverMsgs->setReadOnly(true); + m_serverMsgs->setHScrollBarMode(QScrollView::AlwaysOff); + m_serverMsgs->setMinimumWidth(200); + m_mainLayout->addWidget(m_serverMsgs, 1, 0); + + // LineEdit to enter commands and chat messages. + m_input = new QLineEdit(m_mainWidget, "input"); + m_mainLayout->addWidget(m_input, 2, 0); + + m_serverMsgs->setFocusProxy(m_input); + + connect(m_input, SIGNAL(returnPressed()), this, SLOT(slotSendMsg())); + + // Set stretching where we want it. + m_mainLayout->setRowStretch(1, 1); // make m_board+m_serverMsgs stretch vertically, not the rest + m_mainLayout->setColStretch(1, 1); // make m_board stretch horizontally, not the rest + + // Check command-line args to see if we need to connect or show Monopigator window + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + QCString host = args->getOption("host"); + QCString port = args->getOption("port"); + if (!host.isNull() && !port.isNull()) + m_atlantikNetwork->serverConnect(host, port.toInt()); + else + showSelectServer(); +} + +void Atlantik::readConfig() +{ + // Read configuration settings + KConfig *config = kapp->config(); + + // General configuration + config->setGroup("General"); + m_config.chatTimestamps = config->readBoolEntry("ChatTimeStamps", false); + + // Personalization configuration + config->setGroup("Personalization"); + m_config.playerName = config->readEntry("PlayerName", "Atlantik"); + m_config.playerImage = config->readEntry("PlayerImage", "cube.png"); + + // Board configuration + config->setGroup("Board"); + m_config.indicateUnowned = config->readBoolEntry("IndicateUnowned", true); + m_config.highliteUnowned = config->readBoolEntry("HighliteUnowned", false); + m_config.darkenMortgaged = config->readBoolEntry("DarkenMortgaged", true); + m_config.animateTokens = config->readBoolEntry("AnimateToken", false); + m_config.quartzEffects = config->readBoolEntry("QuartzEffects", true); + + // Meta server configuation + config->setGroup("Monopigator"); + m_config.connectOnStart = config->readBoolEntry("ConnectOnStart", false); + m_config.hideDevelopmentServers = config->readBoolEntry("HideDevelopmentServers", true); + + // Portfolio colors + config->setGroup("WM"); + QColor activeDefault(204, 204, 204), inactiveDefault(153, 153, 153); + m_config.activeColor = config->readColorEntry("activeBackground", &activeDefault); + m_config.inactiveColor = config->readColorEntry("inactiveBlend", &inactiveDefault); +} + +void Atlantik::newPlayer(Player *player) +{ + initBoard(); + m_board->addToken(player); + addPortfolioView(player); + + // Player::changed() is not connected until later this method, so + // we'd better force an update. + playerChanged(player); + + connect(player, SIGNAL(changed(Player *)), this, SLOT(playerChanged(Player *))); + connect(player, SIGNAL(gainedTurn()), this, SLOT(gainedTurn())); + connect(player, SIGNAL(changed(Player *)), m_board, SLOT(playerChanged(Player *))); + + KNotifyClient::event(winId(), "newplayer"); +} + +void Atlantik::newEstate(Estate *estate) +{ + initBoard(); + m_board->addEstateView(estate, m_config.indicateUnowned, m_config.highliteUnowned, m_config.darkenMortgaged, m_config.quartzEffects); +} + +void Atlantik::newTrade(Trade *trade) +{ + TradeDisplay *tradeDisplay = new TradeDisplay(trade, m_atlanticCore, 0, "tradeDisplay"); + m_tradeGUIMap[trade] = tradeDisplay; + tradeDisplay->show(); +} + +void Atlantik::newAuction(Auction *auction) +{ + initBoard(); + m_board->addAuctionWidget(auction); +} + +void Atlantik::removeGUI(Player *player) +{ + // Find and remove portfolioview + PortfolioView *portfolioView = findPortfolioView(player); + if (portfolioView) + m_portfolioViews.remove(portfolioView); + + if (m_board) + m_board->removeToken(player); +} + +void Atlantik::removeGUI(Trade *trade) +{ + if (TradeDisplay *tradeDisplay = m_tradeGUIMap[trade]) + delete tradeDisplay; +} + +void Atlantik::showSelectServer() +{ + if (m_selectServer) + return; + + m_selectServer = new SelectServer(m_config.connectOnStart, m_config.hideDevelopmentServers, m_mainWidget, "selectServer"); + m_mainLayout->addMultiCellWidget(m_selectServer, 0, 2, 1, 1); + m_selectServer->show(); + + if (m_selectGame) + { + delete m_selectGame; + m_selectGame = 0; + } + + m_atlanticCore->reset(true); + initNetworkObject(); + + connect(m_selectServer, SIGNAL(serverConnect(const QString, int)), m_atlantikNetwork, SLOT(serverConnect(const QString, int))); + connect(m_selectServer, SIGNAL(msgStatus(const QString &)), this, SLOT(slotMsgStatus(const QString &))); + + m_selectServer->slotRefresh( m_config.connectOnStart ); +} + +void Atlantik::showSelectGame() +{ + if (m_selectGame) + return; + + m_selectGame = new SelectGame(m_atlanticCore, m_mainWidget, "selectGame"); + m_atlanticCore->emitGames(); + + m_mainLayout->addMultiCellWidget(m_selectGame, 0, 2, 1, 1); + m_selectGame->show(); + + // Reset core and GUI + if (m_board) + { + m_board->hide(); + m_board->reset(); +// delete m_board; +// m_board = 0; + + // m_portfolioViews.clear(); + m_atlanticCore->reset(); + } + + if (m_selectServer) + { + delete m_selectServer; + m_selectServer = 0; + } + if (m_selectConfiguration) + { + delete m_selectConfiguration; + m_selectConfiguration = 0; + } + + connect(m_selectGame, SIGNAL(joinGame(int)), m_atlantikNetwork, SLOT(joinGame(int))); + connect(m_selectGame, SIGNAL(newGame(const QString &)), m_atlantikNetwork, SLOT(newGame(const QString &))); + connect(m_selectGame, SIGNAL(leaveServer()), this, SLOT(showSelectServer())); + connect(m_selectGame, SIGNAL(msgStatus(const QString &)), this, SLOT(slotMsgStatus(const QString &))); +} + +void Atlantik::showSelectConfiguration() +{ + if (m_selectConfiguration) + return; + + if (m_selectGame) + { + delete m_selectGame; + m_selectGame = 0; + } + + m_selectConfiguration = new SelectConfiguration(m_atlanticCore, m_mainWidget, "selectConfiguration"); + m_mainLayout->addMultiCellWidget(m_selectConfiguration, 0, 2, 1, 1); + m_selectConfiguration->show(); + + connect(m_atlanticCore, SIGNAL(createGUI(ConfigOption *)), m_selectConfiguration, SLOT(addConfigOption(ConfigOption *))); + connect(m_atlantikNetwork, SIGNAL(gameOption(QString, QString, QString, QString, QString)), m_selectConfiguration, SLOT(gameOption(QString, QString, QString, QString, QString))); + connect(m_atlantikNetwork, SIGNAL(gameInit()), m_selectConfiguration, SLOT(initGame())); + connect(m_selectConfiguration, SIGNAL(startGame()), m_atlantikNetwork, SLOT(startGame())); + connect(m_selectConfiguration, SIGNAL(leaveGame()), m_atlantikNetwork, SLOT(leaveGame())); + connect(m_selectConfiguration, SIGNAL(changeOption(int, const QString &)), m_atlantikNetwork, SLOT(changeOption(int, const QString &))); + connect(m_selectConfiguration, SIGNAL(buttonCommand(QString)), m_atlantikNetwork, SLOT(writeData(QString))); + connect(m_selectConfiguration, SIGNAL(iconSelected(const QString &)), m_atlantikNetwork, SLOT(setImage(const QString &))); + connect(m_selectConfiguration, SIGNAL(statusMessage(const QString &)), this, SLOT(slotMsgStatus(const QString &))); +} + +void Atlantik::initBoard() +{ + if (m_board) + return; + + m_board = new AtlantikBoard(m_atlanticCore, 40, AtlantikBoard::Play, m_mainWidget, "board"); + m_board->setViewProperties(m_config.indicateUnowned, m_config.highliteUnowned, m_config.darkenMortgaged, m_config.quartzEffects, m_config.animateTokens); + + connect(m_atlantikNetwork, SIGNAL(displayDetails(QString, bool, bool, Estate *)), m_board, SLOT(insertDetails(QString, bool, bool, Estate *))); + connect(m_atlantikNetwork, SIGNAL(addCommandButton(QString, QString, bool)), m_board, SLOT(displayButton(QString, QString, bool))); + connect(m_atlantikNetwork, SIGNAL(addCloseButton()), m_board, SLOT(addCloseButton())); + connect(m_board, SIGNAL(tokenConfirmation(Estate *)), m_atlantikNetwork, SLOT(tokenConfirmation(Estate *))); + connect(m_board, SIGNAL(buttonCommand(QString)), m_atlantikNetwork, SLOT(writeData(QString))); +} + +void Atlantik::showBoard() +{ + if (m_selectGame) + { + delete m_selectGame; + m_selectGame = 0; + } + + if (m_selectConfiguration) + { + delete m_selectConfiguration; + m_selectConfiguration = 0; + } + + if (!m_board) + initBoard(); + + m_runningGame = true; + + m_mainLayout->addMultiCellWidget(m_board, 0, 2, 1, 1); + m_board->displayDefault(); + m_board->show(); + + PortfolioView *portfolioView = 0; + for (QPtrListIterator it(m_portfolioViews); *it; ++it) + if ((portfolioView = dynamic_cast(*it))) + portfolioView->buildPortfolio(); +} + +void Atlantik::freezeBoard() +{ + if (!m_board) + showBoard(); + + m_runningGame = false; + // TODO: m_board->freeze(); +} + +void Atlantik::slotNetworkConnected() +{ +} + +void Atlantik::slotNetworkError(int errnum) +{ + QString errMsg(i18n("Error connecting: ")); + + switch (m_atlantikNetwork->status()) + { + case IO_ConnectError: + if (errnum == ECONNREFUSED) + errMsg.append(i18n("connection refused by host.")); + else + errMsg.append(i18n("could not connect to host.")); + break; + + case IO_LookupError: + errMsg.append(i18n("host not found.")); + break; + + default: + errMsg.append(i18n("unknown error.")); + } + + serverMsgsAppend(errMsg); + + // Re-init network object + initNetworkObject(); +} + +void Atlantik::networkClosed(int status) +{ + switch( status ) + { + case KBufferedIO::involuntary: + slotMsgStatus( i18n("Connection with server %1:%2 lost.").arg(m_atlantikNetwork->host()).arg(m_atlantikNetwork->port()), QString("connect_no") ); + showSelectServer(); + break; + default: + if ( !m_atlantikNetwork->host().isEmpty() ) + slotMsgStatus( i18n("Disconnected from %1:%2.").arg(m_atlantikNetwork->host()).arg(m_atlantikNetwork->port()), QString("connect_no") ); + break; + } +} + +void Atlantik::slotConfigure() +{ + if (m_configDialog == 0) + m_configDialog = new ConfigDialog(this); + m_configDialog->show(); + + connect(m_configDialog, SIGNAL(okClicked()), this, SLOT(slotUpdateConfig())); +} + +void Atlantik::showEventLog() +{ + if (!m_eventLogWidget) + m_eventLogWidget = new EventLogWidget(m_eventLog, 0); + m_eventLogWidget->show(); +} + +void Atlantik::configureNotifications() +{ + KNotifyDialog::configure(this); +} + +void Atlantik::slotUpdateConfig() +{ + KConfig *config=kapp->config(); + bool optBool, configChanged = false; + QString optStr; + + optBool = m_configDialog->chatTimestamps(); + if (m_config.chatTimestamps != optBool) + { + m_config.chatTimestamps = optBool; + configChanged = true; + } + + optStr = m_configDialog->playerName(); + if (m_config.playerName != optStr) + { + m_config.playerName = optStr; + m_atlantikNetwork->setName(optStr); + } + + optStr = m_configDialog->playerImage(); + if (m_config.playerImage != optStr) + { + m_config.playerImage = optStr; + m_atlantikNetwork->setImage(optStr); + } + + optBool = m_configDialog->indicateUnowned(); + if (m_config.indicateUnowned != optBool) + { + m_config.indicateUnowned = optBool; + configChanged = true; + } + + optBool = m_configDialog->highliteUnowned(); + if (m_config.highliteUnowned != optBool) + { + m_config.highliteUnowned = optBool; + configChanged = true; + } + + optBool = m_configDialog->darkenMortgaged(); + if (m_config.darkenMortgaged != optBool) + { + m_config.darkenMortgaged = optBool; + configChanged = true; + } + + optBool = m_configDialog->animateToken(); + if (m_config.animateTokens != optBool) + { + m_config.animateTokens = optBool; + configChanged = true; + } + + optBool = m_configDialog->quartzEffects(); + if (m_config.quartzEffects != optBool) + { + m_config.quartzEffects = optBool; + configChanged = true; + } + + optBool = m_configDialog->connectOnStart(); + if (m_config.connectOnStart != optBool) + { + m_config.connectOnStart = optBool; + configChanged = true; + } + + optBool = m_configDialog->hideDevelopmentServers(); + if (m_config.hideDevelopmentServers != optBool) + { + m_config.hideDevelopmentServers = optBool; + if (m_selectServer) + m_selectServer->setHideDevelopmentServers(optBool); + + configChanged = true; + } + + config->setGroup("General"); + config->writeEntry("ChatTimeStamps", m_config.chatTimestamps); + + config->setGroup("Personalization"); + config->writeEntry("PlayerName", m_config.playerName); + config->writeEntry("PlayerImage", m_config.playerImage); + + config->setGroup("Board"); + config->writeEntry("IndicateUnowned", m_config.indicateUnowned); + config->writeEntry("HighliteUnowned", m_config.highliteUnowned); + config->writeEntry("DarkenMortgaged", m_config.darkenMortgaged); + config->writeEntry("AnimateToken", m_config.animateTokens); + config->writeEntry("QuartzEffects", m_config.quartzEffects); + + config->setGroup("Monopigator"); + config->writeEntry("ConnectOnStart", m_config.connectOnStart); + config->writeEntry("HideDevelopmentServers", m_config.hideDevelopmentServers); + + config->sync(); + + if (configChanged && m_board) + m_board->setViewProperties(m_config.indicateUnowned, m_config.highliteUnowned, m_config.darkenMortgaged, m_config.quartzEffects, m_config.animateTokens); +} + +void Atlantik::slotSendMsg() +{ + m_atlantikNetwork->cmdChat(m_input->text()); + m_input->setText(QString::null); +} + +void Atlantik::slotMsgInfo(QString msg) +{ + serverMsgsAppend(msg); +} + +void Atlantik::slotMsgError(QString msg) +{ + serverMsgsAppend("Error: " + msg); +} + +void Atlantik::slotMsgStatus(const QString &message, const QString &icon) +{ + KMainWindow::statusBar()->changeItem(message, 1); + m_eventLog->addEvent(message, icon); +} + +void Atlantik::slotMsgChat(QString player, QString msg) +{ + if (m_config.chatTimestamps) + { + QTime time = QTime::currentTime(); + serverMsgsAppend(QString("[%1] %2: %3").arg(time.toString("hh:mm")).arg(player).arg(msg)); + } + else + serverMsgsAppend(player + ": " + msg); + KNotifyClient::event(winId(), "chat"); +} + +void Atlantik::serverMsgsAppend(QString msg) +{ + // Use append, not setText(old+new) because that one doesn't wrap + m_serverMsgs->append(msg); + m_serverMsgs->ensureVisible(0, m_serverMsgs->contentsHeight()); +} + +void Atlantik::playerChanged(Player *player) +{ + PortfolioView *portfolioView = findPortfolioView(player); + if (!portfolioView) + portfolioView = addPortfolioView(player); + + Player *playerSelf = m_atlanticCore->playerSelf(); + if (player == playerSelf) + { + // We changed ourselves.. + PortfolioView *portfolioView = 0; + for (QPtrListIterator it(m_portfolioViews); *it; ++it) + if ((portfolioView = dynamic_cast(*it))) + { + // Clear all portfolios if we're not in game + if ( !player->game() ) + portfolioView->clearPortfolio(); + + // Show players in our game, hide the rest + Player *pTmp = portfolioView->player(); + if (pTmp->game() == playerSelf->game()) + portfolioView->show(); + else + portfolioView->hide(); + } + if (!player->game()) + showSelectGame(); + else + { + if ( !m_board || m_board->isHidden() ) + showSelectConfiguration(); + } + + m_roll->setEnabled(player->canRoll()); + m_buyEstate->setEnabled(player->canBuy()); + m_auctionEstate->setEnabled(player->canAuction()); + + // TODO: Should be more finetuned, but monopd doesn't send can_endturn can_payjail can_jailroll yet + m_endTurn->setEnabled(player->hasTurn() && !(player->canRoll() || player->canBuy() || player->inJail())); + m_jailCard->setEnabled(player->canUseCard()); + m_jailPay->setEnabled(player->hasTurn() && player->inJail()); + m_jailRoll->setEnabled(player->hasTurn() && player->inJail()); + } + else + { + // Another player changed, check if we need to show or hide + // his/her portfolioView. + if (playerSelf) + { + if (player->game() == playerSelf->game()) + portfolioView->show(); + else + portfolioView->hide(); + } + else if ( !player->game() ) + portfolioView->hide(); + } +} + +void Atlantik::gainedTurn() +{ + KNotifyClient::event(winId(), "gainedturn", i18n("It is your turn now.") ); +} + +void Atlantik::initEventLog() +{ + m_eventLog = new EventLog(); +} + +void Atlantik::initNetworkObject() +{ + if (m_atlantikNetwork) + { + m_atlantikNetwork->reset(); + return; + } + + m_atlantikNetwork = new AtlantikNetwork(m_atlanticCore); + connect(m_atlantikNetwork, SIGNAL(msgInfo(QString)), this, SLOT(slotMsgInfo(QString))); + connect(m_atlantikNetwork, SIGNAL(msgError(QString)), this, SLOT(slotMsgError(QString))); + connect(m_atlantikNetwork, SIGNAL(msgStatus(const QString &, const QString &)), this, SLOT(slotMsgStatus(const QString &, const QString &))); + connect(m_atlantikNetwork, SIGNAL(msgChat(QString, QString)), this, SLOT(slotMsgChat(QString, QString))); + + connect(m_atlantikNetwork, SIGNAL(connectionSuccess()), this, SLOT(slotNetworkConnected())); + connect(m_atlantikNetwork, SIGNAL(connectionFailed(int)), this, SLOT(slotNetworkError(int))); + connect(m_atlantikNetwork, SIGNAL(closed(int)), this, SLOT(networkClosed(int))); + + connect(m_atlantikNetwork, SIGNAL(receivedHandshake()), this, SLOT(sendHandshake())); + + connect(m_atlantikNetwork, SIGNAL(gameConfig()), this, SLOT(showSelectConfiguration())); + connect(m_atlantikNetwork, SIGNAL(gameInit()), this, SLOT(initBoard())); + connect(m_atlantikNetwork, SIGNAL(gameRun()), this, SLOT(showBoard())); + connect(m_atlantikNetwork, SIGNAL(gameEnd()), this, SLOT(freezeBoard())); + + connect(m_atlantikNetwork, SIGNAL(newEstate(Estate *)), this, SLOT(newEstate(Estate *))); + connect(m_atlantikNetwork, SIGNAL(newAuction(Auction *)), this, SLOT(newAuction(Auction *))); + + connect(m_atlantikNetwork, SIGNAL(clientCookie(QString)), this, SLOT(clientCookie(QString))); + connect(m_atlantikNetwork, SIGNAL(networkEvent(const QString &, const QString &)), m_eventLog, SLOT(addEvent(const QString &, const QString &))); + + connect(this, SIGNAL(rollDice()), m_atlantikNetwork, SLOT(rollDice())); + connect(this, SIGNAL(buyEstate()), m_atlantikNetwork, SLOT(buyEstate())); + connect(this, SIGNAL(auctionEstate()), m_atlantikNetwork, SLOT(auctionEstate())); + connect(this, SIGNAL(endTurn()), m_atlantikNetwork, SLOT(endTurn())); + connect(this, SIGNAL(jailCard()), m_atlantikNetwork, SLOT(jailCard())); + connect(this, SIGNAL(jailPay()), m_atlantikNetwork, SLOT(jailPay())); + connect(this, SIGNAL(jailRoll()), m_atlantikNetwork, SLOT(jailRoll())); +} + +void Atlantik::clientCookie(QString cookie) +{ + KConfig *config = kapp->config(); + + if (cookie.isNull()) + { + if (config->hasGroup("Reconnection")) + config->deleteGroup("Reconnection", true); + } + else if (m_atlantikNetwork) + { + config->setGroup("Reconnection"); + config->writeEntry("Host", m_atlantikNetwork->host()); + config->writeEntry("Port", m_atlantikNetwork->port()); + config->writeEntry("Cookie", cookie); + } + else + return; + + config->sync(); +} + +void Atlantik::sendHandshake() +{ + m_atlantikNetwork->setName(m_config.playerName); + m_atlantikNetwork->setImage(m_config.playerImage); + + // Check command-line args to see if we need to auto-join + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + QCString game = args->getOption("game"); + if (!game.isNull()) + m_atlantikNetwork->joinGame(game.toInt()); +} + +void Atlantik::statusBarClick(int item) +{ + if ( item == 0 ) + { + KAboutApplication dialog(kapp->aboutData(), this); + dialog.exec(); + } + else if ( item == 1) + showEventLog(); +} + +PortfolioView *Atlantik::addPortfolioView(Player *player) +{ + PortfolioView *portfolioView = new PortfolioView(m_atlanticCore, player, m_config.activeColor, m_config.inactiveColor, m_portfolioWidget); + m_portfolioViews.append(portfolioView); + if ( m_portfolioViews.count() > 0 && m_portfolioScroll->isHidden() ) + m_portfolioScroll->show(); + + connect(player, SIGNAL(changed(Player *)), portfolioView, SLOT(playerChanged())); + connect(portfolioView, SIGNAL(newTrade(Player *)), m_atlantikNetwork, SLOT(newTrade(Player *))); + connect(portfolioView, SIGNAL(kickPlayer(Player *)), m_atlantikNetwork, SLOT(kickPlayer(Player *))); + connect(portfolioView, SIGNAL(estateClicked(Estate *)), m_board, SLOT(prependEstateDetails(Estate *))); + + m_portfolioLayout->addWidget(portfolioView); + portfolioView->show(); + + return portfolioView; +} + +PortfolioView *Atlantik::findPortfolioView(Player *player) +{ + PortfolioView *portfolioView = 0; + for (QPtrListIterator it(m_portfolioViews); (portfolioView = *it) ; ++it) + if (player == portfolioView->player()) + return portfolioView; + + return 0; +} + +void Atlantik::closeEvent(QCloseEvent *e) +{ + Game *gameSelf = m_atlanticCore->gameSelf(); + Player *playerSelf = m_atlanticCore->playerSelf(); + + int result = KMessageBox::Continue; + if ( gameSelf && !playerSelf->isBankrupt() && m_runningGame ) + result = KMessageBox::warningContinueCancel( this, i18n("You are currently part of an active game. Are you sure you want to close Atlantik? If you do, you forfeit the game."), i18n("Close & Forfeit?"), i18n("Close && Forfeit") ); + + if ( result == KMessageBox::Continue ) + { + if ( m_atlantikNetwork ) + m_atlantikNetwork->leaveGame(); + + saveMainWindowSettings(kapp->config(), "AtlantikMainWindow"); + KMainWindow::closeEvent(e); + } +} diff --git a/atlantik/client/atlantik.h b/atlantik/client/atlantik.h new file mode 100644 index 00000000..94f2ca7c --- /dev/null +++ b/atlantik/client/atlantik.h @@ -0,0 +1,268 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_ATLANTIK_H +#define ATLANTIK_ATLANTIK_H + +#include +#include +#include +#include +#include + +#include + +#include "configdlg.h" +#include "portfolioview.h" +#include "board.h" + +class QScrollView; + +class AtlanticCore; +class AtlantikNetwork; + +struct AtlantikConfig +{ + // General options; + bool chatTimestamps; + + // Personalization options + QString playerName, playerImage; + + // Board options + bool indicateUnowned; + bool highliteUnowned; + bool darkenMortgaged; + bool quartzEffects; + bool animateTokens; + + // Meta server options + bool connectOnStart; + bool hideDevelopmentServers; + + // Portfolio colors + QColor activeColor, inactiveColor; +}; + +class EventLog; +class EventLogWidget; +class SelectServer; +class SelectGame; +class SelectConfiguration; +class TradeDisplay; + +class Player; +class Estate; +class Trade; + +class LogTextEdit : public QTextEdit +{ +Q_OBJECT + +public: + LogTextEdit( QWidget *parent = 0, const char *name = 0 ); + virtual ~LogTextEdit(); + + QPopupMenu *createPopupMenu( const QPoint & pos ); + +private: + KAction *m_clear, *m_selectAll, *m_copy; +}; + +/** + * Main Atlantik window. + * Manages gameboard, portfolios and pretty much everything else. + * + * @author Rob Kaper + */ +class Atlantik : public KMainWindow +{ +Q_OBJECT + +public: + /** + * Create an Atlantik window. + * + */ + Atlantik(); + + /** + * Read the configuration settings using KConfig. + * + */ + void readConfig(); + + /** + * Appends a message the text view. + * + * @param msg Message to be appended. + */ + void serverMsgsAppend(QString msg); + + AtlantikConfig config() { return m_config; } + +private slots: + void showSelectServer(); + void showSelectGame(); + void showSelectConfiguration(); + void initBoard(); + void showBoard(); + void freezeBoard(); + void clientCookie(QString cookie); + void sendHandshake(); + void statusBarClick(int); + +public slots: + + /** + * A network connection has been established, so we can show the game + * list instead of the server list. + * + */ + void slotNetworkConnected(); + + /** + * An error occurred while setting up the network connection. Inform the + * user. + * + * @param errno See http://doc.trolltech.com/3.0/qsocket.html#Error-enum + */ + void slotNetworkError(int errnum); + + void networkClosed(int status); + + /** + * Creates a new modeless configure dialog or raises it when it already exists. + * + */ + void slotConfigure(); + + /** + * Opens the event log widget. + * + */ + void showEventLog(); + + /** + * Opens the KNotify dialog for configuration events. + * + */ + void configureNotifications(); + + /** + * Reads values from configuration dialog and stores them into + * global configuration struct. If values have changed, appropriate + * methods within the application are called. Configuration is saved + * to file in any case. + * + */ + void slotUpdateConfig(); + + /** + * Writes the contents of the text input field to the network + * interface and clears the text input field. + * + */ + void slotSendMsg(); + + /** + * Informs serverMsgs() to append an incoming message from the + * server to the text view as informational message. + * + * @param msg The message to be appended. + */ + void slotMsgInfo(QString msg); + + void slotMsgStatus(const QString &message, const QString &icon = QString::null); + + /** + * Informs serverMsgs() to append an incoming message from the + * server to the text view as error message. + * + * @param msg The error message to be appended. + */ + void slotMsgError(QString msg); + + /** + * Informs serverMsgs() to append an incoming message from the + * server to the text view as chat message. + * + * @param player The name of the player chatting. + * @param msg The chat message to be appended. + */ + void slotMsgChat(QString player, QString msg); + + void newPlayer(Player *player); + void newEstate(Estate *estate); + void newTrade(Trade *trade); + void newAuction(Auction *auction); + + void removeGUI(Player *player); + void removeGUI(Trade *trade); + + void playerChanged(Player *player); + void gainedTurn(); + +signals: + void rollDice(); + void buyEstate(); + void auctionEstate(); + void endTurn(); + void jailCard(); + void jailPay(); + void jailRoll(); + +protected: + void closeEvent(QCloseEvent *); + +private: + void initEventLog(); + void initNetworkObject(); + PortfolioView *addPortfolioView(Player *player); + PortfolioView *findPortfolioView(Player *player); + + QScrollView *m_portfolioScroll; + QWidget *m_mainWidget, *m_portfolioWidget; + QGridLayout *m_mainLayout; + QVBoxLayout *m_portfolioLayout; + + QLabel *m_portfolioLabel; + QLineEdit *m_input; + QTextEdit *m_serverMsgs; + + KAction *m_roll, *m_buyEstate, *m_auctionEstate, *m_endTurn, + *m_jailCard, *m_jailPay, *m_jailRoll, *m_configure, + *m_showEventLog; + + AtlanticCore *m_atlanticCore; + AtlantikNetwork *m_atlantikNetwork; + AtlantikConfig m_config; + + ConfigDialog *m_configDialog; + AtlantikBoard *m_board; + SelectServer *m_selectServer; + SelectGame *m_selectGame; + SelectConfiguration *m_selectConfiguration; + EventLog *m_eventLog; + EventLogWidget *m_eventLogWidget; + + QPtrList m_portfolioViews; + QMap m_tradeGUIMap; + + bool m_runningGame; +}; + +#endif diff --git a/atlantik/client/configdlg.cpp b/atlantik/client/configdlg.cpp new file mode 100644 index 00000000..a8e152b5 --- /dev/null +++ b/atlantik/client/configdlg.cpp @@ -0,0 +1,334 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include +#include +#include + +#include +#undef KDE_3_1_FEATURES +#undef KDE_3_3_FEATURES +#if defined(KDE_MAKE_VERSION) +#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,0) +#define KDE_3_1_FEATURES +#endif +#if KDE_VERSION >= KDE_MAKE_VERSION(3,2,90) +#define KDE_3_3_FEATURES +#endif +#endif + +#include +#include +#include +#include +#include + +#include "atlantik.h" +#include "configdlg.moc" + +ConfigDialog::ConfigDialog(Atlantik* parent, const char *name) : KDialogBase(IconList, i18n("Configure Atlantik"), Ok|Cancel, Ok, parent, "config_atlantik", false, name) +{ + m_parent = parent; + p_general = addPage(i18n("General"), i18n("General"), BarIcon("configure", KIcon::SizeMedium)); + p_p13n = addPage(i18n("Personalization"), i18n("Personalization"), BarIcon("personal", KIcon::SizeMedium)); + p_board = addPage(i18n("Board"), i18n("Board"), BarIcon("monop_board", KIcon::SizeMedium)); + p_monopigator = addPage(i18n("Meta Server"), i18n("Meta Server"), BarIcon("network", KIcon::SizeMedium)); + + configGeneral = new ConfigGeneral(this, p_general, "configGeneral"); + configPlayer = new ConfigPlayer(this, p_p13n, "configPlayer"); + configBoard = new ConfigBoard(this, p_board, "configBoard"); + configMonopigator = new ConfigMonopigator(this, p_monopigator, "configMonopigator"); + + setMinimumSize(sizeHint()); +} + +bool ConfigDialog::chatTimestamps() +{ + return configGeneral->chatTimestamps(); +} + +bool ConfigDialog::indicateUnowned() +{ + return configBoard->indicateUnowned(); +} + +bool ConfigDialog::highliteUnowned() +{ + return configBoard->highliteUnowned(); +} + +bool ConfigDialog::darkenMortgaged() +{ + return configBoard->darkenMortgaged(); +} + +bool ConfigDialog::animateToken() +{ + return configBoard->animateToken(); +} + +bool ConfigDialog::quartzEffects() +{ + return configBoard->quartzEffects(); +} + +QString ConfigDialog::playerName() +{ + return configPlayer->playerName(); +} + +QString ConfigDialog::playerImage() +{ + return configPlayer->playerImage(); +} + +bool ConfigDialog::connectOnStart() +{ + return configMonopigator->connectOnStart(); +} + +bool ConfigDialog::hideDevelopmentServers() +{ + return configMonopigator->hideDevelopmentServers(); +} + +AtlantikConfig ConfigDialog::config() +{ + return m_parent->config(); +} + +ConfigPlayer::ConfigPlayer(ConfigDialog* configDialog, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_configDialog = configDialog; + QVBoxLayout *layout = new QVBoxLayout(parent, KDialog::marginHint(), KDialog::spacingHint()); + + QLabel *label = new QLabel(i18n("Player name:"), parent); + layout->addWidget(label); + + m_playerName = new QLineEdit(parent); + layout->addWidget(m_playerName); + + QLabel *label2 = new QLabel(i18n("Player image:"), parent); + layout->addWidget(label2); + + m_playerIcon = new KPushButton(parent, "playerIcon"); + layout->addWidget(m_playerIcon); + + connect( m_playerIcon, SIGNAL(clicked()), this, SLOT(chooseImage()) ); + + layout->addStretch(1); + + reset(); +} + +QString ConfigPlayer::playerName() +{ + return m_playerName->text(); +} + +QString ConfigPlayer::playerImage() +{ + return m_playerImage; +} +void ConfigPlayer::chooseImage() +{ + KIconDialog iconDialog( this, "iconDialog" ); +#ifdef KDE_3_1_FEATURES + iconDialog.setCustomLocation( locate("appdata", "themes/default/tokens/") ); +#endif + +#ifdef KDE_3_3_FEATURES + iconDialog.setup( KIcon::Desktop, KIcon::Application, false, 0, true, true, true ); // begin with user icons, lock editing +#else + iconDialog.setup( KIcon::Desktop, KIcon::Application, false, 0, true ); // begin with user icons +#endif + + QString image = iconDialog.openDialog(); + + if ( image.isEmpty() ) + return; + + QStringList splitPath = QStringList::split( '/', image ); + m_playerImage = splitPath[ splitPath.count()-1 ]; + + setImage(); +} + +void ConfigPlayer::setImage() +{ + QString filename = locate("data", "atlantik/themes/default/tokens/" + m_playerImage); + if (KStandardDirs::exists(filename)) + m_playerIcon->setPixmap( QPixmap(filename) ); +} + +void ConfigPlayer::reset() +{ + m_playerName->setText(m_configDialog->config().playerName); + m_playerImage = m_configDialog->config().playerImage; + setImage(); +} + +ConfigMonopigator::ConfigMonopigator(ConfigDialog *configDialog, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_configDialog = configDialog; + QVBoxLayout *layout = new QVBoxLayout(parent, KDialog::marginHint(), KDialog::spacingHint()); + + m_connectOnStart = new QCheckBox(i18n("Request list of Internet servers on start-up"), parent); + layout->addWidget(m_connectOnStart); + + QString message=i18n( + "If checked, Atlantik connects to a meta server on start-up to\n" + "request a list of Internet servers.\n"); + QWhatsThis::add(m_connectOnStart, message); + + m_hideDevelopmentServers = new QCheckBox(i18n("Hide development servers"), parent); + layout->addWidget(m_hideDevelopmentServers); + + message=i18n( + "Some of the Internet servers might be running development\n" + "versions of the server software. If checked, Atlantik will not\n" + "display these servers.\n"); + QWhatsThis::add(m_hideDevelopmentServers, message); + + layout->addStretch(1); + + reset(); +} + +bool ConfigMonopigator::connectOnStart() +{ + return m_connectOnStart->isChecked(); +} + +bool ConfigMonopigator::hideDevelopmentServers() +{ + return m_hideDevelopmentServers->isChecked(); +} + +void ConfigMonopigator::reset() +{ + m_connectOnStart->setChecked(m_configDialog->config().connectOnStart); + m_hideDevelopmentServers->setChecked(m_configDialog->config().hideDevelopmentServers); +} + +ConfigGeneral::ConfigGeneral(ConfigDialog *configDialog, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_configDialog = configDialog; + QVBoxLayout *layout = new QVBoxLayout(parent, KDialog::marginHint(), KDialog::spacingHint()); + + m_chatTimestamps = new QCheckBox(i18n("Show timestamps in chat messages"), parent); + layout->addWidget(m_chatTimestamps); + + QString message=i18n( + "If checked, Atlantik will add timestamps in front of chat\n" + "messages.\n"); + QWhatsThis::add(m_chatTimestamps, message); + + layout->addStretch(1); + + reset(); +} + +bool ConfigGeneral::chatTimestamps() +{ + return m_chatTimestamps->isChecked(); +} + +void ConfigGeneral::reset() +{ + m_chatTimestamps->setChecked(m_configDialog->config().chatTimestamps); +} + +ConfigBoard::ConfigBoard(ConfigDialog *configDialog, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_configDialog = configDialog; + QVBoxLayout *layout = new QVBoxLayout(parent, KDialog::marginHint(), KDialog::spacingHint()); + + QGroupBox *box = new QGroupBox(1, Qt::Horizontal, i18n("Game Status Feedback"), parent); + layout->addWidget(box); + + m_indicateUnowned = new QCheckBox(i18n("Display title deed card on unowned properties"), box); + QString message=i18n( + "If checked, unowned properties on the board display an estate\n" + "card to indicate the property is for sale.\n"); + QWhatsThis::add(m_indicateUnowned, message); + + m_highliteUnowned = new QCheckBox(i18n("Highlight unowned properties"), box); + message=i18n( + "If checked, unowned properties on the board are highlighted to\n" + "indicate the property is for sale.\n"); + QWhatsThis::add(m_highliteUnowned, message); + + m_darkenMortgaged = new QCheckBox(i18n("Darken mortgaged properties"), box); + message=i18n( + "If checked, mortgaged properties on the board will be colored\n" + "darker than of the default color.\n"); + QWhatsThis::add(m_darkenMortgaged, message); + + m_animateToken = new QCheckBox(i18n("Animate token movement"), box); + message=i18n( + "If checked, tokens will move across the board\n" + "instead of jumping directly to their new location.\n"); + QWhatsThis::add(m_animateToken, message); + + m_quartzEffects = new QCheckBox(i18n("Quartz effects"), box); + message=i18n( + "If checked, the colored headers of street estates on the board " + "will have a Quartz effect similar to the Quartz KWin style.\n"); + QWhatsThis::add(m_quartzEffects, message); + +// box = new QGroupBox(1, Qt::Horizontal, i18n("Size"), parent); +// layout->addWidget(box); + + layout->addStretch(1); + + reset(); +} + +bool ConfigBoard::indicateUnowned() +{ + return m_indicateUnowned->isChecked(); +} + +bool ConfigBoard::highliteUnowned() +{ + return m_highliteUnowned->isChecked(); +} + +bool ConfigBoard::darkenMortgaged() +{ + return m_darkenMortgaged->isChecked(); +} + +bool ConfigBoard::animateToken() +{ + return m_animateToken->isChecked(); +} + +bool ConfigBoard::quartzEffects() +{ + return m_quartzEffects->isChecked(); +} + +void ConfigBoard::reset() +{ + m_indicateUnowned->setChecked(m_configDialog->config().indicateUnowned); + m_highliteUnowned->setChecked(m_configDialog->config().highliteUnowned); + m_darkenMortgaged->setChecked(m_configDialog->config().darkenMortgaged); + m_animateToken->setChecked(m_configDialog->config().animateTokens); + m_quartzEffects->setChecked(m_configDialog->config().quartzEffects); +} diff --git a/atlantik/client/configdlg.h b/atlantik/client/configdlg.h new file mode 100644 index 00000000..c1f74294 --- /dev/null +++ b/atlantik/client/configdlg.h @@ -0,0 +1,139 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_CONFIGDLG_H +#define ATLANTIK_CONFIGDLG_H + +#include +#include +#include + +#include + +class QString; + +class KPushButton; + +class Atlantik; +class ConfigDialog; + +struct AtlantikConfig; + +class ConfigPlayer : public QWidget +{ +Q_OBJECT + +public: + ConfigPlayer(ConfigDialog *configDialog, QWidget *parent, const char *name=0); + + QString playerName(); + QString playerImage(); + +private slots: + void chooseImage(); + +private: + void setImage(); + void reset(); + + ConfigDialog *m_configDialog; + QLineEdit *m_playerName; + QString m_playerImage; + KPushButton *m_playerIcon; +}; + +class ConfigBoard : public QWidget +{ +Q_OBJECT + +public: + ConfigBoard(ConfigDialog *configDialog, QWidget *parent, const char *name=0); + + bool indicateUnowned(); + bool highliteUnowned(); + bool darkenMortgaged(); + bool animateToken(); + bool quartzEffects(); + +private: + void reset(); + + ConfigDialog *m_configDialog; + QCheckBox *m_indicateUnowned, *m_highliteUnowned, *m_darkenMortgaged, *m_animateToken, *m_quartzEffects; +}; + +class ConfigMonopigator : public QWidget +{ +Q_OBJECT + +public: + ConfigMonopigator(ConfigDialog *dialog, QWidget *parent, const char *name = 0); + + bool connectOnStart(); + bool hideDevelopmentServers(); + +private: + void reset(); + + ConfigDialog *m_configDialog; + QCheckBox *m_connectOnStart, *m_hideDevelopmentServers; +}; + +class ConfigGeneral : public QWidget +{ +Q_OBJECT + +public: + ConfigGeneral(ConfigDialog *dialog, QWidget *parent, const char *name = 0); + + bool chatTimestamps(); + +private: + void reset(); + + ConfigDialog *m_configDialog; + QCheckBox *m_chatTimestamps; +}; + +class ConfigDialog : public KDialogBase +{ +Q_OBJECT + +public: + ConfigDialog(Atlantik *parent, const char *name=0); + + bool chatTimestamps(); + bool indicateUnowned(); + bool highliteUnowned(); + bool darkenMortgaged(); + bool animateToken(); + bool quartzEffects(); + AtlantikConfig config(); + QString playerName(); + QString playerImage(); + bool connectOnStart(); + bool hideDevelopmentServers(); + +private: + Atlantik *m_parent; + QFrame *p_general, *p_p13n, *p_board, *p_monopigator; + ConfigPlayer *configPlayer; + ConfigBoard *configBoard; + ConfigMonopigator *configMonopigator; + ConfigGeneral *configGeneral; +}; + +#endif diff --git a/atlantik/client/event.cpp b/atlantik/client/event.cpp new file mode 100644 index 00000000..218f6b87 --- /dev/null +++ b/atlantik/client/event.cpp @@ -0,0 +1,42 @@ +// Copyright (c) 2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include + +#include "event.moc" + +Event::Event(const QDateTime &dateTime, const QString &description, const QString &icon) +{ + m_dateTime = dateTime; + m_description = description; + m_icon = icon; +} + +QDateTime Event::dateTime() const +{ + return m_dateTime; +} + +QString Event::description() const +{ + return m_description; +} + +QString Event::icon() const +{ + return m_icon; +} diff --git a/atlantik/client/event.h b/atlantik/client/event.h new file mode 100644 index 00000000..f2f56444 --- /dev/null +++ b/atlantik/client/event.h @@ -0,0 +1,40 @@ +// Copyright (c) 2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_EVENT_H +#define ATLANTIK_EVENT_H + +#include + +class QDateTime; +class QString; + +class Event : public QObject +{ +Q_OBJECT + +public: + Event(const QDateTime &dateTime, const QString &description, const QString &icon = QString::null); + QDateTime dateTime() const; + QString description() const; + QString icon() const; + +private: + QDateTime m_dateTime; + QString m_description, m_icon; +}; + +#endif diff --git a/atlantik/client/eventlogwidget.cpp b/atlantik/client/eventlogwidget.cpp new file mode 100644 index 00000000..b0f77ab8 --- /dev/null +++ b/atlantik/client/eventlogwidget.cpp @@ -0,0 +1,123 @@ +// Copyright (c) 2003-2004 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "event.h" +#include "eventlogwidget.moc" + +EventLog::EventLog() +{ +} + +void EventLog::addEvent(const QString &description, const QString &icon) +{ + Event *event = new Event(QDateTime::currentDateTime(), description, icon); + m_events.append(event); + emit newEvent(event); +} + +QPtrList EventLog::events() +{ + return m_events; +} + +EventLogWidget::EventLogWidget(EventLog *eventLog, QWidget *parent, const char *name) + : QWidget(parent, name, + WType_Dialog | WStyle_Customize | WStyle_DialogBorder | WStyle_Title | + WStyle_Minimize | WStyle_ContextHelp ) +{ + m_eventLog = eventLog; + + connect(m_eventLog, SIGNAL(newEvent(Event *)), this, SLOT(addEvent(Event *))); + + setCaption(i18n("Event Log")); + + QVBoxLayout *listCompBox = new QVBoxLayout(this, KDialog::marginHint()); + + m_eventList = new KListView(this, "eventList"); + listCompBox->addWidget(m_eventList); + + m_eventList->addColumn(i18n("Date/Time")); + m_eventList->addColumn(i18n("Description")); + m_eventList->header()->setClickEnabled( false ); + + QHBoxLayout *actionBox = new QHBoxLayout(this, 0, KDialog::spacingHint()); + listCompBox->addItem(actionBox); + + actionBox->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + m_saveButton = new KPushButton(BarIcon("filesave", KIcon::SizeSmall), i18n("&Save As..."), this); + actionBox->addWidget(m_saveButton); + + connect(m_saveButton, SIGNAL(clicked()), this, SLOT(save())); + + // Populate + QPtrList events = m_eventLog->events(); + for (QPtrListIterator it( events ); (*it) ; ++it) + addEvent( (*it) ); +} + +void EventLogWidget::addEvent(Event *event) +{ + // FIXME: allow a way to view non-squeezed message + // FIXME: allow a way to show older messages + + if ( m_eventList->childCount() >= 25 ) + delete m_eventList->firstChild(); + + QString description = KStringHandler::rsqueeze( event->description(), 200 ); + KListViewItem *item = new KListViewItem(m_eventList, event->dateTime().toString("yyyy-MM-dd hh:mm:ss zzz"), description); + if (event->icon().isEmpty()) + item->setPixmap(1, QPixmap(SmallIcon("atlantik"))); + else + item->setPixmap(1, QPixmap(SmallIcon(event->icon()))); + + m_eventList->ensureItemVisible(item); +} + +void EventLogWidget::closeEvent(QCloseEvent *e) +{ + e->accept(); +} + +void EventLogWidget::save() +{ + QFile file( KFileDialog::getSaveFileName() ); + if ( file.open( IO_WriteOnly ) ) + { + QTextStream stream(&file); + + stream << i18n( "Atlantik log file, saved at %1." ).arg( QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") ) << endl; + + QPtrList events = m_eventLog->events(); + for (QPtrListIterator it( events ); (*it) ; ++it) + stream << (*it)->dateTime().toString("yyyy-MM-dd hh:mm:ss") << " " << (*it)->description() << endl; + file.close(); + } +} diff --git a/atlantik/client/eventlogwidget.h b/atlantik/client/eventlogwidget.h new file mode 100644 index 00000000..4b925d47 --- /dev/null +++ b/atlantik/client/eventlogwidget.h @@ -0,0 +1,73 @@ +// Copyright (c) 2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_EVENTLOGWIDGET_H +#define ATLANTIK_EVENTLOGWIDGET_H + +#include +#include + +class QString; + +class Event; + +class KListView; +class KListViewItem; +class KPushButton; + +class EventLog : public QObject +{ +Q_OBJECT + +public: + EventLog(); + QPtrList events(); + +public slots: + void addEvent(const QString &description, const QString &icon = QString::null); + +signals: + void newEvent(Event *event); + +private: + QPtrList m_events; +}; + +class EventLogWidget : public QWidget +{ +Q_OBJECT + +public: + enum EventLogType { Default, Net_In, Net_Out }; + + EventLogWidget(EventLog *eventLog, QWidget *parent=0, const char *name = 0); + +public slots: + void addEvent(Event *event); + +protected: + void closeEvent(QCloseEvent *e); + +private slots: + void save(); + +private: + EventLog *m_eventLog; + KListView *m_eventList; + KPushButton *m_saveButton; +}; + +#endif diff --git a/atlantik/client/main.cpp b/atlantik/client/main.cpp new file mode 100644 index 00000000..5f0e2fc6 --- /dev/null +++ b/atlantik/client/main.cpp @@ -0,0 +1,78 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include +#include +#include +#include + +#include "main.h" +#include "atlantik.h" + +static KCmdLineOptions options[] = +{ + { "h", 0, 0 }, + { "host ", I18N_NOOP("Connect to this host"), 0 }, + { "p", 0, 0 }, + { "port ", I18N_NOOP("Connect at this port"), "1234" }, + { "g", 0, 0 }, + { "game ", I18N_NOOP("Join this game"), 0 }, + KCmdLineLastOption +}; + +int main(int argc, char *argv[]) +{ + KAboutData aboutData( + "atlantik", + I18N_NOOP("Atlantik"), ATLANTIK_VERSION_STRING, + I18N_NOOP("The Atlantic board game"), + KAboutData::License_GPL, + I18N_NOOP("(c) 1998-2004 Rob Kaper"), + I18N_NOOP("KDE client for playing Monopoly-like games on the monopd network."), + "http://unixcode.org/atlantik/" + ); + + aboutData.addAuthor("Rob Kaper", I18N_NOOP("main author"), "cap@capsi.com", "http://capsi.com/"); + + // Patches and artists + aboutData.addCredit("Thiago Macieira", I18N_NOOP("KExtendedSocket support"), "thiagom@wanadoo.fr"); + aboutData.addCredit("Albert Astals Cid", I18N_NOOP("various patches"), "tsdgeos@terra.es"); + + aboutData.addCredit("Bart Szyszka", I18N_NOOP("application icon"), "bart@gigabee.com", "http://www.gigabee.com/"); + aboutData.addCredit("Rob Malda", I18N_NOOP("token icons"), "", "http://cmdrtaco.net/"); + aboutData.addCredit("Elhay Achiam", I18N_NOOP("icons"), "elhay_a@bezeqint.net"); + aboutData.addCredit("Carlo Caneva", I18N_NOOP("icons"), "webmaster@molecola.com", "http://www.molecola.com/"); + + KCmdLineArgs::init(argc, argv, &aboutData); + KCmdLineArgs::addCmdLineOptions (options); + + KApplication::addCmdLineOptions(); + KApplication kapplication; + KGlobal::locale()->insertCatalogue("libkdegames"); + + if (kapplication.isRestored()) + RESTORE(Atlantik) + else + { + Atlantik *atlantik = new Atlantik; + atlantik->setMinimumSize(640, 480); + atlantik->setCaption(i18n("The Atlantic Board Game")); + atlantik->show(); + } + + return kapplication.exec(); +} diff --git a/atlantik/client/main.h b/atlantik/client/main.h new file mode 100644 index 00000000..4c2f00c6 --- /dev/null +++ b/atlantik/client/main.h @@ -0,0 +1,29 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_MAIN_H +#define ATLANTIK_MAIN_H + +#define ATLANTIK_VERSION 075 +#define ATLANTIK_VERSION_STRING "0.7.5" + +#define ATLANTIK_VERSION_MAJOR 0 +#define ATLANTIK_VERSION_MINOR 7 +#define ATLANTIK_VERSION_RELEASE 5 + +int main(int, char *[]); + +#endif diff --git a/atlantik/client/monopigator.cpp b/atlantik/client/monopigator.cpp new file mode 100644 index 00000000..83ef0d42 --- /dev/null +++ b/atlantik/client/monopigator.cpp @@ -0,0 +1,164 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include +#include + +#include + +#include "monopigator.moc" +#include "main.h" + +Monopigator::Monopigator() +{ + m_downloadData = 0; + m_job = 0; + m_timer = 0; +} + +Monopigator::~Monopigator() +{ + if (m_job) + m_job -> kill(); + delete m_downloadData; + m_downloadData=0L; +} + +void Monopigator::loadData(const KURL &url) +{ + delete m_downloadData; + m_downloadData = new QBuffer(); + m_downloadData->open(IO_WriteOnly); + m_downloadData->reset(); + + m_job = KIO::get(url, true, false); + m_job->addMetaData(QString::fromLatin1("UserAgent"), QString::fromLatin1("Atlantik/" ATLANTIK_VERSION_STRING)); + + if (!m_timer) + { + m_timer = new QTimer(this); + m_timer->start(10000, true); + } + + connect(m_job, SIGNAL(data(KIO::Job *, const QByteArray &)), SLOT(slotData(KIO::Job *, const QByteArray &))); + connect(m_job, SIGNAL(result(KIO::Job *)), SLOT(slotResult(KIO::Job *))); + connect(m_timer, SIGNAL(timeout()), SLOT(slotTimeout())); +} + +void Monopigator::slotData(KIO::Job *, const QByteArray &data) +{ + m_timer->stop(); + m_downloadData->writeBlock(data.data(), data.size()); +} + +void Monopigator::slotResult(KIO::Job *job) +{ + processData(m_downloadData->buffer(), !job->error()); + m_job = 0; +} + +void Monopigator::slotTimeout() +{ + if (m_job) + m_job -> kill(); + m_job = 0; + + emit timeout(); +} + +void Monopigator::processData(const QByteArray &data, bool okSoFar) +{ + if (okSoFar) + { + QString xmlData(data); + QDomDocument domDoc; + if (domDoc.setContent(xmlData)) + { + QDomElement eTop = domDoc.documentElement(); + if (eTop.tagName() != "monopigator") + return; + + QDomNode n = eTop.firstChild(); + while(!n.isNull()) + { + QDomElement e = n.toElement(); + if(!e.isNull()) + { + if (e.tagName() == "server") + emit monopigatorAdd(e.attributeNode(QString("ip")).value(), e.attributeNode(QString("host")).value(), e.attributeNode(QString("port")).value(), e.attributeNode(QString("version")).value(), e.attributeNode(QString("users")).value().toInt()); + } + n = n.nextSibling(); + } + emit finished(); + } + } +} + +MonopigatorEntry::MonopigatorEntry(QListView *parent, QString host, QString latency, QString version, QString users, QString port, QString ip) : QObject(), QListViewItem(parent, host, latency, version, users, port) +{ + m_isDev = ( version.find( QRegExp("(CVS|-dev)") ) != -1 ) ? true : false; + + setEnabled(false); + parent->sort(); + + if ( !ip.isEmpty() ) + host = ip; + m_latencySocket = new KExtendedSocket( host, port.toInt(), KExtendedSocket::inputBufferedSocket | KExtendedSocket::noResolve ); + connect(m_latencySocket, SIGNAL(lookupFinished(int)), this, SLOT(resolved())); + connect(m_latencySocket, SIGNAL(connectionSuccess()), this, SLOT(connected())); + m_latencySocket->startAsyncConnect(); +} + +void MonopigatorEntry::resolved() +{ + time.start(); +} + +void MonopigatorEntry::connected() +{ + setText( 1, QString::number(time.elapsed()) ); + setEnabled(true); + listView()->sort(); + delete m_latencySocket; +} + +int MonopigatorEntry::compare(QListViewItem *i, int col, bool ascending) const +{ + // Colums 1 and 3 are integers (latency and users) + if (col == 1 || col == 3) + { + int myVal = text(col).toInt(), iVal = i->text(col).toInt(); + if (myVal == iVal) + return 0; + else if (myVal > iVal) + return 1; + else + return -1; + } + return key( col, ascending ).compare( i->key( col, ascending) ); +} + +bool MonopigatorEntry::isDev() const +{ + return m_isDev; +} + +void MonopigatorEntry::showDevelopmentServers(bool show) +{ + if ( isVisible() != show ) + setVisible(show); +} diff --git a/atlantik/client/monopigator.h b/atlantik/client/monopigator.h new file mode 100644 index 00000000..1fd57b1b --- /dev/null +++ b/atlantik/client/monopigator.h @@ -0,0 +1,79 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_MONOPIGATOR_H +#define ATLANTIK_MONOPIGATOR_H + +#include +#include +#include +#include + +#include +#include + +class KExtendedSocket; +class QTime; + +class Monopigator : public QObject +{ +Q_OBJECT + +public: + Monopigator(); + ~Monopigator(); + void loadData(const KURL &); + +signals: + void monopigatorAdd(QString ip, QString host, QString port, QString version, int users); + void finished(); + void timeout(); + +private slots: + void slotData(KIO::Job *, const QByteArray &); + void slotResult(KIO::Job *); + void slotTimeout(); + +private: + void processData(const QByteArray &, bool = true); + + QBuffer *m_downloadData; + QTimer *m_timer; + KIO::Job *m_job; +}; + +class MonopigatorEntry : public QObject, public QListViewItem +{ +Q_OBJECT + +public: + MonopigatorEntry(QListView *parent, QString host, QString latency, QString version, QString users, QString port, QString ip); + int compare(QListViewItem *i, int col, bool ascending) const; + bool isDev() const; + +private slots: + void resolved(); + void connected(); + void showDevelopmentServers(bool show); + +private: + KExtendedSocket *m_latencySocket; + QTime time; + QListView *m_parent; + bool m_isDev; +}; + +#endif diff --git a/atlantik/client/selectconfiguration_widget.cpp b/atlantik/client/selectconfiguration_widget.cpp new file mode 100644 index 00000000..0e7d5cdb --- /dev/null +++ b/atlantik/client/selectconfiguration_widget.cpp @@ -0,0 +1,189 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "selectconfiguration_widget.moc" + +SelectConfiguration::SelectConfiguration(AtlanticCore *atlanticCore, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_atlanticCore = atlanticCore; + m_game = 0; + + m_mainLayout = new QVBoxLayout(this, KDialog::marginHint()); + Q_CHECK_PTR(m_mainLayout); + + // Game configuration. + m_configBox = new QVGroupBox(i18n("Game Configuration"), this, "configBox"); + m_mainLayout->addWidget(m_configBox); + + // Player buttons. + QHBoxLayout *playerButtons = new QHBoxLayout(m_mainLayout, KDialog::spacingHint()); + playerButtons->setMargin(0); + + playerButtons->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + // Vertical spacer. + m_mainLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); + + // Server buttons. + QHBoxLayout *serverButtons = new QHBoxLayout(m_mainLayout, KDialog::spacingHint()); + serverButtons->setMargin(0); + + m_backButton = new KPushButton(SmallIcon("back"), i18n("Leave Game"), this); + serverButtons->addWidget(m_backButton); + + connect(m_backButton, SIGNAL(clicked()), this, SIGNAL(leaveGame())); + + serverButtons->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + m_startButton = new KPushButton(SmallIconSet("forward"), i18n("Start Game"), this); + serverButtons->addWidget(m_startButton); + m_startButton->setEnabled(false); + + connect(m_startButton, SIGNAL(clicked()), this, SIGNAL(startGame())); + + Player *playerSelf = m_atlanticCore->playerSelf(); + playerChanged(playerSelf); + connect(playerSelf, SIGNAL(changed(Player *)), this, SLOT(playerChanged(Player *))); + + emit statusMessage(i18n("Retrieving configuration list...")); +} + +void SelectConfiguration::initGame() +{ + emit statusMessage(i18n("Game started. Retrieving full game data...")); +} + +void SelectConfiguration::addConfigOption(ConfigOption *configOption) +{ + // FIXME: only bool types supported! + QCheckBox *checkBox = new QCheckBox(configOption->description(), m_configBox, "checkbox"); + m_configMap[(QObject *)checkBox] = configOption; + m_configBoxMap[configOption] = checkBox; + + checkBox->setChecked( configOption->value().toInt() ); + checkBox->setEnabled( configOption->edit() && m_atlanticCore->selfIsMaster() ); + checkBox->show(); + + connect(checkBox, SIGNAL(clicked()), this, SLOT(changeOption())); + connect(configOption, SIGNAL(changed(ConfigOption *)), this, SLOT(optionChanged(ConfigOption *))); +} + +void SelectConfiguration::gameOption(QString title, QString type, QString value, QString edit, QString command) +{ + // Find if option exists in GUI yet + if (QCheckBox *checkBox = dynamic_cast(m_checkBoxMap[command])) + { + checkBox->setChecked(value.toInt()); + checkBox->setEnabled(edit.toInt()); + return; + } + + // Create option + if (type == "bool") + { + QCheckBox *checkBox = new QCheckBox(title, m_configBox, "checkbox"); + m_optionCommandMap[(QObject *)checkBox] = command; + m_checkBoxMap[command] = checkBox; + checkBox->setChecked(value.toInt()); + checkBox->setEnabled(edit.toInt()); + checkBox->show(); + + connect(checkBox, SIGNAL(clicked()), this, SLOT(optionChanged())); + } + // TODO: create options other than type=bool + + // TODO: Enable edit for master only +} + +void SelectConfiguration::changeOption() +{ + ConfigOption *configOption = m_configMap[(QObject *)QObject::sender()]; + if (configOption) + { + kdDebug() << "checked " << ((QCheckBox *)QObject::sender())->isChecked() << endl; + emit changeOption( configOption->id(), QString::number( ((QCheckBox *)QObject::sender())->isChecked() ) ); + } +} + +void SelectConfiguration::optionChanged(ConfigOption *configOption) +{ + QCheckBox *checkBox = m_configBoxMap[configOption]; + if (checkBox) + { + checkBox->setText( configOption->description() ); + checkBox->setChecked( configOption->value().toInt() ); + checkBox->setEnabled( configOption->edit() && m_atlanticCore->selfIsMaster() ); + } +} + +void SelectConfiguration::optionChanged() +{ + QString command = m_optionCommandMap[(QObject *)QObject::sender()]; + + if (QCheckBox *checkBox = m_checkBoxMap[command]) + { + command.append(QString::number(checkBox->isChecked())); + emit buttonCommand(command); + } +} + +void SelectConfiguration::slotEndUpdate() +{ + emit statusMessage(i18n("Retrieved configuration list.")); +} + +void SelectConfiguration::playerChanged(Player *player) +{ + kdDebug() << "playerChanged" << endl; + + if (player->game() != m_game) + { + kdDebug() << "playerChanged::change" << endl; + + if (m_game) + disconnect(m_game, SIGNAL(changed(Game *)), this, SLOT(gameChanged(Game *))); + + m_game = player->game(); + + if (m_game) + connect(m_game, SIGNAL(changed(Game *)), this, SLOT(gameChanged(Game *))); + } +} + +void SelectConfiguration::gameChanged(Game *game) +{ + m_startButton->setEnabled( game->master() == m_atlanticCore->playerSelf() ); + + for (QMapIterator it = m_configBoxMap.begin() ; it != m_configBoxMap.end() ; ++it) + (*it)->setEnabled( it.key()->edit() && m_atlanticCore->selfIsMaster() ); +} diff --git a/atlantik/client/selectconfiguration_widget.h b/atlantik/client/selectconfiguration_widget.h new file mode 100644 index 00000000..033a0eb0 --- /dev/null +++ b/atlantik/client/selectconfiguration_widget.h @@ -0,0 +1,80 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_SELECTCONFIGURATION_WIDGET_H +#define ATLANTIK_SELECTCONFIGURATION_WIDGET_H + +#include +#include +#include + +#include +#include + +class QCheckBox; +class QListViewItem; + +class AtlanticCore; +class ConfigOption; +class Game; +class Player; + +class SelectConfiguration : public QWidget +{ +Q_OBJECT + +public: + SelectConfiguration(AtlanticCore *atlanticCore, QWidget *parent, const char *name=0); + + void setCanStart(const bool &canStart); + QString hostToConnect() const; + int portToConnect(); + +private slots: + void addConfigOption(ConfigOption *configOption); + void changeOption(); + void gameOption(QString title, QString type, QString value, QString edit, QString command); + void optionChanged(ConfigOption *configOption); + void optionChanged(); + void slotEndUpdate(); + void initGame(); + void playerChanged(Player *player); + void gameChanged(Game *game); + +signals: + void startGame(); + void leaveGame(); + void joinConfiguration(int configurationId); + void newConfiguration(); + void changeOption(int configId, const QString &value); + void buttonCommand(QString); + void iconSelected(const QString &); + void statusMessage(const QString &message); + +private: + QVBoxLayout *m_mainLayout; + QVGroupBox *m_configBox, *m_messageBox; + KPushButton *m_backButton, *m_startButton; + QMap m_optionCommandMap; + QMap m_configMap; + QMap m_configBoxMap; + QMap m_checkBoxMap; + QMap m_items; + Game *m_game; + AtlanticCore *m_atlanticCore; +}; + +#endif diff --git a/atlantik/client/selectgame_widget.cpp b/atlantik/client/selectgame_widget.cpp new file mode 100644 index 00000000..85d4f886 --- /dev/null +++ b/atlantik/client/selectgame_widget.cpp @@ -0,0 +1,192 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "selectgame_widget.h" + +SelectGame::SelectGame(AtlanticCore *atlanticCore, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_atlanticCore = atlanticCore; + + connect(m_atlanticCore, SIGNAL(createGUI(Game *)), this, SLOT(addGame(Game *))); + connect(m_atlanticCore, SIGNAL(removeGUI(Game *)), this, SLOT(delGame(Game *))); + + m_mainLayout = new QVBoxLayout(this, KDialog::marginHint()); + Q_CHECK_PTR(m_mainLayout); + + QVGroupBox *groupBox; + groupBox = new QVGroupBox(i18n("Create or Select monopd Game"), this, "groupBox"); + m_mainLayout->addWidget(groupBox); + + // List of games + m_gameList = new KListView(groupBox, "m_gameList"); + m_gameList->addColumn(i18n("Game")); + m_gameList->addColumn(i18n("Description")); + m_gameList->addColumn(i18n("Id")); + m_gameList->addColumn(i18n("Players")); + m_gameList->setAllColumnsShowFocus(true); +// m_mainLayout->addWidget(m_gameList); + + connect(m_gameList, SIGNAL(clicked(QListViewItem *)), this, SLOT(validateConnectButton())); + connect(m_gameList, SIGNAL(doubleClicked(QListViewItem *)), this, SLOT(connectClicked())); + connect(m_gameList, SIGNAL(rightButtonClicked(QListViewItem *, const QPoint &, int)), this, SLOT(validateConnectButton())); + connect(m_gameList, SIGNAL(selectionChanged(QListViewItem *)), this, SLOT(validateConnectButton())); + + QHBoxLayout *buttonBox = new QHBoxLayout(m_mainLayout, KDialog::spacingHint()); + + KPushButton *backButton = new KPushButton(SmallIcon("back"), i18n("Server List"), this); + buttonBox->addWidget(backButton); + + connect(backButton, SIGNAL(clicked()), this, SIGNAL(leaveServer())); + + buttonBox->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + m_connectButton = new KPushButton(SmallIconSet("forward"), i18n("Create Game"), this); + m_connectButton->setEnabled(false); + buttonBox->addWidget(m_connectButton); + + connect(m_connectButton, SIGNAL(clicked()), this, SLOT(connectClicked())); + +} + +void SelectGame::addGame(Game *game) +{ + connect(game, SIGNAL(changed(Game *)), this, SLOT(updateGame(Game *))); + + if (game->id() == -1) + { + QListViewItem *item = new QListViewItem( m_gameList, i18n("Create a new %1 Game").arg(game->name()), game->description(), QString::null, QString::null, game->type() ); + item->setPixmap(0, QPixmap(SmallIcon("filenew"))); + } + else + { + Player *master = game->master(); + QListViewItem *item = new QListViewItem( m_gameList, i18n("Join %1's %2 Game").arg( (master ? master->name() : QString::null), game->name() ), game->description(), QString::number(game->id()), QString::number(game->players()), game->type() ); + item->setPixmap( 0, QPixmap(SmallIcon("atlantik")) ); + item->setEnabled(game->canBeJoined()); + + KNotifyClient::event(winId(), "newgame"); + + connect(master, SIGNAL(changed(Player *)), this, SLOT(playerChanged(Player *))); + } + +// validateConnectButton(); +} + +void SelectGame::delGame(Game *game) +{ + QListViewItem *item = findItem(game); + if (!item) + return; + + delete item; + + validateConnectButton(); +} + +void SelectGame::updateGame(Game *game) +{ + QListViewItem *item = findItem(game); + if (!item) + return; + + item->setText( 1, game->description() ); + + if (game->id() == -1) + item->setText(0, i18n("Create a new %1 Game").arg(game->name())); + else + { + Player *master = game->master(); + item->setText( 0, i18n("Join %1's %2 Game").arg( (master ? master->name() : QString::null), game->name() ) ); + item->setText( 3, QString::number( game->players() ) ); + item->setEnabled( game->canBeJoined() ); + + connect(master, SIGNAL(changed(Player *)), this, SLOT(playerChanged(Player *))); + } + m_gameList->triggerUpdate(); + + validateConnectButton(); +} + +void SelectGame::playerChanged(Player *player) +{ + QListViewItem *item = m_gameList->firstChild(); + Game *game = 0; + + while (item) + { + game = m_atlanticCore->findGame( item->text(2).toInt() ); + if ( game && game->master() == player ) + { + item->setText( 0, i18n("Join %1's %2 Game").arg( player->name(), game->name() ) ); + return; + } + item = item->nextSibling(); + } +} + +QListViewItem *SelectGame::findItem(Game *game) +{ + QListViewItem *item = m_gameList->firstChild(); + while (item) + { + if ( (game->id() == -1 || item->text(2) == QString::number(game->id())) && item->text(4) == game->type() ) + return item; + + item = item->nextSibling(); + } + return 0; +} + +void SelectGame::validateConnectButton() +{ + if (QListViewItem *item = m_gameList->selectedItem()) + { + if (item->text(2).toInt() > 0) + m_connectButton->setText(i18n("Join Game")); + else + m_connectButton->setText(i18n("Create Game")); + + m_connectButton->setEnabled(true); + } + else + m_connectButton->setEnabled(false); +} + +void SelectGame::connectClicked() +{ + if (QListViewItem *item = m_gameList->selectedItem()) + { + if (int gameId = item->text(2).toInt()) + emit joinGame(gameId); + else + emit newGame(item->text(4)); + } +} + +#include "selectgame_widget.moc" diff --git a/atlantik/client/selectgame_widget.h b/atlantik/client/selectgame_widget.h new file mode 100644 index 00000000..d47e905e --- /dev/null +++ b/atlantik/client/selectgame_widget.h @@ -0,0 +1,65 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_SELECTGAME_WIDGET_H +#define ATLANTIK_SELECTGAME_WIDGET_H + +#include +#include + +#include +#include + +class AtlanticCore; +class Game; +class Player; + +class SelectGame : public QWidget +{ +Q_OBJECT + +public: + SelectGame(AtlanticCore *atlanticCore, QWidget *parent, const char *name=0); + + void initPage(); + bool validateNext(); + QString hostToConnect() const; + int portToConnect(); + +private slots: + void connectClicked(); + void addGame(Game *game); + void delGame(Game *game); + void updateGame(Game *game); + void playerChanged(Player *player); + void validateConnectButton(); + +signals: + void joinGame(int gameId); + void newGame(const QString &gameType); + void leaveServer(); + void msgStatus(const QString &status); + +private: + QListViewItem *findItem(Game *game); + + AtlanticCore *m_atlanticCore; + QVBoxLayout *m_mainLayout; + KListView *m_gameList; + KPushButton *m_connectButton; +}; + +#endif diff --git a/atlantik/client/selectserver_widget.cpp b/atlantik/client/selectserver_widget.cpp new file mode 100644 index 00000000..39c07b50 --- /dev/null +++ b/atlantik/client/selectserver_widget.cpp @@ -0,0 +1,178 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "selectserver_widget.moc" + +SelectServer::SelectServer(bool useMonopigatorOnStart, bool hideDevelopmentServers, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_hideDevelopmentServers = hideDevelopmentServers; + + m_mainLayout = new QVBoxLayout(this, KDialog::marginHint()); + Q_CHECK_PTR(m_mainLayout); + + // Custom server group + QHGroupBox *customGroup = new QHGroupBox(i18n("Enter Custom monopd Server"), this, "customGroup"); + m_mainLayout->addWidget(customGroup); + + QLabel *hostLabel = new QLabel(i18n("Hostname:"), customGroup); + + m_hostEdit = new KLineEdit(customGroup); + m_hostEdit->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum)); + + QLabel *portLabel = new QLabel(i18n("Port:"), customGroup); + + m_portEdit = new KLineEdit(QString::number(1234), customGroup); + m_portEdit->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum)); + + KPushButton *connectButton = new KPushButton( KGuiItem(i18n("Connect"), "network"), customGroup); + connect(connectButton, SIGNAL(clicked()), this, SLOT(customConnect())); + + // Server list group + QVButtonGroup *bgroup = new QVButtonGroup(i18n("Select monopd Server"), this, "bgroup"); + bgroup->setExclusive(true); + m_mainLayout->addWidget(bgroup); + + // List of servers + m_serverList = new KListView(bgroup, "m_serverList"); + m_serverList->addColumn(i18n("Host")); + m_serverList->addColumn(i18n("Latency")); + m_serverList->addColumn(i18n("Version")); + m_serverList->addColumn(i18n("Users")); + m_serverList->setAllColumnsShowFocus(true); + m_serverList->setSorting(1); +// m_mainLayout->addWidget(m_serverList); + + connect(m_serverList, SIGNAL(clicked(QListViewItem *)), this, SLOT(validateConnectButton())); + connect(m_serverList, SIGNAL(doubleClicked(QListViewItem *)), this, SLOT(slotConnect())); + connect(m_serverList, SIGNAL(rightButtonClicked(QListViewItem *, const QPoint &, int)), this, SLOT(validateConnectButton())); + connect(m_serverList, SIGNAL(selectionChanged(QListViewItem *)), this, SLOT(validateConnectButton())); + + QHBoxLayout *buttonBox = new QHBoxLayout(m_mainLayout, KDialog::spacingHint()); + buttonBox->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + // Server List / Refresh + m_refreshButton = new KPushButton( KGuiItem(useMonopigatorOnStart ? i18n("Reload Server List") : i18n("Get Server List"), useMonopigatorOnStart ? "reload" : "network"), this); + buttonBox->addWidget(m_refreshButton); + + connect(m_refreshButton, SIGNAL(clicked()), this, SLOT(slotRefresh())); + + // Connect + m_connectButton = new KPushButton(BarIconSet("forward", KIcon::SizeSmall), i18n("Connect"), this); + m_connectButton->setEnabled(false); + buttonBox->addWidget(m_connectButton); + + connect(m_connectButton, SIGNAL(clicked()), this, SLOT(slotConnect())); + + // Monopigator + m_monopigator = new Monopigator(); + + connect(m_monopigator, SIGNAL(monopigatorAdd(QString, QString, QString, QString, int)), this, SLOT(slotMonopigatorAdd(QString, QString, QString, QString, int))); + connect(m_monopigator, SIGNAL(finished()), SLOT(monopigatorFinished())); + connect(m_monopigator, SIGNAL(timeout()), SLOT(monopigatorTimeout())); +} + +SelectServer::~SelectServer() +{ + delete m_monopigator; +} + +void SelectServer::setHideDevelopmentServers(bool hideDevelopmentServers) +{ + if ( m_hideDevelopmentServers != hideDevelopmentServers ) + { + m_hideDevelopmentServers = hideDevelopmentServers; + emit showDevelopmentServers( !m_hideDevelopmentServers ); + } +} + +void SelectServer::initMonopigator() +{ + // Hardcoded, but there aren't any other Monopigator root servers at the moment + emit msgStatus(i18n("Retrieving server list...")); + + m_refreshButton->setGuiItem(KGuiItem(i18n("Reload Server List"), "reload")); + m_monopigator->loadData(KURL( "http://monopd-gator.kde.org/")); +} + +void SelectServer::slotMonopigatorAdd(QString ip, QString host, QString port, QString version, int users) +{ + MonopigatorEntry *item = new MonopigatorEntry(m_serverList, host, QString::number(9999), version, (users == -1) ? i18n("unknown") : QString::number(users), port, ip); + item->setPixmap(0, BarIcon("atlantik", KIcon::SizeSmall)); + + if ( item->isDev() ) + { + item->setVisible( !m_hideDevelopmentServers ); + connect(this, SIGNAL(showDevelopmentServers(bool)), item, SLOT(showDevelopmentServers(bool))); + } + + validateConnectButton(); +} + +void SelectServer::monopigatorFinished() +{ + emit msgStatus(i18n("Retrieved server list.")); + m_refreshButton->setEnabled(true); +} + +void SelectServer::monopigatorTimeout() +{ + emit msgStatus(i18n("Error while retrieving the server list.")); + m_refreshButton->setEnabled(true); +} + +void SelectServer::validateConnectButton() +{ + if (m_serverList->selectedItem()) + m_connectButton->setEnabled(true); + else + m_connectButton->setEnabled(false); +} + +void SelectServer::slotRefresh(bool useMonopigator) +{ + m_serverList->clear(); + validateConnectButton(); + + if (useMonopigator) + { + m_refreshButton->setEnabled(false); + initMonopigator(); + } +} + +void SelectServer::slotConnect() +{ + if (QListViewItem *item = m_serverList->selectedItem()) + emit serverConnect(item->text(0), item->text(4).toInt()); +} + +void SelectServer::customConnect() +{ + if (!m_hostEdit->text().isEmpty() && !m_portEdit->text().isEmpty()) + emit serverConnect(m_hostEdit->text(), m_portEdit->text().toInt()); +} diff --git a/atlantik/client/selectserver_widget.h b/atlantik/client/selectserver_widget.h new file mode 100644 index 00000000..c5d1b586 --- /dev/null +++ b/atlantik/client/selectserver_widget.h @@ -0,0 +1,73 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_SELECTSERVER_WIDGET_H +#define ATLANTIK_SELECTSERVER_WIDGET_H + +#include +#include +#include + +#include +#include +#include + +#include "monopigator.h" + +class KExtendedSocket; + +class SelectServer : public QWidget +{ +Q_OBJECT + +public: + SelectServer(bool useMonopigatorOnStart, bool hideDevelopmentServers, QWidget *parent, const char *name=0); + virtual ~SelectServer(); + + void initPage(); + void setHideDevelopmentServers(bool hideDevelopmentServers); + bool validateNext(); + QString hostToConnect() const; + int portToConnect(); + +public slots: + void validateConnectButton(); + void slotRefresh(bool useMonopigator = true); + void slotMonopigatorAdd(QString ip, QString host, QString port, QString version, int users); + +private slots: + void slotConnect(); + void customConnect(); + void monopigatorFinished(); + void monopigatorTimeout(); + +signals: + void serverConnect(const QString host, int port); + void msgStatus(const QString &message); + void showDevelopmentServers(bool show); + +private: + void initMonopigator(); + + QVBoxLayout *m_mainLayout; + KListView *m_serverList; + KLineEdit *m_hostEdit, *m_portEdit; + KPushButton *m_addServerButton, *m_refreshButton, *m_customConnect, *m_connectButton; + Monopigator *m_monopigator; + bool m_hideDevelopmentServers; +}; + +#endif diff --git a/atlantik/eventsrc b/atlantik/eventsrc new file mode 100644 index 00000000..a6d0327c --- /dev/null +++ b/atlantik/eventsrc @@ -0,0 +1,475 @@ +[!Global!] +IconName=atlantik +Comment=Atlantik +Comment[be]=Ðтлантыка +Comment[bn]=আটলানà§à¦Ÿà¦¿à¦• +Comment[eo]=Atlantiko +Comment[hi]=अटलांटिक +Comment[lv]=Atlantija +Comment[mk]=Ðтлантик +Comment[ne]=à¤à¤Ÿà¤²à¤¾à¤¨à¥à¤Ÿà¤¿à¤• +Comment[pa]=à¨à¨Ÿà¨²à¨¾à¨Ÿà¨¿à¨• +Comment[ta]=அடà¯à®²à®¾à®£à¯à®Ÿà®¿à®•à¯ +Comment[tg]=Ðтлантик +Comment[wa]=Atlantike + +[gainedturn] +Name=Gained Turn +Name[ar]=ربحت دورا +Name[be]=Канец ходу +Name[bg]=Придобит ред +Name[bn]=চাল দিন +Name[bs]=Potez dobiven +Name[ca]=Et toca jugar +Name[cs]=Získán tah +Name[cy]=Cael Tro +Name[da]=Vundet tur +Name[de]=Gewonnene Runde +Name[el]=ΚεÏδισμένη Ï€Ïοσπάθεια +Name[eo]=Gajnita vico +Name[es]=Turno ganado +Name[et]=Omandatud käik +Name[eu]=Irabazitako txanda +Name[fa]=نوبت به دست آمده +Name[fi]=Voitettu kierros +Name[fr]=Tour gagné +Name[gl]=Quenda Gañada +Name[he]=הרווחת תור +Name[hi]=लाभ वाली बारी +Name[hr]=Dobiven potez +Name[hu]=Nyert forduló +Name[is]=Græddi leik +Name[it]=Turno guadagnato +Name[ja]=順番ãŒå›žã£ã¦ãã¾ã—㟠+Name[km]=បាន​យក​វáŸáž“ +Name[lt]=Gautas Ä—jimas +Name[lv]=IegÅ«ts gÄjiens +Name[mk]=Добиен потег +Name[nb]=Ekstratur +Name[nds]=Wunnen Törn +Name[ne]=पà¥à¤°à¤¾à¤ªà¥à¤¤ मौका +Name[nl]=Gewonnen ronde +Name[nn]=Ekstratur +Name[pl]=Zyskany ruch +Name[pt]=Jogada Ganha +Name[pt_BR]=Rodada vencida +Name[ru]=Конец хода +Name[se]=Liigemátki +Name[sk]=Získaní Å¥ah +Name[sl]=Dobljena poteza +Name[sr]=Добијен потез +Name[sr@Latn]=Dobijen potez +Name[sv]=Du vann omgÃ¥ngen +Name[ta]=திரà¯à®ªà¯à®ªà®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Name[tg]=Gained Чархиш +Name[tr]=Karlı Sıra +Name[uk]=Кінець ходу +Name[wa]=Djeu wangnî +Name[zh_CN]=获得一次机会 +Name[zh_TW]=å¢žåŠ çš„å›žåˆ +Comment=It is your turn now +Comment[ar]=دورك الآن +Comment[be]=Ваш ход +Comment[bg]=Придобит ред +Comment[bn]=à¦à¦–ন আপনার চাল +Comment[br]=Din eo bremañ +Comment[bs]=Na vas je red +Comment[ca]=És el teu torn +Comment[cs]=Jste na tahu +Comment[cy]=Eich tro chi ydy o rwan +Comment[da]=Det er din tur nu +Comment[de]=Sie sind am Zug +Comment[el]=Είναι η σειÏά σας Ï„ÏŽÏα +Comment[eo]=Estas nun via vico +Comment[es]=Es su turno +Comment[et]=Sinu kord täringut veeretada +Comment[eu]=Zure txanda da +Comment[fa]=اکنون نوبت شماست +Comment[fi]=Sinun vuoro +Comment[fr]=C'est à votre tour de jeter les dés +Comment[gl]=É a súa quenda +Comment[he]=תורך עכשיו +Comment[hi]=यह अब आपकी बारी है +Comment[hr]=Vi ste na potezu +Comment[hu]=Most Ön következik +Comment[is]=Þú átt að gera +Comment[it]=Ora tocca a te +Comment[ja]=ã‚ãªãŸã®ç•ªã§ã™ +Comment[km]=ឥឡូវ​វា​ជា​វáŸáž“​របស់​អ្នក +Comment[lt]=Dabar JÅ«sų Ä—jimas +Comment[lv]=Å is ir jÅ«su gÄjiens +Comment[mk]=Вие Ñте на потег +Comment[nb]=Det er din tur nÃ¥ +Comment[nds]=Nu büst Du an de Reeg +Comment[ne]=अब तपाईà¤à¤•à¥‹ पालो हो +Comment[nl]=U bent aan de beurt +Comment[nn]=Det er din tur no +Comment[pa]=ਹà©à¨£ ਤà©à¨¹à¨¾à¨¡à©€ ਵਾਰੀ ਠ+Comment[pl]=Twój ruch +Comment[pt]=É a sua vez de jogar +Comment[pt_BR]=É a sua vez de jogar agora +Comment[ro]=Este rîndul dumneavoastră +Comment[ru]=Ваша очередь +Comment[se]=Du vuorru dál +Comment[sk]=Ste na Å¥ahu +Comment[sl]=Sedaj je vaÅ¡a poteza +Comment[sr]=Сада је ваш потез +Comment[sr@Latn]=Sada je vaÅ¡ potez +Comment[sv]=Det är din tur nu +Comment[ta]=இத௠உஙà¯à®•à®³à¯à®Ÿà¯ˆà®¯ à®®à¯à®±à¯ˆ +Comment[tg]=Ҳоло навбати шумо аÑÑ‚ +Comment[tr]=Sıra ÅŸimdi sizde +Comment[uk]=Тепер ваша черга +Comment[uz]=Endi siz yuriysiz +Comment[uz@cyrillic]=Энди Ñиз юрийÑиз +Comment[wa]=C' est a vos asteure +Comment[zh_CN]=轮到您了 +Comment[zh_TW]=該您擲骰å­äº† +default_presentation=80 + +[chat] +Name=Chat +Name[ar]=دردشة +Name[be]=Размова +Name[bg]=Изпратено Ñъобщение +Name[bn]=আডà§à¦¡à¦¾ +Name[br]=Flapañ +Name[bs]=Razgovor +Name[ca]=Xat +Name[cs]=Rozhovor +Name[cy]=Sgwrs +Name[el]=Κουβέντα +Name[eo]=Babilu +Name[et]=Vestlus +Name[eu]=Berriketa +Name[fa]=Ú¯Ù¾ +Name[fi]=Keskustelu +Name[fr]=Discussion +Name[he]=צ'ט +Name[hi]=गपशप +Name[hr]=Brbljanje +Name[hu]=Csevegés +Name[is]=Spjall +Name[ja]=ãƒãƒ£ãƒƒãƒˆ +Name[km]=សន្ទនា +Name[lt]=Pokalbiai +Name[lv]=ÄŒats +Name[mk]=Разговор +Name[nb]=Prat +Name[nds]=Klönen +Name[ne]=कà¥à¤°à¤¾à¤•à¤¾à¤¨à¥€ +Name[nl]=Gesprek +Name[nn]=Prat +Name[pa]=ਗੱਲਬਾਤ +Name[pl]=Rozmowa +Name[pt]=Conversar +Name[pt_BR]=Bate-papo +Name[ru]=Чат +Name[se]=Buillar +Name[sk]=Rozhovor +Name[sl]=Pogovor +Name[sr]=ЋаÑкање +Name[sr@Latn]=Ćaskanje +Name[sv]=Chatt +Name[ta]=அரடà¯à®Ÿà¯ˆ +Name[tg]=Чат +Name[tr]=Muhabbet +Name[uk]=Розмова +Name[uz@cyrillic]=Чат +Name[wa]=Berdelaedje +Name[zh_CN]=èŠå¤© +Name[zh_TW]=èŠå¤© +Comment=A player sends a chat message +Comment[ar]=لقد أرسل لاعب رسالة دردشة +Comment[be]=Гульнёўца Ñказаў +Comment[bg]=Изпратено е Ñъобщение от играч +Comment[bn]=à¦à¦•à¦œà¦¨ খেলোয়াড় à¦à¦•à¦Ÿà¦¿ বারà§à¦¤à¦¾ পাঠালেন +Comment[bs]=IgraÄ je poslao poruku +Comment[ca]=Un jugador envia un missatge de xat +Comment[cs]=HrÃ¡Ä odesílá zprávu +Comment[cy]=Mae chwaraewr yn danfon neges sgwrs +Comment[da]=En spiller sender en chat-besked +Comment[de]=Ein Spieler sendet eine Chat-Nachricht +Comment[el]=Ένας παίκτης στέλνει ένα μήνυμα κουβέντας +Comment[eo]=Ludanto sendas babilmesaÄon al Vi +Comment[es]=Un jugador ha enviado un mensaje +Comment[et]=Mängija saatis vestlusteate +Comment[eu]=Jokalari batek berriketarako mezu bat bidali du +Comment[fa]=یک بازیکن یک پیام Ú¯Ù¾ را ارسال می‌‌کند +Comment[fi]=Pelaaja lähettää viestin +Comment[fr]=Un joueur envoie un message +Comment[gl]=Un xogador enviou unha mensaxe +Comment[he]=שחקן שולח הודעת צ'ט +Comment[hi]=à¤à¤• खिलाड़ी ने गपशप संदेश भेजा +Comment[hr]=IgraÄ je poslao poruku brbljanja +Comment[hu]=Egy játékos szöveges üzenetet küld +Comment[is]=Leikmaður sendir skilaboð +Comment[it]=Un giocatore ha inviato un messaggio +Comment[ja]=プレイヤーãŒãƒãƒ£ãƒƒãƒˆãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’é€ä¿¡ã—ã¾ã—㟠+Comment[km]=អ្នក​លáŸáž„​ផ្ញើ​សារ​សន្ទនា +Comment[lt]=ŽaidÄ—jas siunÄia žinutÄ™ +Comment[lv]=SpÄ“lÄ“tÄjs nosÅ«ta Äata ziņu +Comment[mk]=Играчот иÑпраќа порака за разговор +Comment[nb]=En spiller har sendt en pratemelding +Comment[nds]=En Speler sendt en Klöön-Naricht +Comment[ne]=खेलाडिले कà¥à¤°à¤¾à¤•à¤¾à¤¨à¥€ सनà¥à¤¦à¥‡à¤¶ पठाउदछ +Comment[nl]=Een speler stuurt een bericht +Comment[nn]=Ein spelar har sendt ei pratemelding +Comment[pa]=ਇੱਕ ਖਿਡਾਰੀ ਇੱਕ ਹੀ ਸà©à¨¨à©‡à¨¹à¨¾ ਭੇਜ ਸਕਦਾ ਹੈ +Comment[pl]=Gracz wysyÅ‚a wiadomość w oknie rozmowy +Comment[pt]=Um jogador envia uma mensagem +Comment[pt_BR]=Um jogador enviou uma mensagem +Comment[ru]=Игрок Ñказал +Comment[se]=Speallár lea sádden buillardandieÄ‘u +Comment[sk]=HrÃ¡Ä poslal správu +Comment[sl]=Igralec poÅ¡ilja pogovorno sporoÄilo +Comment[sr]=Играч је поÑлао ћаÑкајућу поруку +Comment[sr@Latn]=IgraÄ je poslao ćaskajuću poruku +Comment[sv]=En spelare skickar ett chattmeddelande +Comment[ta]= ஒர௠விளையாடà¯à®Ÿà®¾à®³à®°à¯ அரடà¯à®Ÿà¯ˆ செயà¯à®¤à®¿à®¯à¯ˆ அனà¯à®ªà¯à®ªà¯à®•à®¿à®±à®¾à®°à¯ +Comment[tg]=Бозингар паёми чат фириÑтод +Comment[tr]=Oyuncu bir mesaj gönderdi +Comment[uk]=Гравець відÑилає Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ +Comment[uz]=OÊ»yinchi xabar joÊ»natayapti +Comment[uz@cyrillic]=Ўйинчи хабар жўнатаÑпти +Comment[wa]=On djouweu evoye on messaedje di berdelaedje +Comment[zh_CN]=一个玩家å‘é€äº†ä¸€æ¡èŠå¤©ä¿¡æ¯ +Comment[zh_TW]=一個玩家é€å‡ºäº†ä¸€æ¢èŠå¤©è¨Šæ¯ +default_presentation=64 + +[newplayer] +Name=New player +Name[ar]=لاعب جديد +Name[be]=Ðовы гульнёўца +Name[bg]=Ðов играч +Name[bn]=নতà§à¦¨ খেলোয়াড় +Name[br]=C'hoarier nevez +Name[bs]=Novi igraÄ +Name[ca]=Nou jugador +Name[cs]=Nový hrÃ¡Ä +Name[cy]=Chwaraewr Newydd +Name[da]=Ny spiller +Name[de]=Neuer Spieler +Name[el]=Îέος παίκτης +Name[eo]=Nova Ludanto +Name[es]=Nuevo jugador +Name[et]=Uus mängija +Name[eu]=Jokalari berria +Name[fa]=بازیکن جدید +Name[fi]=Uusi pelaaja +Name[fr]=Nouveau joueur +Name[ga]=Imreoir nua +Name[gl]=Novo xogador +Name[he]=שחקן חדש +Name[hi]=नया खिलाड़ी +Name[hr]=Novi igraÄ +Name[hu]=Új játékos +Name[is]=Nýr leikmaður +Name[it]=Nuovo giocatore +Name[ja]=æ–°è¦ãƒ—レイヤー +Name[km]=អ្នក​លáŸáž„​ážáŸ’មី +Name[lt]=Naujas žaidÄ—jas +Name[lv]=Jauns spÄ“lÄ“tÄjs +Name[mk]=Ðов играч +Name[nb]=Ny spiller +Name[nds]=Nieg Speler +Name[ne]=नयाठखेलाडी +Name[nl]=Nieuwe speler +Name[nn]=Ny spelar +Name[pa]=ਨਵਾਂ ਖਿਡਾਰੀ +Name[pl]=Nowy gracz +Name[pt]=Novo jogador +Name[pt_BR]=Novo jogador +Name[ro]=Jucător nou +Name[ru]=Ðовый игрок +Name[se]=OÄ‘Ä‘a speallár +Name[sk]=Nový hrÃ¡Ä +Name[sl]=Nov igralec +Name[sr]=Ðови играч +Name[sr@Latn]=Novi igraÄ +Name[sv]=Ny spelare +Name[ta]=பà¯à®¤à®¿à®¯ விளையாடà¯à®Ÿà¯ வீரர௠+Name[tg]=Бозингари нав +Name[tr]=Yeni oynatıcı +Name[uk]=Ðовий гравець +Name[uz]=Yangi oÊ»yinchi +Name[uz@cyrillic]=Янги ўйинчи +Name[wa]=Novea djouweu +Name[zh_CN]=新玩家 +Name[zh_TW]=新玩家 +Comment=A new player joins the game +Comment[ar]=لقد اشترك لاعب جديد ÙÙŠ اللعبة +Comment[be]=Ðовы гульнёўца далучыўÑÑ Ð´Ð° гульні +Comment[bg]=Ðов играч Ñе приÑъедини към играта +Comment[bn]=à¦à¦•à¦œà¦¨ নতà§à¦¨ খেলোয়াড় খেলায় যোগ দিয়েছেন +Comment[bs]=Novi igraÄ se ukljuÄuje u igru +Comment[ca]=Un nou jugador s'uneix a la partida +Comment[cs]=Nový hrÃ¡Ä se pÅ™ipojuje ke hÅ™e +Comment[cy]=Mae chwaraewr newydd yn ymuno â'r gêm +Comment[da]=En ny spiller gÃ¥r med i spillet +Comment[de]=Ein neuer Spieler ist hinzugekommen +Comment[el]=Ένας νέος παίκτης μπαίνει στο παιχνίδι +Comment[eo]=Nova ludanto ekpartoprenas +Comment[es]=Un nuevo jugador se une a la partida +Comment[et]=Mänguga liitus uus mängija +Comment[eu]=Jokalari berri batek jokoarekin bat egin du +Comment[fa]=یک بازیکن جدید به بازی می‌پیوندد +Comment[fi]=Uusi pelaaja liittyi peliin +Comment[fr]=Un nouveau joueur vient de se joindre à la partie +Comment[gl]=Un novo xogador entra na partida +Comment[he]=שחקן חדש מצטרף למשחק +Comment[hi]=à¤à¤• नया खिलाड़ी खेल में शामिल हà¥à¤† +Comment[hr]=Igri se pridružio novi igraÄ +Comment[hu]=Egy új játékos csatlakozik a játékhoz +Comment[is]=Nýr leikmaður tengist leiknum +Comment[it]=C'è un nuovo giocatore in gara +Comment[ja]=æ–°ã—ã„プレイヤーãŒã‚²ãƒ¼ãƒ ã«å‚加ã—ã¾ã—㟠+Comment[km]=អ្នក​លáŸáž„​ážáŸ’មី​ចូល​រួម​ល្បែង +Comment[lt]=Prisijungia naujas žaidÄ—jas +Comment[lv]=Jauns spÄ“lÄ“tÄjs pievienojas spÄ“lei +Comment[mk]=Ðов играч Ñе приклучува на играта +Comment[nb]=En ny spiller bilr med i spillet +Comment[nds]=En nieg Speler maakt mit +Comment[ne]=नयाठखेलाडीले खेलमा भाग लिनà¥à¤› +Comment[nl]=Een nieuwe speler neemt deel aan het spel +Comment[nn]=Ein ny spelar vert med i spelet +Comment[pa]=ਖੇਡ ਵਿੱਚ ਨਵਾਂ ਖਿਡਾਰੀ ਆਇਆ +Comment[pl]=Nowy gracz doÅ‚Ä…czyÅ‚ do gry +Comment[pt]=Um novo jogador junta-se ao jogo +Comment[pt_BR]=Um novo jogador entrou no jogo +Comment[ro]=Un jucător nou se alătură jocului +Comment[ru]=Ðовый игрок приÑоединилÑÑ Ðº игре +Comment[se]=OÄ‘Ä‘a speallár searvvai speallui +Comment[sk]=Nový hrÃ¡Ä sa pridal k hre +Comment[sl]=Nov igralec se je pridružil igri +Comment[sr]=Ðови играч Ñе придружио игри +Comment[sr@Latn]=Novi igraÄ se pridružio igri +Comment[sv]=En ny spelare gÃ¥r med i spelet +Comment[ta]=ஒர௠பà¯à®¤à®¿à®¯ விளையாடà¯à®Ÿà®¾à®³à®°à¯ சேரà¯à®•à®¿à®±à®¾à®°à¯ +Comment[tg]=Бозингари нав ба бозӣ ҳамроҳ шуд +Comment[tr]=Yeni bir oyuncu oyuna girdi +Comment[uk]=Ðовий гравець приєднуєтьÑÑ Ð´Ð¾ гри +Comment[uz]=OÊ»yinga yangi oÊ»yinchi qoÊ»shilayapti +Comment[uz@cyrillic]=Ўйинга Ñнги ўйинчи қўшилаÑпти +Comment[wa]=On novea djouweu s' a raloyî Ã¥ djeu +Comment[zh_CN]=ä¸€ä¸ªæ–°çŽ©å®¶åŠ å…¥äº†æ¸¸æˆ +Comment[zh_TW]=一個新玩家加入此éŠæˆ² + +[newgame] +Name=New game +Name[ar]=لعبة جديدة +Name[be]=ÐÐ¾Ð²Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ +Name[bg]=Ðова игра +Name[bn]=নতà§à¦¨ খেলা +Name[br]=C'hoari nevez +Name[bs]=Nova igra +Name[ca]=Nova partida +Name[cs]=Nová hra +Name[cy]=Gêm Newydd +Name[da]=Nyt spil +Name[de]=Neues Spiel +Name[el]=Îέο παιχνίδι +Name[eo]=Nova Ludo +Name[es]=Partida nueva +Name[et]=Uus mäng +Name[eu]=Jokoi berria +Name[fa]=بازی جدید +Name[fi]=Uusi peli +Name[fr]=Nouveau jeu +Name[ga]=Cluiche Nua +Name[gl]=Novo xogo +Name[he]=משחק חדש +Name[hi]=नया खेल +Name[hr]=Nova igra +Name[hu]=Új játék +Name[is]=Nýr leikur +Name[it]=Nuova partita +Name[ja]=æ–°è¦ã‚²ãƒ¼ãƒ  +Name[km]=ល្បែង​ážáŸ’មី +Name[lt]=Naujas žaidimas +Name[lv]=Jauna spÄ“le +Name[mk]=Ðова игра +Name[nb]=Nytt spill +Name[nds]=Nieg Speel +Name[ne]=नयाठखेल +Name[nl]=Nieuw spel +Name[nn]=Nytt spel +Name[pa]=ਨਵੀਂ ਖੇਡ +Name[pl]=Nowa gra +Name[pt]=Novo jogo +Name[pt_BR]=Novo jogo +Name[ro]=Joc nou +Name[ru]=ÐÐ¾Ð²Ð°Ñ Ð¸Ð³Ñ€Ð° +Name[se]=OÄ‘Ä‘a speallu +Name[sk]=Nová hra +Name[sl]=Nova igra +Name[sr]=Ðова игра +Name[sr@Latn]=Nova igra +Name[sv]=Nytt spel +Name[ta]=பà¯à®¤à®¿à®¯ விளையாடà¯à®Ÿà¯ +Name[tg]=Бозии нав +Name[tr]=Yeni oyun +Name[uk]=Ðова гра +Name[uz]=Yangi oÊ»yin +Name[uz@cyrillic]=Янги ўйин +Name[wa]=Novea djeu +Name[zh_CN]=æ–°æ¸¸æˆ +Name[zh_TW]=æ–°éŠæˆ² +Comment=A new game is created +Comment[ar]=لقد انشئت لعبة جديدة +Comment[be]=Ð¡Ñ‚Ð²Ð¾Ñ€Ð°Ð½Ð°Ñ Ð½Ð¾Ð²Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ +Comment[bg]=Създадена е нова игра +Comment[bn]=à¦à¦•à¦Ÿà¦¿ নতà§à¦¨ খেলা আরমà§à¦­ হয়েছে +Comment[br]=Krouet eo ur c'hoari nevez +Comment[bs]=Napravljena je nova igra +Comment[ca]=Es crea una nova partida +Comment[cs]=Je vytvoÅ™ena nová hra +Comment[cy]=Mae gêm newydd wedi ei greu +Comment[da]=Et nyt spil bliver oprettet +Comment[de]=Es beginnt ein neues Spiel +Comment[el]=Ένα νέο παιχνίδι δημιουÏγείται +Comment[eo]=Nova ludo kreiÄis +Comment[es]=Una nueva partida ha sido creada +Comment[et]=Loodi uus mäng +Comment[eu]=Joko berri bat sortu da +Comment[fa]=یک بازی جدید ایجاد می‌‌‌شود +Comment[fi]=Uusi peli luotiin +Comment[fr]=Une nouvelle partie vient d'être créée +Comment[gl]=Creouse unha nova partida +Comment[he]=נוצר משחק חדש +Comment[hi]=à¤à¤• नया खेल बनाया गया +Comment[hr]=Pokrenuta je nova igra +Comment[hu]=Új játék jön létre +Comment[is]=Nýr leikur er búinn til +Comment[it]=Viene avviata una nuova partita +Comment[ja]=æ–°ã—ã„ゲームを作æˆã—ã¾ã—㟠+Comment[km]=ល្បែង​ážáŸ’មី​ážáŸ’រូវ​បាន​បង្កើហ+Comment[lt]=Sukuriamas naujas žaidimas +Comment[lv]=Jauna spÄ“le ir izveidota +Comment[mk]=Создадена е нова игра +Comment[nb]=Et nytt spill startes +Comment[nds]=En nieg Speel warrt opstellt +Comment[ne]=नयाठखेल सिरà¥à¤œà¤¨à¤¾ गरियो +Comment[nl]=Er is een nieuw spel aangemaakt +Comment[nn]=Eit nytt spel vert starta +Comment[pa]=ਇੱਕ ਨਵੀਂ ਖੇਡ ਬਣਾਈ ਗਈ ਹੈ +Comment[pl]=Utworzenie nowej gry +Comment[pt]=Um novo jogo é criado +Comment[pt_BR]=Um novo jogo foi criado +Comment[ro]=Este creat un joc nou +Comment[ru]=Ðачата Ð½Ð¾Ð²Ð°Ñ Ð¸Ð³Ñ€Ð° +Comment[se]=OÄ‘Ä‘a speallu ráhkaduvvo +Comment[sk]=Vytvorená nová hra +Comment[sl]=Ustvarjena je bila nova igra +Comment[sr]=Ðаправљена је нова игра +Comment[sr@Latn]=Napravljena je nova igra +Comment[sv]=Ett nytt spel har skapats +Comment[ta]=ஒர௠பà¯à®¤à¯ விளையாடà¯à®Ÿà¯ உரà¯à®µà®¾à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®•à®¿à®±à®¤à¯ +Comment[tg]=Бозии нав офарида шуд +Comment[tr]=Yeni bir oyun baÅŸlatıldı +Comment[uk]=Ðову гру Ñтворено +Comment[uz]=Yangi oÊ»yin yaratilmoqda +Comment[uz@cyrillic]=Янги ўйин Ñратилмоқда +Comment[wa]=On novea djeu a stî enondé +Comment[zh_CN]=åˆ›å»ºäº†ä¸€ä¸ªæ–°æ¸¸æˆ +Comment[zh_TW]=一個新éŠæˆ²é–‹å•Ÿå®Œæˆ diff --git a/atlantik/kio_atlantik/Makefile.am b/atlantik/kio_atlantik/Makefile.am new file mode 100644 index 00000000..69279666 --- /dev/null +++ b/atlantik/kio_atlantik/Makefile.am @@ -0,0 +1,15 @@ +INCLUDES = -I$(top_srcdir)/atlantik/libatlantic $(all_includes) +METASOURCES = AUTO + +kde_module_LTLIBRARIES = kio_atlantik.la + +kio_atlantik_la_SOURCES = kio_atlantik.cpp +kio_atlantik_la_LIBADD = $(LIB_KIO) +kio_atlantik_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) +noinst_HEADERS = kio_atlantik.h + +kdelnk_DATA = atlantik.protocol +kdelnkdir = $(kde_servicesdir) + +messages: + $(XGETTEXT) *.cpp -o $(podir)/kio_atlantik.pot diff --git a/atlantik/kio_atlantik/atlantik.protocol b/atlantik/kio_atlantik/atlantik.protocol new file mode 100644 index 00000000..6839c736 --- /dev/null +++ b/atlantik/kio_atlantik/atlantik.protocol @@ -0,0 +1,7 @@ +[Protocol] +exec=kio_atlantik +protocol=atlantik +input=none +output=none +reading=true +Icon=atlantik diff --git a/atlantik/kio_atlantik/kio_atlantik.cpp b/atlantik/kio_atlantik/kio_atlantik.cpp new file mode 100644 index 00000000..3707d41b --- /dev/null +++ b/atlantik/kio_atlantik/kio_atlantik.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include + +#include +#undef KDE_3_1_FEATURES +#ifdef KDE_MAKE_VERSION +#if KDE_VERSION > KDE_MAKE_VERSION (3, 1, 0) +#define KDE_3_1_FEATURES +#endif +#endif +#include +#include +#include + +#include "kio_atlantik.h" +#include "libatlantic_export.h" + +extern "C" +{ + int LIBATLANTIC_EXPORT kdemain( int, char **argv ) + { + KInstance instance( "kio_atlantik" ); + AtlantikProtocol slave(argv[2], argv[3]); + slave.dispatchLoop(); + return 0; + } +} + +void AtlantikProtocol::get( const KURL& url ) +{ + KProcess *proc = new KProcess; + *proc << "atlantik"; + +#ifdef KDE_3_1_FEATURES + QString host = url.hasHost() ? url.host() : KProcess::quote( url.queryItem("host") ); +#else + QString host = url.hasHost() ? url.host() : url.queryItem("host"); +#endif + QString port = QString::number( url.port() ? url.port() : 1234 ); + int game = url.queryItem("game").toInt(); + QString gameString = game ? QString::number( game ) : QString::null; + + if (!host.isNull() && !port.isNull()) + { + *proc << "--host" << host << "--port" << port; + if (!gameString.isNull()) + *proc << "--game" << gameString; + } + + proc->start(KProcess::DontCare); + proc->detach(); + finished(); +} diff --git a/atlantik/kio_atlantik/kio_atlantik.h b/atlantik/kio_atlantik/kio_atlantik.h new file mode 100644 index 00000000..ac794ca9 --- /dev/null +++ b/atlantik/kio_atlantik/kio_atlantik.h @@ -0,0 +1,22 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +class AtlantikProtocol : public KIO::SlaveBase +{ +public: + AtlantikProtocol( const QCString &pool, const QCString &app) : SlaveBase( "atlantik", pool, app ) {} + virtual void get( const KURL& url ); +}; diff --git a/atlantik/libatlantic/Makefile.am b/atlantik/libatlantic/Makefile.am new file mode 100644 index 00000000..314a0215 --- /dev/null +++ b/atlantik/libatlantic/Makefile.am @@ -0,0 +1,15 @@ +KDE_OPTIONS = qtonly + +INCLUDES = $(all_includes) +lib_LTLIBRARIES = libatlantic.la +libatlantic_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) -no-undefined -version-info 3:0:2 +libatlantic_la_LIBADD = $(LIB_QT) + +libatlantic_la_SOURCES = atlantic_core.cpp auction.cpp configoption.cpp estate.cpp \ + estategroup.cpp game.cpp player.cpp trade.cpp + +libatlanticincludedir = $(includedir)/atlantic +libatlanticinclude_HEADERS = atlantic_core.h auction.h configoption.h estate.h \ + estategroup.h game.h player.h trade.h libatlantic_export.h + +METASOURCES = AUTO diff --git a/atlantik/libatlantic/atlantic_core.cpp b/atlantik/libatlantic/atlantic_core.cpp new file mode 100644 index 00000000..39e1200e --- /dev/null +++ b/atlantik/libatlantic/atlantic_core.cpp @@ -0,0 +1,369 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include "atlantic_core.h" + +#include "auction.h" +#include "configoption.h" +#include "estate.h" +#include "estategroup.h" +#include "game.h" +#include "player.h" +#include "trade.h" + +AtlanticCore::AtlanticCore(QObject *parent, const char *name) : QObject(parent, name) +{ + m_playerSelf = 0; +} + +void AtlanticCore::reset(bool deletePermanents) +{ + m_auctions.setAutoDelete(true); + m_auctions.clear(); + m_auctions.setAutoDelete(false); + m_estates.setAutoDelete(true); + m_estates.clear(); + m_estates.setAutoDelete(false); + m_estateGroups.setAutoDelete(true); + m_estateGroups.clear(); + m_estateGroups.setAutoDelete(false); + m_configOptions.setAutoDelete(true); + m_configOptions.clear(); + m_configOptions.setAutoDelete(false); + + Trade *trade = 0; + for (QPtrListIterator it(m_trades); (trade = *it) ; ++it) + { + emit removeGUI(trade); + trade->deleteLater(); + } + m_trades.clear(); + + Player *player = 0; + for (QPtrListIterator it(m_players); (player = *it) ; ++it) + { + if (deletePermanents) + { + emit removeGUI(player); + player->deleteLater(); + } + else + { + player->setLocation(0); + player->setDestination(0); + } + } + if (deletePermanents) + { + m_players.clear(); + m_playerSelf = 0; + + Game *game = 0; + for (QPtrListIterator it(m_games); (game = *it) ; ++it) + { + emit removeGUI(game); + game->deleteLater(); + } + m_games.clear(); + } +} + +bool AtlanticCore::selfIsMaster() const +{ + return (m_playerSelf && m_playerSelf->game() && m_playerSelf->game()->master() == m_playerSelf); +} + +void AtlanticCore::setPlayerSelf(Player *player) +{ + m_playerSelf = player; +} + +Player *AtlanticCore::playerSelf() +{ + return m_playerSelf; +} + +QPtrList AtlanticCore::players() +{ + return m_players; +} + +Player *AtlanticCore::newPlayer(int playerId, const bool &playerSelf) +{ + Player *player = new Player(playerId); + m_players.append(player); + + if (playerSelf) + { + player->setIsSelf(playerSelf); + m_playerSelf = player; + } + + emit createGUI(player); + + return player; +} + +Player *AtlanticCore::findPlayer(int playerId) +{ + Player *player = 0; + for (QPtrListIterator it(m_players); (player = *it) ; ++it) + if (player->id() == playerId) + return player; + + return 0; +} + +void AtlanticCore::removePlayer(Player *player) +{ + m_players.remove(player); + emit removeGUI(player); + player->deleteLater(); +} + +QPtrList AtlanticCore::games() +{ + return m_games; +} + +Game *AtlanticCore::newGame(int gameId, const QString &type) +{ + Game *game = new Game(gameId); + m_games.append(game); + + if ( !type.isNull() ) + game->setType(type); + + emit createGUI(game); + + return game; +} + +Game *AtlanticCore::findGame(const QString &type) +{ + Game *game = 0; + for (QPtrListIterator it(m_games); (game = *it) ; ++it) + if (game->id() == -1 && game->type() == type) + return game; + + return 0; +} + +Game *AtlanticCore::findGame(int gameId) +{ + if (gameId == -1) + return 0; + + Game *game = 0; + for (QPtrListIterator it(m_games); (game = *it) ; ++it) + if (game->id() == gameId) + return game; + + return 0; +} + +Game *AtlanticCore::gameSelf() +{ + return( m_playerSelf ? m_playerSelf->game() : 0 ); +} + +void AtlanticCore::removeGame(Game *game) +{ + m_games.remove(game); + emit removeGUI(game); + game->deleteLater(); +} + +void AtlanticCore::emitGames() +{ + for (QPtrListIterator it(m_games); (*it) ; ++it) + emit createGUI( (*it) ); +} + +QPtrList AtlanticCore::estates() +{ + return m_estates; +} + +Estate *AtlanticCore::newEstate(int estateId) +{ + Estate *estate = new Estate(estateId); + m_estates.append(estate); + return estate; +} + +Estate *AtlanticCore::findEstate(int estateId) +{ + Estate *estate = 0; + for (QPtrListIterator it(m_estates); (estate = *it) ; ++it) + if (estate->id() == estateId) + return estate; + + return 0; +} + +Estate *AtlanticCore::estateAfter(Estate *estate) +{ + Estate *eFirst = 0, *eTmp = 0; + bool useNext = false; + for (QPtrListIterator it(m_estates); (eTmp = *it) ; ++it) + { + if (!eFirst) + eFirst = eTmp; + if (eTmp == estate) + useNext = true; + else if (useNext) + return eTmp; + } + return eFirst; +} + +QPtrList AtlanticCore::estateGroups() +{ + return m_estateGroups; +} + +EstateGroup *AtlanticCore::newEstateGroup(int groupId) +{ + EstateGroup *estateGroup = new EstateGroup(groupId); + m_estateGroups.append(estateGroup); + return estateGroup; +} + +EstateGroup *AtlanticCore::findEstateGroup(int groupId) +{ + EstateGroup *estateGroup = 0; + for (QPtrListIterator it(m_estateGroups); (estateGroup = *it) ; ++it) + if (estateGroup->id() == groupId) + return estateGroup; + + return 0; +} + +QPtrList AtlanticCore::trades() +{ + return m_trades; +} + +Trade *AtlanticCore::newTrade(int tradeId) +{ + Trade *trade = new Trade(tradeId); + m_trades.append(trade); + + emit createGUI(trade); + + return trade; +} + +Trade *AtlanticCore::findTrade(int tradeId) +{ + Trade *trade = 0; + for (QPtrListIterator it(m_trades); (trade = *it) ; ++it) + if (trade->tradeId() == tradeId) + return trade; + + return 0; +} + +void AtlanticCore::removeTrade(Trade *trade) +{ + m_trades.remove(trade); + emit removeGUI(trade); + trade->deleteLater(); +} + +QPtrList AtlanticCore::auctions() +{ + return m_auctions; +} + +Auction *AtlanticCore::newAuction(int auctionId, Estate *estate) +{ + Auction *auction = new Auction(auctionId, estate); + m_auctions.append(auction); + return auction; +} + +void AtlanticCore::delAuction(Auction *auction) +{ + m_auctions.remove(auction); + delete auction; +} + +ConfigOption *AtlanticCore::newConfigOption(int configId) +{ + ConfigOption *configOption = new ConfigOption(configId); + m_configOptions.append(configOption); + + emit createGUI(configOption); + + return configOption; +} + +void AtlanticCore::removeConfigOption(ConfigOption *configOption) +{ + m_configOptions.remove(configOption); + emit removeGUI(configOption); + configOption->deleteLater(); +} + +ConfigOption *AtlanticCore::findConfigOption(int configId) +{ + ConfigOption *configOption = 0; + for (QPtrListIterator it(m_configOptions); (configOption = *it) ; ++it) + if (configOption->id() == configId) + return configOption; + + return 0; +} + +void AtlanticCore::printDebug() +{ + Player *player = 0; + for (QPtrListIterator it(m_players); (player = *it) ; ++it) + if (player == m_playerSelf) + std::cout << "PS: " << player->name().latin1() << ", game " << QString::number(player->game() ? player->game()->id() : -1).latin1() << std::endl; + else + std::cout << " P: " << player->name().latin1() << ", game " << QString::number(player->game() ? player->game()->id() : -1).latin1() << std::endl; + + Game *game = 0; + for (QPtrListIterator it(m_games); (game = *it) ; ++it) + std::cout << " G: " << QString::number(game->id()).latin1() << ", master: " << QString::number(game->master() ? game->master()->id() : -1 ).latin1() << std::endl; + + Estate *estate = 0; + for (QPtrListIterator it(m_estates); (estate = *it) ; ++it) + std::cout << " E: " << estate->name().latin1() << std::endl; + + EstateGroup *estateGroup = 0; + for (QPtrListIterator it(m_estateGroups); (estateGroup = *it) ; ++it) + std::cout << "EG: " << estateGroup->name().latin1() << std::endl; + + Auction *auction = 0; + for (QPtrListIterator it(m_auctions); (auction = *it) ; ++it) + std::cout << " A: " << QString::number(auction->auctionId()).latin1() << std::endl; + + Trade *trade = 0; + for (QPtrListIterator it(m_trades); (trade = *it) ; ++it) + std::cout << " T: " << QString::number(trade->tradeId()).latin1() << std::endl; + + ConfigOption *configOption = 0; + for (QPtrListIterator it(m_configOptions); (configOption = *it) ; ++it) + std::cout << "CO:" << QString::number(configOption->id()).latin1() << " " << configOption->name().latin1() << " " << configOption->value().latin1() << std::endl; +} + +#include "atlantic_core.moc" diff --git a/atlantik/libatlantic/atlantic_core.h b/atlantik/libatlantic/atlantic_core.h new file mode 100644 index 00000000..bca5b783 --- /dev/null +++ b/atlantik/libatlantic/atlantic_core.h @@ -0,0 +1,105 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIC_CORE_H +#define LIBATLANTIC_CORE_H + +#include +#include + +#include "libatlantic_export.h" + +class Player; +class ConfigOption; +class Estate; +class EstateGroup; +class Game; +class Trade; +class Auction; + +class LIBATLANTIC_EXPORT AtlanticCore : public QObject +{ +Q_OBJECT + +public: + AtlanticCore(QObject *parent, const char *name); + + void reset(bool deletePermanents = false); + + bool selfIsMaster() const; + + void setPlayerSelf(Player *player); + Player *playerSelf(); + + QPtrList players(); + Player *newPlayer(int playerId, const bool &playerSelf = false); + Player *findPlayer(int playerId); + void removePlayer(Player *player); + + QPtrList games(); + Game *newGame(int gameId, const QString &type = QString::null); + Game *findGame(const QString &type); // finds game types + Game *findGame(int gameId); // finds actual games + Game *gameSelf(); + void removeGame(Game *game); + void emitGames(); + + QPtrList estates(); + Estate *newEstate(int estateId); + Estate *findEstate(int estateId); + Estate *estateAfter(Estate *estate); + + QPtrList estateGroups(); + EstateGroup *newEstateGroup(int groupId); + EstateGroup *findEstateGroup(int groupId); + + QPtrList trades(); + Trade *newTrade(int tradeId); + Trade *findTrade(int tradeId); + void removeTrade(Trade *trade); + + QPtrList auctions(); + Auction *newAuction(int auctionId, Estate *estate); + void delAuction(Auction *auction); + + ConfigOption *newConfigOption(int configId); + void removeConfigOption(ConfigOption *configOption); + ConfigOption *findConfigOption(int configId); + + void printDebug(); + +signals: + void createGUI(Player *player); + void removeGUI(Player *player); + void createGUI(Game *game); + void removeGUI(Game *game); + void createGUI(Trade *trade); + void removeGUI(Trade *trade); + void createGUI(ConfigOption *configOption); + void removeGUI(ConfigOption *configOption); + +private: + Player *m_playerSelf; + QPtrList m_players; + QPtrList m_games; + QPtrList m_estates; + QPtrList m_estateGroups; + QPtrList m_trades; + QPtrList m_auctions; + QPtrList m_configOptions; +}; + +#endif diff --git a/atlantik/libatlantic/auction.cpp b/atlantik/libatlantic/auction.cpp new file mode 100644 index 00000000..70734c4e --- /dev/null +++ b/atlantik/libatlantic/auction.cpp @@ -0,0 +1,56 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include "auction.h" +#include "auction.moc" +#include "player.h" +#include "estate.h" + +Auction::Auction(int auctionId, Estate *estate) : QObject() +{ + m_auctionId = auctionId; + m_estate = estate; + m_status = 0; + m_changed = false; +} + +Auction::~Auction() +{ + emit completed(); +} + +void Auction::setStatus(int status) +{ + if (m_status != status) + { + m_status = status; + m_changed = true; + } +} + +void Auction::newBid(Player *player, int amount) +{ + emit updateBid(player, amount); +} + +void Auction::update(bool force) +{ + if (m_changed || force) + { + emit changed(); + m_changed = false; + } +} diff --git a/atlantik/libatlantic/auction.h b/atlantik/libatlantic/auction.h new file mode 100644 index 00000000..cc44cce5 --- /dev/null +++ b/atlantik/libatlantic/auction.h @@ -0,0 +1,57 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIC_AUCTION_H +#define LIBATLANTIC_AUCTION_H + +#include + +#include "libatlantic_export.h" + +class Player; +class Estate; + +class LIBATLANTIC_EXPORT Auction : public QObject +{ +Q_OBJECT + +public: + Auction(int auctionId, Estate *estate); + virtual ~Auction(); + + int auctionId() { return m_auctionId; } + Estate *estate() { return m_estate; } + + void setStatus(int status); + int status() { return m_status; } + + void newBid(Player *player, int bid); + + void update(bool force = false); + +signals: + void changed(); + void completed(); + void bid(Auction *auction, int amount); + void updateBid(Player *player, int amount); + +private: + bool m_changed; + int m_auctionId, m_status; + Estate *m_estate; +}; + +#endif diff --git a/atlantik/libatlantic/configoption.cpp b/atlantik/libatlantic/configoption.cpp new file mode 100644 index 00000000..00a8eb12 --- /dev/null +++ b/atlantik/libatlantic/configoption.cpp @@ -0,0 +1,99 @@ +// Copyright (c) 2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include "configoption.h" + +ConfigOption::ConfigOption(int configId) : QObject() +{ + m_id = configId; + m_name = ""; + m_description = ""; + m_edit = false; + m_value = ""; + m_changed = false; +} + +int ConfigOption::id() +{ + return m_id; +} + +void ConfigOption::setName(const QString &name) +{ + if (m_name != name) + { + m_name = name; + m_changed = true; + } +} + +QString ConfigOption::name() const +{ + return m_name; +} + +void ConfigOption::setDescription(const QString &description) +{ + if (m_description != description) + { + m_description = description; + m_changed = true; + } +} + +QString ConfigOption::description() const +{ + return m_description; +} + +void ConfigOption::setEdit(bool edit) +{ + if (m_edit != edit) + { + m_edit = edit; + m_changed = true; + } +} + +bool ConfigOption::edit() +{ + return m_edit; +} + +void ConfigOption::setValue(const QString &value) +{ + if (m_value != value) + { + m_value = value; + m_changed = true; + } +} + +QString ConfigOption::value() const +{ + return m_value; +} + +void ConfigOption::update(bool force) +{ + if (m_changed || force) + { + emit changed(this); + m_changed = false; + } +} + +#include "configoption.moc" diff --git a/atlantik/libatlantic/configoption.h b/atlantik/libatlantic/configoption.h new file mode 100644 index 00000000..a29d6b45 --- /dev/null +++ b/atlantik/libatlantic/configoption.h @@ -0,0 +1,51 @@ +// Copyright (c) 2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIC_CONFIGOPTION_H +#define LIBATLANTIC_CONFIGOPTION_H + +#include +#include + +#include "libatlantic_export.h" + +class LIBATLANTIC_EXPORT ConfigOption : public QObject +{ +Q_OBJECT + +public: + ConfigOption(int configId); + int id(); + void setName(const QString &name); + QString name() const; + void setDescription(const QString &description); + QString description() const; + void setEdit(bool edit); + bool edit(); + void setValue(const QString &value); + QString value() const; + void update(bool force = false); + +signals: + void changed(ConfigOption *configOption); + +private: + int m_id; + bool m_changed, m_edit; + QString m_name, m_description, m_value; +}; + +#endif diff --git a/atlantik/libatlantic/estate.cpp b/atlantik/libatlantic/estate.cpp new file mode 100644 index 00000000..eef69280 --- /dev/null +++ b/atlantik/libatlantic/estate.cpp @@ -0,0 +1,163 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +//#include + +#include "estate.h" +#include "estate.moc" +#include "player.h" + +Estate::Estate(int estateId) : QObject() +{ + m_id = estateId; + m_name = QString::null; + m_owner = 0; + m_houses = 0; + m_price = 0; + m_money = 0; + m_estateGroup = 0; + m_changed = m_canBeOwned = m_canBuyHouses = m_canSellHouses = m_isMortgaged = m_canToggleMortgage = false; + m_bgColor = QColor(); + m_color = QColor(); +} + +void Estate::setEstateGroup(EstateGroup *estateGroup) +{ + if (m_estateGroup != estateGroup) + m_estateGroup = estateGroup; +} + +void Estate::setOwner(Player *player) +{ + if (m_owner != player) + { + m_owner = player; + m_changed = true; + } +} +bool Estate::isOwned() const +{ + if (m_owner) + return true; + else + return false; +} + +bool Estate::isOwnedBySelf() const +{ + if (m_owner && m_owner->isSelf()) + return true; + else + return false; +} + +void Estate::setHouses(unsigned int houses) +{ + if (m_houses != houses) + m_houses = houses; + m_changed = true; +} + +void Estate::setName(QString name) +{ + if (m_name != name) + { + m_name = name; + m_changed = true; + } +} + +QString Estate::name() const +{ + return m_name; +} + +void Estate::setColor(QColor color) +{ + if (m_color != color) + { + m_color = color; + m_changed = true; + } +} + +void Estate::setBgColor(QColor color) +{ + if (m_bgColor != color) + { + m_bgColor = color; + m_changed = true; + } +} + +void Estate::setCanBeOwned(const bool canBeOwned) +{ + if (m_canBeOwned != canBeOwned) + m_canBeOwned = canBeOwned; +} + +void Estate::setCanBuyHouses(const bool canBuyHouses) +{ + if (m_canBuyHouses != canBuyHouses) + m_canBuyHouses = canBuyHouses; +} + +void Estate::setCanSellHouses(const bool canSellHouses) +{ + if (m_canSellHouses != canSellHouses) + m_canSellHouses = canSellHouses; +} + +void Estate::setIsMortgaged(const bool isMortgaged) +{ + if (m_isMortgaged != isMortgaged) + { + m_isMortgaged = isMortgaged; + m_changed = true; + } +} + +void Estate::setCanToggleMortgage(const bool canToggleMortgage) +{ + if (m_canToggleMortgage != canToggleMortgage) + { + m_canToggleMortgage = canToggleMortgage; + m_changed = true; + } +} + +void Estate::setMoney(int money) +{ + if (m_money != money) + { + m_money = money; + m_changed = true; + } +} + +int Estate::money() +{ + return m_money; +} + +void Estate::update(bool force) +{ + if (m_changed || force) + { + emit changed(); + m_changed = false; + } +} diff --git a/atlantik/libatlantic/estate.h b/atlantik/libatlantic/estate.h new file mode 100644 index 00000000..b6b768a5 --- /dev/null +++ b/atlantik/libatlantic/estate.h @@ -0,0 +1,95 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIC_ESTATE_H +#define LIBATLANTIC_ESTATE_H + +#include +#include + +#include "libatlantic_export.h" + +class EstateGroup; +class Player; + +class LIBATLANTIC_EXPORT Estate : public QObject +{ +Q_OBJECT + +public: + Estate(int estateId); + int id() { return m_id; } + void setName(QString name); + QString name() const; + void setEstateGroup(EstateGroup *estateGroup); + EstateGroup *estateGroup() { return m_estateGroup; } + void setOwner(Player *player); + bool isOwned() const; + bool isOwnedBySelf() const; + Player *owner() { return m_owner; } + void setHouses(unsigned int houses); + unsigned int houses() { return m_houses; } + void setCanBeOwned(const bool canBeOwned); + bool canBeOwned() const { return m_canBeOwned; } + void setCanBuyHouses(const bool canBuyHouses); + bool canBuyHouses() const { return m_canBuyHouses; } + void setCanSellHouses(const bool canSellHouses); + bool canSellHouses() const { return m_canSellHouses; } + void setHousePrice(const unsigned int housePrice) { m_housePrice = housePrice; } + unsigned int housePrice() const { return m_housePrice; } + void setHouseSellPrice(const unsigned int houseSellPrice) { m_houseSellPrice = houseSellPrice; } + unsigned int houseSellPrice() const { return m_houseSellPrice; } + void setIsMortgaged(const bool isMortgaged); + bool isMortgaged() const { return m_isMortgaged; } + void setCanToggleMortgage(const bool canToggleMortgage); + bool canToggleMortgage() const { return m_canToggleMortgage; } + void setMortgagePrice(const unsigned int mortgagePrice) { m_mortgagePrice = mortgagePrice; } + unsigned int mortgagePrice() const { return m_mortgagePrice; } + void setUnmortgagePrice(const unsigned int unmortgagePrice) { m_unmortgagePrice = unmortgagePrice; } + unsigned int unmortgagePrice() const { return m_unmortgagePrice; } + void setColor(QColor color); + QColor color() const { return m_color; } + void setBgColor(QColor color); + QColor bgColor() const { return m_bgColor; } + void setPrice(const unsigned int price) { m_price = price; } + unsigned int price() const { return m_price; } + void setMoney(int money); + int money(); + void update(bool force = false); + +signals: + void changed(); + void estateToggleMortgage(Estate *estate); + void estateHouseBuy(Estate *estate); + void estateHouseSell(Estate *estate); + void newTrade(Player *player); + void LMBClicked(Estate *estate); + +protected: + bool m_changed; + int m_id; + +private: + QString m_name; + Player *m_owner; + EstateGroup *m_estateGroup; + unsigned int m_houses, m_price, m_housePrice, m_houseSellPrice, m_mortgagePrice, m_unmortgagePrice; + int m_money; + bool m_canBeOwned, m_canBuyHouses, m_canSellHouses, m_isMortgaged, m_canToggleMortgage; + QColor m_bgColor, m_color; +}; + +#endif diff --git a/atlantik/libatlantic/estategroup.cpp b/atlantik/libatlantic/estategroup.cpp new file mode 100644 index 00000000..e0148afc --- /dev/null +++ b/atlantik/libatlantic/estategroup.cpp @@ -0,0 +1,41 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include "estategroup.h" +#include "estategroup.moc" + +EstateGroup::EstateGroup(const int id) : QObject() +{ + m_id = id; +} + +void EstateGroup::setName(const QString name) +{ + if (m_name != name) + { + m_name = name; + m_changed = true; + } +} + +void EstateGroup::update(bool force) +{ + if (m_changed || force) + { + emit changed(); + m_changed = false; + } +} diff --git a/atlantik/libatlantic/estategroup.h b/atlantik/libatlantic/estategroup.h new file mode 100644 index 00000000..3e08a9ce --- /dev/null +++ b/atlantik/libatlantic/estategroup.h @@ -0,0 +1,44 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIC_ESTATEGROUP_H +#define LIBATLANTIC_ESTATEGROUP_H + +#include + +#include "libatlantic_export.h" + +class LIBATLANTIC_EXPORT EstateGroup : public QObject +{ +Q_OBJECT + +public: + EstateGroup(const int id); + int id() { return m_id; } + void setName(const QString name); + QString name() const { return m_name; } + void update(bool force = false); + +signals: + void changed(); + +private: + int m_id; + bool m_changed; + QString m_name; +}; + +#endif diff --git a/atlantik/libatlantic/game.cpp b/atlantik/libatlantic/game.cpp new file mode 100644 index 00000000..1f4eb244 --- /dev/null +++ b/atlantik/libatlantic/game.cpp @@ -0,0 +1,130 @@ +// Copyright (c) 2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include "game.h" + +Game::Game(int gameId) : QObject() +{ + m_id = gameId; + m_description = QString::null; + m_type = QString::null; + m_players = 0; + m_master = 0; + + m_changed = false; +} + +int Game::id() const +{ + return m_id; +} + +void Game::setCanBeJoined(const bool &canBeJoined) +{ + if (m_canBeJoined != canBeJoined) + { + m_canBeJoined = canBeJoined; + m_changed = true; + } +} + +bool Game::canBeJoined() const +{ + return m_canBeJoined; +} + +void Game::setDescription(const QString &description) +{ + if (m_description != description) + { + m_description = description; + m_changed = true; + } +} + +QString Game::description() const +{ + return m_description; +} + +void Game::setName(const QString &name) +{ + if (m_name != name) + { + m_name = name; + m_changed = true; + } +} + +QString Game::name() const +{ + return m_name; +} + +void Game::setType(const QString &type) +{ + if (m_type != type) + { + m_type = type; + m_changed = true; + } +} + +QString Game::type() const +{ + return m_type; +} + +void Game::update(bool force) +{ + if (m_changed || force) + { + emit changed(this); + m_changed = false; + } +} + +int Game::players() +{ + return m_players; +} + +void Game::setPlayers(int players) +{ + if (m_players != players) + { + m_players = players; + m_changed = true; + } +} + +Player *Game::master() +{ + return m_master; +} + +void Game::setMaster(Player *master) +{ + if (m_master != master) + { + m_master = master; + m_changed = true; + } +} + +#include "game.moc" diff --git a/atlantik/libatlantic/game.h b/atlantik/libatlantic/game.h new file mode 100644 index 00000000..8eaa85f6 --- /dev/null +++ b/atlantik/libatlantic/game.h @@ -0,0 +1,62 @@ +// Copyright (c) 2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIC_GAME_H +#define LIBATLANTIC_GAME_H + +#include + +#include "libatlantic_export.h" + +class QString; + +class Player; + +class LIBATLANTIC_EXPORT Game : public QObject +{ +Q_OBJECT + +public: + Game(int gameId); + + int id() const; + void setCanBeJoined(const bool &canBeJoined); + bool canBeJoined() const; + void setDescription(const QString &description); + QString description() const; + void setName(const QString &name); + QString name() const; + void setType(const QString &type); + QString type() const; + int players(); + void setPlayers(int players); + Player *master(); + void setMaster(Player *master); + + void update(bool force = false); + +signals: + void changed(Game *game); + +private: + bool m_changed; + bool m_canBeJoined; + QString m_description, m_name, m_type; + int m_id, m_players; + Player *m_master; +}; + +#endif diff --git a/atlantik/libatlantic/libatlantic_export.h b/atlantik/libatlantic/libatlantic_export.h new file mode 100644 index 00000000..6ea0423a --- /dev/null +++ b/atlantik/libatlantic/libatlantic_export.h @@ -0,0 +1,25 @@ +// Copyright (c) 2004 Dirk Mueller + +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIC_EXPORT_H +#define LIBATLANTIC_EXPORT_H + +#include + +#define LIBATLANTIC_EXPORT KDE_EXPORT + +#endif diff --git a/atlantik/libatlantic/player.cpp b/atlantik/libatlantic/player.cpp new file mode 100644 index 00000000..ab5e9268 --- /dev/null +++ b/atlantik/libatlantic/player.cpp @@ -0,0 +1,183 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include "player.h" +#include "player.moc" +#include "estate.h" + +Player::Player(int playerId) : QObject() +{ + m_id = playerId; + m_game = 0; + m_name = ""; + m_host = ""; + m_image = ""; + m_location = m_destination = 0; + m_money = 0; + m_changed = m_isSelf = false; + m_bankrupt = m_hasDebt = m_hasTurn = m_canRoll = m_canBuy = m_canAuction = m_canUseCard = m_inJail = false; +} + +void Player::setGame(Game *game) +{ + if (m_game != game) + { + m_game = game; + m_changed = true; + } +} + +Game *Player::game() +{ + return m_game; +} + +void Player::setLocation(Estate *location) +{ + if (m_location != location) + { + m_location = location; + m_changed = true; + } +} + +void Player::setDestination(Estate *destination) +{ + if (m_destination != destination) + { + m_destination = destination; + m_changed = true; + } +} + +void Player::setBankrupt(bool bankrupt) +{ + if (m_bankrupt != bankrupt) + { + m_bankrupt = bankrupt; + m_changed = true; + } +} + +void Player::setHasDebt(bool hasDebt) +{ + if (m_hasDebt != hasDebt) + { + m_hasDebt = hasDebt; + m_changed = true; + } +} + +void Player::setHasTurn(const bool hasTurn) +{ + if (m_hasTurn != hasTurn) + { + m_hasTurn = hasTurn; + m_changed = true; + if (m_hasTurn && m_isSelf) + emit gainedTurn(); + } +} + +void Player::setCanRoll(bool canRoll) +{ + if (m_canRoll != canRoll) + { + m_canRoll = canRoll; + m_changed = true; + } +} + +void Player::setCanBuy(bool canBuy) +{ + if (m_canBuy != canBuy) + { + m_canBuy = canBuy; + m_changed = true; + } +} + +void Player::setCanAuction(bool canAuction) +{ + if (m_canAuction != canAuction) + { + m_canAuction = canAuction; + m_changed = true; + } +} + +void Player::setCanUseCard(bool canUseCard) +{ + if (m_canUseCard != canUseCard) + { + m_canUseCard = canUseCard; + m_changed = true; + } +} + +void Player::setInJail(const bool inJail) +{ + if (m_inJail != inJail) + { + m_inJail = inJail; + m_changed = true; + } +} + +void Player::setName(const QString _n) +{ + if (m_name != _n) + { + m_name = _n; + m_changed = true; + } +} + +void Player::setHost(const QString &host) +{ + if (m_host != host) + { + m_host = host; + m_changed = true; + } +} + +void Player::setImage(const QString &image) +{ + if (m_image != image) + { + m_image = image; + m_changed = true; + } +} + +void Player::setMoney(unsigned int money) +{ + if (m_money != money) + { + m_money = money; + m_changed = true; + } +} + +void Player::update(bool force) +{ + if (m_changed || force) + { + emit changed(this); + m_changed = false; + } +} diff --git a/atlantik/libatlantic/player.h b/atlantik/libatlantic/player.h new file mode 100644 index 00000000..571276ef --- /dev/null +++ b/atlantik/libatlantic/player.h @@ -0,0 +1,84 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIC_PLAYER_H +#define LIBATLANTIC_PLAYER_H + +#include +#include + +#include "libatlantic_export.h" + +class Estate; +class Game; + +class LIBATLANTIC_EXPORT Player : public QObject +{ +Q_OBJECT + +public: + Player(int playerId); + + int id() { return m_id; } + void setGame(Game *game); + Game *game(); + void setLocation(Estate *location); + Estate *location() { return m_location; } + void setDestination(Estate *destination); + Estate *destination() { return m_destination; } + void setIsSelf(const bool isSelf) { m_isSelf = isSelf; } + bool isSelf() const { return m_isSelf; } + void setBankrupt(bool bankrupt); + bool isBankrupt() { return m_bankrupt; } + void setHasDebt(bool hasDebt); + bool hasDebt() { return m_hasDebt; } + void setHasTurn(const bool hasTurn); + bool hasTurn() const { return m_hasTurn; } + void setCanRoll(bool canRoll); + bool canRoll() const { return m_canRoll; } + void setCanBuy(bool canBuy); + bool canBuy() const { return m_canBuy; } + void setCanAuction(bool canAuction); + bool canAuction() const { return m_canAuction; } + void setCanUseCard(bool canUseCard); + bool canUseCard() const { return m_canUseCard; } + void setInJail(const bool inJail); + bool inJail() const { return m_inJail; } + void setName(const QString _n); + QString name() const { return m_name; } + void setHost(const QString &host); + QString host() const { return m_host; } + void setImage(const QString &image); + const QString image() const { return m_image; } + void setMoney(unsigned int _m); + unsigned int money() const { return m_money; } + void update(bool force = false); + +signals: + void changed(Player *player); + void gainedTurn(); + +private: + int m_id; + bool m_changed, m_isSelf; + bool m_bankrupt, m_hasDebt, m_hasTurn, m_canRoll, m_canBuy, m_canAuction, m_canUseCard, m_inJail; + unsigned int m_money; + QString m_name, m_host, m_image; + Game *m_game; + Estate *m_location, *m_destination; +}; + +#endif diff --git a/atlantik/libatlantic/trade.cpp b/atlantik/libatlantic/trade.cpp new file mode 100644 index 00000000..b516dc70 --- /dev/null +++ b/atlantik/libatlantic/trade.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include "trade.h" +#include "trade.moc" +#include "player.h" +#include "estate.h" + +Trade::Trade(int tradeId) +{ + m_tradeId = tradeId; + m_revision = 0; + m_changed = m_rejected = false; +} + +void Trade::setRevision(int revision) +{ + m_revision = revision; +} + +int Trade::revision() const +{ + return m_revision; +} + +void Trade::addPlayer(Player *player) +{ + m_playerAcceptMap[player] = false; +} + +void Trade::removePlayer(Player *player) +{ + m_playerAcceptMap[player] = false; +} + +unsigned int Trade::count( bool acceptOnly ) +{ + unsigned int count = 0; + for (QMapIterator it = m_playerAcceptMap.begin() ; it != m_playerAcceptMap.end() ; ++it) + if ( !acceptOnly || it.data() ) + count++; + return count; +} + +void Trade::updateEstate(Estate *estate, Player *to) +{ + TradeEstate *t=0; + + for (QPtrListIterator i(mTradeItems); *i; ++i) + { + t=dynamic_cast(*i); + + if (!t) + continue; + + if (t->estate()==estate) + break; + + t=0; + } + if (t) + { + if (to) + { + if (t->to() == to) + return; + t->setTo(to); + } + else + { + mTradeItems.removeRef(t); + emit itemRemoved(t); + t->deleteLater(); + } + } + else if (estate && to) + { + // new trade + t = new TradeEstate(estate, this, to); + + mTradeItems.append(t); + emit itemAdded(t); + } +} + +void Trade::updateMoney(unsigned int money, Player *from, Player *to) +{ + TradeMoney *t=0; + + for (QPtrListIterator i(mTradeItems); *i; ++i) + { + t=dynamic_cast(*i); + + if (!t) + continue; + + if (t->from() == from && t->to() == to && t->money()) + break; + + t=0; + } + if (t) + { + if (from && to && money) + { + if (t->money() == money) + return; + t->setMoney(money); + } + else + { + mTradeItems.removeRef(t); + emit itemRemoved(t); + t->deleteLater(); + } + } + else if (from && to && money) + { + // new trade + t = new TradeMoney(money, this, from, to); + + mTradeItems.append(t); + emit itemAdded(t); + } +} + +void Trade::updateAccept(Player *player, bool accept) +{ + if (m_playerAcceptMap[player] != accept) + { + m_playerAcceptMap[player] = accept; + m_changed = true; + } +} + +void Trade::reject(Player *player) +{ + m_rejected = true; + emit rejected(player); +} + +void Trade::update(bool force) +{ + if (m_changed || force) + { + emit changed(this); + m_changed = false; + } +} + +TradeItem::TradeItem(Trade *trade, Player *from, Player *to) : mFrom(from), mTo(to), mTrade(trade) +{ + connect(from, SIGNAL(changed(Player *)), this, SLOT(playerChanged())); + connect(to, SIGNAL(changed(Player *)), this, SLOT(playerChanged())); +} + +void TradeItem::playerChanged() +{ + emit changed(this); +} + +TradeEstate::TradeEstate(Estate *estate, Trade *trade, Player *to) : TradeItem(trade, estate->owner(), to), mEstate(estate) +{ +} + +QString TradeEstate::text() const +{ + return mEstate->name(); +} + +TradeMoney::TradeMoney(unsigned int money, Trade *trade, Player *from, Player *to) : TradeItem(trade, from, to), m_money(money) +{ +} + +void TradeMoney::setMoney(unsigned int money) +{ + if (m_money != money) + { + m_money = money; + emit changed(this); + } +} + +QString TradeMoney::text() const +{ + return QString("$%1").arg(m_money); +} diff --git a/atlantik/libatlantic/trade.h b/atlantik/libatlantic/trade.h new file mode 100644 index 00000000..5d8f3c01 --- /dev/null +++ b/atlantik/libatlantic/trade.h @@ -0,0 +1,152 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIC_TRADE_H +#define LIBATLANTIC_TRADE_H + +#include +#include +#include + +#include "libatlantic_export.h" +#include "player.h" + +class Player; +class Trade; +class Estate; + +class LIBATLANTIC_EXPORT TradeItem : public QObject +{ +Q_OBJECT + +public: + TradeItem(Trade *trade, Player *from, Player *to); + virtual ~TradeItem() { } + + Player *from() { return mFrom; } + Player *to() { return mTo; } + void setTo(Player *p) { mTo=p; } + Trade *trade() { return mTrade; } + + /** + * how to visualize this + **/ + virtual QString text() const=0; + +signals: + void changed(TradeItem *); + +private slots: + void playerChanged(); + +private: + Player *mFrom, *mTo; + Trade *mTrade; +}; + +class LIBATLANTIC_EXPORT TradeEstate : public TradeItem +{ +Q_OBJECT + +public: + TradeEstate(Estate *estate, Trade *trade, Player *to); + + Estate *estate() { return mEstate; } + + virtual QString text() const; + +signals: + void updateEstate(Trade *trade, Estate *estate, Player *player); + void updateMoney(Trade *trade, unsigned int money, Player *from, Player *to); + +private: + Estate *mEstate; +}; + +class LIBATLANTIC_EXPORT TradeMoney : public TradeItem +{ +Q_OBJECT + +public: + TradeMoney(unsigned int money, Trade *trade, Player *from, Player *to); + + unsigned int money() const { return m_money; } + void setMoney(unsigned int money); + + virtual QString text() const; + +signals: + void changed(TradeItem *tradeItem); + +private: + unsigned int m_money; +}; + + +class LIBATLANTIC_EXPORT Trade : public QObject +{ +Q_OBJECT + +public: + Trade(int tradeId); + int tradeId() { return m_tradeId; } + + void setRevision(int revision); + int revision() const; + + void addPlayer(Player *player); + void removePlayer(Player *player); + + unsigned int count( bool acceptOnly ); + + bool isRejected() { return m_rejected; } + +private slots: + /** + * tell someone that this changed + **/ +// void changed(TradeItem *i) { emit itemChanged(i); } + +public: + void update(bool force = false); + void updateEstate(Estate *estate, Player *player); + void updateMoney(unsigned int money, Player *from, Player *to); + void updateAccept(Player *player, bool accept); + void reject(Player *player); + +signals: + void changed(Trade *); + void rejected(Player *player); + + void itemAdded(TradeItem *); + void itemRemoved(TradeItem *); + + void updateEstate(Trade *trade, Estate *estate, Player *to); + void updateMoney(Trade *trade, unsigned int money, Player *from, Player *to); + void reject(Trade *trade); + void accept(Trade *trade); + +private: + bool m_changed, m_rejected; + int m_tradeId, m_revision; + + QPtrList mPlayers; + QMap m_playerAcceptMap; + + QPtrList mTradeItems; +}; + +#endif diff --git a/atlantik/libatlantikclient/Makefile.am b/atlantik/libatlantikclient/Makefile.am new file mode 100644 index 00000000..92c79eb2 --- /dev/null +++ b/atlantik/libatlantikclient/Makefile.am @@ -0,0 +1,10 @@ +INCLUDES = -I$(top_srcdir)/atlantik/libatlantic $(all_includes) +lib_LTLIBRARIES = libatlantikclient.la +libatlantikclient_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) -no-undefined -version-info 3:0:2 +libatlantikclient_la_LIBADD = ../libatlantic/libatlantic.la $(LIB_KIO) + +libatlantikclient_la_SOURCES = atlantik_network.cpp monopdprotocol.cpp + +noinst_HEADERS = atlantik_network.h monopdprotocol.h + +METASOURCES = AUTO diff --git a/atlantik/libatlantikclient/atlantik_network.cpp b/atlantik/libatlantikclient/atlantik_network.cpp new file mode 100644 index 00000000..7b1926d3 --- /dev/null +++ b/atlantik/libatlantikclient/atlantik_network.cpp @@ -0,0 +1,928 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atlantik_network.h" + +AtlantikNetwork::AtlantikNetwork(AtlanticCore *atlanticCore) : KExtendedSocket(0, 0, KExtendedSocket::inputBufferedSocket) +{ + m_atlanticCore = atlanticCore; + m_textStream = new QTextStream(this); + m_textStream->setCodec(QTextCodec::codecForName("utf8")); + m_playerId = -1; + m_serverVersion = ""; + + QObject::connect(this, SIGNAL(readyRead()), this, SLOT(slotRead())); + QObject::connect(this, SIGNAL(lookupFinished(int)), + this, SLOT(slotLookupFinished(int))); + QObject::connect(this, SIGNAL(connectionSuccess()), + this, SLOT(slotConnectionSuccess())); + QObject::connect(this, SIGNAL(connectionFailed(int)), + this, SLOT(slotConnectionFailed(int))); +} + +AtlantikNetwork::~AtlantikNetwork(void) +{ + delete m_textStream; +} + +void AtlantikNetwork::rollDice() +{ + writeData(".r"); +} + +void AtlantikNetwork::buyEstate() +{ + writeData(".eb"); +} + +void AtlantikNetwork::auctionEstate() +{ + writeData(".ea"); +} + +void AtlantikNetwork::startGame() +{ + writeData(".gs"); +} + +void AtlantikNetwork::reconnect(const QString &cookie) +{ + writeData(".R" + cookie); +} + +void AtlantikNetwork::leaveGame() +{ + writeData(".gx"); +} + +void AtlantikNetwork::endTurn() +{ + writeData(".E"); +} + +void AtlantikNetwork::setName(QString name) +{ + // Almost deprecated, will be replaced by libmonopdprotocol + writeData(QString(".n%1").arg(name)); +} + +void AtlantikNetwork::tokenConfirmation(Estate *estate) +{ + writeData(QString(".t%1").arg(estate ? estate->id() : -1)); +} + +void AtlantikNetwork::estateToggleMortgage(Estate *estate) +{ + writeData(QString(".em%1").arg(estate ? estate->id() : -1)); +} + +void AtlantikNetwork::estateHouseBuy(Estate *estate) +{ + writeData(QString(".hb%1").arg(estate ? estate->id() : -1)); +} + +void AtlantikNetwork::estateHouseSell(Estate *estate) +{ + writeData(QString(".hs%1").arg(estate ? estate->id() : -1)); +} + +void AtlantikNetwork::newGame(const QString &gameType) +{ + writeData(QString(".gn%1").arg(gameType)); +} + +void AtlantikNetwork::joinGame(int gameId) +{ + writeData(QString(".gj%1").arg(gameId)); +} + +void AtlantikNetwork::cmdChat(QString msg) +{ + writeData(msg); +} + +void AtlantikNetwork::newTrade(Player *player) +{ + writeData(QString(".Tn%1").arg(player ? player->id() : -1)); +} + +void AtlantikNetwork::kickPlayer(Player *player) +{ + writeData(QString(".gk%1").arg(player ? player->id() : -1)); +} + +void AtlantikNetwork::tradeUpdateEstate(Trade *trade, Estate *estate, Player *player) +{ + writeData(QString(".Te%1:%2:%3").arg(trade ? trade->tradeId() : -1).arg(estate ? estate->id() : -1).arg(player ? player->id() : -1)); +} + +void AtlantikNetwork::tradeUpdateMoney(Trade *trade, unsigned int money, Player *pFrom, Player *pTo) +{ + writeData(QString(".Tm%1:%2:%3:%4").arg(trade ? trade->tradeId() : -1).arg(pFrom ? pFrom->id() : -1).arg(pTo ? pTo->id() : -1).arg(money)); +} + +void AtlantikNetwork::tradeReject(Trade *trade) +{ + writeData(QString(".Tr%1").arg(trade ? trade->tradeId() : -1)); +} + +void AtlantikNetwork::tradeAccept(Trade *trade) +{ + writeData(QString(".Ta%1:%2").arg(trade ? trade->tradeId() : -1).arg(trade ? trade->revision() : -1)); +} + +void AtlantikNetwork::auctionBid(Auction *auction, int amount) +{ + writeData(QString(".ab%1:%2").arg(auction ? auction->auctionId() : -1).arg(amount)); +} + +void AtlantikNetwork::setImage(const QString &name) +{ + writeData(QString(".pi%1").arg(name)); +} + +void AtlantikNetwork::jailPay() +{ + writeData(".jp"); +} + +void AtlantikNetwork::jailRoll() +{ + writeData(".jr"); +} + +void AtlantikNetwork::jailCard() +{ + writeData(".jc"); +} + +void AtlantikNetwork::changeOption(int configId, const QString &value) +{ + writeData( QString(".gc%1:%2").arg(configId).arg(value) ); +} + +void AtlantikNetwork::writeData(QString msg) +{ + emit networkEvent(msg, "1rightarrow"); + msg.append("\n"); + if (socketStatus() == KExtendedSocket::connected) + *m_textStream << msg; + else + kdDebug() << "warning: socket not connected!" << endl; +} + +void AtlantikNetwork::slotRead() +{ + if ( socketStatus() != KExtendedSocket::connected ) + return; + + if (canReadLine()) + { + processMsg(m_textStream->readLine()); + // There might be more data + QTimer::singleShot(0, this, SLOT(slotRead())); + } + else + { + // Maximum message size. Messages won't get bigger than 32k anyway, so + // if we didn't receive a newline by now, we probably won't anyway. + if (bytesAvailable() > (1024 * 32)) + flush(); + } +} + +void AtlantikNetwork::processMsg(const QString &msg) +{ + emit networkEvent(msg, "1leftarrow"); + + QDomDocument dom; + dom.setContent(msg); + QDomElement e = dom.documentElement(); + if (e.tagName() != "monopd") + { + // Invalid data, request full update from server + writeData(".f"); + return; + } + QDomNode n = e.firstChild(); + processNode(n); + m_atlanticCore->printDebug(); +} + +void AtlantikNetwork::processNode(QDomNode n) +{ + QDomAttr a; + + for ( ; !n.isNull() ; n = n.nextSibling() ) + { + QDomElement e = n.toElement(); + if(!e.isNull()) + { + if (e.tagName() == "server") + { + a = e.attributeNode( QString("version") ); + if ( !a.isNull() ) + m_serverVersion = a.value(); + + emit receivedHandshake(); + } + else if (e.tagName() == "msg") + { + a = e.attributeNode(QString("type")); + if (!a.isNull()) + { + if (a.value() == "error") + emit msgError(e.attributeNode(QString("value")).value()); + else if (a.value() == "info") + emit msgInfo(e.attributeNode(QString("value")).value()); + else if (a.value() == "chat") + emit msgChat(e.attributeNode(QString("author")).value(), e.attributeNode(QString("value")).value()); + } + } + else if (e.tagName() == "display") + { + int estateId = -1; + + a = e.attributeNode(QString("estateid")); + if (!a.isNull()) + { + estateId = a.value().toInt(); + Estate *estate; + estate = m_atlanticCore->findEstate(a.value().toInt()); + + emit displayDetails(e.attributeNode(QString("text")).value(), e.attributeNode(QString("cleartext")).value().toInt(), e.attributeNode(QString("clearbuttons")).value().toInt(), estate); + + bool hasButtons = false; + for( QDomNode nButtons = n.firstChild() ; !nButtons.isNull() ; nButtons = nButtons.nextSibling() ) + { + QDomElement eButton = nButtons.toElement(); + if (!eButton.isNull() && eButton.tagName() == "button") + { + emit addCommandButton(eButton.attributeNode(QString("command")).value(), eButton.attributeNode(QString("caption")).value(), eButton.attributeNode(QString("enabled")).value().toInt()); + hasButtons = true; + } + } + + if (!hasButtons) + emit addCloseButton(); + } + } + else if (e.tagName() == "client") + { + a = e.attributeNode(QString("playerid")); + if (!a.isNull()) + m_playerId = a.value().toInt(); + + a = e.attributeNode(QString("cookie")); + if (!a.isNull()) + emit clientCookie(a.value()); + } + else if (e.tagName() == "configupdate") + { + int configId = -1; + a = e.attributeNode(QString("configid")); + if (!a.isNull()) + { + configId = a.value().toInt(); + ConfigOption *configOption; + if (!(configOption = m_atlanticCore->findConfigOption(configId))) + configOption = m_atlanticCore->newConfigOption( configId ); + + a = e.attributeNode(QString("name")); + if (configOption && !a.isNull()) + configOption->setName(a.value()); + + a = e.attributeNode(QString("description")); + if (configOption && !a.isNull()) + configOption->setDescription(a.value()); + + a = e.attributeNode(QString("edit")); + if (configOption && !a.isNull()) + configOption->setEdit(a.value().toInt()); + + a = e.attributeNode(QString("value")); + if (configOption && !a.isNull()) + configOption->setValue(a.value()); + + if (configOption) + configOption->update(); + } + + int gameId = -1; + a = e.attributeNode(QString("gameid")); + if (!a.isNull()) + { + gameId = a.value().toInt(); + for( QDomNode nOptions = n.firstChild() ; !nOptions.isNull() ; nOptions = nOptions.nextSibling() ) + { + QDomElement eOption = nOptions.toElement(); + if (!eOption.isNull() && eOption.tagName() == "option") + emit gameOption(eOption.attributeNode(QString("title")).value(), eOption.attributeNode(QString("type")).value(), eOption.attributeNode(QString("value")).value(), eOption.attributeNode(QString("edit")).value(), eOption.attributeNode(QString("command")).value()); + } + emit endConfigUpdate(); + } + } + else if (e.tagName() == "deletegame") + { + a = e.attributeNode(QString("gameid")); + if (!a.isNull()) + { + int gameId = a.value().toInt(); + + Game *game = m_atlanticCore->findGame(gameId); + if (game) + m_atlanticCore->removeGame(game); + } + } + else if (e.tagName() == "gameupdate") + { + int gameId = -1; + + a = e.attributeNode(QString("gameid")); + if (!a.isNull()) + { + gameId = a.value().toInt(); + + Player *playerSelf = m_atlanticCore->playerSelf(); + if ( playerSelf && playerSelf->game() ) + kdDebug() << "gameupdate for " << QString::number(gameId) << " with playerSelf in game " << QString::number(playerSelf->game()->id()) << endl; + else + kdDebug() << "gameupdate for " << QString::number(gameId) << endl; + + + Game *game = 0; + if (gameId == -1) + { + a = e.attributeNode(QString("gametype")); + if ( !a.isNull() && !(game = m_atlanticCore->findGame(a.value())) ) + game = m_atlanticCore->newGame(gameId, a.value()); + } + else if (!(game = m_atlanticCore->findGame(gameId))) + game = m_atlanticCore->newGame(gameId); + + a = e.attributeNode(QString("canbejoined")); + if (game && !a.isNull()) + game->setCanBeJoined(a.value().toInt()); + + a = e.attributeNode(QString("description")); + if (game && !a.isNull()) + game->setDescription(a.value()); + + a = e.attributeNode(QString("name")); + if (game && !a.isNull()) + game->setName(a.value()); + + a = e.attributeNode(QString("players")); + if (game && !a.isNull()) + game->setPlayers(a.value().toInt()); + + a = e.attributeNode(QString("master")); + if (game && !a.isNull()) + { + // Ensure setMaster succeeds by creating player if necessary + Player *player = m_atlanticCore->findPlayer( a.value().toInt() ); + if ( !player ) + player = m_atlanticCore->newPlayer( a.value().toInt() ); + game->setMaster( player ); + } + + QString status = e.attributeNode(QString("status")).value(); + if ( m_serverVersion.left(4) == "0.9." || (playerSelf && playerSelf->game() == game) ) + { + if (status == "config") + emit gameConfig(); + else if (status == "init") + emit gameInit(); + else if (status == "run") + emit gameRun(); + else if (status == "end") + emit gameEnd(); + } + + if (game) + game->update(); + } + } + else if (e.tagName() == "deleteplayer") + { + a = e.attributeNode(QString("playerid")); + if (!a.isNull()) + { + int playerId = a.value().toInt(); + + Player *player = m_atlanticCore->findPlayer(playerId); + if (player) + m_atlanticCore->removePlayer(player); + } + } + else if (e.tagName() == "playerupdate") + { + int playerId = -1; + + a = e.attributeNode(QString("playerid")); + if (!a.isNull()) + { + playerId = a.value().toInt(); + + Player *player; + if (!(player = m_atlanticCore->findPlayer(playerId))) + player = m_atlanticCore->newPlayer( playerId, (m_playerId == playerId) ); + + // Update player name + a = e.attributeNode(QString("name")); + if (player && !a.isNull()) + player->setName(a.value()); + + // Update player game + a = e.attributeNode(QString("game")); + if (player && !a.isNull()) + { + int gameId = a.value().toInt(); + if (gameId == -1) + player->setGame( 0 ); + else + { + // Ensure setGame succeeds by creating game if necessary + Game *game = m_atlanticCore->findGame(a.value().toInt()); + if (!game) + game = m_atlanticCore->newGame(a.value().toInt()); // + player->setGame( game ); + } + } + + // Update player host + a = e.attributeNode(QString("host")); + if (player && !a.isNull()) + player->setHost(a.value()); + + // Update player image/token + a = e.attributeNode(QString("image")); + if (player && !a.isNull()) + player->setImage(a.value()); + + // Update player money + a = e.attributeNode(QString("money")); + if (player && !a.isNull()) + player->setMoney(a.value().toInt()); + + a = e.attributeNode(QString("bankrupt")); + if (player && !a.isNull()) + player->setBankrupt(a.value().toInt()); + + a = e.attributeNode(QString("hasdebt")); + if (player && !a.isNull()) + player->setHasDebt(a.value().toInt()); + + a = e.attributeNode(QString("hasturn")); + if (player && !a.isNull()) + player->setHasTurn(a.value().toInt()); + + // Update whether player can roll + a = e.attributeNode(QString("can_roll")); + if (player && !a.isNull()) + player->setCanRoll(a.value().toInt()); + + // Update whether player can buy + a = e.attributeNode(QString("can_buyestate")); + if (player && !a.isNull()) + player->setCanBuy(a.value().toInt()); + + // Update whether player can auction + a = e.attributeNode(QString("canauction")); + if (player && !a.isNull()) + player->setCanAuction(a.value().toInt()); + + // Update whether player can use a card + a = e.attributeNode(QString("canusecard")); + if (player && !a.isNull()) + player->setCanUseCard(a.value().toInt()); + + // Update whether player is jailed + a = e.attributeNode(QString("jailed")); + if (player && !a.isNull()) + { + player->setInJail(a.value().toInt()); + // TODO: emit signal with player ptr so board can setText and display something + } + + // Update player location + a = e.attributeNode(QString("location")); + if (!a.isNull()) + { + m_playerLocationMap[player] = a.value().toInt(); + + bool directMove = false; + + Estate *estate = m_atlanticCore->findEstate(a.value().toInt()); + + a = e.attributeNode(QString("directmove")); + if (!a.isNull()) + directMove = a.value().toInt(); + + if (player && estate) + { + if (directMove) + player->setLocation(estate); + else + player->setDestination(estate); + } + } + + if (player) + player->update(); + } + } + else if (e.tagName() == "estategroupupdate") + { + a = e.attributeNode(QString("groupid")); + if (!a.isNull()) + { + int groupId = a.value().toInt(); + + EstateGroup *estateGroup = 0; + bool b_newEstateGroup = false; + + if (!(estateGroup = m_atlanticCore->findEstateGroup(groupId))) + { + // Create EstateGroup object + estateGroup = m_atlanticCore->newEstateGroup(a.value().toInt()); + b_newEstateGroup = true; + } + + a = e.attributeNode(QString("name")); + if (estateGroup && !a.isNull()) + estateGroup->setName(a.value()); + + // Emit signal so GUI implementations can create view(s) + // TODO: port to atlanticcore and create view there + if (estateGroup) + { + if (b_newEstateGroup) + emit newEstateGroup(estateGroup); + estateGroup->update(); + } + } + } + else if (e.tagName() == "estateupdate") + { + int estateId = -1; + + a = e.attributeNode(QString("estateid")); + if (!a.isNull()) + { + estateId = a.value().toInt(); + + Estate *estate = 0; + bool b_newEstate = false; + + // FIXME: allow any estateId, GUI should not use it to determin its geometry + if (estateId >= 0 && estateId < 100 && !(estate = m_atlanticCore->findEstate(a.value().toInt()))) + { + // Create estate object + estate = m_atlanticCore->newEstate(estateId); + b_newEstate = true; + + QObject::connect(estate, SIGNAL(estateToggleMortgage(Estate *)), this, SLOT(estateToggleMortgage(Estate *))); + QObject::connect(estate, SIGNAL(estateHouseBuy(Estate *)), this, SLOT(estateHouseBuy(Estate *))); + QObject::connect(estate, SIGNAL(estateHouseSell(Estate *)), this, SLOT(estateHouseSell(Estate *))); + QObject::connect(estate, SIGNAL(newTrade(Player *)), this, SLOT(newTrade(Player *))); + + // Players without estate should get one + Player *player = 0; + QPtrList playerList = m_atlanticCore->players(); + for (QPtrListIterator it(playerList); (player = *it) ; ++it) + if (m_playerLocationMap[player] == estate->id()) + player->setLocation(estate); + } + + a = e.attributeNode(QString("name")); + if (estate && !a.isNull()) + estate->setName(a.value()); + + a = e.attributeNode(QString("color")); + if (estate && !a.isNull() && !a.value().isEmpty()) + estate->setColor(a.value()); + + a = e.attributeNode(QString("bgcolor")); + if (estate && !a.isNull()) + estate->setBgColor(a.value()); + + a = e.attributeNode(QString("owner")); + Player *player = m_atlanticCore->findPlayer(a.value().toInt()); + if (estate && !a.isNull()) + estate->setOwner(player); + + a = e.attributeNode(QString("houses")); + if (estate && !a.isNull()) + estate->setHouses(a.value().toInt()); + + a = e.attributeNode(QString("mortgaged")); + if (estate && !a.isNull()) + estate->setIsMortgaged(a.value().toInt()); + + a = e.attributeNode(QString("group")); + if (!a.isNull()) + { + EstateGroup *estateGroup = m_atlanticCore->findEstateGroup(a.value().toInt()); + if (estate) + estate->setEstateGroup(estateGroup); + } + + a = e.attributeNode(QString("can_toggle_mortgage")); + if (estate && !a.isNull()) + estate->setCanToggleMortgage(a.value().toInt()); + + a = e.attributeNode(QString("can_be_owned")); + if (estate && !a.isNull()) + estate->setCanBeOwned(a.value().toInt()); + + a = e.attributeNode(QString("can_buy_houses")); + if (estate && !a.isNull()) + estate->setCanBuyHouses(a.value().toInt()); + + a = e.attributeNode(QString("can_sell_houses")); + if (estate && !a.isNull()) + estate->setCanSellHouses(a.value().toInt()); + + a = e.attributeNode(QString("price")); + if (estate && !a.isNull()) + estate->setPrice(a.value().toInt()); + + a = e.attributeNode(QString("houseprice")); + if (estate && !a.isNull()) + estate->setHousePrice(a.value().toInt()); + + a = e.attributeNode(QString("sellhouseprice")); + if (estate && !a.isNull()) + estate->setHouseSellPrice(a.value().toInt()); + + a = e.attributeNode(QString("mortgageprice")); + if (estate && !a.isNull()) + estate->setMortgagePrice(a.value().toInt()); + + a = e.attributeNode(QString("unmortgageprice")); + if (estate && !a.isNull()) + estate->setUnmortgagePrice(a.value().toInt()); + + a = e.attributeNode(QString("money")); + if (estate && !a.isNull()) + estate->setMoney(a.value().toInt()); + + // Emit signal so GUI implementations can create view(s) + // TODO: port to atlanticcore and create view there + if (estate) + { + if (b_newEstate) + emit newEstate(estate); + estate->update(); + } + } + } + else if (e.tagName() == "tradeupdate") + { + a = e.attributeNode(QString("tradeid")); + if (!a.isNull()) + { + int tradeId = a.value().toInt(); + + Trade *trade = m_atlanticCore->findTrade(tradeId); + if (!trade) + { + // Create trade object + trade = m_atlanticCore->newTrade(tradeId); + + QObject::connect(trade, SIGNAL(updateEstate(Trade *, Estate *, Player *)), this, SLOT(tradeUpdateEstate(Trade *, Estate *, Player *))); + QObject::connect(trade, SIGNAL(updateMoney(Trade *, unsigned int, Player *, Player *)), this, SLOT(tradeUpdateMoney(Trade *, unsigned int, Player *, Player *))); + QObject::connect(trade, SIGNAL(reject(Trade *)), this, SLOT(tradeReject(Trade *))); + QObject::connect(trade, SIGNAL(accept(Trade *)), this, SLOT(tradeAccept(Trade *))); + } + + a = e.attributeNode(QString("revision")); + if (trade && !a.isNull()) + trade->setRevision(a.value().toInt()); + + QString type = e.attributeNode(QString("type")).value(); + if (type=="new") + { + // TODO: trade->setActor + // Player *player = m_atlanticCore->findPlayer(e.attributeNode(QString("actor")).value().toInt()); + // if (trade && player) + // trade->setActor(player); + + QDomNode n_player = n.firstChild(); + while(!n_player.isNull()) + { + QDomElement e_player = n_player.toElement(); + if (!e_player.isNull() && e_player.tagName() == "tradeplayer") + { + Player *player = m_atlanticCore->findPlayer(e_player.attributeNode(QString("playerid")).value().toInt()); + if (trade && player) + { + trade->addPlayer(player); + QObject::connect(m_atlanticCore, SIGNAL(removePlayer(Player *)), trade, SLOT(removePlayer(Player *))); + } + } + n_player = n_player.nextSibling(); + } + } + else if (type=="accepted" && trade) + emit msgTradeUpdateAccepted(trade); + else if (type=="completed" && trade) + { + m_atlanticCore->removeTrade(trade); + trade = 0; + } + else if (type=="rejected") + { + Player *player = m_atlanticCore->findPlayer(e.attributeNode(QString("actor")).value().toInt()); + if (trade) + trade->reject(player); + if ( player && player == m_atlanticCore->playerSelf() ) + { + m_atlanticCore->removeTrade(trade); + trade = 0; + } + } + else + { + // No type specified, edit is implied. + + QDomNode n_child = n.firstChild(); + while(!n_child.isNull()) + { + QDomElement e_child = n_child.toElement(); + if (!e_child.isNull()) + { + if (e_child.tagName() == "tradeplayer") + { + a = e_child.attributeNode(QString("playerid")); + if (!a.isNull()) + { + Player *player = m_atlanticCore->findPlayer(a.value().toInt()); + + a = e_child.attributeNode(QString("accept")); + if (trade && player && !a.isNull()) + trade->updateAccept(player, (bool)(a.value().toInt())); + } + } + else if (e_child.tagName() == "tradeestate") + { + a = e_child.attributeNode(QString("estateid")); + if (!a.isNull()) + { + Estate *estate = m_atlanticCore->findEstate(a.value().toInt()); + a = e_child.attributeNode(QString("targetplayer")); + if (!a.isNull()) + { + Player *player = m_atlanticCore->findPlayer(a.value().toInt()); + // Allow NULL player, it will remove the component + if (trade && estate) + trade->updateEstate(estate, player); + } + } + } + else if (e_child.tagName() == "trademoney") + { + Player *pFrom = 0, *pTo = 0; + + a = e_child.attributeNode(QString("playerfrom")); + if (!a.isNull()) + pFrom = m_atlanticCore->findPlayer(a.value().toInt()); + + a = e_child.attributeNode(QString("playerto")); + if (!a.isNull()) + pTo = m_atlanticCore->findPlayer(a.value().toInt()); + + a = e_child.attributeNode(QString("money")); + kdDebug() << "tradeupdatemoney" << (pFrom ? "1" : "0") << (pTo ? "1" : "0") << (a.isNull() ? "0" : "1") << endl; + if (trade && pFrom && pTo && !a.isNull()) + trade->updateMoney(a.value().toInt(), pFrom, pTo); + } + } + n_child = n_child.nextSibling(); + } + } + + if (trade) + trade->update(); + } + } + else if (e.tagName() == "auctionupdate") + { + a = e.attributeNode(QString("auctionid")); + if (!a.isNull()) + { + int auctionId = a.value().toInt(); + + Auction *auction; + bool b_newAuction = false; + if (!(auction = m_auctions[auctionId])) + { + // Create auction object + auction = m_atlanticCore->newAuction(auctionId, m_atlanticCore->findEstate(e.attributeNode(QString("estateid")).value().toInt())); + m_auctions[auctionId] = auction; + + QObject::connect(auction, SIGNAL(bid(Auction *, int)), this, SLOT(auctionBid(Auction *, int))); + + b_newAuction = true; + } + + a = e.attributeNode(QString("highbidder")); + if (!a.isNull()) + { + Player *player = m_atlanticCore->findPlayer(e.attributeNode(QString("highbidder")).value().toInt()); + a = e.attributeNode(QString("highbid")); + if (auction && !a.isNull()) + auction->newBid(player, a.value().toInt()); + } + + a = e.attributeNode(QString("status")); + if (auction && !a.isNull()) + { + int status = a.value().toInt(); + auction->setStatus(status); + + // TODO: find a good way to visualise "sold!" + if (status == 3) + { + m_atlanticCore->delAuction(auction); + m_auctions[auctionId] = 0; + auction = 0; + } + } + + // Emit signal so GUI implementations can create view(s) + // TODO: port to atlanticcore and create view there + if (auction) + { + if (b_newAuction) + emit newAuction(auction); + auction->update(); + } + } + } + else + kdDebug() << "ignored TAG: " << e.tagName() << endl; + } + // TODO: remove permanently? + // QDomNode node = n.firstChild(); + // processNode(node); + } +} + +void AtlantikNetwork::serverConnect(const QString host, int port) +{ + setAddress(host, port); + enableRead(true); + emit msgStatus(i18n("Connecting to %1:%2...").arg(host).arg(QString::number(port)), "connect_creating"); + startAsyncConnect(); +} + +void AtlantikNetwork::slotLookupFinished(int count) +{ + emit msgStatus(i18n("Server host name lookup finished...")); +} + +void AtlantikNetwork::slotConnectionSuccess() +{ + emit msgStatus(i18n("Connected to %1:%2.").arg(host()).arg(port()), "connect_established"); +} + +void AtlantikNetwork::slotConnectionFailed(int error) +{ + emit msgStatus(i18n("Connection failed! Error code: %1").arg(error), "connect_no"); +} + +#include "atlantik_network.moc" diff --git a/atlantik/libatlantikclient/atlantik_network.h b/atlantik/libatlantikclient/atlantik_network.h new file mode 100644 index 00000000..087a01be --- /dev/null +++ b/atlantik/libatlantikclient/atlantik_network.h @@ -0,0 +1,155 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIK_NETWORK_H +#define LIBATLANTIK_NETWORK_H + +#include + +#include +#include "libatlantic_export.h" +class QDomNode; +class QTextStream; + +class AtlanticCore; + +class Player; +class Estate; +class EstateGroup; +class Trade; +class Auction; + +class LIBATLANTIC_EXPORT AtlantikNetwork : public KExtendedSocket +{ +Q_OBJECT + +public: + AtlantikNetwork(AtlanticCore *atlanticCore); + virtual ~AtlantikNetwork(void); + void setName(QString name); + void cmdChat(QString msg); + +private slots: + void writeData(QString msg); + void rollDice(); + void endTurn(); + void newGame(const QString &gameType); + void reconnect(const QString &cookie); + void startGame(); + void buyEstate(); + void auctionEstate(); + void estateToggleMortgage(Estate *estate); + void estateHouseBuy(Estate *estate); + void estateHouseSell(Estate *estate); + void jailCard(); + void jailPay(); + void jailRoll(); + void newTrade(Player *player); + void kickPlayer(Player *player); + void tokenConfirmation(Estate *); + void tradeUpdateEstate(Trade *trade, Estate *estate, Player *player); + void tradeUpdateMoney(Trade *trade, unsigned int money, Player *pFrom, Player *pTo); + void tradeReject(Trade *trade); + void tradeAccept(Trade *trade); + void auctionBid(Auction *auction, int amount); + void changeOption(int, const QString &value); + void slotLookupFinished(int count); + void slotConnectionSuccess(); + void slotConnectionFailed(int error); + +public slots: + void serverConnect(const QString host, int port); + void joinGame(int gameId); + void leaveGame(); + void slotRead(); + void setImage(const QString &name); + +signals: + /** + * A new estate was created. This signal might be replaced with one in + * the AtlanticCore class in the future, but it is here now because we + * do not want GUI implementations to create a view until the + * estateupdate message has been fully parsed. + * + * @param estate Created Estate object. + */ + void newEstate(Estate *estate); + + /** + * A new estate group was created. This signal might be replaced with + * one in the AtlanticCore class in the future, but it is here now + * because we do not want GUI implementations to create a view until the + * estategroupupdate message has been fully parsed. + * + * @param estateGroup Created EstateGroup object. + */ + void newEstateGroup(EstateGroup *estateGroup); + + void msgInfo(QString); + void msgError(QString); + void msgChat(QString, QString); + void msgStatus(const QString &data, const QString &icon = QString::null); + void networkEvent(const QString &data, const QString &icon); + + void displayDetails(QString text, bool clearText, bool clearButtons, Estate *estate = 0); + void addCommandButton(QString command, QString caption, bool enabled); + void addCloseButton(); + + void gameOption(QString title, QString type, QString value, QString edit, QString command); + void endConfigUpdate(); + + void gameConfig(); + void gameInit(); + void gameRun(); + void gameEnd(); + + /** + * The trade has been completed. Emitted after all necessary estate and + * player updates are processed. + * + * @param trade Trade + */ + void msgTradeUpdateAccepted(Trade *trade); + + /** + * One of the players rejected the trade and the trade object has been + * deleted from the server. + * + * @param trade Trade + * @param playerId Unique player identifier of rejecting player + */ + void msgTradeUpdateRejected(Trade *trade, int playerId); + + void newAuction(Auction *auction); + void auctionCompleted(Auction *auction); + void receivedHandshake(); + void clientCookie(QString cookie); + +private: + void processMsg(const QString &msg); + void processNode(QDomNode); + + AtlanticCore *m_atlanticCore; + QTextStream *m_textStream; + + int m_playerId; + QString m_serverVersion; + + QMap m_playerLocationMap; + QMap m_auctions; +}; + +#endif diff --git a/atlantik/libatlantikclient/monopdprotocol.cpp b/atlantik/libatlantikclient/monopdprotocol.cpp new file mode 100644 index 00000000..5f6c401b --- /dev/null +++ b/atlantik/libatlantikclient/monopdprotocol.cpp @@ -0,0 +1,80 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +/* +#include +#include +#include +#include +#include +#include +*/ + +#include + +#include "monopdprotocol.h" +#include "monopdprotocol.moc" + +MonopdProtocol::MonopdProtocol() : QObject() +{ +} + +void MonopdProtocol::auctionEstate() +{ + sendData(QString::fromLatin1(".ea")); +} + +void MonopdProtocol::buyEstate() +{ + sendData(QString::fromLatin1(".eb")); +} + +void MonopdProtocol::confirmTokenLocation(Estate *estate) +{ + QString data(".t"); + data.append(QString::number(estate ? estate->id() : -1)); + sendData(data); +} + +void MonopdProtocol::endTurn() +{ + sendData(QString::fromLatin1(".E")); +} + +void MonopdProtocol::rollDice() +{ + sendData(QString::fromLatin1(".r")); +} + +void MonopdProtocol::setName(QString name) +{ + QString data(".n"); + data.append(name); + sendData(data); +} + +void MonopdProtocol::startGame() +{ + sendData(QString::fromLatin1(".gs")); +} + +void MonopdProtocol::sendData(QString) +{ + // Your reimplementation of this method should send send data over the + // network. +} diff --git a/atlantik/libatlantikclient/monopdprotocol.h b/atlantik/libatlantikclient/monopdprotocol.h new file mode 100644 index 00000000..0fc16ad8 --- /dev/null +++ b/atlantik/libatlantikclient/monopdprotocol.h @@ -0,0 +1,58 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +// WARNING: this codebase is not being used yet. Please use AtlantikNetwork +// until the protocol seperation has been completed. + +#ifndef MONOPDPROTOCOL_H_H +#define MONOPDPROTOCOL_H_H + +#include + +class QString; + +/* +class AtlanticCore; + +class Player; +class EstateGroup; +class Trade; +class Auction; +*/ + +class Estate; + +class MonopdProtocol : public QObject +{ +Q_OBJECT + +public: + MonopdProtocol(); + +private slots: + void auctionEstate(); + void buyEstate(); + void confirmTokenLocation(Estate *estate); + void endTurn(); + void rollDice(); + void setName(QString name); + void startGame(); + +private: + virtual void sendData(QString data); +}; + +#endif diff --git a/atlantik/libatlantikui/Makefile.am b/atlantik/libatlantikui/Makefile.am new file mode 100644 index 00000000..2e3bbbed --- /dev/null +++ b/atlantik/libatlantikui/Makefile.am @@ -0,0 +1,15 @@ +INCLUDES = -I$(top_srcdir)/atlantik/libatlantic $(all_includes) +lib_LTLIBRARIES = libatlantikui.la +libatlantikui_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) -no-undefined -version-info 3:0:2 +libatlantikui_la_LIBADD = ../libatlantic/libatlantic.la $(LIB_KIO) + +libatlantikui_la_SOURCES = auction_widget.cpp board.cpp estatedetails.cpp \ + estateview.cpp kwrappedlistviewitem.cpp portfolioestate.cpp \ + portfolioview.cpp token.cpp trade_widget.cpp + +libatlantikuiincludedir = $(includedir)/atlantik/ui +libatlantikuiinclude_HEADERS = auction_widget.h board.h estatedetails.h \ + estateview.h kwrappedlistviewitem.h portfolioestate.h \ + portfolioview.h token.h trade_widget.h libatlantikui_export.h + +METASOURCES = AUTO diff --git a/atlantik/libatlantikui/auction_widget.cpp b/atlantik/libatlantikui/auction_widget.cpp new file mode 100644 index 00000000..e7dc7fd8 --- /dev/null +++ b/atlantik/libatlantikui/auction_widget.cpp @@ -0,0 +1,141 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "auction_widget.moc" + +AuctionWidget::AuctionWidget(AtlanticCore *atlanticCore, Auction *auction, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_atlanticCore = atlanticCore; + + m_auction = auction; + connect(m_auction, SIGNAL(changed()), this, SLOT(auctionChanged())); + connect(m_auction, SIGNAL(updateBid(Player *, int)), this, SLOT(updateBid(Player *, int))); + connect(this, SIGNAL(bid(Auction *, int)), m_auction, SIGNAL(bid(Auction *, int))); + + m_mainLayout = new QVBoxLayout(this, KDialog::marginHint()); + Q_CHECK_PTR(m_mainLayout); + + // Player list + Estate *estate = auction->estate(); + m_playerGroupBox = new QVGroupBox(estate ? i18n("Auction: %1").arg(estate->name()) : i18n("Auction"), this, "groupBox"); + m_mainLayout->addWidget(m_playerGroupBox); + + m_playerList = new KListView(m_playerGroupBox); + m_playerList->addColumn(i18n("Player")); + m_playerList->addColumn(i18n("Bid")); + m_playerList->setSorting(1, false); + + KListViewItem *item; + Player *player, *pSelf = m_atlanticCore->playerSelf(); + + QPtrList playerList = m_atlanticCore->players(); + for (QPtrListIterator it(playerList); *it; ++it) + { + if ( (player = *it) && player->game() == pSelf->game() ) + { + item = new KListViewItem(m_playerList, player->name(), QString("0")); + item->setPixmap(0, QPixmap(SmallIcon("personal"))); + m_playerItems[player] = item; + + connect(player, SIGNAL(changed(Player *)), this, SLOT(playerChanged(Player *))); + } + } + + // Bid spinbox and button + QHBox *bidBox = new QHBox(this); + m_mainLayout->addWidget(bidBox); + + m_bidSpinBox = new QSpinBox(1, 10000, 1, bidBox); + + KPushButton *bidButton = new KPushButton(i18n("Make Bid"), bidBox, "bidButton"); + connect(bidButton, SIGNAL(clicked()), this, SLOT(slotBidButtonClicked())); + + // Status label + m_statusLabel = new QLabel(this, "statusLabel"); + m_mainLayout->addWidget(m_statusLabel); +} + +void AuctionWidget::auctionChanged() +{ + QString status; + switch (m_auction->status()) + { + case 1: + status = i18n("Going once..."); + break; + + case 2: + status = i18n("Going twice..."); + break; + + case 3: + status = i18n("Sold!"); + break; + + default: + status = QString::null; + } + m_statusLabel->setText(status); +} + +void AuctionWidget::playerChanged(Player *player) +{ + if (!player) + return; + + QListViewItem *item; + if (!(item = m_playerItems[player])) + return; + + item->setText(0, player->name()); + m_playerList->triggerUpdate(); +} + +void AuctionWidget::updateBid(Player *player, int amount) +{ + if (!player) + return; + + QListViewItem *item; + if (!(item = m_playerItems[player])) + return; + + item->setText(1, QString::number(amount)); + m_bidSpinBox->setMinValue(amount+1); + m_playerList->triggerUpdate(); +} + +void AuctionWidget::slotBidButtonClicked() +{ + emit bid(m_auction, m_bidSpinBox->value()); +} diff --git a/atlantik/libatlantikui/auction_widget.h b/atlantik/libatlantikui/auction_widget.h new file mode 100644 index 00000000..a87b8fc4 --- /dev/null +++ b/atlantik/libatlantikui/auction_widget.h @@ -0,0 +1,65 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_AUCTION_WIDGET_H +#define ATLANTIK_AUCTION_WIDGET_H + +#include +#include +#include + +#include + +class QVGroupBox; +class QSpinBox; +class QLabel; + +class KListViewItem; + +class AtlanticCore; +class Player; +class Auction; + +class AuctionWidget : public QWidget +{ +Q_OBJECT + +public: + AuctionWidget(AtlanticCore *atlanticCore, Auction *auction, QWidget *parent, const char *name=0); + +private slots: + void auctionChanged(); + void playerChanged(Player *player); + void updateBid(Player *player, int amount); + void slotBidButtonClicked(); + +signals: + void bid(Auction *auction, int amount); + +private: + QVBoxLayout *m_mainLayout; + QVGroupBox *m_playerGroupBox; + QSpinBox *m_bidSpinBox; + QMap m_playerItems; + QLabel *m_statusLabel; + + KListView *m_playerList; + + AtlanticCore *m_atlanticCore; + Auction *m_auction; +}; + +#endif diff --git a/atlantik/libatlantikui/board.cpp b/atlantik/libatlantikui/board.cpp new file mode 100644 index 00000000..a4fdf3ce --- /dev/null +++ b/atlantik/libatlantikui/board.cpp @@ -0,0 +1,601 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "auction_widget.h" +#include "estatedetails.h" +#include "estateview.h" +#include "token.h" + +#include "board.h" +#include "board.moc" + +AtlantikBoard::AtlantikBoard(AtlanticCore *atlanticCore, int maxEstates, DisplayMode mode, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_atlanticCore = atlanticCore; + m_maxEstates = maxEstates; + m_mode = mode; + m_animateTokens = false; + m_lastServerDisplay = 0; + + setMinimumSize(QSize(500, 500)); + + int sideLen = maxEstates/4; + + // Animated token movement + m_movingToken = 0; + m_timer = new QTimer(this); + connect(m_timer, SIGNAL(timeout()), this, SLOT(slotMoveToken())); + m_resumeTimer = false; + + m_gridLayout = new QGridLayout(this, sideLen+1, sideLen+1); + for(int i=0;i<=sideLen;i++) + { + if (i==0 || i==sideLen) + { + m_gridLayout->setRowStretch(i, 3); + m_gridLayout->setColStretch(i, 3); + } + else + { + m_gridLayout->setRowStretch(i, 2); + m_gridLayout->setColStretch(i, 2); + } + } + +// spacer = new QWidget(this); +// m_gridLayout->addWidget(spacer, sideLen, sideLen); // SE + + m_displayQueue.setAutoDelete(true); + m_estateViews.setAutoDelete(true); + m_tokens.setAutoDelete(true); + + displayDefault(); +} + +AtlantikBoard::~AtlantikBoard() +{ + reset(); +} + +void AtlantikBoard::reset() +{ + kdDebug() << "AtlantikBoard::reset" << endl; + + m_tokens.clear(); + m_estateViews.clear(); + m_displayQueue.clear(); + m_lastServerDisplay = 0; + m_movingToken = 0; +} + +void AtlantikBoard::setViewProperties(bool indicateUnowned, bool highliteUnowned, bool darkenMortgaged, bool quartzEffects, bool animateTokens) +{ + if (m_animateTokens != animateTokens) + m_animateTokens = animateTokens; + + // Update EstateViews + EstateView *estateView; + for (QPtrListIterator it(m_estateViews); *it; ++it) + if ((estateView = dynamic_cast(*it))) + estateView->setViewProperties(indicateUnowned, highliteUnowned, darkenMortgaged, quartzEffects); +} + +int AtlantikBoard::heightForWidth(int width) +{ + return width; +} + +EstateView *AtlantikBoard::findEstateView(Estate *estate) +{ + EstateView *estateView; + for (QPtrListIterator i(m_estateViews); *i; ++i) + { + estateView = dynamic_cast(*i); + if (estateView && estateView->estate() == estate) + return estateView; + } + return 0; +} + +void AtlantikBoard::addEstateView(Estate *estate, bool indicateUnowned, bool highliteUnowned, bool darkenMortgaged, bool quartzEffects) +{ + QString icon = QString(); + int estateId = estate->id(); + EstateOrientation orientation = North; + int sideLen = m_gridLayout->numRows() - 1; + + if (estateId < sideLen) + orientation = North; + else if (estateId < 2*sideLen) + orientation = East; + else if (estateId < 3*sideLen) + orientation = South; + else //if (estateId < 4*sideLen) + orientation = West; + + EstateView *estateView = new EstateView(estate, orientation, icon, indicateUnowned, highliteUnowned, darkenMortgaged, quartzEffects, this, "estateview"); + m_estateViews.append(estateView); + + connect(estate, SIGNAL(changed()), estateView, SLOT(estateChanged())); + connect(estateView, SIGNAL(estateToggleMortgage(Estate *)), estate, SIGNAL(estateToggleMortgage(Estate *))); + connect(estateView, SIGNAL(LMBClicked(Estate *)), estate, SIGNAL(LMBClicked(Estate *))); + connect(estateView, SIGNAL(estateHouseBuy(Estate *)), estate, SIGNAL(estateHouseBuy(Estate *))); + connect(estateView, SIGNAL(estateHouseSell(Estate *)), estate, SIGNAL(estateHouseSell(Estate *))); + connect(estateView, SIGNAL(newTrade(Player *)), estate, SIGNAL(newTrade(Player *))); + + // Designer has its own LMBClicked slot + if (m_mode == Play) + connect(estateView, SIGNAL(LMBClicked(Estate *)), this, SLOT(prependEstateDetails(Estate *))); + + if (estateIdaddWidget(estateView, sideLen, sideLen-estateId); + else if (estateId<2*sideLen) + m_gridLayout->addWidget(estateView, 2*sideLen-estateId, 0); + else if (estateId<3*sideLen) + m_gridLayout->addWidget(estateView, 0, estateId-2*sideLen); + else + m_gridLayout->addWidget(estateView, estateId-3*sideLen, sideLen); + + estateView->show(); + + if (m_atlanticCore) + { + Player *player = 0; + QPtrList playerList = m_atlanticCore->players(); + for (QPtrListIterator it(playerList); (player = *it) ; ++it) + if (player->location() == estate) + addToken(player); + } +} + +void AtlantikBoard::addAuctionWidget(Auction *auction) +{ + AuctionWidget *auctionW = new AuctionWidget(m_atlanticCore, auction, this); + m_lastServerDisplay = auctionW; + m_displayQueue.prepend(auctionW); + updateCenter(); + + connect(auction, SIGNAL(completed()), this, SLOT(displayDefault())); +} + +Token *AtlantikBoard::findToken(Player *player) +{ + Token *token = 0; + for (QPtrListIterator it(m_tokens); (token = *it) ; ++it) + if (token->player() == player) + return token; + return 0; +} + +void AtlantikBoard::addToken(Player *player) +{ + if (!player->location()) + { + kdDebug() << "addToken ignored - estateView null" << endl; + return; + } + + Player *playerSelf = 0; + if (m_atlanticCore) + playerSelf = m_atlanticCore->playerSelf(); + + if (playerSelf && playerSelf->game() != player->game() ) + { + kdDebug() << "addToken ignored - not in same game as playerSelf" << endl; + return; + } + + kdDebug() << "addToken" << endl; + + Token *token = new Token(player, this, "token"); + m_tokens.append(token); + connect(player, SIGNAL(changed(Player *)), token, SLOT(playerChanged())); + + jumpToken(token); + + // Timer to reinit the gameboard _after_ event loop + QTimer::singleShot(100, this, SLOT(slotResizeAftermath())); +} + +void AtlantikBoard::playerChanged(Player *player) +{ + kdDebug() << "playerChanged: playerLoc " << (player->location() ? player->location()->name() : "none") << endl; + + Player *playerSelf = 0; + if (m_atlanticCore) + playerSelf = m_atlanticCore->playerSelf(); + + // Update token + Token *token = findToken(player); + if (token) + { + kdDebug() << "playerChanged: tokenLoc " << (token->location() ? token->location()->name() : "none") << endl; + if (player->isBankrupt() || (playerSelf && playerSelf->game() != player->game()) ) + token->hide(); + if (player->hasTurn()) + token->raise(); + + bool jump = false, move = false; + + if (token->inJail() != player->inJail()) + { + token->setInJail(player->inJail()); + + // If any status such as jail is ever allowed to + // change in the future, during movement, this needs + // to be addressed in moveToken and subsequent steps. + if (token != m_movingToken) + jump = true; + } + + if (token->location() != player->location()) + { + token->setLocation(player->location()); + jump = true; + } + + if (player->destination() && token->destination() != player->destination()) + { + if (m_animateTokens) + { + token->setDestination(player->destination()); + move = true; + } + else + { + token->setLocation(player->destination()); + jump = true; + } + } + + if (move) + moveToken(token); + else if (jump) + jumpToken(token); + } + else + addToken(player); +} + +void AtlantikBoard::removeToken(Player *player) +{ + Token *token = findToken(player); + if (!token) + return; + + if (token == m_movingToken) + { + m_timer->stop(); + m_movingToken = 0; + } + + m_tokens.remove(token); +} + +void AtlantikBoard::jumpToken(Token *token) +{ + if (!token || !token->location()) + return; + + kdDebug() << "jumpToken to " << token->location()->name() << endl; + + QPoint tGeom = calculateTokenDestination(token); + token->setGeometry(tGeom.x(), tGeom.y(), token->width(), token->height()); + + Player *player = token->player(); + if (player) + { + player->setLocation(token->location()); + player->setDestination(0); + + if (token->isHidden() && !player->isBankrupt()) + token->show(); + } + + if (token == m_movingToken) + { + m_timer->stop(); + + if (!m_resumeTimer) + m_movingToken = 0; + } + + emit tokenConfirmation(token->location()); +} + +void AtlantikBoard::moveToken(Token *token) +{ + kdDebug() << "moveToken to " << token->destination()->name() << endl; + + m_movingToken = token; + + // Start timer + m_timer->start(15); +} + +QPoint AtlantikBoard::calculateTokenDestination(Token *token, Estate *eDest) +{ + if (!eDest) + eDest = token->location(); + + EstateView *evDest = findEstateView(eDest); + if (!evDest) + return QPoint(0, 0); + + int x = 0, y = 0; + if (token->player()->inJail()) + { + x = evDest->geometry().right() - token->width() - 2; + y = evDest->geometry().top(); + } + else + { + x = evDest->geometry().center().x() - (token->width()/2); + y = evDest->geometry().center().y() - (token->height()/2); + +/* + // Re-center because of EstateView headers + switch(evDest->orientation()) + { + case North: + y += evDest->height()/8; break; + case East: + x -= evDest->width()/8; break; + case South: + y -= evDest->height()/8; break; + case West: + x += evDest->width()/8; break; + } +*/ + } + return QPoint(x, y); +} + +void AtlantikBoard::slotMoveToken() +{ + // Requires a core with estates to operate on + if (!m_atlanticCore) + { + kdDebug() << "slotMoveToken ignored - no atlanticCore" << endl; + return; + } + + // Do we actually have a token to move? + if (!m_movingToken) + { + m_timer->stop(); + return; + } + + // Where are we? + int xCurrent = m_movingToken->geometry().x(); + int yCurrent = m_movingToken->geometry().y(); + + // Where do we want to go today? + Estate *eDest = m_atlanticCore->estateAfter(m_movingToken->location()); + QPoint tGeom = calculateTokenDestination(m_movingToken, eDest); + + int xDest = tGeom.x(); + int yDest = tGeom.y(); + + if (xDest - xCurrent > 1) + xDest = xCurrent + 2; + else if (xCurrent - xDest > 1) + xDest = xCurrent - 2; + else + xDest = xCurrent; + + if (yDest - yCurrent > 1) + yDest = yCurrent + 2; + else if (yCurrent - yDest > 1) + yDest = yCurrent - 2; + else + yDest = yCurrent; + +// kdDebug() << "TOKEN: at " << xCurrent << "," << yCurrent << " and going to " << xDest << "," << yDest << endl; + + if (xCurrent != xDest || yCurrent != yDest) + { + m_movingToken->setGeometry(xDest, yDest, m_movingToken->width(), m_movingToken->height()); + return; + } + + // We have arrived at our destination! + m_movingToken->setLocation(eDest); + m_movingToken->player()->setLocation(eDest); + emit tokenConfirmation(eDest); + + // We have arrived at our _final_ destination! + if (eDest == m_movingToken->destination()) + { + m_movingToken->setDestination(0); + m_movingToken->player()->setDestination(0); + + m_timer->stop(); + m_movingToken = 0; + } +} + +void AtlantikBoard::resizeEvent(QResizeEvent *) +{ + // Stop moving tokens, slotResizeAftermath will re-enable this + if (m_timer!=0 && m_timer->isActive()) + { + m_timer->stop(); + m_resumeTimer=true; + } + +/* + // Adjust spacer to make sure board stays a square + int q = e->size().width() - e->size().height(); + if (q > 0) + { + QSize s(q, 0); + spacer->setFixedSize(s); + } + else + { + QSize s(0, -q); + spacer->setFixedSize(s); + } +*/ + // Timer to reinit the gameboard _after_ resizeEvent + QTimer::singleShot(0, this, SLOT(slotResizeAftermath())); +} + +void AtlantikBoard::slotResizeAftermath() +{ + kdDebug() << "AtlantikBoard::slotResizeAftermath" << endl; + // Move tokens back to their last known location (this has to be done + // _after_ resizeEvent has returned to make sure we have the correct + // adjusted estate geometries. + + Token *token = 0; + for (QPtrListIterator it(m_tokens); (token = *it) ; ++it) + jumpToken(token); + + // Restart the timer that was stopped in resizeEvent + if (m_resumeTimer && m_timer!=0 && !m_timer->isActive()) + { + m_timer->start(15); + m_resumeTimer=false; + } +} + +void AtlantikBoard::displayDefault() +{ + switch(m_displayQueue.count()) + { + case 0: + m_displayQueue.prepend(new QWidget(this)); + break; + case 1: + if (EstateDetails *display = dynamic_cast(m_lastServerDisplay)) + display->setEstate(0); + break; + default: + if (m_displayQueue.getFirst() == m_lastServerDisplay) + m_lastServerDisplay = 0; + m_displayQueue.removeFirst(); + break; + } + updateCenter(); +} + +void AtlantikBoard::displayButton(QString command, QString caption, bool enabled) +{ + if (EstateDetails *display = dynamic_cast(m_lastServerDisplay)) + display->addButton(command, caption, enabled); +} + +void AtlantikBoard::addCloseButton() +{ + EstateDetails *eDetails = 0; + if ((eDetails = dynamic_cast(m_lastServerDisplay)) && eDetails != m_displayQueue.getLast()) + eDetails->addCloseButton(); +} + +void AtlantikBoard::insertDetails(QString text, bool clearText, bool clearButtons, Estate *estate) +{ + EstateDetails *eDetails = 0; + + if ((eDetails = dynamic_cast(m_lastServerDisplay))) + { + if (clearText) + eDetails->setText(text); + else + eDetails->appendText(text); + + if (clearButtons) + eDetails->clearButtons(); + + eDetails->setEstate(estate); + return; + } + + if (m_displayQueue.getFirst() != m_lastServerDisplay) + m_displayQueue.removeFirst(); + + eDetails = new EstateDetails(estate, text, this); + m_lastServerDisplay = eDetails; + connect(eDetails, SIGNAL(buttonCommand(QString)), this, SIGNAL(buttonCommand(QString))); + connect(eDetails, SIGNAL(buttonClose()), this, SLOT(displayDefault())); + + m_displayQueue.insert(0, eDetails); + updateCenter(); +} + +void AtlantikBoard::prependEstateDetails(Estate *estate) +{ + if (!estate) + return; + + EstateDetails *eDetails = 0; + + if (m_displayQueue.getFirst() == m_lastServerDisplay) + { + eDetails = new EstateDetails(estate, QString::null, this); + m_displayQueue.prepend(eDetails); + + connect(eDetails, SIGNAL(buttonCommand(QString)), this, SIGNAL(buttonCommand(QString))); + connect(eDetails, SIGNAL(buttonClose()), this, SLOT(displayDefault())); + } + else + { + eDetails = dynamic_cast ( m_displayQueue.getFirst() ); + if (eDetails) + { + eDetails->setEstate(estate); + eDetails->setText( QString::null ); + // eDetails->clearButtons(); + } + else + { + kdDebug() << "manual estatedetails with first in queue neither server nor details" << endl; + return; + } + } + + eDetails->addDetails(); + eDetails->addCloseButton(); + + updateCenter(); +} + +void AtlantikBoard::updateCenter() +{ + QWidget *center = m_displayQueue.getFirst(); + m_gridLayout->addMultiCellWidget(center, 1, m_gridLayout->numRows()-2, 1, m_gridLayout->numCols()-2); + center->show(); +} + +QWidget *AtlantikBoard::centerWidget() +{ + return m_displayQueue.getFirst(); +} diff --git a/atlantik/libatlantikui/board.h b/atlantik/libatlantikui/board.h new file mode 100644 index 00000000..deedb3d6 --- /dev/null +++ b/atlantik/libatlantikui/board.h @@ -0,0 +1,102 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_BOARD_H +#define ATLANTIK_BOARD_H + +#include +#include +#include +#include +#include "libatlantikui_export.h" +class QPoint; + +class AtlanticCore; +class Auction; +class Estate; +class Player; +class Token; + +class EstateView; + +class LIBATLANTIKUI_EXPORT AtlantikBoard : public QWidget +{ +Q_OBJECT + +public: + enum DisplayMode { Play, Edit }; + + AtlantikBoard(AtlanticCore *atlanticCore, int maxEstates, DisplayMode mode, QWidget *parent, const char *name=0); + ~AtlantikBoard(); + void reset(); + + void setViewProperties(bool indicateUnowned, bool highliteUnowned, bool darkenMortgaged, bool quartzEffects, bool animateTokens); + int heightForWidth(int); + void addEstateView(Estate *estate, bool indicateUnowned = false, bool highliteUnowned = false, bool darkenMortgaged = false, bool quartzEffects = false); + void addAuctionWidget(Auction *auction); + + void addToken(Player *player); + void removeToken(Player *player); + + void indicateUnownedChanged(); + EstateView *findEstateView(Estate *estate); + QWidget *centerWidget(); + +public slots: + void slotMoveToken(); + void slotResizeAftermath(); + void displayDefault(); + +private slots: + void playerChanged(Player *player); + void displayButton(QString command, QString caption, bool enabled); + void prependEstateDetails(Estate *); + void insertDetails(QString text, bool clearText, bool clearButtons, Estate *estate = 0); + void addCloseButton(); + +signals: + void tokenConfirmation(Estate *estate); + void buttonCommand(QString command); + +protected: + void resizeEvent(QResizeEvent *); + +private: + Token *findToken(Player *player); + void jumpToken(Token *token); + void moveToken(Token *token); + QPoint calculateTokenDestination(Token *token, Estate *estate = 0); + + void updateCenter(); + + AtlanticCore *m_atlanticCore; + DisplayMode m_mode; + + QWidget *spacer, *m_lastServerDisplay; + QGridLayout *m_gridLayout; + Token *m_movingToken; + QTimer *m_timer; + bool m_resumeTimer; + + bool m_animateTokens; + int m_maxEstates; + + QPtrList m_estateViews; + QPtrList m_tokens; + QPtrList m_displayQueue; +}; + +#endif diff --git a/atlantik/libatlantikui/estatedetails.cpp b/atlantik/libatlantikui/estatedetails.cpp new file mode 100644 index 00000000..d143033c --- /dev/null +++ b/atlantik/libatlantikui/estatedetails.cpp @@ -0,0 +1,327 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "estatedetails.h" +#include "kwrappedlistviewitem.h" + +EstateDetails::EstateDetails(Estate *estate, QString text, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_pixmap = 0; + m_quartzBlocks = 0; + b_recreate = true; + m_recreateQuartz = true; + + m_estate = 0; + + m_closeButton = 0; + m_buttons.setAutoDelete(true); + + m_mainLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint()); + Q_CHECK_PTR(m_mainLayout); + + m_mainLayout->addItem(new QSpacerItem(KDialog::spacingHint(), KDialog::spacingHint()+50, QSizePolicy::Fixed, QSizePolicy::Minimum)); + + m_infoListView = new KListView(this, "infoListView"); + m_infoListView->addColumn(m_estate ? m_estate->name() : QString("") ); + m_infoListView->setSorting(-1); + m_mainLayout->addWidget(m_infoListView); + + appendText(text); + + m_buttonBox = new QHBoxLayout(m_mainLayout, KDialog::spacingHint()); + m_buttonBox->setMargin(0); + + m_buttonBox->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + setEstate(estate); +} + +EstateDetails::~EstateDetails() +{ + delete m_pixmap; + delete m_quartzBlocks; + delete m_infoListView; +} + +void EstateDetails::paintEvent(QPaintEvent *) +{ + if (m_recreateQuartz) + { +/* + if (m_quartzBlocks) + { + delete m_quartzBlocks; + m_quartzBlocks = 0; + } + + if (m_estate->color().isValid()) + { + m_quartzBlocks = new KPixmap(); + + if (m_orientation == North || m_orientation == South) + m_quartzBlocks->resize(25, m_titleHeight-2); + else + m_quartzBlocks->resize(25, m_titleWidth-2); + + drawQuartzBlocks(m_quartzBlocks, *m_quartzBlocks, m_estate->color().light(60), m_estate->color()); + m_quartzBlocks = rotatePixmap(m_quartzBlocks); + } +*/ + m_recreateQuartz = false; + b_recreate = true; + } + + if (b_recreate) + { + delete m_pixmap; + m_pixmap = new QPixmap(width(), height()); + + QColor greenHouse(0, 255, 0); + QColor redHotel(255, 51, 51); + QPainter painter; + painter.begin(m_pixmap, this); + + painter.setPen(Qt::black); + + painter.setBrush(m_estate ? m_estate->bgColor() : Qt::white); + painter.drawRect(rect()); + +/* + // Paint icon only when it exists and fits + if (icon!=0 && width() > icon->width() && height() > icon->height()) + painter.drawPixmap( (width() - icon->width())/2, (height() - icon->height())/2, *icon); +*/ + + if (m_estate) + { + int titleHeight = 50; + QColor titleColor = (m_estate->color().isValid() ? m_estate->color() : m_estate->bgColor().light(80)); + + KPixmap* quartzBuffer = new KPixmap; + quartzBuffer->resize(25, (height()/4)-2); + + QPainter quartzPainter; + quartzPainter.begin(quartzBuffer, this); + + painter.setBrush(titleColor); + painter.drawRect(0, 0, width(), titleHeight); + + if (m_quartzBlocks) + { + quartzPainter.drawPixmap(0, 0, *m_quartzBlocks); + painter.drawPixmap(1, 1, *quartzBuffer); + } + + if (m_estate->houses() > 0) + { + int titleWidth = width() / 5; + + if (m_estate->houses() == 5) + { + // Hotel + painter.setBrush(redHotel); + painter.drawRect(2, 2, titleWidth-4, titleHeight-4); + } + else + { + // Houses + painter.setBrush(greenHouse); + int h = titleHeight-4, w = titleWidth-4; + for ( unsigned int i=0 ; i < m_estate->houses() ; i++ ) + painter.drawRect(2+(i*(w+2)), 2, w, h); + } + } + + quartzPainter.end(); + delete quartzBuffer; + + // TODO: steal blur code from kicker/taskbar/taskcontainer.cpp + + // Estate name + painter.setPen(Qt::white); + int fontSize = KGlobalSettings::generalFont().pointSize(); + if (fontSize == -1) + fontSize = KGlobalSettings::generalFont().pixelSize(); + + painter.setFont(QFont(KGlobalSettings::generalFont().family(), fontSize * 2, QFont::Bold)); + painter.drawText(KDialog::marginHint(), KDialog::marginHint(), width()-KDialog::marginHint(), titleHeight, Qt::AlignJustify, m_estate->name()); + + painter.setPen(Qt::black); + + int xText = 0; + + // Estate group + if (m_estate->estateGroup()) + { + xText = titleHeight - fontSize - KDialog::marginHint(); + painter.setFont(QFont(KGlobalSettings::generalFont().family(), fontSize, QFont::Bold)); + painter.drawText(5, xText, width()-10, titleHeight, Qt::AlignRight, m_estate->estateGroup()->name().upper()); + } + + xText = titleHeight + fontSize + 5; + painter.setFont(QFont(KGlobalSettings::generalFont().family(), fontSize, QFont::Normal)); + } + b_recreate = false; + + } + bitBlt(this, 0, 0, m_pixmap); +} + +void EstateDetails::resizeEvent(QResizeEvent *) +{ + m_recreateQuartz = true; + b_recreate = true; +} + +void EstateDetails::addDetails() +{ + if (m_estate) + { + QListViewItem *infoText = 0; + + // Price + if (m_estate->price()) + { + infoText = new QListViewItem(m_infoListView, m_infoListView->lastItem(), i18n("Price: %1").arg(m_estate->price())); + infoText->setPixmap(0, QPixmap(SmallIcon("info"))); + } + + // Owner, houses, isMortgaged + if (m_estate && m_estate->canBeOwned()) + { + infoText = new QListViewItem(m_infoListView, m_infoListView->lastItem(), i18n("Owner: %1").arg(m_estate->owner() ? m_estate->owner()->name() : i18n("unowned"))); + infoText->setPixmap(0, QPixmap(SmallIcon("info"))); + + if (m_estate->isOwned()) + { + infoText = new QListViewItem(m_infoListView, m_infoListView->lastItem(), i18n("Houses: %1").arg(m_estate->houses())); + infoText->setPixmap(0, QPixmap(SmallIcon("info"))); + + infoText = new QListViewItem(m_infoListView, m_infoListView->lastItem(), i18n("Mortgaged: %1").arg(m_estate->isMortgaged() ? i18n("Yes") : i18n("No"))); + infoText->setPixmap(0, QPixmap(SmallIcon("info"))); + } + } + } +} + +void EstateDetails::addButton(QString command, QString caption, bool enabled) +{ + KPushButton *button = new KPushButton(caption, this); + m_buttons.append(button); + m_buttonCommandMap[(QObject *)button] = command; + m_buttonBox->addWidget(button); + + if (m_estate) + { + QColor bgColor, fgColor; + bgColor = m_estate->bgColor().light(110); + fgColor = ( bgColor.red() + bgColor.green() + bgColor.blue() < 255 ) ? Qt::white : Qt::black; + + button->setPaletteForegroundColor( fgColor ); + button->setPaletteBackgroundColor( bgColor ); + } + button->setEnabled(enabled); + button->show(); + + connect(button, SIGNAL(pressed()), this, SLOT(buttonPressed())); +} + +void EstateDetails::addCloseButton() +{ + if (!m_closeButton) + { + m_closeButton = new KPushButton(KStdGuiItem::close(), this); + m_buttonBox->addWidget(m_closeButton); + m_closeButton->show(); + connect(m_closeButton, SIGNAL(pressed()), this, SIGNAL(buttonClose())); + } +} + +void EstateDetails::setEstate(Estate *estate) +{ + if (m_estate != estate) + { + m_estate = estate; + + m_infoListView->setColumnText( 0, m_estate ? m_estate->name() : QString("") ); + + b_recreate = true; + update(); + } +} + +void EstateDetails::setText(QString text) +{ + m_infoListView->clear(); + appendText(text); +} + +void EstateDetails::appendText(QString text) +{ + if ( text.isEmpty() ) + return; + + KWrappedListViewItem *infoText = new KWrappedListViewItem(m_infoListView, m_infoListView->lastItem(), text); + + if ( text.find( QRegExp("rolls") ) != -1 ) + infoText->setPixmap(0, QPixmap(SmallIcon("roll"))); + else + infoText->setPixmap(0, QPixmap(SmallIcon("atlantik"))); + + m_infoListView->ensureItemVisible( infoText ); +} + +void EstateDetails::clearButtons() +{ + if (m_closeButton) + { + delete m_closeButton; + m_closeButton = 0; + } + + // Delete buttons + m_buttons.clear(); + m_buttonCommandMap.clear(); +} + +void EstateDetails::buttonPressed() +{ + emit buttonCommand(QString(m_buttonCommandMap[(QObject *)QObject::sender()])); +} + +#include "estatedetails.moc" diff --git a/atlantik/libatlantikui/estatedetails.h b/atlantik/libatlantikui/estatedetails.h new file mode 100644 index 00000000..b8264a51 --- /dev/null +++ b/atlantik/libatlantikui/estatedetails.h @@ -0,0 +1,77 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_ESTATEDETAILS_H +#define ATLANTIK_ESTATEDETAILS_H + +#include + +class QPixmap; +class QString; +class QHBoxLayout; +class QVBoxLayout; +class QVGroupBox; + +class KListView; +class KPixmap; +class KPushButton; + +class Player; +class Estate; + +class EstateDetails : public QWidget +{ +Q_OBJECT + +public: + EstateDetails(Estate *estate, QString text, QWidget *parent, const char *name = 0); + ~EstateDetails(); + Estate *estate() { return m_estate; } + + void addDetails(); + void addButton(const QString command, const QString caption, bool enabled); + void addCloseButton(); + void setEstate(Estate *estate); + void setText(QString text); + void appendText(QString text); + void clearButtons(); + +protected: + void paintEvent(QPaintEvent *); + void resizeEvent(QResizeEvent *); + +private slots: + void buttonPressed(); + +signals: + void buttonCommand(QString); + void buttonClose(); + +private: + Estate *m_estate; + QPixmap *m_pixmap; + KPixmap *m_quartzBlocks; + KListView *m_infoListView; + KPushButton *m_closeButton; + bool b_recreate, m_recreateQuartz; + QVBoxLayout *m_mainLayout; + QHBoxLayout *m_buttonBox; + QVGroupBox *m_textGroupBox; + QMap m_buttonCommandMap; + QPtrList m_buttons; +}; + +#endif diff --git a/atlantik/libatlantikui/estateview.cpp b/atlantik/libatlantikui/estateview.cpp new file mode 100644 index 00000000..b8c3f38c --- /dev/null +++ b/atlantik/libatlantikui/estateview.cpp @@ -0,0 +1,558 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "estateview.moc" +#include "config.h" + +EstateView::EstateView(Estate *estate, EstateOrientation orientation, const QString &_icon, bool indicateUnowned, bool highliteUnowned, bool darkenMortgaged, bool quartzEffects, QWidget *parent, const char *name) : QWidget(parent, name, WResizeNoErase) +{ + m_estate = estate; + m_orientation = orientation; + + m_indicateUnowned = indicateUnowned; + m_highliteUnowned = highliteUnowned; + m_darkenMortgaged = darkenMortgaged; + m_quartzEffects = quartzEffects; + + setBackgroundMode(NoBackground); // avoid flickering + + qpixmap = 0; + b_recreate = true; + + m_quartzBlocks = 0; + m_recreateQuartz = true; + + pe = 0; + updatePE(); + + icon = new QPixmap(locate("data", "atlantik/pics/" + _icon)); + icon = rotatePixmap(icon); + + updateToolTip(); +} + +void EstateView::updateToolTip() +{ + QToolTip::remove(this); + + if ( m_estate ) + { + QString toolTip = m_estate->name(); + if ( m_estate->isOwned() ) + { + toolTip.append( "\n" + i18n("Owner: %1").arg( m_estate->owner()->name() ) ); + if ( m_estate->isMortgaged() ) + toolTip.append( "\n" + i18n("Unmortgage Price: %1").arg( m_estate->unmortgagePrice() ) ); + else + toolTip.append( "\n" + i18n("Mortgage Value: %1").arg( m_estate->mortgagePrice() ) ); + if ( m_estate->canSellHouses() ) + toolTip.append( "\n" + i18n("House Value: %1").arg( m_estate->houseSellPrice() ) ); + if ( m_estate->canBuyHouses() ) + toolTip.append( "\n" + i18n("House Price: %1").arg( m_estate->housePrice() ) ); + } + else if ( m_estate->canBeOwned() ) + toolTip.append( "\n" + i18n("Price: %1").arg( m_estate->price() ) ); + else if ( m_estate->money() ) + toolTip.append( "\n" + i18n("Money: %1").arg( m_estate->money() ) ); + + QToolTip::add( this, toolTip ); + } +} + +void EstateView::setViewProperties(bool indicateUnowned, bool highliteUnowned, bool darkenMortgaged, bool quartzEffects) +{ + if (m_indicateUnowned != indicateUnowned) + { + m_indicateUnowned = indicateUnowned; + b_recreate = true; + updatePE(); + } + + if (m_highliteUnowned != highliteUnowned) + { + m_highliteUnowned = highliteUnowned; + b_recreate = true; + } + + if (m_darkenMortgaged != darkenMortgaged) + { + m_darkenMortgaged = darkenMortgaged; + b_recreate = true; + } + + if (m_quartzEffects != quartzEffects) + { + m_quartzEffects = quartzEffects; + b_recreate = true; +// m_recreateQuartz = true; + } + + if (b_recreate || m_recreateQuartz) + update(); +} + +QPixmap *EstateView::rotatePixmap(QPixmap *p) +{ + if (p==0 || p->isNull()) + return 0; + + QWMatrix m; + + switch(m_orientation) + { + case East: + m.rotate(90); + break; + case West: + m.rotate(-90); + break; + case South: + m.rotate(180); + break; + default:; + } + *p = p->xForm(m); + return p; +} + +KPixmap *EstateView::rotatePixmap(KPixmap *p) +{ + if (p==0 || p->isNull()) + return 0; + + QWMatrix m; + + switch(m_orientation) + { + case East: + m.rotate(90); + break; + case West: + m.rotate(-90); + break; + case South: + m.rotate(180); + break; + default:; + } + *p = p->xForm(m); + return p; +} + +void EstateView::updatePE() +{ + // Don't show a when a property is not unowned, cannot be owned at all + // or when the user has configured Atlantik not to show them. + if (m_estate->isOwned() || !m_estate->canBeOwned() || m_indicateUnowned==false) + { + delete pe; + pe = 0; + } + else + { + if (pe==0) + { + // Display a coloured portfolioestate to indicate property is + // for sale + pe = new PortfolioEstate(m_estate, 0, true, this, "board-portfolioestate"); + repositionPortfolioEstate(); + + pe->show(); + } + else if (!pe->isVisible()) + pe->show(); + } +} + +void EstateView::estateChanged() +{ + updateToolTip(); + + b_recreate = true; + m_recreateQuartz = true; + + update(); + updatePE(); +} + +void EstateView::repositionPortfolioEstate() +{ + if (pe!=0) + { + int x = (m_orientation == West ? (width()-2 - pe->width()) : 2); + int y = (m_orientation == North ? (height()-2 - pe->height()) : 2); + pe->setGeometry(x, y, pe->width(), pe->height()); + } +} + +void EstateView::paintEvent(QPaintEvent *) +{ + m_titleHeight = height()/4; + m_titleWidth = width()/4; + + if (m_recreateQuartz) + { + if (m_quartzBlocks) + { + delete m_quartzBlocks; + m_quartzBlocks = 0; + } + + if (m_estate->color().isValid()) + { + m_quartzBlocks = new KPixmap(); + + if (m_orientation == North || m_orientation == South) + m_quartzBlocks->resize(25, m_titleHeight-2); + else + m_quartzBlocks->resize(25, m_titleWidth-2); + + drawQuartzBlocks(m_quartzBlocks, *m_quartzBlocks, m_estate->color().light(60), m_estate->color()); + m_quartzBlocks = rotatePixmap(m_quartzBlocks); + } + + m_recreateQuartz = false; + b_recreate = true; + } + + if (b_recreate) + { + delete qpixmap; + qpixmap = new QPixmap(width(), height()); + + QColor greenHouse(0, 255, 0); + QColor redHotel(255, 51, 51); + QPainter painter; + painter.begin(qpixmap, this); + + painter.setPen(Qt::black); + + if (m_darkenMortgaged==true && m_estate->isMortgaged()) + painter.setBrush(m_estate->bgColor().light(10)); + else if (m_highliteUnowned==true && m_estate->canBeOwned() && !m_estate->isOwned()) + painter.setBrush(m_estate->bgColor().light(190)); + else + painter.setBrush(m_estate->bgColor()); + + painter.drawRect(rect()); + + // Paint icon only when it exists and fits + if (icon!=0 && width() > icon->width() && height() > icon->height()) + painter.drawPixmap( (width() - icon->width())/2, (height() - icon->height())/2, *icon); + + if (m_estate->color().isValid()) + { + KPixmap* quartzBuffer = new KPixmap; + if (m_orientation == North || m_orientation == South) + quartzBuffer->resize(25, m_titleHeight-2); + else + quartzBuffer->resize(m_titleWidth-2, 25); + + QPainter quartzPainter; + quartzPainter.begin(quartzBuffer, this); + + painter.setBrush(m_estate->color()); + switch(m_orientation) + { + case North: + painter.drawRect(0, 0, width(), m_titleHeight); + + if (m_quartzEffects && m_quartzBlocks) + { + quartzPainter.drawPixmap(0, 0, *m_quartzBlocks); + painter.drawPixmap(1, 1, *quartzBuffer); + } + + if (m_estate->houses() > 0) + { + if (m_estate->houses() == 5) + { + // Hotel + painter.setBrush(redHotel); + painter.drawRect(2, 2, (width()/2)-4, (m_titleHeight)-4); + } + else + { + // Houses + painter.setBrush(greenHouse); + int h = (m_titleHeight)-4, w = (m_titleWidth)-4; + for( unsigned int i=0 ; i < m_estate->houses() ; i++ ) + painter.drawRect(2+(i*(w+2)), 2, w, h); + } + } + break; + case South: + painter.drawRect(0, height()-(m_titleHeight), width(), m_titleHeight); + + if (m_quartzEffects && m_quartzBlocks) + { + quartzPainter.drawPixmap(0, 0, *m_quartzBlocks); + painter.drawPixmap(width()-quartzBuffer->width()-1, height()-m_titleHeight+1, *quartzBuffer); + } + + if (m_estate->houses() > 0) + { + if (m_estate->houses() == 5) + { + // Hotel + painter.setBrush(redHotel); + painter.drawRect(2, (3*(m_titleHeight))+2, (width()/2)-4, (m_titleHeight)-4); + } + else + { + // Houses + painter.setBrush(greenHouse); + int h = (m_titleHeight)-4, w = (m_titleWidth)-4; + for( unsigned int i=0 ; i < m_estate->houses() ; i++ ) + painter.drawRect(2+(i*(w+2)), (3*(m_titleHeight))+2, w, h); + } + } + break; + case West: + painter.drawRect(0, 0, m_titleWidth, height()); + + if (m_quartzEffects && m_quartzBlocks) + { + quartzPainter.drawPixmap(0, 0, *m_quartzBlocks); + painter.drawPixmap(1, height()-quartzBuffer->height()-1, *quartzBuffer); + } + + if (m_estate->houses() > 0) + { + if (m_estate->houses() == 5) + { + // Hotel + painter.setBrush(redHotel); + painter.drawRect(2, 2, (m_titleWidth)-4, (height()/2)-4); + } + else + { + // Houses + painter.setBrush(greenHouse); + int h = (m_titleHeight)-4, w = (m_titleWidth)-4; + for( unsigned int i=0 ; i < m_estate->houses() ; i++ ) + painter.drawRect(2, 2+(i*(h+2)), w, h); + } + } + break; + case East: + painter.drawRect(width()-(m_titleWidth), 0, m_titleWidth, height()); + + if (m_quartzEffects && m_quartzBlocks) + { + quartzPainter.drawPixmap(0, 0, *m_quartzBlocks); + painter.drawPixmap(width()-quartzBuffer->width()-1, 1, *quartzBuffer); + } + + if (m_estate->houses() > 0) + { + if (m_estate->houses() == 5) + { + // Hotel + painter.setBrush(redHotel); + painter.drawRect((3*(m_titleWidth))+2, 2, (m_titleWidth)-4, (height()/2)-4); + } + else + { + // Houses + painter.setBrush(greenHouse); + int h = (m_titleHeight)-4, w = (m_titleWidth)-4; + for( unsigned int i=0 ; i < m_estate->houses() ; i++ ) + painter.drawRect((3*(m_titleWidth))+2, 2+(i*(h+2)), w, h); + } + } + break; + } + + + quartzPainter.end(); + delete quartzBuffer; + } + + QFont font = QFont( KGlobalSettings::generalFont().family(), KGlobalSettings::generalFont().pointSize(), QFont::Normal ); + painter.setFont(font); + QString estateName = m_estate->name(); +#if defined(KDE_MAKE_VERSION) +#if KDE_VERSION >= KDE_MAKE_VERSION(3,2,0) + if ( m_estate->color().isValid() && ( m_orientation == West || m_orientation == East ) ) + estateName = KStringHandler::rPixelSqueeze( m_estate->name(), QFontMetrics( font ), 3*width()/4 ); + else + estateName = KStringHandler::rPixelSqueeze( m_estate->name(), QFontMetrics( font ), width() ); +#endif +#endif + if (m_estate->color().isValid() && m_orientation == West) + painter.drawText( width()/4 + 2, height()/2, estateName ); + else + painter.drawText(2, height()/2, estateName ); + + b_recreate = false; + } + bitBlt(this, 0, 0, qpixmap); +} + +void EstateView::resizeEvent(QResizeEvent *) +{ + m_recreateQuartz = true; + b_recreate = true; + + QTimer::singleShot(0, this, SLOT(slotResizeAftermath())); +} + +void EstateView::mousePressEvent(QMouseEvent *e) +{ + if (e->button()==RightButton && m_estate->isOwned()) + { + KPopupMenu *rmbMenu = new KPopupMenu(this); + rmbMenu->insertTitle(m_estate->name()); + + if (m_estate->isOwnedBySelf()) + { + Player *player = m_estate->owner(); + + // Mortgage toggle + if (m_estate->isMortgaged()) + { + rmbMenu->insertItem(i18n("Unmortgage"), 0); + if (!m_estate->canToggleMortgage() || player->hasDebt()) + rmbMenu->setItemEnabled(0, false); + } + else + { + rmbMenu->insertItem(i18n("Mortgage"), 0); + if (!m_estate->canToggleMortgage()) + rmbMenu->setItemEnabled(0, false); + } + + // Estate construction + if (m_estate->houses()>=4) + rmbMenu->insertItem(i18n("Build Hotel"), 1); + else + rmbMenu->insertItem(i18n("Build House"), 1); + + if (!m_estate->canBuyHouses() || player->hasDebt()) + rmbMenu->setItemEnabled(1, false); + + // Estate destruction + if (m_estate->houses()==5) + rmbMenu->insertItem(i18n("Sell Hotel"), 2); + else + rmbMenu->insertItem(i18n("Sell House"), 2); + + if (!(m_estate->canSellHouses())) + rmbMenu->setItemEnabled(2, false); + } + else + { + // Request trade + if (Player *player = m_estate->owner()) + rmbMenu->insertItem(i18n("Request Trade with %1").arg(player->name()), 3); + } + + KPopupMenu *pm = dynamic_cast(rmbMenu); + if (pm) { + connect(pm, SIGNAL(activated(int)), this, SLOT(slotMenuAction(int))); + } + QPoint g = QCursor::pos(); + rmbMenu->exec(g); + delete rmbMenu; + } + else if (e->button()==LeftButton) + emit LMBClicked(m_estate); +} + +void EstateView::slotResizeAftermath() +{ + repositionPortfolioEstate(); +} + +void EstateView::slotMenuAction(int item) +{ + switch (item) + { + case 0: + emit estateToggleMortgage(m_estate); + break; + + case 1: + emit estateHouseBuy(m_estate); + break; + + case 2: + emit estateHouseSell(m_estate); + break; + + case 3: + emit newTrade(m_estate->owner()); + break; + } +} + +// Kudos to Gallium for writing the Quartz KWin style and +// letting me use the ultra slick algorithm! +void EstateView::drawQuartzBlocks(KPixmap *pi, KPixmap &p, const QColor &c1, const QColor &c2) +{ + QPainter px; + + if (pi==0 || pi->isNull()) + return; + + px.begin(pi); + + KPixmapEffect::gradient(p, c1, c2, KPixmapEffect::HorizontalGradient); + + px.fillRect( 2, 1, 3, 3, c1.light(120) ); + px.fillRect( 2, 5, 3, 3, c1 ); + px.fillRect( 2, 9, 3, 3, c1.light(110) ); + px.fillRect( 2, 13, 3, 3, c1 ); + + px.fillRect( 6, 1, 3, 3, c1.light(110) ); + px.fillRect( 6, 5, 3, 3, c2.light(110) ); + px.fillRect( 6, 9, 3, 3, c1.light(120) ); + px.fillRect( 6, 13, 3, 3, c2.light(130) ); + + px.fillRect( 10, 5, 3, 3, c1.light(110) ); + px.fillRect( 10, 9, 3, 3, c2.light(120) ); + px.fillRect( 10, 13, 3, 3, c2.light(150) ); + + px.fillRect( 14, 1, 3, 3, c1.dark(110) ); + px.fillRect( 14, 9, 3, 3, c2.light(120) ); + px.fillRect( 14, 13, 3, 3, c1.dark(120) ); + + px.fillRect( 18, 5, 3, 3, c1.light(110) ); + px.fillRect( 18, 13, 3, 3, c1.dark(110) ); + + px.fillRect( 22, 9, 3, 3, c2.light(120)); + px.fillRect( 22, 13, 3, 3, c2.light(110) ); +} diff --git a/atlantik/libatlantikui/estateview.h b/atlantik/libatlantikui/estateview.h new file mode 100644 index 00000000..864c8983 --- /dev/null +++ b/atlantik/libatlantikui/estateview.h @@ -0,0 +1,80 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_ESTATEVIEW_H +#define ATLANTIK_ESTATEVIEW_H + +#include +#include + +#include + +#include "portfolioestate.h" + +enum EstateOrientation { North=0, East=1, South=2, West=3 }; + +class Player; +class Estate; + +class EstateView : public QWidget +{ +Q_OBJECT + + public: + EstateView(Estate *estate, EstateOrientation orientation, const QString &, bool indicateUnowned, bool highliteUnowned, bool darkenMortgaged, bool quartzEffects, QWidget *parent, const char *name = 0); + void setViewProperties(bool indicateUnowned, bool highliteUnowned, bool darkenMortgaged, bool quartzEffects); + Estate *estate() { return m_estate; } + void updatePE(); + EstateOrientation orientation() { return m_orientation; } + + public slots: + void slotResizeAftermath(); + + signals: + void estateToggleMortgage(Estate *estate); + void estateHouseBuy(Estate *estate); + void estateHouseSell(Estate *estate); + void newTrade(Player *player); + void LMBClicked(Estate *estate); + + protected: + void paintEvent(QPaintEvent *); + void resizeEvent(QResizeEvent *); + void mousePressEvent(QMouseEvent *); + +private: + void updateToolTip(); + + QPixmap *rotatePixmap(QPixmap *); + KPixmap *rotatePixmap(KPixmap *); + void drawQuartzBlocks(KPixmap *pi, KPixmap &p, const QColor &c1, const QColor &c2); + void repositionPortfolioEstate(); + + Estate *m_estate; + QPixmap *qpixmap, *icon; + KPixmap *m_quartzBlocks; + bool m_indicateUnowned, m_highliteUnowned, m_darkenMortgaged, m_quartzEffects; + bool b_recreate, m_recreateQuartz; + int m_titleWidth, m_titleHeight; + EstateOrientation m_orientation; + PortfolioEstate *pe; + + private slots: + void slotMenuAction(int); + void estateChanged(); +}; + +#endif diff --git a/atlantik/libatlantikui/kwrappedlistviewitem.cpp b/atlantik/libatlantikui/kwrappedlistviewitem.cpp new file mode 100644 index 00000000..7bd9e2cf --- /dev/null +++ b/atlantik/libatlantikui/kwrappedlistviewitem.cpp @@ -0,0 +1,116 @@ +// Copyright (c) 2004 Rob Kaper . All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS'' AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + +#include +#include + +#include +#include +#include + +#include "kwrappedlistviewitem.h" + +KWrappedListViewItem::KWrappedListViewItem( QListView *parent, QString text, QString t2 ) +: QObject(), KListViewItem( parent ) +{ + init( parent, text, t2 ); +} + +KWrappedListViewItem::KWrappedListViewItem( QListView *parent, QListViewItem *after, QString text, QString t2 ) +: QObject(), KListViewItem( parent, after ) +{ + init( parent, text, t2 ); +} + +void KWrappedListViewItem::setup() +{ + widthChanged(); +} + +/* +int KWrappedListViewItem::width( const QFontMetrics&, const QListView*, int ) const +{ + return m_wrap->boundingRect().width(); +} +*/ + +void KWrappedListViewItem::wrapColumn( int c ) +{ + if ( c != m_wrapColumn ) + return; + + QListView *lv = listView(); + if ( !lv ) + return; + + QFont font = QFont( KGlobalSettings::generalFont().family(), KGlobalSettings::generalFont().pointSize(), QFont::Normal ); + QFontMetrics fm = QFontMetrics( font ); + + int wrapWidth = lv->width(); + for ( int i = 0 ; i < m_wrapColumn ; i++ ) + wrapWidth -= ( width(fm, lv, i) + lv->itemMargin() ); + + if ( pixmap(c) ) + wrapWidth -= ( pixmap( c )->width() + lv->itemMargin() ); + + QScrollBar *scrollBar = lv->verticalScrollBar(); + if ( !scrollBar->isHidden() ) + wrapWidth -= scrollBar->width(); + + QRect rect = QRect( 0, 0, wrapWidth - 20, -1 ); + + KWordWrap *wrap = KWordWrap::formatText( fm, rect, 0, m_origText ); + setText( c, wrap->wrappedString() ); + + int lc = text(c).contains( QChar( '\n' ) ) + 1; + setHeight( wrap->boundingRect().height() + lc*lv->itemMargin() ); + + widthChanged( c ); + + delete wrap; +} + +void KWrappedListViewItem::init( QListView *parent, QString text, QString t2 ) +{ + m_wrapColumn = 0; + setMultiLinesEnabled( true ); + parent->setResizeMode( QListView::LastColumn ); + + m_origText = text; + + if ( !t2.isNull() ) + { + setText( 0, text ); + m_origText = t2; + m_wrapColumn = 1; + } + else + m_origText = text; + + wrapColumn( m_wrapColumn ); + + connect( parent->header(), SIGNAL(sizeChange(int, int, int)), this, SLOT(wrapColumn(int))); +} + +#include "kwrappedlistviewitem.moc" diff --git a/atlantik/libatlantikui/kwrappedlistviewitem.h b/atlantik/libatlantikui/kwrappedlistviewitem.h new file mode 100644 index 00000000..056cef6d --- /dev/null +++ b/atlantik/libatlantikui/kwrappedlistviewitem.h @@ -0,0 +1,54 @@ +// Copyright (c) 2004 Rob Kaper . All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS'' AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + +#ifndef KWRAPPEDLISTVIEWITEM_H +#define KWRAPPEDLISTVIEWITEM_H + +#include +#include + +#include + +class KWordWrap; + +class KWrappedListViewItem : public QObject, public KListViewItem +{ +Q_OBJECT + +public: + KWrappedListViewItem( QListView *parent, QString text, QString=QString::null ); + KWrappedListViewItem( QListView *parent, QListViewItem *after, QString text, QString=QString::null ); + void setup(); +// int width(const QFontMetrics& fm, const QListView* lv, int c) const; + +private slots: + void wrapColumn( int c ); + +private: + void init( QListView *parent, QString text, QString=QString::null ); + QString m_origText; + int m_wrapColumn; +}; + +#endif diff --git a/atlantik/libatlantikui/libatlantikui_export.h b/atlantik/libatlantikui/libatlantikui_export.h new file mode 100644 index 00000000..9ea1695f --- /dev/null +++ b/atlantik/libatlantikui/libatlantikui_export.h @@ -0,0 +1,25 @@ +// Copyright (c) 2004 Dirk Mueller + +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIKUI_EXPORT_H +#define LIBATLANTIKUI_EXPORT_H + +#include + +#define LIBATLANTIKUI_EXPORT KDE_EXPORT + +#endif diff --git a/atlantik/libatlantikui/portfolioestate.cpp b/atlantik/libatlantikui/portfolioestate.cpp new file mode 100644 index 00000000..625fb055 --- /dev/null +++ b/atlantik/libatlantikui/portfolioestate.cpp @@ -0,0 +1,94 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include +#include + +#include "portfolioestate.moc" +#include "estate.h" + +PortfolioEstate::PortfolioEstate(Estate *estate, Player *player, bool alwaysOwned, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_estate = estate; + m_player = player; + m_alwaysOwned = alwaysOwned; + + QSize s(PE_WIDTH, PE_HEIGHT); + setFixedSize(s); + + b_recreate = true; +} + +void PortfolioEstate::estateChanged() +{ + b_recreate = true; + update(); +} + +QPixmap PortfolioEstate::drawPixmap(Estate *estate, Player *player, bool alwaysOwned) +{ + QColor lightGray(204, 204, 204), darkGray(153, 153, 153); + QPixmap qpixmap(PE_WIDTH, PE_HEIGHT); + QPainter painter; + painter.begin(&qpixmap); + + painter.setPen(lightGray); + painter.setBrush(white); + painter.drawRect(QRect(0, 0, PE_WIDTH, PE_HEIGHT)); + if (alwaysOwned || (estate && estate->isOwned() && player == estate->owner())) + { + painter.setPen(darkGray); + for (int y=5;y<=13;y+=2) + painter.drawLine(2, y, 10, y); + + painter.setPen(Qt::white); + painter.drawPoint(8, 5); + painter.drawPoint(8, 7); + painter.drawPoint(8, 9); + painter.drawPoint(5, 11); + painter.drawPoint(9, 11); + painter.drawPoint(3, 13); + painter.drawPoint(10, 13); + + painter.setPen(estate->color()); + painter.setBrush(estate->color()); + } + else + { + painter.setPen(lightGray); + painter.setBrush(lightGray); + } + painter.drawRect(0, 0, PE_WIDTH, 3); + + return qpixmap; +} + +void PortfolioEstate::paintEvent(QPaintEvent *) +{ + if (b_recreate) + { + m_pixmap = drawPixmap(m_estate, m_player, m_alwaysOwned); + b_recreate = false; + } + bitBlt(this, 0, 0, &m_pixmap); +} + +void PortfolioEstate::mousePressEvent(QMouseEvent *e) +{ + if (e->button()==LeftButton) + emit estateClicked(m_estate); +} diff --git a/atlantik/libatlantikui/portfolioestate.h b/atlantik/libatlantikui/portfolioestate.h new file mode 100644 index 00000000..65bd5db3 --- /dev/null +++ b/atlantik/libatlantikui/portfolioestate.h @@ -0,0 +1,55 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_PORTFOLIOESTATE_H +#define ATLANTIK_PORTFOLIOESTATE_H + +#include +#include + +#define PE_WIDTH 13 +#define PE_HEIGHT 16 + +class Estate; +class Player; + +class PortfolioEstate : public QWidget +{ +Q_OBJECT + +public: + PortfolioEstate(Estate *estate, Player *player, bool alwaysOwned, QWidget *parent, const char *name = 0); + Estate *estate() { return m_estate; } + static QPixmap drawPixmap(Estate *estate, Player *player = 0, bool alwaysOwned = true); + +protected: + void paintEvent(QPaintEvent *); + void mousePressEvent(QMouseEvent *); + +private slots: + void estateChanged(); + +signals: + void estateClicked(Estate *estate); + +private: + Estate *m_estate; + Player *m_player; + QPixmap m_pixmap; + bool b_recreate, m_alwaysOwned; +}; + +#endif diff --git a/atlantik/libatlantikui/portfolioview.cpp b/atlantik/libatlantikui/portfolioview.cpp new file mode 100644 index 00000000..c07d426b --- /dev/null +++ b/atlantik/libatlantikui/portfolioview.cpp @@ -0,0 +1,295 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "portfolioview.moc" + +#define PE_DISTW 4 +#define PE_DISTH 4 +#define PE_SPACE 2 +#define PE_MARGINW 5 +#define PE_MARGINH 2 +#define ICONSIZE 48 + +PortfolioView::PortfolioView(AtlanticCore *core, Player *player, QColor activeColor, QColor inactiveColor, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_atlanticCore = core; + m_player = player; + m_activeColor = activeColor; + m_inactiveColor = inactiveColor; + m_lastPE = 0; + + qpixmap = 0; + b_recreate = true; + + m_portfolioEstates.setAutoDelete(true); + setBackgroundColor(Qt::white); + setMinimumHeight(ICONSIZE); + + // Init icon + m_image = 0; + m_imageName = "hamburger.png"; + loadIcon(); +} + +PortfolioView::~PortfolioView() +{ + clearPortfolio(); + delete m_image; + delete qpixmap; +} + +Player *PortfolioView::player() +{ + return m_player; +} + +void PortfolioView::buildPortfolio() +{ + if ( m_portfolioEstates.count() ) + clearPortfolio(); + + // Loop through estate groups in order + QPtrList estateGroups = m_atlanticCore->estateGroups(); + PortfolioEstate *lastPE = 0, *firstPEprevGroup = 0; + + int x = 100, y = 25, marginHint = 5, bottom; + bottom = ICONSIZE - PE_HEIGHT - marginHint; + + EstateGroup *estateGroup; + for (QPtrListIterator it(estateGroups); *it; ++it) + { + if ((estateGroup = *it)) + { + // New group + lastPE = 0; + + // Loop through estates + QPtrList estates = m_atlanticCore->estates(); + Estate *estate; + for (QPtrListIterator it(estates); *it; ++it) + { + if ((estate = *it) && estate->estateGroup() == estateGroup) + { + // Create PE + PortfolioEstate *portfolioEstate = new PortfolioEstate(estate, m_player, false, this, "portfolioestate"); + m_portfolioEstates.append(portfolioEstate); + + connect(portfolioEstate, SIGNAL(estateClicked(Estate *)), this, SIGNAL(estateClicked(Estate *))); + if (lastPE) + { + x = lastPE->x() + 2; + y = lastPE->y() + 4; + if (y > bottom) + bottom = y; + } + else if (firstPEprevGroup) + { + x = firstPEprevGroup->x() + PE_WIDTH + 8; + y = 20 + marginHint; + firstPEprevGroup = portfolioEstate; + } + else + { + x = ICONSIZE + marginHint; + y = 20 + marginHint; + if (y > bottom) + bottom = y; + firstPEprevGroup = portfolioEstate; + } + + portfolioEstate->setGeometry(x, y, portfolioEstate->width(), portfolioEstate->height()); + portfolioEstate->show(); + + connect(estate, SIGNAL(changed()), portfolioEstate, SLOT(estateChanged())); + + lastPE = portfolioEstate; + } + } + } + } + setMinimumWidth(x + PE_WIDTH + marginHint); + int minHeight = bottom + PE_HEIGHT + marginHint; + if (minHeight > minimumHeight()) + setMinimumHeight(minHeight); +} + +void PortfolioView::clearPortfolio() +{ + m_portfolioEstates.clear(); +} + +void PortfolioView::loadIcon() +{ + if (m_imageName == m_player->image()) + return; + m_imageName = m_player->image(); + + delete m_image; + m_image = 0; + + if (!m_imageName.isEmpty()) + { + QString filename = locate("data", "atlantik/themes/default/tokens/" + m_imageName); + if (KStandardDirs::exists(filename)) + m_image = new QPixmap(filename); + } + + if (!m_image) + { + return; + +/* + m_imageName = "hamburger.png"; + + QString filename = locate("data", "atlantik/themes/default/tokens/" + m_imageName); + if (KStandardDirs::exists(filename)) + m_image = new QPixmap(filename); +*/ + } + else if (ICONSIZE > minimumHeight()) + setMinimumHeight(ICONSIZE); + + QWMatrix m; + m.scale(double(ICONSIZE) / m_image->width(), double(ICONSIZE) / m_image->height()); + QPixmap *scaledPixmap = new QPixmap(ICONSIZE, ICONSIZE); + *scaledPixmap = m_image->xForm(m); + + delete m_image; + m_image = scaledPixmap; +} + +void PortfolioView::paintEvent(QPaintEvent *) +{ + if (b_recreate) + { + delete qpixmap; + qpixmap = new QPixmap(width(), height()); + + QPainter painter; + painter.begin(qpixmap, this); + + painter.setPen(Qt::white); + painter.setBrush(Qt::white); + painter.drawRect(rect()); + + painter.setPen(m_player->hasTurn() ? m_activeColor : Qt::black); + painter.setBrush(m_player->hasTurn() ? m_activeColor : Qt::black); + painter.drawRect(0, 0, width(), 20); + + if (m_image) + { + painter.setPen(Qt::black); + painter.setBrush(Qt::white); + painter.drawRect(0, 0, ICONSIZE, ICONSIZE); + + painter.drawPixmap(0, 0, *m_image); + } + + painter.setPen(Qt::white); + painter.setFont(QFont(KGlobalSettings::generalFont().family(), KGlobalSettings::generalFont().pointSize(), QFont::Bold)); + painter.drawText(ICONSIZE + KDialog::marginHint(), 15, m_player->name()); + + if ( m_portfolioEstates.count() ) + painter.drawText(width() - 50, 15, QString::number(m_player->money())); + else + { + painter.setPen(Qt::black); + painter.setBrush(Qt::white); + + painter.setFont(QFont(KGlobalSettings::generalFont().family(), KGlobalSettings::generalFont().pointSize(), QFont::Normal)); + painter.drawText(ICONSIZE + KDialog::marginHint(), 30, m_player->host()); + } + + b_recreate = false; + } + bitBlt(this, 0, 0, qpixmap); +} + +void PortfolioView::resizeEvent(QResizeEvent *) +{ + b_recreate = true; +} + +void PortfolioView::playerChanged() +{ + loadIcon(); + + b_recreate = true; + update(); +} + +void PortfolioView::mousePressEvent(QMouseEvent *e) +{ + Player *playerSelf = m_atlanticCore->playerSelf(); + + if ( e->button()==RightButton && (m_player != playerSelf) ) + { + KPopupMenu *rmbMenu = new KPopupMenu(this); + rmbMenu->insertTitle(m_player->name()); + + if ( m_portfolioEstates.count() ) + { + // Start trade + rmbMenu->insertItem(i18n("Request Trade with %1").arg(m_player->name()), 0); + } + else + { + // Kick player + rmbMenu->insertItem(i18n("Boot Player %1 to Lounge").arg(m_player->name()), 0); + rmbMenu->setItemEnabled( 0, m_atlanticCore->selfIsMaster() ); + } + + connect(rmbMenu, SIGNAL(activated(int)), this, SLOT(slotMenuAction(int))); + QPoint g = QCursor::pos(); + rmbMenu->exec(g); + } +} + +void PortfolioView::slotMenuAction(int item) +{ + switch (item) + { + case 0: + if ( m_portfolioEstates.count() ) + emit newTrade(m_player); + else + emit kickPlayer(m_player); + break; + } +} +#undef PE_DISTW +#undef PE_DISTH +#undef PE_SPACE +#undef PE_MARGINW +#undef PE_MARGINH +#undef ICONSIZE diff --git a/atlantik/libatlantikui/portfolioview.h b/atlantik/libatlantikui/portfolioview.h new file mode 100644 index 00000000..26317e2b --- /dev/null +++ b/atlantik/libatlantikui/portfolioview.h @@ -0,0 +1,73 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_PORTFOLIOVIEW_H +#define ATLANTIK_PORTFOLIOVIEW_H + +#include +#include +#include + +#include "portfolioestate.h" +#include "libatlantikui_export.h" +class QColor; +class QString; + +class AtlanticCore; +class Player; +class Estate; + +class LIBATLANTIKUI_EXPORT PortfolioView : public QWidget +{ +Q_OBJECT + +public: + PortfolioView(AtlanticCore *core, Player *_player, QColor activeColor, QColor inactiveColor, QWidget *parent, const char *name = 0); + ~PortfolioView(); + + void buildPortfolio(); + void clearPortfolio(); + + Player *player(); + +protected: + void paintEvent(QPaintEvent *); + void resizeEvent(QResizeEvent *); + void mousePressEvent(QMouseEvent *); + +signals: + void newTrade(Player *player); + void kickPlayer(Player *player); + void estateClicked(Estate *); + +private slots: + void playerChanged(); + void slotMenuAction(int item); + +private: + void loadIcon(); + + AtlanticCore *m_atlanticCore; + Player *m_player; + PortfolioEstate *m_lastPE; + QColor m_activeColor, m_inactiveColor; + QPixmap *qpixmap, *m_image; + QString m_imageName; + bool b_recreate; + QPtrList m_portfolioEstates; +}; + +#endif diff --git a/atlantik/libatlantikui/token.cpp b/atlantik/libatlantikui/token.cpp new file mode 100644 index 00000000..6f13333f --- /dev/null +++ b/atlantik/libatlantikui/token.cpp @@ -0,0 +1,157 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include +#include +#include + +#include + +#include +#include + +#include "board.h" +#include "estate.h" +#include "player.h" + +#include "token.moc" + +#define TOKEN_ICONSIZE 32 + +Token::Token(Player *player, AtlantikBoard *parent, const char *name) : QWidget(parent, name) +{ + setBackgroundMode(NoBackground); // avoid flickering + + m_parentBoard = parent; + + m_player = player; + connect(m_player, SIGNAL(changed(Player *)), this, SLOT(playerChanged())); + + m_inJail = m_player->inJail(); + m_location = m_player->location(); + m_destination = 0; + + qpixmap = 0; + b_recreate = true; + + // Init icon + m_image = 0; + loadIcon(); + + setFixedSize(QSize(TOKEN_ICONSIZE, TOKEN_ICONSIZE + KGlobalSettings::generalFont().pointSize())); +} + +Token::~Token() +{ + delete m_image; +} + +Player *Token::player() +{ + return m_player; +} + +void Token::setLocation(Estate *location) +{ + if (m_location != location) + m_location = location; +} + +void Token::setDestination(Estate *estateView) +{ + if (m_destination != estateView) + m_destination = estateView; +} + +void Token::playerChanged() +{ + if (m_imageName != m_player->image()) + loadIcon(); + + b_recreate = true; + update(); +} + +void Token::loadIcon() +{ + m_imageName = m_player->image(); + + delete m_image; + m_image = 0; + + if (!m_imageName.isEmpty()) + { + QString filename = locate("data", "atlantik/themes/default/tokens/" + m_imageName); + if (KStandardDirs::exists(filename)) + m_image = new QPixmap(filename); + } + + if (!m_image) + { + m_imageName = "hamburger.png"; + + QString filename = locate("data", "atlantik/themes/default/tokens/" + m_imageName); + if (KStandardDirs::exists(filename)) + m_image = new QPixmap(filename); + } + + QWMatrix m; + m.scale(double(TOKEN_ICONSIZE) / m_image->width(), double(TOKEN_ICONSIZE) / m_image->height()); + QPixmap *scaledPixmap = new QPixmap(TOKEN_ICONSIZE, TOKEN_ICONSIZE); + *scaledPixmap = m_image->xForm(m); + + delete m_image; + m_image = scaledPixmap; +} + +void Token::paintEvent(QPaintEvent *) +{ + if (b_recreate) + { + delete qpixmap; + qpixmap = new QPixmap(width(), height()); + + QPainter painter; + painter.begin(qpixmap, this); + + if (m_image) + { + painter.setPen(Qt::black); + painter.setBrush(Qt::white); + painter.drawRect(0, 0, TOKEN_ICONSIZE, TOKEN_ICONSIZE); + + painter.drawPixmap(0, 0, *m_image); + } + + painter.setPen(Qt::black); + painter.setBrush(Qt::black); + painter.drawRect(0, TOKEN_ICONSIZE, width(), KGlobalSettings::generalFont().pointSize()); + + painter.setPen(Qt::white); + painter.setFont(QFont(KGlobalSettings::generalFont().family(), KGlobalSettings::generalFont().pointSize(), QFont::DemiBold)); + painter.drawText(1, height()-1, (m_player ? m_player->name() : QString::null)); + + b_recreate = false; + } + bitBlt(this, 0, 0, qpixmap); +} + +void Token::resizeEvent(QResizeEvent *) +{ + b_recreate = true; +} diff --git a/atlantik/libatlantikui/token.h b/atlantik/libatlantikui/token.h new file mode 100644 index 00000000..f0e52f2b --- /dev/null +++ b/atlantik/libatlantikui/token.h @@ -0,0 +1,62 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_TOKEN_H +#define ATLANTIK_TOKEN_H + +#include + +class QPixmap; + +class Player; +class Estate; +class AtlantikBoard; + +class Token : public QWidget +{ +Q_OBJECT + + public: + Token (Player *player, AtlantikBoard *parent, const char *name = 0); + ~Token(); + Player *player(); + void setLocation(Estate *estate); + Estate *location() { return m_location; } + void setDestination(Estate *estate); + Estate *destination() { return m_destination; } + void setInJail (bool inJail) { m_inJail = inJail; } + bool inJail() { return m_inJail; } + + private slots: + void playerChanged(); + + protected: + void paintEvent(QPaintEvent *); + void resizeEvent(QResizeEvent *); + +private: + void loadIcon(); + + Player *m_player; + Estate *m_location, *m_destination; + bool m_inJail; + AtlantikBoard *m_parentBoard; + bool b_recreate; + QPixmap *qpixmap, *m_image; + QString m_imageName; +}; + +#endif diff --git a/atlantik/libatlantikui/trade_widget.cpp b/atlantik/libatlantikui/trade_widget.cpp new file mode 100644 index 00000000..b2658abb --- /dev/null +++ b/atlantik/libatlantikui/trade_widget.cpp @@ -0,0 +1,374 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "trade_widget.moc" + +TradeDisplay::TradeDisplay(Trade *trade, AtlanticCore *atlanticCore, QWidget *parent, const char *name) + : QWidget(parent, name, + WType_Dialog | WStyle_Customize | WStyle_DialogBorder | WStyle_Title | + WStyle_Minimize | WStyle_ContextHelp ) +{ + m_trade = trade; + m_atlanticCore = atlanticCore; + + setCaption(i18n("Trade %1").arg(trade->tradeId())); + + QVBoxLayout *listCompBox = new QVBoxLayout(this, KDialog::marginHint()); + + m_updateComponentBox = new QHGroupBox(i18n("Add Component"), this); + listCompBox->addWidget(m_updateComponentBox); + + m_editTypeCombo = new KComboBox(m_updateComponentBox); + m_editTypeCombo->insertItem(i18n("Estate")); + m_editTypeCombo->insertItem(i18n("Money")); + + connect(m_editTypeCombo, SIGNAL(activated(int)), this, SLOT(setTypeCombo(int))); + + m_estateCombo = new KComboBox(m_updateComponentBox); + QPtrList estateList = m_atlanticCore->estates(); + Estate *estate; + for (QPtrListIterator it(estateList); *it; ++it) + { + if ((estate = *it) && estate->isOwned()) + { + m_estateCombo->insertItem( PortfolioEstate::drawPixmap(estate), estate->name() ); + m_estateMap[m_estateCombo->count() - 1] = estate; + m_estateRevMap[estate] = m_estateCombo->count() - 1; + } + } + + connect(m_estateCombo, SIGNAL(activated(int)), this, SLOT(setEstateCombo(int))); + + m_moneyBox = new QSpinBox(0, 10000, 1, m_updateComponentBox); + + QPtrList playerList = m_atlanticCore->players(); + Player *player, *pSelf = m_atlanticCore->playerSelf(); + + m_fromLabel = new QLabel(m_updateComponentBox); + m_fromLabel->setText(i18n("From")); + m_playerFromCombo = new KComboBox(m_updateComponentBox); + + m_toLabel = new QLabel(m_updateComponentBox); + m_toLabel->setText(i18n("To")); + m_playerTargetCombo = new KComboBox(m_updateComponentBox); + + for (QPtrListIterator it(playerList); *it; ++it) + { + if ((player = *it) && player->game() == pSelf->game()) + { + m_playerFromCombo->insertItem(player->name()); + m_playerFromMap[m_playerFromCombo->count() - 1] = player; + m_playerFromRevMap[player] = m_playerFromCombo->count() - 1; + + m_playerTargetCombo->insertItem(player->name()); + m_playerTargetMap[m_playerTargetCombo->count() - 1] = player; + m_playerTargetRevMap[player] = m_playerTargetCombo->count() - 1; + + connect(player, SIGNAL(changed(Player *)), this, SLOT(playerChanged(Player *))); + } + } + + m_updateButton = new KPushButton(i18n("Update"), m_updateComponentBox); + m_updateButton->setEnabled(false); + + connect(m_updateButton, SIGNAL(clicked()), this, SLOT(updateComponent())); + + m_componentList = new KListView(this, "componentList"); + listCompBox->addWidget(m_componentList); + + m_componentList->addColumn(i18n("Player")); + m_componentList->addColumn(i18n("Gives")); + m_componentList->addColumn(i18n("Player")); + m_componentList->addColumn(i18n("Item")); + + connect(m_componentList, SIGNAL(contextMenu(KListView*, QListViewItem *, const QPoint&)), SLOT(contextMenu(KListView *, QListViewItem *, const QPoint&))); + connect(m_componentList, SIGNAL(clicked(QListViewItem *)), this, SLOT(setCombos(QListViewItem *))); + + QHBoxLayout *actionBox = new QHBoxLayout(this, 0, KDialog::spacingHint()); + listCompBox->addItem(actionBox); + + actionBox->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + m_rejectButton = new KPushButton(BarIcon("cancel", KIcon::SizeSmall), i18n("Reject"), this); + actionBox->addWidget(m_rejectButton); + + connect(m_rejectButton, SIGNAL(clicked()), this, SLOT(reject())); + + m_acceptButton = new KPushButton(BarIcon("ok", KIcon::SizeSmall), i18n("Accept"), this); +// m_acceptButton->setEnabled(false); + actionBox->addWidget(m_acceptButton); + + connect(m_acceptButton, SIGNAL(clicked()), this, SLOT(accept())); + + m_status = new QLabel(this); + listCompBox->addWidget(m_status); + m_status->setText( i18n( "%1 out of %2 players accept current trade proposal." ).arg( m_trade->count( true ) ).arg( m_trade->count( false ) ) ); + +// mPlayerList->header()->hide(); +// mPlayerList->setRootIsDecorated(true); +// mPlayerList->setResizeMode(KListView::AllColumns); + + connect(m_trade, SIGNAL(itemAdded(TradeItem *)), this, SLOT(tradeItemAdded(TradeItem *))); + connect(m_trade, SIGNAL(itemRemoved(TradeItem *)), this, SLOT(tradeItemRemoved(TradeItem *))); + connect(m_trade, SIGNAL(changed(Trade *)), this, SLOT(tradeChanged())); + connect(m_trade, SIGNAL(rejected(Player *)), this, SLOT(tradeRejected(Player *))); + connect(this, SIGNAL(updateEstate(Trade *, Estate *, Player *)), m_trade, SIGNAL(updateEstate(Trade *, Estate *, Player *))); + connect(this, SIGNAL(updateMoney(Trade *, unsigned int, Player *, Player *)), m_trade, SIGNAL(updateMoney(Trade *, unsigned int, Player *, Player *))); + connect(this, SIGNAL(reject(Trade *)), m_trade, SIGNAL(reject(Trade *))); + connect(this, SIGNAL(accept(Trade *)), m_trade, SIGNAL(accept(Trade *))); + + setTypeCombo(m_editTypeCombo->currentItem()); + setEstateCombo(m_estateCombo->currentItem()); + + m_contextTradeItem = 0; +} + +void TradeDisplay::closeEvent(QCloseEvent *e) +{ + // Don't send network event when trade is already rejected + if (m_trade->isRejected()) + m_atlanticCore->removeTrade(m_trade); + else + emit reject(m_trade); + + e->accept(); +} + +void TradeDisplay::tradeItemAdded(TradeItem *tradeItem) +{ + KListViewItem *item = new KListViewItem(m_componentList, (tradeItem->from() ? tradeItem->from()->name() : QString("?")), i18n("gives is transitive ;)", "gives"), (tradeItem->to() ? tradeItem->to()->name() : QString("?")), tradeItem->text()); + connect(tradeItem, SIGNAL(changed(TradeItem *)), this, SLOT(tradeItemChanged(TradeItem *))); + + item->setPixmap(0, QPixmap(SmallIcon("personal"))); + item->setPixmap(2, QPixmap(SmallIcon("personal"))); + + if (TradeEstate *tradeEstate = dynamic_cast(tradeItem)) + item->setPixmap(3, PortfolioEstate::drawPixmap(tradeEstate->estate())); +// else if (TradeMoney *tradeMoney = dynamic_cast(tradeItem)) +// item->setPixmap(3, PortfolioEstate::pixMap(tradeEstate->estate())); + + m_componentMap[tradeItem] = item; + m_componentRevMap[item] = tradeItem; +} + +void TradeDisplay::tradeItemRemoved(TradeItem *t) +{ + KListViewItem *item = m_componentMap[t]; + delete item; + m_componentMap[t] = 0; +} + +void TradeDisplay::tradeItemChanged(TradeItem *t) +{ + KListViewItem *item = m_componentMap[t]; + if (item) + { + item->setText(0, t->from() ? t->from()->name() : QString("?")); + item->setPixmap(0, QPixmap(SmallIcon("personal"))); + item->setText(2, t->to() ? t->to()->name() : QString("?")); + item->setPixmap(2, QPixmap(SmallIcon("personal"))); + item->setText(3, t->text()); + } +} + +void TradeDisplay::tradeChanged() +{ + // TODO: add notification whether playerSelf has accepted or not and + // enable/disable accept button based on that + m_status->setText( i18n( "%1 out of %2 players accept current trade proposal." ).arg( m_trade->count( true ) ).arg( m_trade->count( false ) ) ); +} + +void TradeDisplay::playerChanged(Player *player) +{ + m_playerFromCombo->changeItem(player->name(), m_playerFromRevMap[player]); + m_playerTargetCombo->changeItem(player->name(), m_playerTargetRevMap[player]); + + TradeItem *item = 0; + for (QMap::Iterator it=m_componentRevMap.begin() ; it != m_componentRevMap.end() && (item = *it) ; ++it) + tradeItemChanged(item); +} + +void TradeDisplay::tradeRejected(Player *player) +{ + if (player) + m_status->setText(i18n("Trade proposal was rejected by %1.").arg(player->name())); + else + m_status->setText(i18n("Trade proposal was rejected.")); + + // Disable GUI elements + m_updateButton->setEnabled(false); + m_componentList->setEnabled(false); + m_rejectButton->setEnabled(false); + m_acceptButton->setEnabled(false); + + // TODO: add/enable close button +} + +void TradeDisplay::setTypeCombo(int index) +{ + switch (index) + { + case 0: + // Editing estate component + + m_estateCombo->show(); + m_estateCombo->setMaximumWidth(9999); + + m_moneyBox->hide(); + m_moneyBox->setMaximumWidth(0); + + setEstateCombo(m_estateCombo->currentItem()); // also updates playerfromCombo + m_playerFromCombo->setEnabled(false); + + m_updateButton->setEnabled( m_estateCombo->count() > 0 ); + + break; + + case 1: + // Editing money component + + m_estateCombo->hide(); + m_estateCombo->setMaximumWidth(0); + + m_moneyBox->show(); + m_moneyBox->setMaximumWidth(9999); + + m_playerFromCombo->setEnabled(true); + + m_updateButton->setEnabled(true); + + break; + } +} + +void TradeDisplay::setEstateCombo(int index) +{ + if (m_estateCombo->currentItem() != index) + m_estateCombo->setCurrentItem(index); + + if (Estate *estate = m_estateMap[index]) + m_playerFromCombo->setCurrentItem( m_playerFromRevMap[estate->owner()] ); +} + +void TradeDisplay::setCombos(QListViewItem *i) +{ + TradeItem *item = m_componentRevMap[(KListViewItem *)(i)]; + if (TradeEstate *tradeEstate = dynamic_cast(item)) + { + setTypeCombo(0); + setEstateCombo( m_estateRevMap[tradeEstate->estate()] ); // also updates playerFromCombo + m_playerTargetCombo->setCurrentItem( m_playerTargetRevMap[tradeEstate->to()] ); + } + else if (TradeMoney *tradeMoney = dynamic_cast(item)) + { + setTypeCombo(1); + m_moneyBox->setValue( tradeMoney->money() ); + m_playerFromCombo->setCurrentItem( m_playerFromRevMap[tradeMoney->from()] ); + m_playerTargetCombo->setCurrentItem( m_playerTargetRevMap[tradeMoney->to()] ); + } +} + +void TradeDisplay::updateComponent() +{ + Estate *estate; + Player *pFrom, *pTarget; + + switch (m_editTypeCombo->currentItem()) + { + case 0: + // Updating estate component + estate = m_estateMap[m_estateCombo->currentItem()]; + pTarget = m_playerTargetMap[m_playerTargetCombo->currentItem()]; + + if (estate && pTarget) + emit updateEstate(m_trade, estate, pTarget); + + break; + + case 1: + // Updating money component + pFrom = m_playerFromMap[m_playerFromCombo->currentItem()]; + pTarget = m_playerTargetMap[m_playerTargetCombo->currentItem()]; + + if (pFrom && pTarget) + emit updateMoney(m_trade, m_moneyBox->value(), pFrom, pTarget); + + break; + } +} + +void TradeDisplay::reject() +{ + emit reject(m_trade); +} + +void TradeDisplay::accept() +{ + emit accept(m_trade); +} + +void TradeDisplay::contextMenu(KListView *, QListViewItem *i, const QPoint& p) +{ + m_contextTradeItem = m_componentRevMap[(KListViewItem *)(i)]; + + KPopupMenu *rmbMenu = new KPopupMenu(this); +// rmbMenu->insertTitle( ... ); + rmbMenu->insertItem(i18n("Remove From Trade"), 0); + + connect(rmbMenu, SIGNAL(activated(int)), this, SLOT(contextMenuClicked(int))); + rmbMenu->exec(p); +} + +void TradeDisplay::contextMenuClicked(int) +{ + if (!m_contextTradeItem) + return; + + if (TradeEstate *tradeEstate = dynamic_cast(m_contextTradeItem)) + emit updateEstate(m_trade, tradeEstate->estate(), 0); + else if (TradeMoney *tradeMoney = dynamic_cast(m_contextTradeItem)) + emit updateMoney(m_trade, 0, tradeMoney->from(), tradeMoney->to()); + + m_contextTradeItem = 0; +} diff --git a/atlantik/libatlantikui/trade_widget.h b/atlantik/libatlantikui/trade_widget.h new file mode 100644 index 00000000..642cc919 --- /dev/null +++ b/atlantik/libatlantikui/trade_widget.h @@ -0,0 +1,98 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef TRADEWIDGET_H +#define TRADEWIDGET_H + +#include +#include +#include "libatlantikui_export.h" + +class QHGroupBox; +class QLabel; +class QListViewItem; +class QSpinBox; + +class KListView; +class KListViewItem; +class KComboBox; +class KPushButton; + +class AtlanticCore; +class Player; +class Trade; +class TradeItem; + +class LIBATLANTIKUI_EXPORT TradeDisplay : public QWidget +{ +Q_OBJECT + +public: + TradeDisplay(Trade *trade, AtlanticCore *atlanticCore, QWidget *parent=0, const char *name = 0); + + Trade *trade() { return mTrade; } + +protected: + void closeEvent(QCloseEvent *e); + +private slots: + void tradeItemAdded(TradeItem *); + void tradeItemRemoved(TradeItem *); + void tradeItemChanged(TradeItem *); + void tradeChanged(); + void playerChanged(Player *player); + void tradeRejected(Player *); + + void setTypeCombo(int); + void setEstateCombo(int); + void setCombos(QListViewItem *i); + + void updateComponent(); + void reject(); + void accept(); + + void contextMenu(KListView *l, QListViewItem *i, const QPoint& p); + void contextMenuClicked(int item); + +signals: + void updateEstate(Trade *trade, Estate *estate, Player *to); + void updateMoney(Trade *trade, unsigned int money, Player *from, Player *to); + void reject(Trade *trade); + void accept(Trade *trade); + +private: + QHGroupBox *m_updateComponentBox; + QLabel *m_status, *m_fromLabel, *m_toLabel; + QSpinBox *m_moneyBox; + + KComboBox *m_editTypeCombo, *m_playerFromCombo, *m_playerTargetCombo, *m_estateCombo; + KListView *m_componentList; + KPushButton *m_updateButton, *m_rejectButton, *m_acceptButton; + + AtlanticCore *m_atlanticCore; + Trade *mTrade, *m_trade; + TradeItem *m_contextTradeItem; + + // TODO: Wouldn't QPair make more sense here? + QMap m_componentMap; + QMap m_componentRevMap; + QMap m_estateMap; + QMap m_estateRevMap; + QMap m_playerFromMap, m_playerTargetMap; + QMap m_playerFromRevMap, m_playerTargetRevMap; +}; + +#endif diff --git a/atlantik/pics/Makefile.am b/atlantik/pics/Makefile.am new file mode 100644 index 00000000..e0fd3547 --- /dev/null +++ b/atlantik/pics/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = board misc toolbar + +KDE_ICON = atlantik diff --git a/atlantik/pics/board/Makefile.am b/atlantik/pics/board/Makefile.am new file mode 100644 index 00000000..1a6d0d6c --- /dev/null +++ b/atlantik/pics/board/Makefile.am @@ -0,0 +1,2 @@ +boardicondir = $(kde_datadir)/atlantik/pics +boardicon_DATA = arrow.png qmark-blue.png qmark-red.png token.png train.png diff --git a/atlantik/pics/board/arrow.png b/atlantik/pics/board/arrow.png new file mode 100644 index 00000000..167d4766 Binary files /dev/null and b/atlantik/pics/board/arrow.png differ diff --git a/atlantik/pics/board/qmark-blue.png b/atlantik/pics/board/qmark-blue.png new file mode 100644 index 00000000..6462dff0 Binary files /dev/null and b/atlantik/pics/board/qmark-blue.png differ diff --git a/atlantik/pics/board/qmark-red.png b/atlantik/pics/board/qmark-red.png new file mode 100644 index 00000000..6aff93b3 Binary files /dev/null and b/atlantik/pics/board/qmark-red.png differ diff --git a/atlantik/pics/board/token.png b/atlantik/pics/board/token.png new file mode 100644 index 00000000..eba2ed1b Binary files /dev/null and b/atlantik/pics/board/token.png differ diff --git a/atlantik/pics/board/train.png b/atlantik/pics/board/train.png new file mode 100644 index 00000000..4603e537 Binary files /dev/null and b/atlantik/pics/board/train.png differ diff --git a/atlantik/pics/hi16-app-atlantik.png b/atlantik/pics/hi16-app-atlantik.png new file mode 100644 index 00000000..e89961c6 Binary files /dev/null and b/atlantik/pics/hi16-app-atlantik.png differ diff --git a/atlantik/pics/hi32-app-atlantik.png b/atlantik/pics/hi32-app-atlantik.png new file mode 100644 index 00000000..daa8ec15 Binary files /dev/null and b/atlantik/pics/hi32-app-atlantik.png differ diff --git a/atlantik/pics/hi48-app-atlantik.png b/atlantik/pics/hi48-app-atlantik.png new file mode 100644 index 00000000..5a259675 Binary files /dev/null and b/atlantik/pics/hi48-app-atlantik.png differ diff --git a/atlantik/pics/misc/Makefile.am b/atlantik/pics/misc/Makefile.am new file mode 100644 index 00000000..9652d96c --- /dev/null +++ b/atlantik/pics/misc/Makefile.am @@ -0,0 +1,2 @@ +atlantikicondir = $(kde_datadir)/atlantik/icons +atlantikicon_ICON = AUTO diff --git a/atlantik/pics/misc/cr32-action-monop_board.png b/atlantik/pics/misc/cr32-action-monop_board.png new file mode 100644 index 00000000..c610aadc Binary files /dev/null and b/atlantik/pics/misc/cr32-action-monop_board.png differ diff --git a/atlantik/pics/toolbar/Makefile.am b/atlantik/pics/toolbar/Makefile.am new file mode 100644 index 00000000..76f0a0a7 --- /dev/null +++ b/atlantik/pics/toolbar/Makefile.am @@ -0,0 +1,2 @@ +atlantiktoolbardir = $(kde_datadir)/atlantik/icons +atlantiktoolbar_ICON = AUTO diff --git a/atlantik/pics/toolbar/cr16-action-jail_pay.png b/atlantik/pics/toolbar/cr16-action-jail_pay.png new file mode 100644 index 00000000..952b0ead Binary files /dev/null and b/atlantik/pics/toolbar/cr16-action-jail_pay.png differ diff --git a/atlantik/pics/toolbar/cr22-action-atlantik_buy_estate.png b/atlantik/pics/toolbar/cr22-action-atlantik_buy_estate.png new file mode 100644 index 00000000..2dec599e Binary files /dev/null and b/atlantik/pics/toolbar/cr22-action-atlantik_buy_estate.png differ diff --git a/atlantik/pics/toolbar/cr22-action-jail_pay.png b/atlantik/pics/toolbar/cr22-action-jail_pay.png new file mode 100644 index 00000000..d45caec4 Binary files /dev/null and b/atlantik/pics/toolbar/cr22-action-jail_pay.png differ diff --git a/atlantik/pics/toolbar/cr32-action-atlantik_buy_estate.png b/atlantik/pics/toolbar/cr32-action-atlantik_buy_estate.png new file mode 100644 index 00000000..e1a86e47 Binary files /dev/null and b/atlantik/pics/toolbar/cr32-action-atlantik_buy_estate.png differ diff --git a/atlantik/pics/toolbar/cr32-action-auction.png b/atlantik/pics/toolbar/cr32-action-auction.png new file mode 100644 index 00000000..0a179532 Binary files /dev/null and b/atlantik/pics/toolbar/cr32-action-auction.png differ diff --git a/atlantik/pics/toolbar/cr32-action-jail_pay.png b/atlantik/pics/toolbar/cr32-action-jail_pay.png new file mode 100644 index 00000000..915a7e46 Binary files /dev/null and b/atlantik/pics/toolbar/cr32-action-jail_pay.png differ diff --git a/atlantik/pics/toolbar/lo16-action-atlantik_buy_estate.png b/atlantik/pics/toolbar/lo16-action-atlantik_buy_estate.png new file mode 100644 index 00000000..3007a2df Binary files /dev/null and b/atlantik/pics/toolbar/lo16-action-atlantik_buy_estate.png differ diff --git a/atlantik/themes/Makefile.am b/atlantik/themes/Makefile.am new file mode 100644 index 00000000..dcc4764a --- /dev/null +++ b/atlantik/themes/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = default diff --git a/atlantik/themes/default/Makefile.am b/atlantik/themes/default/Makefile.am new file mode 100644 index 00000000..7890f6ec --- /dev/null +++ b/atlantik/themes/default/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = tokens diff --git a/atlantik/themes/default/tokens/Makefile.am b/atlantik/themes/default/tokens/Makefile.am new file mode 100644 index 00000000..17e38396 --- /dev/null +++ b/atlantik/themes/default/tokens/Makefile.am @@ -0,0 +1,5 @@ +icondir = $(kde_datadir)/atlantik/themes/default/tokens +icon_DATA = badge.png beachball.png bell.png bomb.png cat.png cookie.png \ + cube.png eyeball.png flag.png ghost.png globe.png hamburger.png \ + lips.png puzzle.png pyramid.png skull.png traffic_light.png \ + wizard.png diff --git a/atlantik/themes/default/tokens/badge.png b/atlantik/themes/default/tokens/badge.png new file mode 100644 index 00000000..e0b58dee Binary files /dev/null and b/atlantik/themes/default/tokens/badge.png differ diff --git a/atlantik/themes/default/tokens/beachball.png b/atlantik/themes/default/tokens/beachball.png new file mode 100644 index 00000000..7247b78f Binary files /dev/null and b/atlantik/themes/default/tokens/beachball.png differ diff --git a/atlantik/themes/default/tokens/bell.png b/atlantik/themes/default/tokens/bell.png new file mode 100644 index 00000000..cf5ac8c2 Binary files /dev/null and b/atlantik/themes/default/tokens/bell.png differ diff --git a/atlantik/themes/default/tokens/bomb.png b/atlantik/themes/default/tokens/bomb.png new file mode 100644 index 00000000..dfdf434c Binary files /dev/null and b/atlantik/themes/default/tokens/bomb.png differ diff --git a/atlantik/themes/default/tokens/cat.png b/atlantik/themes/default/tokens/cat.png new file mode 100644 index 00000000..78b51245 Binary files /dev/null and b/atlantik/themes/default/tokens/cat.png differ diff --git a/atlantik/themes/default/tokens/cookie.png b/atlantik/themes/default/tokens/cookie.png new file mode 100644 index 00000000..8944fa29 Binary files /dev/null and b/atlantik/themes/default/tokens/cookie.png differ diff --git a/atlantik/themes/default/tokens/cube.png b/atlantik/themes/default/tokens/cube.png new file mode 100644 index 00000000..d2487696 Binary files /dev/null and b/atlantik/themes/default/tokens/cube.png differ diff --git a/atlantik/themes/default/tokens/eyeball.png b/atlantik/themes/default/tokens/eyeball.png new file mode 100644 index 00000000..56660202 Binary files /dev/null and b/atlantik/themes/default/tokens/eyeball.png differ diff --git a/atlantik/themes/default/tokens/flag.png b/atlantik/themes/default/tokens/flag.png new file mode 100644 index 00000000..d0b1f15f Binary files /dev/null and b/atlantik/themes/default/tokens/flag.png differ diff --git a/atlantik/themes/default/tokens/ghost.png b/atlantik/themes/default/tokens/ghost.png new file mode 100644 index 00000000..03efcc2a Binary files /dev/null and b/atlantik/themes/default/tokens/ghost.png differ diff --git a/atlantik/themes/default/tokens/globe.png b/atlantik/themes/default/tokens/globe.png new file mode 100644 index 00000000..33741975 Binary files /dev/null and b/atlantik/themes/default/tokens/globe.png differ diff --git a/atlantik/themes/default/tokens/hamburger.png b/atlantik/themes/default/tokens/hamburger.png new file mode 100644 index 00000000..d994115c Binary files /dev/null and b/atlantik/themes/default/tokens/hamburger.png differ diff --git a/atlantik/themes/default/tokens/lips.png b/atlantik/themes/default/tokens/lips.png new file mode 100644 index 00000000..171fa3a1 Binary files /dev/null and b/atlantik/themes/default/tokens/lips.png differ diff --git a/atlantik/themes/default/tokens/puzzle.png b/atlantik/themes/default/tokens/puzzle.png new file mode 100644 index 00000000..7f70b8d6 Binary files /dev/null and b/atlantik/themes/default/tokens/puzzle.png differ diff --git a/atlantik/themes/default/tokens/pyramid.png b/atlantik/themes/default/tokens/pyramid.png new file mode 100644 index 00000000..e0792d20 Binary files /dev/null and b/atlantik/themes/default/tokens/pyramid.png differ diff --git a/atlantik/themes/default/tokens/skull.png b/atlantik/themes/default/tokens/skull.png new file mode 100644 index 00000000..07361122 Binary files /dev/null and b/atlantik/themes/default/tokens/skull.png differ diff --git a/atlantik/themes/default/tokens/traffic_light.png b/atlantik/themes/default/tokens/traffic_light.png new file mode 100644 index 00000000..0f2f6476 Binary files /dev/null and b/atlantik/themes/default/tokens/traffic_light.png differ diff --git a/atlantik/themes/default/tokens/wizard.png b/atlantik/themes/default/tokens/wizard.png new file mode 100644 index 00000000..c7ad6f50 Binary files /dev/null and b/atlantik/themes/default/tokens/wizard.png differ diff --git a/configure.in.in b/configure.in.in new file mode 100644 index 00000000..666fd760 --- /dev/null +++ b/configure.in.in @@ -0,0 +1,34 @@ +#MIN_CONFIG +KDE_ENABLE_HIDDEN_VISIBILITY +AC_CHECK_RANDOM +AC_CHECK_USLEEP +CXXFLAGS="$CXXFLAGS $KDE_DEFAULT_CXXFLAGS" + +KDE_INIT_DOXYGEN([KDE Games API Reference], [Version $VERSION]) + +# Allow for stand-alone releases of applications with cvs2pack +if test -d $srcdir/libkdegames ; then + AC_SUBST(LIB_KDEGAMES, "\$(top_builddir)/libkdegames/libkdegames.la") + AC_SUBST(LIB_KDEGAMES_DEP, '$(LIB_KDEGAMES)') +else + AC_SUBST(LIB_KDEGAMES, "-lkdegames") + AC_SUBST(LIB_KDEGAMES_DEP, "") +fi + +artsc_config_test_path=$prefix/bin:$exec_prefix/bin:$KDEDIR/bin:$PATH +AC_PATH_PROG(ARTSCCONFIG, artsc-config, no, $artsc_config_test_path) + +if test "x$build_arts" = "xyes" && test "x$ARTSCCONFIG" != "xno" ; then + LIB_ARTS="-lartskde" + ARTS_PREFIX=[`$ARTSCCONFIG --arts-prefix`] + ARTS_CFLAGS="-I$ARTS_PREFIX/include/arts" + AC_DEFINE(HAVE_ARTS, 1, [have arts support in juk]) +else + build_arts="no" + LIB_ARTS="" + ARTS_CFLAGS="" + AC_DEFINE(HAVE_ARTS, 0, [no arts support in juk]) +fi + +AC_SUBST(LIB_ARTS) +AC_SUBST(ARTS_CFLAGS) diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 00000000..6812bd2d --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,5 @@ + +KDE_LANG = en +KDE_DOCS = AUTO +SUBDIRS = $(AUTODIRS) + diff --git a/doc/api/kcarddialog.png b/doc/api/kcarddialog.png new file mode 100644 index 00000000..3446c461 Binary files /dev/null and b/doc/api/kcarddialog.png differ diff --git a/doc/atlantik/Makefile.am b/doc/atlantik/Makefile.am new file mode 100644 index 00000000..da8216ae --- /dev/null +++ b/doc/atlantik/Makefile.am @@ -0,0 +1,4 @@ + +KDE_LANG = en +KDE_DOCS = AUTO +KDE_MANS = AUTO diff --git a/doc/atlantik/index.docbook b/doc/atlantik/index.docbook new file mode 100644 index 00000000..56c5079d --- /dev/null +++ b/doc/atlantik/index.docbook @@ -0,0 +1,323 @@ + + + + + +]> + + + + +The &atlantik; Handbook + + + +Rob +Kaper + +
kaper@kde.org
+
+
+ +
+ +&FDLNotice; + + +20022004 +&Rob.Kaper; + + +2005-12-10 +0.7.5 + + + +&atlantik; is a &kde; client for playing Monopoly-like boardgames on the +monopd network. + + + + + + +KDE +kdegames +Atlantik +monopd + + +
+ + +Introduction + +Purpose of the &atlantik; board game is to acquire land in major +cities in North America and Europe while being a transatlantic traveler. To +win the game, players improve monopolized land with profitable buildings in +the hopes of bankrupting all other players. + +All game modes are served by monopd, a dedicated game server designed +for &atlantik;. One of the game modes plays like the popular real estate +board game known as Monopoly. + + + + +Connecting to a Server + +&atlantik; requires a game server to connect to. If you have an +Internet connection, you can request a list of public Internet servers and +you will not require additional software. If you want to play &atlantik; +locally, on a LAN or on a private Internet server, you +can enter the hostname and port to connect to. In this case, you will need +the +monopd server +software installed and running on the host you are connecting to. + +If you have problems connecting to a server, the following troubleshoot +notes might help you: + + +Try another server. The public server list is updated every +three minutes, and the server you are trying to connect to might not be +available any longer. + + +Check your firewall and masquerading settings. Restrictions might be in +place preventing you from connecting to servers or receiving reply traffic. +By default, monopd servers use TCP port 1234. If you're not sure, contact +your system administrator. + +&atlantik; makes use of +KExtendedSocket for network connections, which in +turn uses QDns. This might cause issues with IPv6 +and/or resolving hostnames. + + + + + +Menu Reference + + +<guimenu>Game</guimenu> Menu + + + +&Ctrl;L +GameShow Event Log +Display the event log + + + +&Ctrl;Q +GameQuit +Quits &atlantik;. + + + + + +<guimenu>Move</guimenu> Menu + + + +&Ctrl;R +MoveRoll Dice +As you may expect, roll the dice. + + + + +MoveEnd Turn + +Let the other player know you have finished +moving. + + + +&Ctrl;R +MoveRoll Dice +As you may expect, roll the dice. + + + +&Ctrl;B +MoveBuy +Buy land or buy buildings on your properties. + + + +&Ctrl;A +MoveAuction +Start an auction. + + + + +MoveUse Card to Leave Jail +Use a card to leave jail + + + +&Ctrl;P +MovePay to Leave Jail +Pay money to leave the jail. + + + +&Ctrl;J +MoveRoll to Leave Jail +Roll the dice to leave the jail. + + + + + +<guimenu>Settings</guimenu> Menu + + + + +Settings +Configure Notifications... + + +Displays a standard &kde; notifications +configuration dialog to change the audio and visual notifications for &atlantik;. + + + + + +Settings +Configure &atlantik;... +Opens the configuration +dialog which lets you tweak a lot of &atlantik;'s options. + + + + + +<guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + +Reporting Bugs + +If you think you have found a bug in &atlantik;, please report +it. Developers often catch and fix bugs themselves, but you might +experience issues not yet known. Not reporting your problems might +very well prevent them from being fixed indefinitely. + +Some bugs might be in the monopd server instead of &atlantik;, but you +don't need to worry about that: you can report all bugs under the &atlantik; +package on the &kde; bug +wizard. Use the +HelpReport +Bug menu item to visit the bug wizard with some +details of your &atlantik; version filled in automatically. + +Please specify details in your bug report, such as game +conditions. For example, a bug might only occur when people are in jail, or +during an auction. If possible, save the event log and attach it to the bug +report or send it to the author. It might contain vital clues about behavior +that could lead to a faster resolution for the bug. + + + + +Download + +&atlantik; is part of &kde; releases and as such the recommended +releases are those shipped with &kde;, part of the &package; package. Some +vendors might have individual packages for &atlantik; based on these +releases. + +Users interested in the development version can likewise use the +standard &kde; CVS +instructions to acquire &atlantik; from the kdegames module. + +Bleeding edge development requiring the development version of monopd +is done using arch. For more information on this, or releases in general, +visit the &atlantik; download +page. + + + + +Answers to Frequently Asked Questions + + + + + +How do I add a computer player? + + +Unfortunately, computer opponents are not yet supported by &atlantik;. +Work is in progress to development a network bot that can connect to +servers, called Pacifik, but is has +not yet reached a useful level. +Users are advised to take advantage of the public +Internet servers or a private LAN server. + + + + + +How do I trade? + + +Click on a player portfolio or estate with the &RMB; and you can open +a trade. +There are a two usability issues with trades that can cause confusion. +If you in any way include another player in a trade, that player will get +the trade window. Any player can reject the terms, which unnecessarily ends +the trade session for all other players as well. A trade has to be accepted +by all players in it before it can completed, which can be complicated +because players remain a participant in deals even when they are no longer +involved with tradeable items. + + + + + + + +Credits and Licenses + +The program &atlantik; and the documentation are copyright © +1998-2004 Rob Kaper kaper@kde.org. + + + +&underFDL; +&underGPL; + + + + +&documentation.index; + +
+ + diff --git a/doc/atlantik/man-atlantik.6.docbook b/doc/atlantik/man-atlantik.6.docbook new file mode 100644 index 00000000..340650a6 --- /dev/null +++ b/doc/atlantik/man-atlantik.6.docbook @@ -0,0 +1,86 @@ + + +]> + + + +LauriWatts +lauri@kde.org +March 7, 2003 + + + +atlantik +6 + + + +atlantik +&kde; monopd client + + + + +atlantik + host + port + game + + + + + + + +Description + +&atlantik; is a &kde; client for playing Monopoly-like +boardgames on the monopd network. + +Purpose of the atlantik board game is to acquire land in major +cities in North America and Europe while being a transatlantic +traveller. All game modes are served by monopd, a dedicated game +server designed for &atlantik;. One of the game modes plays like the +popular real estate board game known as Monopoly. + + + + +Options + + +&atlantik; options + + host + +Connect to this host. + + + + + port +Connect to this port + + + + game +Join this game + + + + + + +See Also + +More detailed user documentation is available from help:/atlantik (either enter this +URL into &konqueror;, or run +khelpcenter +help:/atlantik). + + + + diff --git a/doc/kasteroids/Makefile.am b/doc/kasteroids/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/kasteroids/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/kasteroids/fuel.png b/doc/kasteroids/fuel.png new file mode 100644 index 00000000..7ee0ff73 Binary files /dev/null and b/doc/kasteroids/fuel.png differ diff --git a/doc/kasteroids/index.docbook b/doc/kasteroids/index.docbook new file mode 100644 index 00000000..61563db0 --- /dev/null +++ b/doc/kasteroids/index.docbook @@ -0,0 +1,328 @@ + + + + + +]> + + + + + +The &kasteroids; Handbook + + + +&Martin.R.Jones; &Martin.R.Jones.mail; + + + +&Philip.Rodrigues; &Philip.Rodrigues.mail; + + + + + + +2000 +Martin R. Jones + + + +2001-2005 +Philip Rodrigues + +&FDLNotice; + +2005-01-18 +2.3 + +&kasteroids; is, as you would expect, the &kde; clone of the +popular Asteroids game. + + +KDE +games +linux +asteroids + + + + + +Introduction + +The objective of &kasteroids; is to destroy all the asteroids on the +screen to advance to the next level. Your ship is destroyed if it makes contact +with an asteroid. + + + + +Your Ship + +When your ship is first launched, it has only basic abilities: Turning, +Thrusting and Shooting + +In order to move your ship, rotate it to face the direction you wish to +travel and press the thrust key. Of course the ship has momentum, so you will +have to compensate when you select the angle of the ship. Thrusting uses fuel, +so you should not use your thrusters more than necessary. + +You have an unlimited amount of ammunition, so shoot away. Only a limited +number of shots can be active at once, though. If you repeatedly shoot and +miss, you will find that you are unable to shoot until some of the shots have +expired. + +Your ship has a limited amount of fuel. When all the ship's fuel has been +consumed it is unable to thrust anymore. This leaves you immobile, but not +defenseless ‐ you can still rotate and shoot. + +Fortunately the asteroids occasionally release fuel when they are shot. +Fly your ship into the fuel symbol to collect it. + + +Fuel Upgrade + + + + + +Fuel Upgrade + +Fuel Upgrade + + + + + + +Ship Upgrades + +Occasionally when you shoot an asteroid a symbol will be produced. These +symbols represent upgrades to your ship. Fly over the symbol to install it in +your ship. The upgrades have a cumulative effect, up to a maximum of 5. + +The improvements available are: + + + +>Brakes +Brakes stop your ship as quickly as possible. The more brake +upgrades you have the faster you can stop, with lower fuel +usage. + + + +Shields +Shields absorb the collision with asteroids, but use a lot of +fuel. You need at least two shield upgrades to survive a hit from a medium +sized rock, and 3 to survive a large rock. You will also need to have enough +fuel to maintain the shield during the strike. + + + + + + +Controls + +The key bindings are configurable via the +SettingsConfigure +Shortcuts... menu item. + +The defaults are: + + +Rotate Anti-Clockwise - Left +Arrow +Rotate Clockwise - Right +Arrow +Thrust - Up +Arrow +Shoot - Space +Bar +Shields - S +Pause - P +Brake - X + + + + + +Configuring &kasteroids; +&kasteroids; only has a few options, which can be reached from +SettingsConfigure +KAsteroids.... + + +Start new game with n +ships +When you start a new game, you have ships in +reserve, in case of an accident ;-). This specifies how many ships you have +in total when the game starts. + + + +Show Highscores on Game Over +If selected, shows all of the highscores each time you finish a +game, regardless of whether or not you achieve a highscore. + + + +Player can destroy Powerups +If selected, shooting a powerup destroys it. If unselected, the +powerup remains, allowing you to still pick it up. + + + + + + + + +Tips + +Here are some tips that may help: + +If you want to stop your ship, use the brakes rather than stopping +manually. The brakes stop you faster, with less fuel usage. + +Shields are expensive. They should be used as a last resort. Try to +use your thrusters to avoid collisions when practical. + +Avoid the edges of the playing field. It's more difficult to see +asteroids approaching from the opposite side of the field. + + + +Menu Reference + +The <guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New + +Starts a new game of +&kasteroids; + + +P +Game +Pause + +Pauses the game + + + +&Ctrl;H + +Game +Show Highscores + +Shows the High Scores for +&kasteroids; + + + +&Ctrl;Q + +Game +Quit + +Quits &kasteroids; + + + + + + +The <guimenu>Settings</guimenu> Menu + + + +SettingsConfigure Shortcuts... +Brings up the Shortcuts Configuration dialog. This is +a standard &kde; shortcuts configuration dialog, which you are +probably familiar with. See for details of the +default controls. + + + + + +Settings +Configure KAsteroids... + +Brings up the configuration dialog for +&kasteroids;. See . + + + + + + +The <guimenuitem>Help</guimenuitem> Menu + +&help.menu.documentation; + + + + + + + +Credits + +&kasteroids; + +Program Copyright 1997 &Martin.R.Jones; &Martin.R.Jones.mail; + +Documentation based on the original by &Martin.R.Jones;, currently +maintained by &Philip.Rodrigues; &Philip.Rodrigues.mail;. + + + +&underFDL; +&underGPL; + + + + +Installation + + +How to obtain &kasteroids; + +&install.intro.documentation; + + + + +Compilation and Installation + +&install.compile.documentation; + + + + + + + + + + diff --git a/doc/katomic/Makefile.am b/doc/katomic/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/katomic/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/katomic/index.docbook b/doc/katomic/index.docbook new file mode 100644 index 00000000..c1ffb2c7 --- /dev/null +++ b/doc/katomic/index.docbook @@ -0,0 +1,358 @@ + + + + + +]> + + + +The Atomic Entertainment Handbook + + + +Dirk +Doerflinger + + + +StephanKulowDeveloper
&Stephan.Kulow.mail;
+
+ +CristianTibirnaDeveloper
&Cristian.Tibirna.mail;
+
+ +MikeMcBride +Reviewer +
&Mike.McBride.mail;
+
+ +
+ +2005-12-14 +2.0 + + +This Handbook describes &katomic; Version 2.0 + + + +KDE +kdegames +KAtomic +game +atomic entertainment + +
+ + +Introduction + +Atomic Entertainment is a small game which resembles +Sokoban. The object of the game is to build chemical +molecules on a Sokoban like board. + + + + +Playing <application>Atomic Entertainment</application> + + +Rules + +The aim of Atomic Entertainment is to build +chemical molecules using basic atoms you are given. The molecule being built is +shown in a frame in the main window. + +Clicking on an atom will cause arrows to appear beside it. These arrows +show the direction the atom can be moved. After an arrow is clicked, the atom +will move in this direction until it reaches the next border or another atom. If +two atoms touch each other with the corresponding connectors, they form a +molecule. The atoms can only be moved one at a time. + +The level is solved when the new molecule has the same structure as shown +in the preview window. + +In the higher levels, some tactical skill will be necessary for solving +the puzzle. + +The best score in this game is actually the lower score, because the +goal is to solve a level with as few moves as +possible. Highscore: in the main-window shows +the lowest number of moves used for this level. Your score +so far: shows the current number of moves. + +The scrollbar on the top right of the main window changes the game +level. + + + + + + +Menu Reference + + +The <guimenu>Game</guimenu> Menu + + + + + + +F5 +Game +Restart Game +This will restart the current level. + + + + +&Ctrl;H + +Game +Show Highscores +This will show the best scores for the current level. + + + + +&Ctrl;Q + +Game +Quit +Selecting this item will end your current game, and exit the +&katomic;. + + + + + +The <guimenu>Move</guimenu> Menu + + + + + + +&Ctrl;Z + + +Move +Undo + + + +Undo the last move you made. + + + + + + +&Ctrl;&Shift;Z + +Move +Redo + + +If you have previously undone a move, you can redo it +here. + + + + + + + +The <guimenu>Settings</guimenu> Menu + + + +SettingsConfigure +Shortcuts... +This item lets you change the key settings of +Atomic Entertainment. See the section for a list of the +defaults. + + + +Settings +Configure &katomic;... +In the dialog box that appears, the animation-speed of the +atomic movement can be set. + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + +Shortcuts + +Default shortcuts are: + + +Shortcuts + + + +Restart Game + +F5 + + + +Quit + +&Ctrl;Q + + + +Show Highscores + +&Ctrl;H + + + +Undo + +&Ctrl;Z + + + +Redo + +&Ctrl;&Shift;Z + + + +Atom Down + +Down Arrow + + + +Atom Left + +Left Arrow + + + +Atom Right + +Right Arrow + + + +Atom Up + +Up Arrow + + + +Next Atom +Tab + + +Previous Atom +&Shift; + + +Help +F1 + + +What's this? + +&Shift;F1 + + + + +
+ +
+
+ + + +Credits and License + + +Atomic Entertainment Copyright 1999 &Stephan.Kulow; +&Stephan.Kulow.mail; and Cristian Tibirna +&Cristian.Tibirna.mail;. + + +Atomic Copyright Andreas Wuest Andreas Wuest@gmx.de. + + +Documentation Copyright 2000 &Dirk.Doerflinger; +ddoerflinger@gmx.net + + +Proofreading by Michael McBride +&Mike.McBride.mail; + + + +&underFDL; +&underGPL; + + + + +Installation + +How to obtain <application>Atomic Entertainment</application> + + +Atomic Entertainment (&katomic;) is written for the &kde; project +http://www.kde.org by &Stephan.Kulow; +&Stephan.Kulow.mail; and Cristian Tiberna +&Cristian.Tibirna.mail;. +It is based on Atomic 1.0.67 by Andreas Wuest +AndreasWuest@gmx.de. + + +&install.intro.documentation; + + + + +Requirements + +In order to successfully compile Atomic Entertainment, you need &kde; 3.0 + +All required libraries as well as Atomic Entertainment itself can be found on +&kde-ftp;. + + + + +Compilation and Installation + +&install.compile.documentation; + + + + +
+ + + diff --git a/doc/kbackgammon/Makefile.am b/doc/kbackgammon/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/kbackgammon/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/kbackgammon/board.png b/doc/kbackgammon/board.png new file mode 100644 index 00000000..2556cbcd Binary files /dev/null and b/doc/kbackgammon/board.png differ diff --git a/doc/kbackgammon/index.docbook b/doc/kbackgammon/index.docbook new file mode 100644 index 00000000..d8dfe39d --- /dev/null +++ b/doc/kbackgammon/index.docbook @@ -0,0 +1,622 @@ + + + + + +]> + + + + +The &kbackgammon; Handbook + + + +Jens +Hoefkens +
&Jens.Hoefkens.mail;
+
+ + +Bo +Thorsen +
&Bo.Thorsen.mail;
+Developer +
+ +
+ + +19992000 +&Jens.Hoefkens; + + +&FDLNotice; + +2005-12-22 +2.6.0 + + +&kbackgammon; is a graphical backgammon program for &kde;. It +supports backgammon games with other players, games against computer +engines like &GNU; bg and even on-line games +on the First Internet Backgammon +Server. + + + +KDE +game +backgammon +FIBS +gnubg + + +
+ + +Introduction + +&kbackgammon; is a graphical backgammon program. The objective +of backgammon is to move your checkers off the board before your +opponent does. While the rules of backgammon are moderately difficult +(the most difficult part apparently being the initial positioning of +the checkers), this document does not try to teach these rules: please +refer to the Web, a book, or (probably best) a friend for this. + +&kbackgammon; offers you a graphical backgammon board that can +be used almost entirely by using the mouse (although textual commands +are also available and it should be possible to play backgammon +without the mouse, too). Therefore, &kbackgammon; is easy to use and +allows you to concentrate on the important aspects of playing +backgammon. + +In order to play backgammon, you usually need an +opponent. &kbackgammon; offers you to play against hundreds of +different opponents of various strength on the First Internet Backgammon +Server. If you prefer, you may also use the Offline Engine which allows you to +play against yourself or friends that are with you at your +computer. + +In the near future &kbackgammon; will allow you to play against +&GNU; Backgammon, which is a +powerful backgammon program that usually runs without a graphical +frontend. It plays on FIBS with a rating of about +2000 (which is quite high). + +Additionally, plans are on the way that will allow games over +the Internet between two &kbackgammon; programs (and their respective +users). Finally, the architecture of &kbackgammon; is quite open and +it is possible to include support for other engines easily (if you are +interested in this, please contact the author). + + + + +How to use the Board + +&kbackgammon; is centered around a graphical backgammon +board. This board consists of the checkers, the dice, and the +cube. All these game elements can be conveniently manipulated with the +mouse. + +The following image shows a typical game situation with the +white player moving from 1 to 24 and the black player moving from 24 +to 1. Currently, the white player owns the cube and has just rolled 3 +and 4. + + +The backgammon board + + + + + +The Board + + + + +If it is their turn, players can roll the dice by double +clicking on the squares representing the dice or by using the +corresponding menu entry or toolbar icon (if the toolbar is visible +and rolling has been included in the toolbar). + +If they own the cube, players can double the cube by either +double clicking on the square representing the cube or by using the +corresponding menu entry or toolbar icon (if the toolbar is visible +and the cube has been included in the toolbar). + +If it is their turn to move, players can move checkers by +clicking on them and moving the mouse pointer to the desired location +(while holding the mouse button continuously pressed). This is called +dragging the checker. Depending on the selection made in the current +engine's configuration, the move will be finished once +the maximum number of checkers has been moved (this may be anywhere +between 1 and 4 checkers, although it will usually be 2 +checkers). + +In addition to dragging the checkers, the board offers a feature +called short move feature. If this has been +enabled in the board's configuration menu, checkers can be moved by +simply clicking on them. The board will then automatically make the +shortest move possible away from the current field. If the short move +feature is enabled, it may be set to +single or double click. This option is very useful for users of +touchpads that are sometimes difficult to use for dragging. + +Finally, the board has a certain smartness: it will only accept +moves that are allowed (based on the current dice). While the offline engine allows this to be +switched off (for the Edit Mode), this is usually +very helpful for unexperienced users (and advanced players will +probably never notice this). This feature can also be used to cancel a +move in progress: just drop it over an illegal field. + + + + +Backgammon Engines + +&kbackgammon; is built around the backgammon board, which +doesn't know anything about how to play backgammon. The whole +infrastructure (rolling the dice, &etc;) that allows backgammon games +is handled by backgammon engines. + + +The Offline Engine + +The offline engine Open Board allows you to play against yourself, or +probably more fun, another person sitting with you. + + + + +The <acronym>FIBS</acronym> Engine + +FIBS is the First Internet Backgammon +Server, a 24-hour international online community of backgammon +players of all ages and experience from Beginner to Experts. + +As well as playing the game, FIBS allows you +to save games, chat with other players, and has a complex rating +system. + +There is also a large website well worth some of your +time, if you are a backgammon fan. + + + + +The &GNU; Backgammon Engine + +The GNUbg engine does not yet exist (although it already has an +entry in the Engine menu). Therefore, the +remainder of this section should be taken with a grain of salt. + +Use of the GNUbg engine requires a working installation of the +actual GNUbg program. &kbackgammon; then starts GNUbg in the +background and communicates with it. All this is transparent to the +user (&ie; it is hidden from the user) and all interaction with GNUbg +is through the &GUI; elements of &kbackgammon;. + +If there are other enhancements that can help using GNUbg from +&kbackgammon;, please contact the author of &kbackgammon; (or better: +send a patch). Some of the more advanced features will probably be +position analysis and save/restore functions. However, playing is the +most pressing issue. + +The one thing that will never happen is a combination of the +GNUbg and the FIBS engine! Using computer programs +to enhance user ratings is cheating! + + + + + + +Game options + +The options to &kbackgammon; are set by selecting +Settings Configure &kbackgammon;... + from the menubar. This will bring up a dialog +box. + + + + +Menu Reference/Keyboard Shortcuts + +The following sections briefly describe each menubar option. + + +<guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New + + +Start a new game. This item opens a dialog to enter the nicknames of both players. + + + + + + +&Ctrl;P + +Game +Print... + + +Print a picture of the board. + + + + + + +&Ctrl;Q + +Game +Quit + + +Quit and close &kbackgammon; + + + + + + + +<guimenu>Move</guimenu> Menu + + + + + +&Ctrl;Z + + +Move +Undo + + + +Undo the last move you made. + + + + + + +&Ctrl;&Shift;Z + +Move +Redo + + +If you have previously undone a move, you can redo it +here. + + + + + +Move +Redisplay + +Redraw the board. + + + + + +&Ctrl;R + + +Move +Roll Dice + + +As you may expect, roll the dice. + + + + + +Move +End Turn + + +Let the other player know you have finished +moving. + + + + + +Move +Double Cube + + +Offer the opponent player during the course of a game +(just before you roll the dice) to continue the game at twice the current stakes. +If the opponent rejects this offer, you win the game. + + + + + + +Move +Engine + + + +Choose from the various game engines (Open Board, FIBS, GNU Backgammon, Next Generation) here. + + + + + + + + +<guimenu>Command</guimenu> Menu + +This menu has different items depending on the chosen game engine. + +Open Board engine + + + +Command +New Game + + +Start a new game. This item opens a dialog to enter the nicknames of both players. + + + + + +Command +Edit Mode + + + +In this mode the board accepts moves that are not allowed +(based on the current dice). + + + + + +Command +Swap Colors + + + +Swap the colors of the checkers between player 1 and player 2. + + + + + + + + + +<guimenu>Settings</guimenu> Menu + + + + + +&Ctrl;M + + +Settings +Show Menubar + + +Toggle on and off the menubar. + + + + + +Settings +Toolbars +Main (&kbackgammon;) + +Toggle the Main Toolbar + + + + +Settings +Toolbars +Command Characters (&kbackgammon;) + +Toggle the Command Toolbar + + + + +Settings +Show Statusbar + + +Toggle on and off the statusbar. + + + + + +Settings +Save Settings + + + +Saves the current settings to become the default for &kbackgammon;. + + + + + +Settings +Configure Shortcuts... + +Opens a dialog for changing the key bindings. +Using this option you can change the standard key shortcut for &kbackgammon;'s commands +or create new ones. + + + + +Settings +Configure Toolbars... + +Opens a dialog for configuring the toolbar. You +can add and remove toolbuttons for &kbackgammon;'s commands with this +option. + + + + +Settings +Configure &kbackgammon;... + +Opens a dialog for changing some options for +&kbackgammon;. + + + + + + + + +<guimenu>Help</guimenu> Menu + +&kbackgammon; has a standard &kde; Help as described +below, with one addition: + + + +Help +Backgammon on the Web + + +Opens the websites of FIBS Home, Backgammon Rules, or &kbackgammon; in &konqueror;. + + + + +The standard &kde; Help entries are: + +&help.menu.documentation; + + + +Default Shortcuts + +The following tables show you the default shortcuts of +&kbackgammon;. + + +Key bindings + +Key ComboAction + +&Ctrl;NNew Game +&Ctrl;RRoll Dice +&Ctrl;PPrint Game +&Ctrl;QQuit &kbackgammon; +F1Help Contents +&Shift;F1What's This? Help +&Ctrl;MShow Menubar +&Ctrl;ZUndo move +&Ctrl;&Shift;ZRedo previously undone move + + +
+ +These shortcuts can be changed by selecting +Settings Configure +Shortcuts from the menubar. + +
+ +
+ + +Credits and License + +&kbackgammon; + +Program copyright 1999-2000 &Jens.Hoefkens; +&Jens.Hoefkens.mail; + +Documentation copyright 2000 &Jens.Hoefkens; +&Jens.Hoefkens.mail; + + + +&underFDL; +&underGPL; + + + + +Installation + + +How to obtain &kbackgammon; + +&install.intro.documentation; + +The home page of &kbackgammon; is located at http://backgammon.sourceforge.net. +It will usually contain the most up-to-date information the program +available. + + + + +Compilation and Installation + +&install.compile.documentation; + + + + + +
+ + + diff --git a/doc/kbattleship/Makefile.am b/doc/kbattleship/Makefile.am new file mode 100644 index 00000000..41691557 --- /dev/null +++ b/doc/kbattleship/Makefile.am @@ -0,0 +1,3 @@ +KDE_LANG = en +KDE_DOCS = AUTO + diff --git a/doc/kbattleship/index.docbook b/doc/kbattleship/index.docbook new file mode 100644 index 00000000..9e380d17 --- /dev/null +++ b/doc/kbattleship/index.docbook @@ -0,0 +1,485 @@ + + + + + +]> + + + +The &kbattleship; Handbook + + + +&Daniel.Molkentin; &Daniel.Molkentin.mail; + + + +&Nikolas.Zimmermann; &Nikolas.Zimmermann.mail; + + + +&Frerich.Raabe; + +&Frerich.Raabe.mail; + + + + + + +&FDLNotice; + + +2001 +&Daniel.Molkentin; + + +2001 +&Nikolas.Zimmermann; + + +2001 +Kevin Krammer + + + +2005-12-14 +1.1 + + + +&kbattleship; is a network-enabled implementation of the famous Battle Ship game for &kde;. + + + +KDE +kdegames +kbattleship +game +battleship +battle + + + + + +Introduction + + +Features + +Network Gaming +Computer Player (AI) +Sounds +Chat +Statistics +Highscore List + + + + +&kbattleship; uses an &XML; based communication protocol so you can write +clients for every platform and in any language. If you want to write +one in your favorite programming language or/and environment, contact +us. We would really like to hear of it. + + + + + + + +Using &kbattleship; + + +If you want to play &kbattleship;, you will need two players, either play +against the computer or in a network against another player. + +In a network game one player has to open the game via +Game Start +Server or by pressing F3. + + + +A dialog box opens which will ask you for for a +Nick name: and Port:. Normally, +&kbattleship; will suggest your login name but you can enter any string +you want. The predefined port should be OK. However, if you encounter +problems, you can choose any other free ports above 1024. + + + +You need to tell the other player in case you use a port other than +the default as both players need to use the same port in order to be able +to establish a connection. + + + +The other player has to choose Game +Connect to Server, or press +F2. Again, a Nick name: is +suggested, but you can choose any name you like. + + + +An important point is the field Server:. Here, you +have to enter the host name of the server (the machine of the player +that initiated the game). + + + +Another possibility is to play &kbattleship; against your +computer. Select Single Player from the +Game Menu, or press F4. + + + +When you are done, you can start the game. Simply follow the +instructions in the statusbar. It will issue hints and suggest what to +do next. When you now look at the screen, you will find two grid fields, +the so-called battle areas. The left area belongs to +you. This is where you place your ships and where you can follow the +military actions of your enemy. The right area is where your enemy's +fleet is located. When it's your turn to fire, you need to click on a +certain sector (a field of the battle area) where you suppose the ships +to be located. + + + +First, you need to place your ships. The game initiator starts. When +he/she is done, player two sets his/her ships. + + + +Ship placement is very easy: Simply click on the field where you want to +place your ship. The first one will have a length of four squares, the next +will be three squares long &etc;. Click on the field where you want to start +the placement. If you click with the &LMB; the ship will be placed horizontally, +a &Shift; &LMB; click will cause it to be placed +vertically. &Shift; itself will flip the ship placement preview. + + + +Now you can blindly fire with a &LMB; click on the enemy battle area. The status bar indicates +who is about to shoot. + + + +The first player destroying all their opponents ships wins the game! + + + + + +The Menus + + +The <guimenu>Game</guimenu> menu + + + + + +F2 +Game +Connect to Server... + + + + +Initiate a connection to another player's server. + + + + + + + +F3 +Game +Start Server... + + + + +Start the server so another player can connect to you. + + + + + + + +F4 +Game +Single Player... + + + + +Start a game with your computer as the opponent. + + + + + + + +&Ctrl;H +Game +Show Highscores + + + + +Show the highest scores so far. + + + + + + + +F11 +Game +Enemy Info + + +Show the enemy's client (might also be &Mac;), the client +version, a short description and the protocol version used. + + + + + + + +&Ctrl;Q + +Game +Quit + + + + +Exit &kbattleship; + + + + + + + +The <guimenu>Settings</guimenu> menu + + + + + +Settings +Show Statusbar + + + + +Toggle on or off the display of the statusbar. The +default is on. + + + + + + + + +Settings +Show Grid + + + + +Toggle on and off the display of a grid on the playing +field. The default is off. + + + + + + + +Settings +Play Sounds + + + + +Toggle whether sounds (played when shooting) should be played. +The default is on. + + + + + + + +Settings +Configure Shortcuts... + + + + +Configure the keyboard shortcuts used by &kbattleship;. + + + + + + + +Settings +Configure Notifications... + + + + +Configure the audio and visual notifications used by &kbattleship;. + + + + + + + + + +The <guimenu>Help</guimenu> menu + +&help.menu.documentation; + + + + + +Questions, Answers, and Tips + + +Frequently asked questions + + +I get the error: Couldn't connect to &arts; +Soundserver. Sound deactivated. + + +&kbattleship; relies on &arts;, &kde;'s soundserver, to play any +sound. Enable &arts; in the &kcontrolcenter; by browsing to +Sound & Multimedia +Sound System and making sure that +the box labelled Enable the sound system is checked. + + + + + +I have a question that is likely to become a &FAQ;. Who should I +contact? + + +Contact the authors. They will most likely add it here. + + + + + + + +Credits and Licenses + +&kbattleship; Copyright 2000, 2001 + + +Authors + + +&Nikolas.Zimmermann; &Nikolas.Zimmermann.mail; + + + + +&Daniel.Molkentin; &Daniel.Molkentin.mail; + + + + +Kevin Krammer kevin.krammer@gmx.at + + + + + +Contributors + + +Benjamin Adler benadler@bigfoot.de + + + + +Nils Trzebin nils.trzebin@stud.uni-hannover.de + + + + +Elmar Hoefner elmar.hoefner@uibk.ac.at + + + + +Documentation updated for &kde; 3.4 by +BrianBeck +brian.beck@mchsi.com + + + +&underFDL; +&underGPL; + + + + +Installation + +&install.intro.documentation; + + +Requirements + + +At the time of writing, &kbattleship; requires &kde; 3.x or greater and +&Qt; 3.x or greater. + + + + + +Compiling + +&install.compile.documentation; + + + + + + + + diff --git a/doc/kblackbox/Makefile.am b/doc/kblackbox/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/kblackbox/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/kblackbox/index.docbook b/doc/kblackbox/index.docbook new file mode 100644 index 00000000..d45c2f76 --- /dev/null +++ b/doc/kblackbox/index.docbook @@ -0,0 +1,491 @@ + + + + + +]> + + + + +&kblackbox; Game Manual + + + +&Robert.Cimrman; &Robert.Cimrman.mail; + + + +&Philip.Rodrigues; &Philip.Rodrigues.mail; + + + +&Robert.Cimrman; &Robert.Cimrman.mail; + + + + +&Lauri.Watts; &Lauri.Watts.mail; + + + + + + + + +19982000 +&Robert.Cimrman; + + + +2001-2003 +&Philip.Rodrigues; + + +&FDLNotice; + +2005-12-10 +0.3.0 + +&kblackbox; is a superb graphical logical game, inspired +by the emacs blackbox game. + + +KDE +KBlackBox +kdegames +blackbox +game + + + + + +What is &kblackbox;? + +&kblackbox; is a superb :-) graphical logical game, inspired by +the emacs blackbox game. A major part of this help +file is based on the original emacs +help. + +&kblackbox; is a game of hide and seek played on a grid of +boxes. Your opponent (the Random number generator, in this case) has +hidden several balls within this box. By shooting rays into the box and +observing where they emerge it is possible to deduce the positions of +the hidden balls. The fewer rays you use to find the balls, the better +(the lower) your score. + + + + +Game Description + +In the first part of this section a description of the game board +will be given. The second part deals with user interaction with the +game board and finally in the third part the actual game rules are +explained. + + +Game Board Description + +The following types of field are found on the game board: + + + +Black squares +The black box. Here you must mark the squares you think +a ball is in. + + + +Green squares +These are the lasers, shooting rays of light when +switched on. + + + +Light Gray squares +Nothing here of interest, this is just a +border :-). + + + +Blue balls +There must be one there! you think. These mark +where you suspect a ball is placed in the black box. + + + +Cyan balls +Show where the balls actually are. + + + +Red balls +Incorrectly positioned balls you have marked are +indicated in red. + + + +Brown squares +Marking color + + + +The names of colors are used just for identifying the +different types of the fields in this text. They might +not be in any relation with the actual color of the +fields. Simply said: the black box is in the center, around are the +lasers and around them is the border. Remap the colors yourself +:-). + + + + +User Interaction + +The cursor can be moved around the box with the +standard cursor movement keys or the mouse. Switching of lasers or +marking of black boxes is done with the &LMB;, or by pressing the +Return or &Enter; key. + +You can mark the fields where you think a ball cannot be, too. Just press +the &RMB;. It often helps you to find an area where a ball could possibly +be. To clear any marks (blue or brown) press the +&LMB;. Brown marks cannot overwrite blue +marks. This way you cannot erase the blue marks (guessed balls) by accident when +playing with the &RMB;. + +When you think the configuration of balls you have placed is +correct, press the &MMB;. You +will be informed whether you are correct or not, and be given your +score. Your score is the number of letters and numbers around the +outside of the box plus five for each incorrectly placed ball. If you +placed any balls incorrectly, they will be indicated with red fields, +and their actual positions indicated with cyan fields. + + + + +Game Rules + +You have to find balls hidden in the black box. Your means are +limited - you can just fire lasers which are around the box. There are +three possible outcomes for each ray you send into the box: + + + + + +Detour +The ray is deflected and emerges somewhere other than +where you sent it in. On the playfield, detours are denoted by matching +pairs of numbers - one where the ray went in, and the other where it +came out. + + + +Reflection +The ray is reflected and emerges in the same place it +was sent in. On the playfield, reflections are denoted by the letter +R. + + + +Hit +The ray strikes a ball directly and is absorbed. It does not +emerge from the box. On the playfield, hits are denoted by the letter +H. + + + +The rules for how balls deflect rays are simple and are best shown by +example. + +As a ray approaches a ball it is deflected ninety degrees. Rays +can be deflected multiple times. In the diagrams below, the dashes +represent empty box locations and the letter O +represents a ball. The entrance and exit points of each ray are marked +with numbers as described under Detour +above. Note that the entrance and exit points are always +interchangeable. * denotes the path taken by the +ray. + +Note carefully the relative positions of the ball and the ninety +degree deflection it causes. + + + 1 + - * - - - - - - - - - - - - - - - - - - - - - - + - * - - - - - - - - - - - - - - - - - - - - - - +1 * * - - - - - - - - - - - - - - - O - - - - O - + - - O - - - - - - - O - - - - - - - * * * * - - + - - - - - - - - - - - * * * * * 2 3 * * * - - * - - + - - - - - - - - - - - * - - - - - - - O - * - - + - - - - - - - - - - - * - - - - - - - - * * - - + - - - - - - - - - - - * - - - - - - - - * - O - + 2 3 + + + +As mentioned above, a reflection occurs when a ray emerges from the same +point it was sent in. This can happen in several ways: + + + + - - - - - - - - - - - - - - - - - - - - - - - - + - - - - O - - - - - O - O - - - - - - - - - - - +R * * * * - - - - - - - * - - - - O - - - - - - - + - - - - O - - - - - - * - - - - R - - - - - - - - + - - - - - - - - - - - * - - - - - - - - - - - - + - - - - - - - - - - - * - - - - - - - - - - - - + - - - - - - - - R * * * * - - - - - - - - - - - - + - - - - - - - - - - - - O - - - - - - - - - - - + + + +In the first example, the ray is deflected downwards by the upper +ball, then left by the lower ball, and finally retraces its path to its +point of origin. The second example is similar. The third example is a +bit anomalous but can be rationalized by realizing the ray never gets a +chance to get into the box. Alternatively, the ray can be thought of as +being deflected downwards and immediately emerging from the box. + +A hit occurs when a ray runs straight into a ball: + + + - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - O - - - + - - - - - - - - - - - - O - - - H * * * * - - - - + - - - - - - - - H * * * * O - - - - - - * - - - - + - - - - - - - - - - - - O - - - - - - O - - - - +H * * * O - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - + + + +Be sure to compare the second example of a hit with the first +example of a reflection. + + + + +&GUI; description + + +The <guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New +Starts a new game (and abandons the current, if +any.) + + + +Game +Give Up +Shows you positions of the balls. + + + +Game +Done +Checks whether all balls are placed. If yes, it terminates +the current game, computes the final score and indicates real positions of +the balls. The middle mouse button has the same +function. + + + +Game +Resize +Resizes the main window, so that its contents fit perfectly. This is useful when you accidentally change the size of the +window... + + + + + +&Ctrl;Q + +Game +Quit +Quits &kblackbox; + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +SettingsShow/Hide Toolbar + + + +Shows or hides the &kblackbox; toolbar. + + + + + +SettingsShow/Hide Statusbar + + +Shows or hides the &kblackbox; status bar at the base of the screen. + + + + +Settings +Size +Sets the size of the game field (black box). You may choose +between 8 x 8, 10 x 10 and +12 x 12. The default is 8 x +8. + + + +Settings +Balls +Sets the number of balls in the black box. You may choose +between 4 (the default), 6 +or 8. + + + + +Settings +Tutorial +Switches the tutorial mode on or off. In tutorial mode, +you can see where the balls actually are. Note that you have to start a new +game for this change to take effect. + + + +Settings +Configure Shortcuts... +Displays a standard &kde; shortcut configuration dialog, in +which you can change the keyboard shortcuts used by &kblackbox;. + + + + +SettingsConfigure Toolbars... + + +Brings up the standard &kde; toolbar configuration dialog to customize the &kblackbox; toolbar. + + + + + + + + +The <guimenuitem>Help</guimenuitem> Menu + +&help.menu.documentation; + + + + +The Toolbar + + +&kblackbox;'s toolbar + + + + + + + +The &kblackbox; toolbar offers quick access to the most commonly used +&kblackbox; functions. From left to right, the icons are: + + + + +New +Starts a new game. + + + +Give Up +Shows you positions of the balls. + + + +Done +Checks whether all balls are placed. If yes, it terminates +the current game, computes the final score and indicates real positions of +the balls. The middle mouse button has the same +function. + + + + + + + +Credits and License + +&kblackbox; + +Program Copyright 1998-2000 &Robert.Cimrman; &Robert.Cimrman.mail; + +Documentation by &Robert.Cimrman;. Updated and converted to Docbook +for &kde; 2.0 by &Lauri.Watts; &Lauri.Watts.mail; + + +Current maintainer &Philip.Rodrigues; &Philip.Rodrigues.mail; + + + + +&underFDL; +&underGPL; + + + + + +Installation + +&install.intro.documentation; + + +Compilation and Installation + +&install.compile.documentation; + + + + + + + + diff --git a/doc/kblackbox/kblackboxtbar.png b/doc/kblackbox/kblackboxtbar.png new file mode 100644 index 00000000..1f129bc8 Binary files /dev/null and b/doc/kblackbox/kblackboxtbar.png differ diff --git a/doc/kbounce/Makefile.am b/doc/kbounce/Makefile.am new file mode 100644 index 00000000..085981d9 --- /dev/null +++ b/doc/kbounce/Makefile.am @@ -0,0 +1,4 @@ + +KDE_LANG = en +KDE_DOCS = AUTO + diff --git a/doc/kbounce/index.docbook b/doc/kbounce/index.docbook new file mode 100644 index 00000000..8e4896d1 --- /dev/null +++ b/doc/kbounce/index.docbook @@ -0,0 +1,383 @@ + + + + + +]> + + + + +The &kbounce; Handbook + + + +&Aaron.J.Seigo; &Aaron.J.Seigo.mail; + + + +&Lauri.Watts; &Lauri.Watts.mail; + + + + + + +&FDLNotice; + +2005-12-10 +0.5 + + + + + +&kbounce; is a ball game for &kde;. + + + + + +KDE +kdegames +jezzball + + + + + +How To Play + +&kbounce; is played on a field, surrounded by a wall, with two or +more balls that move about in the field bouncing off of the +walls. + +Walls are a darker color while the active areas of the field are +a lighter color. + +The size of the active area of the field is decreased by making +new walls that enclose areas without balls in them. To complete a +level, the player must decrease the size of the active field by at +least 75% within the time allowed. + +With each new level, another ball is added to the field, and the +player is given one more life than in the level before. The time +allotted to complete the level is also increased. + +Scoring is based on how much of the field is cleared. + +New walls are built by clicking the +left mouse button in an active area of the +field, at which point two walls will begin to grow in opposite +directions from the square the mouse was clicked in. Only two walls +may be growing on the screen at any given time. + + +A new wall growing + + + + + +A new wall growing + + + + +When the mouse is on the field, the cursor is shown as a pair of +arrows pointing in opposite directions, either horizontally or +vertically. The arrows point in the direction the walls will grow +when the left is clicked. This direction +can be changed by clicking the right mouse +button. + +A new wall has a head which moves away from the +point where the mouse was clicked. A wall is not permanent until this +head runs into another wall. If a ball collides with +any part of the wall except the head, before the head has run into +another wall, the new wall will disappear completely and one life will +be lost. If a ball collides with the head in the direction of the +wall's growth, the wall will stop growing there, and become permanent, +with no loss of life. If a ball collides with the head from any other +side, the ball will bounce off and the wall will continue to grow +normally. + + + + +Strategy + +Many players find the game becomes quite difficult by only the +third or fourth level, given the number of balls on the field at +once. + +The trick to playing &kbounce; successfully is to build +corridors. To build a corridor, start a pair of walls +growing close to another wall, and time it such that one of the walls +will be hit by a ball and one of the walls will not, and thus become +permanent. + + +Building a corridor + + + + + +Building a corridor + + + + +This will leave a narrow corridor only a few squares high on the +field, surrounded on three sides by walls. Wait for the balls to +bounce into the open end of the corridor, and close the corridor +behind the ball with a new wall. Although you will most likely lose a +life for each corridor created, you can trap several balls in a single +corridor. + + +Capturing balls in a corridor + + + + + +Capturing balls in a corridor + + + + +A final tip — take your time! There is plenty of time, as +shown in the Time display on the right hand side +of the screen. Rushing will just get you in trouble! + + + + +Menu Reference + + +The <guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New + + +Start a new game. + + + + + + +&Ctrl;End + + +Game +End Game + + +End the current game. + + + + + + +P +Game +Pause + + +Pause or resume the game. + + + + + + +&Ctrl;H + +Game +Show Highscores + + +Opens a dialog that displays different high score tables. +Export the high scores into a file or click on +Configure to open a dialog to customize your Nickname +and add a Comment. + + + + + +&Ctrl;Q + +Game +Quit + + +Quit and close &kbounce;. + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +Settings +Select Background Folder... + + +Opens a dialog to select a folder for the background images. + + + + + +Settings +Show Backgrounds + + +Shows the background images in the selected folder. Only enabled, if a background folder is already selected + + + + + +Settings +Show/Hide Toolbar + + +Shows/hides the &kbounce; toolbar. + + + + + +Settings +Show/Hide Statusbar + + +Shows/hides the &kbounce; statusbar. + + + + + +Settings +Play Sounds + + +If checked, &kbounce; game sounds are played. + + + + + +Settings +Configure Shortcuts... + + +Opens a standard &kde; shortcut configuration dialog, in which +you can change the keyboard shortcuts used by &kbounce;. + + + + + +Settings +Configure Toolbars... + + +Opens the standard &kde; toolbar configuration dialog to customize the &kbounce; toolbar. + + + + + +Settings +Configure Highscores... + + +Opens a dialog to customize your Nickname and +add a Comment. + + + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + + +Credits and Licenses + + +&kbounce; + +Copyright 2000, Stefan Schimanski + + +Developers + +Stefan Schimanski schimmi@kde.org + + +Sandro Sigala ssigala@globalnet.it - +Highscore + + + +Documentation copyright 2002, &Aaron.J.Seigo; +&Aaron.J.Seigo.mail; + +This handbook is dedicated to Dennis E. Powell. + + + +&underFDL; + +&underGPL; + + + + +Installation + +&install.intro.documentation; + +&install.compile.documentation; + + + +&documentation.index; + + + diff --git a/doc/kbounce/jezball_corridor1.png b/doc/kbounce/jezball_corridor1.png new file mode 100644 index 00000000..42df31c8 Binary files /dev/null and b/doc/kbounce/jezball_corridor1.png differ diff --git a/doc/kbounce/jezball_corridor2.png b/doc/kbounce/jezball_corridor2.png new file mode 100644 index 00000000..38c84169 Binary files /dev/null and b/doc/kbounce/jezball_corridor2.png differ diff --git a/doc/kbounce/jezball_newWall.png b/doc/kbounce/jezball_newWall.png new file mode 100644 index 00000000..4700df3e Binary files /dev/null and b/doc/kbounce/jezball_newWall.png differ diff --git a/doc/kenolaba/Makefile.am b/doc/kenolaba/Makefile.am new file mode 100644 index 00000000..8c5320d4 --- /dev/null +++ b/doc/kenolaba/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en +KDE_MANS = AUTO diff --git a/doc/kenolaba/index.docbook b/doc/kenolaba/index.docbook new file mode 100644 index 00000000..b8d87be5 --- /dev/null +++ b/doc/kenolaba/index.docbook @@ -0,0 +1,869 @@ + + + + + +]> + + + +The &kenolaba; Handbook + + + +&Josef.Weidendorfer; + + + +&Philip.Rodrigues; &Philip.Rodrigues.mail; + + + +&Lauri.Watts; &Lauri.Watts.mail; + + + + + + + +2006-01-13 +1.06b + + +2001 +&Philip.Rodrigues; + + +&FDLNotice; + +&kenolaba; is a simple board strategy game that is +played by two players. + + + +KDE +game +kenolaba +linux + + + + +Introduction + +&kenolaba; is a simple board strategy game that is played by two +players. There are red and yellow pieces for each player. Beginning from +a position where each player has 14 pieces, moves are drawn until +one player has pushed 6 of his opponent's pieces out of the +board. + +The original program was developed in 1993 for DOS and pure +Xlib. For &kde; there was a major rewrite. + + If you know the board game called Abalone, you will like this +program. In fact, this program was inspired by the mentioned game. +Abalone is a trademark of Abalone SA, France. + + + + +Rules of the Game + +Red always moves first. + +Two types of moves are allowed: + + + +Normal +One, two or three of your pieces in a row can be moved by one +space in the 6 directions. Side moves are also allowed. + +Press the &LMB; on the first piece you want to move. +The piece will then be highlighted. Now drag the mouse in the desired +direction. If the move is valid, the cursor will change to an arrow in +that direction and all the pieces of the move will be highlighted. If +this is the move you want to draw, release the mouse button. + +Side moves are handled another way: For two adjacent pieces click between +them and drag; for three pieces press the &MMB; on the middle piece - if a side +move is allowed for the pieces they will be highlighted. Otherwise only the +middle one is highlighted and you draw a normal move. + + + + + +Pushing + +You can push a maximum of two pieces of the opponent in +front of your own pieces as long as the number of pieces you move is +greater than the number of the opponent's pieces that you push. That +means with three of your pieces you can push one or two of the +opponent's and with two pieces one. + +Special pushing moves are those which push a piece of the opponent +out of the board. If you have pushed six of your opponent's pieces out +of the board you have won. + + + + + +If you still do not know what &kenolaba; is all about, look at a +game where the computer plays both sides. After starting &kenolaba;, +select SettingsComputer +PlayBoth and start +a new game. + + + + +Network Play + +General Network Play + + +&kenolaba; supports playing across a network. This allows two people to +play against each other on different computers, or one person to observe +the game play of another &kenolaba; program. To use &kenolaba;'s network +features, &kenolaba; must be set to Network Mode by selecting +GameNetwork Play. + + + +All running &kenolaba; programs which are in Network Mode broadcast +changed positions to each other. + +To exchange positions, the &kenolaba; programs have to be told +about each other. If they are running on the same machine, nothing is +needed (apart from switching to Network Mode). If running on different +computers you have to specify the other's machine on the command line +with the switch, as so: + +%kenolaba SomeHostName + + For insiders: &kenolaba;, when in Network Mode, listens on +a TCP socket for position change commands by other +&kenolaba; programs. You can specify the port number with the + command line switch. You need this if you want to +play two different &kenolaba; Network games. + + +Examples + + + +Chris on machine1 and +Mary on machine2 want to +play against each other: Mary simply starts &kenolaba;, sets +OptionsComputer +Play to None and +switches to Network Mode. Chris starts &kenolaba; with + +%kenolaba machine2 + +and then does the same as Mary. +Now one of the two can start the game and draw a red move. Each time the +position is changed in one program, it automatically changes in the other one +too. + + + +John wants to observe the game: He does the same as Chris, but +doesn't draw a move himself. + + + +Two different &kenolaba; Network Games will run among machines +m1 and m2. To distinguish the games, we choose +port number 12345 for one. On m1 we start as usual: + +%kenolaba + +for the first game, and (not so usual): + +%kenolaba 12345 + +for the second. On m2 we +start the first game with: + +%kenolaba m1 + +and the second with: + +%kenolaba m1:12345 + +(The colon separates host and port as in a &URL;). + + + + + + + + +Advanced Options + + +Modify Mode + + +You can edit the current board position by selecting +EditModify +to put &kenolaba; in Modify Mode.You can add red or yellow pieces or +delete them. The status line shows the number of red and yellow pieces, +the move number, the side which is to draw the next move, and whether +the position is valid: An exclamation sign means no, a checked symbol +yes. + + + + +If the position is invalid and you switch back to normal Play Mode, you +can not play! Only valid positions can be used as a starting position for +a game. + + + +MoveTake Back + and +MoveForward + simply decrement and increment the move number, but don't change +any pieces. + + +Usage + +By pressing the left or +right mouse button on an empty or yellow +field, you enter Red piece Adding mode. By pressing the +middle button on an empty or red field, you +enter Yellow piece Adding mode. Finally by pressing the +left or the right +mouse button on a red piece, or the middle +mouse button on an yellow piece, you enter Piece Deleting +Mode. Any piece that the mouse passes over with the button pressed will +be deleted. + + +To exit Modify Mode and re-enter Play Mode, just uncheck +Modify in the Edit +menu. + + +Using the Clipboard + +The current &kenolaba; board can be copied to the clipboard using +the Copy command in the +Edit menu, and then pasted back in using +Paste. This way you can copy the board +positions of one &kenolaba; program to another by pressing +Copy in one program and +Paste in the other, but this is better done +with Network Mode (see above). Another usage is to save positions into a +text file (using Copy and your Editor of +choice) and retrieve a position later by selecting it in the editor and +pasting in &kenolaba;. + + + + + +Computer Level +The computer can play at four different levels, namely: + +Easy + +Normal + +Hard + +Challenge + + + +The harder the setting, the longer the computer searches for a +move. You can interrupt the search by pressing the S +key. + + + + +Spying + + +Spying can be turned on and off from the +Settings menu, and the Spy +option. If it is your turn, you can see the computer's rating of the +move you want to play in the status bar. If it's the computer's turn you +can see (in status bar and highlighted pieces) the move he actually +thinks is the best to play. Of course this changes along his +search. + + + +Selecting what color the computer plays + + +You can make the computer play Red, Yellow or both sides. Choose between +these options before starting a new game. Of course it works in a game +too if you want to change sides. Choose None +to play against another human. This is very useful in Network +Mode. + + + + + +Advanced Configuration + +&kenolaba; offers advanced configuration options for the daring, enabling +you to change the whole scoring system if you wish. To access these advanced +options, select SettingsConfigure +Kenolaba.... As you change settings, the score that +your modified settings give for the current move is displayed at the bottom of +the dialog, next to Evaluation of actual position:. + + +The <guilabel>Moves</guilabel> Tab +The number of points added to the total for each type of move can be +modified here, depending on the type of move, and how many pieces are +involved. The moves are divided into three types: + + + + +Normal +A normal move is one in which you move one or more pieces of +your own, but do not push any of your opponent's pieces. + + + + +Push +A push move is one in which you push one or more of your +opponent's pieces, but they remain on the board. + + + + +Push Out +A push out move is one in which you push one or more of your +opponent's pieces off the board. + + + + + + + + +The <guilabel>Position</guilabel> Tab + +The number of points added to the total score for a board position is +dependent on which ring on the board the pieces are on. For each ring, from the +center out, an average score to add can be set, with a +/- range. The score +given for a particular position is varied randomly within the +/- range. This +is to stop computer-computer games going into an infinite loop. + + + + +The <guilabel>In-A-Row</guilabel> Tab + +For each time the number of pieces in a row occurs, the given number of points +is added to the score, ⪚ + + + + + + X O O O + + +adds the score for three in a row, and also two times the score for two in a row. + + + +The <guilabel>Count</guilabel> Tab + +Adds the given amount of points based on the difference in the number of balls +on both sides, from 1 Ball more: up to 5 Balls +more:. If there is a difference of 6 balls, the game is over. + + + + +The <guilabel>Evaluation Schemes</guilabel> Tab + +Your evaluation schemes, defined in all other tabs of this dialog, +can be stored or deleted here. + + + + + +Menu Reference + +Drawing moves is explained under Rules of the Game. The buttons in the +toolbar have the same meaning as entries in the Game +or Edit menu. + + +The <guimenu>Game</guimenu> Menu + + + + +&Ctrl;N + + +Game +New + +Starts a new game even if a game is +currently in play. + + + + + + + +N + +GameNetwork Play + + + +If this toggle is switched on, the application is in Network Mode. + + + + + + + + +&Ctrl;Q + +GameQuit + + + +Quits &kenolaba;. + + + + + + + +The <guimenu>Edit</guimenu> Menu + + + + + + + +&Ctrl;C + +EditCopy + + +Copy a representation of the &kenolaba; board to the clipboard + + + + + + +&Ctrl;V + +EditPaste + + +Interpret the content of the X clipboard as an ASCII representation of +a &kenolaba; board, and copy it to the current board. A valid +ASCII representation is generated by Copy. + + + + + + +&Ctrl;Insert + +EditModify + + +Switches &kenolaba; to Modify Mode. + + + + + + +&Ctrl;S + +EditSave Position + + +Save the actual position to be retrieved later with +Edit Restore Position +. + + + + + + + +&Ctrl;O + +EditRestore Position + + +Restore the board position stored in the &kenolaba; Configuration file +by Edit Save +Position. + + + + + + + +The <guimenu>Move</guimenu> Menu + + + + + + + +H + +MoveHint + + + + +A hint for your next move is shown. Only possible if you play at level +normal or above, the move number is 2 or greater and you have not undone +your move. + + + + + + + +S + +Move +Stop Search + + + + +When the computer is thinking, his depth search is interrupted and he +draws the best move he has found so far. + + + + + + + +PagegUp + +MoveTake Back + + + + +Take back your previous move. Two (!) moves are undone: the opponents +move and your last move. So it's your turn again. 100 moves are +remembered; so don't hesitate to take back moves until you are at the +beginning of the game. When in Modify mode (see +EditModify +), decrement move number of this position. + + + + + + + +PageDown + +MoveForward + + + +Only useful in Modify mode: Increment move number of this +position. + + + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + + +&Ctrl; M + + +Settings +Show Menubar + + + +Shows or hides the &kenolaba; menubar. + + + + + +SettingsShow Toolbar + + + +Shows or hides the &kenolaba; toolbar. + + + + + +SettingsShow Statusbar + + +Shows or hides the &kenolaba; status bar at the bottom of the screen. + + + + + +SettingsSave Settings + + + +Saves the current settings to become the default for &kenolaba;. + + + + + +SettingsChoose Game Type + + +Select the level that the computer plays at. Can be one of: + +Easy + +Normal + +Hard + +Challenge + + + + + + + + +SettingsComputer +Play + + +Choose which colors the computer plays. Can be one of: + + +Red + + +Yellow + + +Both + + +None + + + + + + + + +SettingsMove Slow + + A move drawn by the computer when this option is +checked is shown by blinking and highlighting the pieces used in this +move. Uncheck this option if you don't want to wait for this animation: +Then only a quick highlighting is done. + + + + + +SettingsRender Balls + + + +If checked, the pieces are rendered online by an internal simple ray +tracer when needed (⪚ when resizing the window). + + + + + +SettingsSpy + + +If checked, turns on spying. + + + + + +SettingsConfigure Shortcuts... + + +Brings up the standard &kde; shortcuts configuration dialog to customize the shortcuts used by &kenolaba;. + + + + + +SettingsConfigure Toolbars... + + +Brings up the standard &kde; toolbar configuration dialog to customize the &kenolaba; toolbar. + + + + + +SettingsConfigure &kenolaba;... + + +Opens the &kenolaba; configuration dialog to change &kenolaba; +settings. See . + + + + + + + + + +The <guimenuitem>Help</guimenuitem> Menu + +&help.menu.documentation; + + + + + + +Command line options + + +kenolaba + + +MyPortNumber + + + +host:port + + + +With you specify the port number of the +listening TCP socket when in Network Mode. + +With you specify a remote &kenolaba; process +(with optional port number, when not using the default port on the +remote machine) to communicate with when in Network mode. You can +specify multiple remote processes. + + + +Credits & Licenses + +&kenolaba; + +Program copyright 1997-2000 &Josef.Weidendorfer; +&Josef.Weidendorfer.mail; + + +Original Documentation by Robert Williams +rwilliams@kde.org and &Josef.Weidendorfer; + +Documentation maintained by &Philip.Rodrigues; &Philip.Rodrigues.mail;. + + + +&underFDL; +&underGPL; + + + + +Installation + +How to obtain &kenolaba; + +&install.intro.documentation; + + + + +Compilation and Installation + +&install.compile.documentation; + + + + + + + + + + + + + diff --git a/doc/kenolaba/kenolaba-pieces.png b/doc/kenolaba/kenolaba-pieces.png new file mode 100644 index 00000000..58869a47 Binary files /dev/null and b/doc/kenolaba/kenolaba-pieces.png differ diff --git a/doc/kenolaba/man-kenolaba.6.docbook b/doc/kenolaba/man-kenolaba.6.docbook new file mode 100644 index 00000000..a89a79fd --- /dev/null +++ b/doc/kenolaba/man-kenolaba.6.docbook @@ -0,0 +1,80 @@ + + +]> + + + +JanSchaumann +jschauma@netmeister.org +April 8, 2003 + + + +kenolaba +6 + + + +kenolaba +A &kde; based game + + + + +kenolaba + + hostname + port + + + + + + + +Description + +&kenolaba; is a simple board strategy game that is played by two +players. There are red and yellow pieces for each player. Beginning +from a start position where each player has 14 pieces, moves are drawn +until one player has pushed 6 of his opponent's pieces out of the +board. + + + + +Options + + + hostname +Use hostname for network game. + + + port +Use port for network game. + + + + + + +See Also + +More detailed user documentation is available from help:/kenolaba (either enter this +URL into &konqueror;, or run +khelpcenter +help:/kenolaba). + + + + + +Bugs + +&kenolaba; was written by JosephWeidendorferkde@jowenn.at +This man page is based on the one prepared by JanSchaumann jschauma@netmeister.org for the Missing Man Pages Project at http://www.netmeister.org/misc/m2p2/index.html. + + + diff --git a/doc/kfouleggs/Makefile.am b/doc/kfouleggs/Makefile.am new file mode 100644 index 00000000..085981d9 --- /dev/null +++ b/doc/kfouleggs/Makefile.am @@ -0,0 +1,4 @@ + +KDE_LANG = en +KDE_DOCS = AUTO + diff --git a/doc/kfouleggs/gamescreen.png b/doc/kfouleggs/gamescreen.png new file mode 100644 index 00000000..4f510ddb Binary files /dev/null and b/doc/kfouleggs/gamescreen.png differ diff --git a/doc/kfouleggs/index.docbook b/doc/kfouleggs/index.docbook new file mode 100644 index 00000000..1d68cce8 --- /dev/null +++ b/doc/kfouleggs/index.docbook @@ -0,0 +1,802 @@ + + + + + +]> + + + + +The &kfouleggs; Handbook + + + +&Philip.Rodrigues; &Philip.Rodrigues.mail; + + + +&Nicolas.Hadacek; &Nicolas.Hadacek.mail; + + + + +&Lauri.Watts; &Lauri.Watts.mail; + + + + + + + + +2000-2003 +&Philip.Rodrigues; + + +&FDLNotice; + +2006-06-19 +2.1.11 + +&kfouleggs; is a clone of the Japanese PuyoPuyo game for +the K Desktop Environment. + + +KDE +kdegames +KFoulEggs +PuyoPuyo + + + + + +Introduction + +&kfouleggs; is a clone of the Japanese PuyoPuyo game, with advanced +features such as multiplayer games against human or AI. +If you have played Tetris, or one of +its many clones, you will find &kfouleggs; easy to learn. + + + + + +Game Play + + +Starting a New Game + +When you start &kfouleggs;, just click on the Start + button, or select New from the +Game menu at any time to start a new game. + + + + +Aim of the Game + +The aim of the game is to achieve the highest score possible by +moving the falling tiles in such a way that tiles of the same color are +adjacent, and disappear. The more tiles you remove, the higher your +score. + + + + +Playing the Game + +Use the Left and Right arrow +keys to move the falling tile in the relevant direction, and the +Up arrow and Return keys to rotate the +tile left and right respectively. The Shift key drops +the tile down one line, and the Down key drops the tile +all the way down - use it as a way of saving time. + +All of these shortcuts can be customized by selecting +Configure Shortcuts... from the +Settings menu. + +If two or more tiles of the same color come to rest horizontally +or vertically next to each other, they become glued. If +four or more pieces of the same color come to be glued, +they disappear, and any pieces above them fall down according to the law +of gravity ;-). If this causes four or more pieces to become +glued, then they will also disappear, and so on. + +Every time you remove 100 tiles, you advance to the next level, +where the tiles fall faster, and the game is more difficult. + +The game ends when the central column fills with tiles, and no +more can fall. + + + + +The Game Screen + + + + + + + + + &kfouleggs; in Action + + + + +A quick explanation of what each of the parts of the game screen are for... + + + +Along the left hand side, from top to bottom: + + + + +Score +Shows your current score. + + + + + +Puyos +Shows the number of removed sets of pieces. + + + + +Level +Shows the number of the level you are currently on. The first, +easiest level is level one, progressing to the last, hardest level, 20. + + + + + + + +In the center is the area where the action takes place. The tiles fall through +the rectangle (whose properties can be changed in +>SettingsConfigure KFoulEggs... +, see ) until reaching the +bottom. The two small black rectangles under the main one are the tile's +shadow which show where it will land. You can toggle the tile's +shadow in . + + + +On the right hand side, the Next Tile box shows what the +next tile to fall will be, if you have enabled it in . + + + + + + + + +&kfouleggs; Configuration and Default Shortcuts + + +Configuring Gameplay + +The &kfouleggs; configuration is accessed from +Settings +Configure &kfouleggs;.... The +options are as follows: + + + +Game Configuration + + + + +Initial level: +Set the level which you will play at startup, from 1 (easiest) +to 20 (hardest). Default is level one. + + + +Direct drop down +If checked, pressing the Down Arrow will cause +a tile to immediately fall to the bottom of the screen. If unchecked, pressing +the Down Arrow only causes the piece to fall until the key is released. + + + + + + + + +Appearance Configuration + + + + +Enable animations +If checked, pieces are shown with an +animation (a small rebound effect), when they touch the bottom. + + +Show piece's shadow +If checked, a shadow is placed beneath the game board +showing where the piece will fall. + + + +Show next piece +If checked, shows the next tile that will fall onto the +game board. + + +Show detailed "removed lines" +field +If checked, the Puyos counter on the left +of the screen shows how many times each number of Puyos have been removed. If +unchecked, just the total number removed is shown. + + + + +Background +Select the color and the opacity for &kfouleggs; background. +An opacity of zero makes the &kfouleggs; background +completely transparent, and a setting of one makes the &kfouleggs; background +completely opaque. + + + + + + + +Colors Configuration +Here you can select the colors used for the tiles in &kfouleggs;. + + + +A.I. Configuration +Here you can configure the A.I. in &kfouleggs;. + + + + +Configuring Highscores + +The configuration is accessed from +Settings +Configure Highscores.... The +options are as follows: + + + +The <guilabel>Main</guilabel> Tab + + + + +Nickname: +Displays your current nickname and allows you to change it. + + + + +Comment +A comment about yourself. You choose... + + + + +World-wide highscores enabled +If checked and if you are connected to the Internet, +&kfouleggs; will send your score automatically at the end of the game +to the highscore web server (kfouleggs.sf.net). + + + + + + +The <guilabel>Advanced</guilabel> Tab + +This tab displays your Registration Data +on kfouleggs.sf.net: + + + +Nickname: +Displays your current nickname from the Main tab. + + + + +Key: +This key was generated when you registered on kfouleggs.sf.net by +selecting World-wide highscores enabled the first time. +The registration key is used in conjunction with the +nickname to identify uniquely users, but users cannot have the same nickname. +Click on the Remove button to delete you from the world highscores +list. + + + + + + + + +Default Shortcuts + +The default shortcuts for &kfouleggs; in all player modes are as +follows: + + + + + +New +&Ctrl;N + + +Pause +P + + +Quit +&Ctrl;Q + + +Zoom In +&Ctrl;+ + + +Zoom Out +&Ctrl;- + + +Show Highscores +&Ctrl;H + + +Show Menubar +&Ctrl;M + + +&kfouleggs; Handbook +F1 + + +What's This? +&Shift;F1 + + + + + +The default shortcuts for &kfouleggs; in one-player mode are as +follows: + + + + + +Rotate Left +Up Arrow + + +Rotate Right +Return + + +Move Left +Left Arrow + + +Move Right +Right Arrow + + +Move to Left Column +&Ctrl;Left Arrow + + +Move to Right Column +&Ctrl;Right Arrow + + +Drop Down +Down Arrow + + + + + + + + + +Multiplayer Play + + +General Information + +&kfouleggs; supports two local multiplayer types - Human vs. Human +and Human vs. Computer. To play either of these, select it from the +Multiplayer menu. + + +Multiplayer Games + +In Human vs. Human or Human vs. Computer mode, two gameboards are +shown, one for the first player and one for the second. Each has its own +Score, Puyos and +Level indicator. The keys for the first human +player become, by default: + + + + + +Rotate Left +E + + +Rotate Right +C + + +Move Left +F + + +Move Right +G + + +Move to Left Column +&Shift;F + + +Move to Right Column +&Shift;G + + +Drop Down +D + + +Drop One Line +Space + + + + + +The keys for the second human player are the same as for the +player in single player mode. + +To return to single player mode, select +Multiplayer and Single +Human. + + + + + + + + +Command Reference + + +The Main &kfouleggs; window + + +The <guimenu>Game</guimenu> Menu + + + + + + +&Ctrl;N + +Game +New + +Starts a new game. + + + + + +P + +Game +Pause + +Pauses or resumes the game + + + + + +&Ctrl;H + +Game +Show Highscores + +Opens a dialog that displays different high score tables. +Clicking on the links below the tables downloads world-wide scores. + + + + + +&Ctrl;Q + +Game +Quit + +Quits &kfouleggs; + + + + + + + +The <guimenu>View</guimenu> Menu + + + + + +&Ctrl;+ +View +Zoom In + +Enlarges the game board + + + + + +&Ctrl;- +View +Zoom Out + +Reduces the game board size + + + + + + + + +The <guimenu>Multiplayer</guimenu> Menu + + + + + +Multiplayer +Single Human + +Sets the multiplayer mode to single +player + + + + +Multiplayer +Human vs Human + +Sets the multiplayer mode to two player with two human +players. + + + + +Multiplayer +Human vs Computer + +Sets the multiplayer mode to two player with one human +and one computer player. + + + + +Multiplayer +More... + +Displays the multiplayer +settings dialog. + + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + + +&Ctrl;M + +Settings +Show Menubar + +Shows or hides the Menubar.To return the +menubar, right-click anywhere on the gameboard and select Show +Menubar. + + + + +Settings +Configure Shortcuts... + +Displays a standard &kde; key bindings +configuration dialog to change the keyboard shortcuts for &kfouleggs;. + + + + +Settings +Configure Notifications... + +Displays a standard &kde; notifications +configuration dialog to change the audio and visual notifications for &kfouleggs;. + + + + +Settings +Configure Highscores... + +Displays the high score configuration dialog, +in which you can change several settings that affect how &kfouleggs; treats highscores. + + + + +Settings +Configure &kfouleggs;... + +Displays the &kfouleggs; +configuration dialog. + + + + + + + +The <guimenuitem>Help</guimenuitem> Menu + +&help.menu.documentation; + + + + + + + + + +Credits and License + +&kfouleggs; + +Program core engine copyright 1995 Eirik End. +Program copyright 1996-2001 &Nicolas.Hadacek; &Nicolas.Hadacek.mail;. + +Documentation copyright 2000-2003 &Philip.Rodrigues; &Philip.Rodrigues.mail;. + + + +&underFDL; + +&underGPL; + + + + +Installation + + +How to obtain &kfouleggs; + +&install.intro.documentation; + + +Compilation and Installation + +&install.compile.documentation; + +Should you run into problems please report them to the &kde; +mailing list, or the program maintainer, &Nicolas.Hadacek;, at +&Nicolas.Hadacek.mail;. + + + + + + + +&documentation.index; + + + + + diff --git a/doc/kgoldrunner/Makefile.am b/doc/kgoldrunner/Makefile.am new file mode 100644 index 00000000..41691557 --- /dev/null +++ b/doc/kgoldrunner/Makefile.am @@ -0,0 +1,3 @@ +KDE_LANG = en +KDE_DOCS = AUTO + diff --git a/doc/kgoldrunner/editbar.png b/doc/kgoldrunner/editbar.png new file mode 100644 index 00000000..72c16150 Binary files /dev/null and b/doc/kgoldrunner/editbar.png differ diff --git a/doc/kgoldrunner/enemy.png b/doc/kgoldrunner/enemy.png new file mode 100644 index 00000000..3a13cfab Binary files /dev/null and b/doc/kgoldrunner/enemy.png differ diff --git a/doc/kgoldrunner/hero.png b/doc/kgoldrunner/hero.png new file mode 100644 index 00000000..002fb700 Binary files /dev/null and b/doc/kgoldrunner/hero.png differ diff --git a/doc/kgoldrunner/index.docbook b/doc/kgoldrunner/index.docbook new file mode 100644 index 00000000..b420d16d --- /dev/null +++ b/doc/kgoldrunner/index.docbook @@ -0,0 +1,1674 @@ + +KGoldrunner"> + + + + +]> + + + + +The &kgoldrunner; Handbook + + +Ian +Wadham +
ianw@netspace.net.au
+
+ +Marco +Krüger + + + +
+ + +2003 +Ian WadhamMarco Krüger + + +&FDLNotice; + +2005-12-10 +2.0 + + + + + + +&kgoldrunner;, a game of action and puzzle solving. Run through +the maze, dodge your enemies, collect all the gold and climb up to the +next level. + + + +KDE +KGoldrunner + +
+ + +Playing &kgoldrunner; + + +Introduction + +&kgoldrunner; is an action game where the hero + + + +runs through a maze and dodges enemies + + + + + +You must guide the hero with the mouse or keyboard and collect +all the gold nuggets, then you can climb up into the next level. Your +enemies are also after the gold. Worse still, they are after you! They +will kill you if they catch you! + +The problem is you have no weapon to kill +them. All you can do is run away, dig holes in +the floor to trap them or lure them into some area where they cannot +hurt you. After a short time a trapped enemy climbs out of his hole, +but if it closes before that, he will die and reappear somewhere else. + + +If you have never played before, try the Tutorial +game, which teaches you the rules and basic skills. Then try the +Initiation game. Experts might enjoy the +Challenge or Vengeance of Peter W games, +but the Initiation game has 100 levels and some are +very difficult. It is a good game for achieving a High Score. + + As you move to more advanced levels, you will find that +&kgoldrunner; combines action, strategy, tactics and puzzle +solving — all in one game. Good luck! + + + + +A Typical Game + +Below is a typical game in progress. The hero (green) is at +level 4 in the Advanced Tutorial game. The window's status bar shows +how many lives he has left and how many points he has scored. It also +shows that there is a hint available for this level (as there always +is in tutorial games) and that the action is stopped (while taking +this snapshot), but can be continued by pressing P or +Esc. + +The hero started at the top left, dug a hole and dropped into a +line of false bricks and gold where you can now see him falling. The +enemy above him will fall into the hole, then climb out and be trapped +up there till the end of the level. He could cause the hero some +problems then, because that is where the hidden ladders appear when +all the gold is gone. The hero has to use them to get to the next +level. + + +&kgoldrunner; typical game + + +&kgoldrunner; typical game + + + +The enemy on the right is falling and carrying some gold. +Notice his gold outline. The third enemy is about to come down a ladder to +chase the hero. He will have to dodge both enemies when he reaches +the floor ... This level also shows diggable bricks and undiggable +concrete and has lots of traps (false bricks), which look just like +ordinary bricks. + + + + +How To Play + + +Starting the Game + +When the &kgoldrunner; window appears, click on the name of one +of the games in the list displayed, then click on the Start +Game button. Level 1 of the game you selected will appear +and the mouse pointer will be over the hero. You start play by moving +the mouse, clicking the mouse or pressing any key that can control the +hero (⪚ Space), but avoid using +Q, S, P or &Esc; at +this stage. You start each level in the same way. + +You have five lives at the start and gain a bonus life for each +level completed. + + + + +Moving Around + +By default you control the hero (green) with the mouse, but you +can also control him with the keyboard (see below). He moves towards +wherever you place the mouse pointer. He cannot move as fast as you +can move the mouse, but he will try hard to catch up. If the mouse +pointer is above or below his level, he will always go up or down when +there is a ladder available or a place to fall into. Otherwise he will +move horizontally until he is above, below or right at the mouse +pointer. You can make him follow simple paths (like _ | L or U), but +be careful not to get too many twists and turns ahead of him. + +You can move left or right over bricks, concrete, ladders or +bars and up or down over ladders. If you are on an empty square or a +gold nugget and there is no brick, concrete, ladder or bar to hold you +up, gravity takes over and you fall. You can also fall by moving down +from a bar or the bottom of a ladder, so keep the mouse pointer up +there if you want to hold on. + + + + +Keyboard Control + +If you prefer, you can use the keyboard to control the hero. The +default keys are the arrow keys and I, +J, K and L (in +honor of the original Apple II game), with the keys +Z and C or U and +O for digging. You can play one-handed by using +U, I, O, +J, K, L and +Space (on a QWERTY keyboard) or +two-handed by using the arrow keys, Space, +Z and C. You can also choose other +keys, on the Settings menu, if you have the &kde; +desktop version of &kgoldrunner;. + +You enter keyboard mode by pressing one of the keys that can +control the hero or by selecting Keyboard +Controls Hero on the Settings +menu. + +The movement keys (I, J, +K, L or arrows) start the hero +moving up, down, left or right. He continues moving in that direction, +if he can, until you press another movement key or +Space to stop. In mouse mode, he stops automatically +when he gets to the mouse pointer. + +A word of warning: using the keyboard is much harder, in the +long run, than using the mouse. Think of the mouse as a +joystick. + + + + +Taking a Break + +You can stop the game at any time by pressing a Pause key +(either P or &Esc;). You can resume by pressing the +P or &Esc; key again. There is also a +Pause option in the Game +menu, but the problem is to get up there without moving the hero and +getting into trouble. + +When you want to use the &kgoldrunner; menu or do +some work in another window, always press a Pause key and stop the +game. Otherwise the hero will go on following the mouse pointer and +might get into trouble. + + + + +Winning a Level + +The object of the game is to collect all the gold, by moving or +falling onto it. When there is none left, you must move to the top of +the playing area to get to the next level. Often, as you collect the +last piece of gold, hidden ladders will appear and you can use them to +climb to the top. + + + + +Beware of False Bricks + +False bricks, otherwise known as fall-through bricks or traps, +are the other hidden feature of the game. They look just like ordinary +bricks, but if you run over one you fall. This can be bad if there is +an enemy or a pit down there, or good if there is some gold. + + + + +Enemies + +Enemies move in very much the same way as the hero. The main +difference is when they fall into a hole the hero has dug (see Rules of Digging below). They will +pick up gold either always or at random, depending on the game +settings, and will drop gold at random on brick, concrete or the top +of a ladder. An enemy who is carrying gold has a gold outline. You +must get the gold away from him before you can finish the +level. + +If an enemy touches you or you move down onto one, you die and +must start the level again, if you have any lives left. However, it is +possible to walk or stand on an enemy's head and you can ride an enemy +down when he is falling. Some advanced levels require such a +move. + + + + +Losing a Level + +As explained above, you can lose a level and a life by touching +an enemy. You can also lose by getting caught in a dug brick when it +closes. Otherwise you can commit suicide by pressing key +Q or using the menu item +GameKill +Hero. Why would you want to do that? Well, +sometimes you can get trapped in a place from which you cannot +escape. You will stay imprisoned for ever unless you commit +suicide! + + + + +The Importance of Digging + +Digging is one of the most powerful tactics you have. To dig a +hole in a brick to the left or right, just click the +Left or Right +mouse button, or use one of the digging keys (defaults +Z, C, U, +O.) + +You can dodge an oncoming enemy by digging a hole, waiting for +him to fall in and then running quickly over his head. At the same +time you can collect any gold he was carrying. Usually you can dig two +holes to capture and run over two enemies who are close together, but +this rarely works if the enemies are separated or there are more than +two of them. + +You can kill enemies by digging enough holes in their path. It +usually takes two or more holes to kill one enemy and up to eight or +more holes to kill four or five. When enemies die, they reappear +immediately, either where they started the level or near the top of +the playing area, depending on whether the game follows +&kgoldrunner; or Traditional rules and +settings (see Choice of Rules). + + + + +The Rules of Digging + +You click the Left or +Right mouse button, to dig a hole to the +left or right, or use the Z, C, +U or O keys. Only bricks can be dug, +not anything else. The hole will appear in the floor on one side or +the other (&ie; below and to the left or right of the hero). You can +be running, standing or falling when you dig. There must be an empty +space or hole above the brick (&ie; no digging under a ladder, bar, +gold, brick, concrete, false brick or enemy). + +The hero can move in any direction through dug holes, including +falling right through a hole to escape an enemy. After a short time, +dug holes close up, so you must use them quickly. Plan your digging in +advance. There is no time to stop and think while you dig. + +Enemies are always captured if they fall into a dug hole and +will always climb UP to get out of it. Their time in the hole is less +than the time the hole stays open, so to kill enemies you usually have +to dig several holes in quick succession. Enemies always give up gold +as they fall into a hole, so you can quickly run onto the enemy's head +and grab the gold before he climbs out. + +Enemies will never fall down through a hole from above, but +depending on the game rules and Settings, they can +run horizontally into a dug hole and not be captured and can fall from +there or run into another hole or run out of the hole. Several levels +that follow Traditional rules depend on an enemy being +able to run through a hole. + + + + +Winning and Losing the Game + +You win by completing the last level in the game. You lose when +your last life is gone. Either way, if you have achieved one of the +ten highest scores for that game, you can record your achievement in +the KGoldrunner Hall of Fame + + + + + +Scoring + + + +Start with 5 lives. + + +Lose a life and repeat a level when the hero dies + + +Gain a life when you complete a level + + +75 points for trapping an enemy + + +75 points for killing an enemy + + +250 points for collecting gold + + +1500 points for completing a level + + + + + + +Choice of Rules + +Most of the rules of &kgoldrunner; are covered in the section +How to Play, however there are two +major rule settings that affect the style of play: known as +Traditional and &kgoldrunner;. It is +important to know which settings are in force when you are playing a +game. Many Traditional levels are impossible with +&kgoldrunner; settings. Most +&kgoldrunner; levels are easier with +Traditional settings, but the +Traditional games are no pushover. To make life easier, +&kgoldrunner; automatically chooses the correct settings for a game +when you go into it, but you can can change them if you wish. + +The main difference between Traditional and +&kgoldrunner; settings is the method by which the +enemies choose a path to the hero. + + + +In the Traditional search method, enemies look +for vertical paths (ladders and falls) and they try to stay above the +hero or at the same height. They go below him only as a last +resort. The enemies make no attempt to chase the hero in the +horizontal direction until they are at the same height and can find a +horizontal path. This leads to situations where you can +control enemies who are far away from you and make them +work for you or at least keep out of your hair. + + +The &kgoldrunner; method searches alternately for +horizontal and vertical paths, which tends to keep the enemies over +your side of the playing area and at about the same height (&ie; they +are more aggressive and not so easy to manipulate). That leads to a +more action-packed game, but with less opportunity for strategy and +puzzle-solving. + + + +The other differences are listed below: + + + +In &kgoldrunner; play the enemies and hero always +move at the same speed and bricks always take the same time to +close. In Traditional play the game speed depends on +the number of enemies in a level. The more enemies, the slower they +run. The hero also becomes slower, but not so noticeably. + + +In Traditional play, enemies always collect gold +nuggets when they run into them. In &kgoldrunner; play +it is a random choice. The enemies drop nuggets after a random time in +both types of play. + + +Enemies can run horizontally through holes in +Traditional play but not in +&kgoldrunner; play. The trick in several +Traditional levels depends on releasing an enemy from +behind a brick wall. When you dig away the bricks, he runs out through +the hole. + + +When enemies die in &kgoldrunner; play, they go +back to where they started the level. In Traditional +play, they reappear at a random place near the top of the playing +area. This makes a big difference, especially in levels where you have +to get enemies to fetch nuggets down for you, but also because the +enemies keep falling down onto you as fast as you can kill +them. + + + + + + + +The Level Selection Dialog + + +Using the Level Selection Dialog Box + + +The Level Selection dialog + + + + + +The Level Selection dialog + + + + +This box appears when the game starts and also when you select +options from the Game or Editor +menus. You use it to choose a game and a level to play, edit, save, +move or delete. The main button at the bottom is the +OK" button. Its label changes according to what +you are doing: in this case choosing a game and level to edit. + + +Selecting a Game + +There are several System games that come with the +&kgoldrunner; release and they appear at the top of the list box. If +you have composed games and levels yourself, they appear below the +System games. To select one of the games in the list, +just click on its name. + +When you select a game, the dialog automatically shows what rule +settings apply (see Choice of Rules) +and how many levels there are. The More Info... +button shows you further information about the game. + + + + +Selecting a Level + +When you are starting a game, the level is fixed at +001. In other cases, such as this example, you can +choose a level. The default selection will be something reasonable, +such as the last level you played or edited. + +You can select a level either by typing it in or by moving the +slider. The arrows at the end increase or decrease the number by +one. Also you can drag the slider with the left mouse button held down +and change the number rapidly or you can change it by 10 if you click +in the space to the left or right of the slider. + +As the level number changes, a preview of the level appears in the +small preview window and the level's name (if it has one) appears +below the slider. + + + + +Completing your Selection + +When you have chosen a game and a level, just click the main +button at the bottom to proceed with your edit or play. + +Until you do that, nothing changes, so you can always click +Cancel and go back to what you were doing +earlier. Note that the game action is frozen while this dialog box +appears, so you can continue playing where you left off if you choose +Cancel. + + + + +Saving Changes + +If you select an action on the Game or +Editor menu and you were previously editing and had +not saved your changes, you will get a message asking you to save, +abandon or continue your work. The level selection dialog box for the +new action will not appear until you have made a decision about your +previous work. + + + + +Playing or Editing <quote>Any</quote> Level + +Note that the menus allow you to select and play or edit +any level. If you start a game after level 1, that is +OK, you will just not get such a high score. Dedicated players often +prefer to play high-numbered levels for fun, rather than go for a high +score, or they might like to train on higher levels +before attempting a high score. + +If you select a System level for editing, that is OK too, but +you must save it in one of your own games: not back in the System +game. If it is a very difficult level, you might want to change it so +that you don't go back to the start of the level every time you +die. + +To see what this means, try the Challenge game, +level 16, The Three Musketeers. That level has about +ten difficult puzzles in it. When you have solved puzzle 1, you don't +want to keep repeating it while you work on puzzle 2. The secret is to +make an editable copy, then keep updating it and changing the hero's +starting point, until you have solved all the puzzles. Then you can +attempt the real thing. + + + + + + +The Menu Reference + + +Overview of Menus + + + +Game + +This menu contains options affecting game play, such as starting +and finishing games, saving games, loading saved games, showing high +scores and getting a hint (if available). The +Game Menu also contains the +Quit option. You can also +quit by clicking the X at the top right of the +&kgoldrunner; window. + + + + +Editor + +This menu has everything you need to create your own games and +levels and maintain them, including features to help you re-order levels +or move them between games or to delete them when no longer needed. When +you reorganize your levels, the level numbers in your game remain +consecutive (no gaps) and the levels are automatically +renumbered. +When you are using the Game Editor a graphical toolbar with tool +tip text appears under the menu bar. See the Game Editor for more details of how to +create and edit &kgoldrunner; levels. + + + +Landscapes + +This menu offers you a choice of landscapes (actually color +schemes) in which to play. All the landscapes have keyboard shortcuts, +so you can actually change the landscape as the game is playing. Try +Shift+A, to switch to the nostalgic Apple II landscape. + + + + +Settings + +This menu has four groups of settings: the choice of mouse or +keyboard control, the game-speed options, the choice of of +&kgoldrunner; or Traditional playing rules +(see Choice of Rules) and +lastly the options to increase or decrease the size of the playing area. +The current selections are checked. The settings are automatically +selected when you start playing, but you can use this menu to vary them +if you wish. +On the &kde; desktop version of &kgoldrunner;, there is also an +option to re-assign keyboard shortcuts and hero-control keys. + + + + +Help + +The Help menu contains an option +to get a hint (if available), access to the +&kgoldrunner; Handbook (this document), About +&kgoldrunner; and About &kde; (or +About Qt Toolkit). +In the &kde; desktop version, there is also an option to report a +Bug or Wishlist item. +If you would like to make a suggestion or you have some new levels +to contribute, the current author's email address is in the +About &kgoldrunner; menu item. + + + + + + + +The <guimenu>Game</guimenu> Menu + + + + +&Ctrl;N +GameNew Game... + + + +Shows the level selection dialog box, where you choose a game +(which will start at level 1). + + + + + +&Ctrl;O +GameLoad Saved Game... + + + +Brings up a table of previously saved games, sorted with the +latest first. Each line lists the game, level, number of lives, score, +day of week, date and time. If you select a line and click the +OK button, that game starts at the beginning of +that level, with the lives and score you saved. + + + + + +GamePlay Any Level... + + +Shows the level selection dialog box, where you choose +a game and a level to play. + + + + + +GamePlay Next Level... + + + +Shows the level selection dialog box with the game +and level set to one more than previously. You can then choose to play +that level or another. + + + +S +GameSave Game... + + + +Saves the current game, level, lives and +score. The option will only save the position and score as at the start +of the current level. You can save when you are mid-way through a +level, but &kgoldrunner; will still save the position (and score) as it +was at the start of the level. You will receive a warning message about +that. Before using this option, you must press a Pause key +(P or &Esc;) to freeze the game as you move the mouse, +but it is much easier to use the S key as a +short-cut. + + + + + +&Ctrl;S +GameSave Edits... + + +This option is enabled only if you have been using +the Game Editor. It brings up the level selection dialog box and lets +you choose a level number and game in which to save your work. Its +action is exactly the same as the Save +Edits option on the Editor menu and +the disk icon on the Editor toolbar. + + + + + +P or &Esc; +GamePause + + + +Stops or restarts the game action. + + + + + +&Ctrl;H +GameShow Highscores + + + +Shows a table of up to ten high scores for the currently selected +game. Each line shows the player's rank and name, the level reached, +the score achieved, the day of the week and the date. + + + + + +GameGet Hint + + + +If the current level has a hint, this option pops it up. The +option is grayed out if there is no hint. The status bar +also has a section to show you whether there is a hint for the current +level. It is customary for championship and tutorial games to have a hint +on every level. + + + + +Q +GameKill Hero + + + +Kills the hero when he is in a position from which he cannot +escape. + + + + + +&Ctrl;Q +GameQuit + + + +Terminates &kgoldrunner; immediately. If a game is in +progress, it disappears forever (unless you have just saved it) and +there is no check for a high score. If you were editing a level, you +are given an opportunity to save your work. + + + + + + + + +The <guimenu>Editor</guimenu> Menu + + + + +EditorCreate Level + + + +Shows the Editor toolbar and a blank playing area +on which you can draw a new level. When done, use +Save Edits to assign the new level to a game +and level number. + + + + + +EditorEdit Any Level... + + + +Shows the level selection dialog box, where +you choose a game and level to edit. You can choose a System game and +level, but you get a warning that you can only save the edited level +in one of your own games. + + + + + +EditorEdit Next Level... + + + +Shows the level selection dialog box with the game +and level set to one more than current. You can then choose to edit +that level or another. + + + + + +&Ctrl;S +EditorSave Edits... + + + +Shows the level selection dialog box with +appropriate default settings for saving a new or edited level. You can +change the game and level number and achieve a Save As... +effect. This dialog box has a special button, Edit Level Name and +Hint, which shows a dialog box where you can add an optional +name and hint to your level. + + + + + +EditorMove Level... + + + +This is a two-part operation. Before using Move +Level, you must load a level to be moved by selecting it +for play or edit. The Move Level option +then shows the level selection dialog box, where you choose a new place +for the loaded level. You can change both the game and the level +number to move a level to another game or you can just change the level +number to re-order levels within a game. You cannot move a System +level. + + + + + +EditorDelete Level... + + + +Shows the level selection dialog box, where you choose a level +to delete. You cannot delete a System level. + + + + + +EditorCreate Game... + + + +You need to use this option before you start +creating your own &kgoldrunner; levels. It shows a dialog box where you +can enter the name of a new game, a 1-5 character prefix for +level-file names, the default rules for your game +(&kgoldrunner; or Traditional) and an +optional description or comment about the game. + + + + + +EditorEdit Game Info... + + + +Shows the level selection dialog box, where +you choose a game to be edited, then shows the game information in a +dialog box where you can edit the name, rules and description, but +not the file name prefix, in case you have saved some levels. That +is because the prefix is used in level-file names internally. + + + + + + + + +The <guimenu>Landscapes</guimenu> Menu + + + + + +&Shift;G +Landscapes&kgoldrunner; + + +Gives you the default &kgoldrunner; landscape, with +red bricks and wooden ladders. + + + + + +&Shift;A +LandscapesApple II + + +Gives you a nostalgic look at the original Apple II game, as it +was when you plugged your Apple II into your TV set — otherwise you had +a monochrome monitor. + +Actually, the hero was white, the enemies had white +pants and orange (flesh-tint) tops and the gold was white boxes with orange +panels. The whole effect was fuzzier (a sort of hardware anti-aliasing), +which made the bricks look almost three-dimensional — difficult to +recreate on today's pin-sharp monitors. + + + + + +&Shift;I +LandscapesIce Cave + + +For fans of the movie Superman or the +James Bond movie Die Another Day — or you can +use it on a hot day. + + + + + +&Shift;M +LandscapesMidnight + + +For real &kgoldrunner; addicts, on those late-night sessions. + + + + + +&Shift;K +Landscapes&kde; Kool + + +Inspired by the icon colors on the &kde; desktop. + + + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +SettingsMouse Controls Hero + + +Sets mouse control of the hero +(see Moving Around). + + + + + +SettingsKeyboard Controls Hero + + +Sets keyboard control of the hero +(see Keyboard Control). + + + + + +SettingsNormal Speed + + +Sets normal game speed (12 units). + + + + + +SettingsBeginner Speed + + +Sets beginner game speed (6 units, half of normal speed). + + + + + +SettingsChampion Speed + + +Sets champion game speed (18 units, 1.5 times normal speed). + + + + ++ +SettingsIncrease Speed + + +Increases the game speed by one unit, up to a maximum of 24 +units (twice normal speed). You can use the + key +as a shortcut. + + + + + +- +SettingsDecrease Speed + + +Decreases the game speed by one unit, down to a minimum of 3 +units (a quarter of normal speed). You can use the - key +as a shortcut. + + + + + +SettingsTraditional Rules + + +Sets Traditional default playing rules +(see Choice of Rules). + + + + + +Settings&kgoldrunner; Rules + + +Sets &kgoldrunner; default playing rules +(see Choice of Rules). + + + + + +SettingsLarger Playing Area + + +When &kgoldrunner; starts, it chooses the size of the playing area +(in pixels), based on your screen's resolution (e.g. 1024x768). This +option allows you to expand the playing area, up to a maximum, and +enlarge all the corresponding graphics. + + + + + +SettingsSmaller Playing Area + + +When &kgoldrunner; starts, it chooses the size of the playing area +(in pixels), based on your screen's resolution (e.g. 1024x768). This +option allows you to reduce the size of the playing area, down to a minimum, +and shrink all the corresponding graphics. + + + + + +SettingsConfigure Shortcuts... + + +In the &kde; version only, this item allows you to change the keyboard +assignments (⪚ for keyboard control of the hero) or to assign your own +shortcut keys for menu items. + + + + + + + +The <guimenu>Help</guimenu> Menu + +In addition to the standard items listed below, &kgoldrunner; has +the following special entry: + + + + + +HelpGet Hint + +If the current level has a hint, this option pops it up. The +option is grayed out if there is no hint. The status bar +also has a section to show you whether there is a hint for the current +level. It is customary for championship and tutorial games to have a hint +on every level. + + + + + +The following are standard items: + +&help.menu.documentation; + + + + + + +The Game Editor + + +Getting Started with the Editor + + +Creating a Game + +Before you create or edit a &kgoldrunner; level, you must create +a game in which to save it. Use Create Game +on the Editor menu. If you +forget, you will be reminded. + +The most important decisions to make when creating a game are to +choose the rules you are going to follow (Traditional +or &kgoldrunner;, see Choice of Rules) and to choose a +unique 1-5 character file name prefix for your game and levels. You +also need a name and description for your game, but those can easily +be changed later. + +The prefix is used internally by &kgoldrunner; to identify level +files, high score files and saved games. You can use your initials as +a prefix provided they are not the same as a &kgoldrunner; prefix. So +far, the prefixes level, plws, +wad, plwv, tute and +tutea have been used and trad and +chmp are reserved for future use. + +The four-letter tute prefix is reserved for +tutorial games, which show the game description and hints on levels as +they play. If you compose your own Tutorial game you could use the +prefix tutex, to make it run as a tutorial but not get +its files confused with those of the basic Tutorial or Advanced Tutorial +(prefixes tute and tutea). + + + + +Creating a Level + +When you have a game set up, use Create +Level on the Editor menu to start +creating a level. It provides you with a blank playing area of 28x20 +squares, with the hero at the top left. You can put the hero somewhere +else if you prefer and you do not have to use the whole 28x20 +area. Lots of interesting levels use smaller areas. + +The minimum requirement for a level to be playable is to have a +hero, a gold nugget he can get to and a visible or hidden ladder +leading to the top of the playing area. You do not have to have +enemies, bars or even bricks. There are many challenging levels that +have no enemies or no bricks. + + + + + +Editing and Testing + + +Editing a Level + +Use Edit Any Level or +Edit Next Level on the +Editor menu to start editing an existing level or use +Create Level to start editing a new level. + +If you choose +a System level, you are warned that you will have to save it in one of +your own games. Otherwise, the default is to save the level back where +it came from, but you can vary that. + + + + +Painting Objects in a Level + +The picture below shows the menubar and the edit toolbar, which +appears when you first choose Create +Level, Edit Any Level or +Edit Next Level from the +Editor menu and so change from Play to Edit +mode. It disappears when you go back to Play mode. Note also that, in +Edit mode, false bricks and hidden ladders are made visible in the +playing area. + +The three icons on the left of the edit toolbar have the same +actions as the menu options Create Level, +Edit Any Level and Save +Edits. The other eleven icons are for editing and +painting objects in your level. + + +From left to right the other eleven icons are Edit Name/Hint + (light bulb), Empty space (background), +Hero, +Enemy, Brick (can dig), +Concrete (cannot dig), Trap +(can fall through), Ladder, +Hidden ladder, Pole (or bar) and +Gold nugget. + + +Editing a level + + + + +Editing a level + + + +The Edit Name/Hint icon pops up a dialog in which +you can create or edit your level's name and hint. Both are optional, +but they are essential for tutorial levels and it is usual to provide +them with very difficult levels (see the Vengeance of Peter +W game). + +When you click on one of the other 10 icons, the mouse becomes a +brush that paints that icon. Initially the brush is set +to brick. + +You paint either by pointing and clicking the &LMB;, to place a +single object in a square, or by holding the &LMB; down and dragging +the mouse, to fill a line or area, such as a long ladder, a large +block of bricks or a concrete floor. Painting stops wherever you +release the mouse button. If you make a mistake, you can use the +Empty space icon to erase it. + +The hero icon works differently, because there can be only one +hero. When you paint the hero, he moves from his previous position to +wherever you release the mouse button. + + + + +Saving Your Work + +When you have finished painting, save your work using the +disk icon or Save Edits +in the Editor menu or Save +Edits in the Game menu. You must +always save into your own games, never into the System games. + +If you are creating a level, you use the Level Selection Dialog +to assign it to a game and level number. You use the same dialog if +you have been editing a level, but the default is to save it where it +came from. You can change the game and level, to get a Save +as... effect. If you have been editing a System level you must +save it as a copy in one of your own games. + + + + +Adding a Level Name and Hint + +On the Save version of the Level Selection Dialog box is a +button marked Edit Level Name & Hint. You can use +this to put the finishing touches on your level by adding a name and +hint. Both are optional, but they are essential for tutorial levels +and it is usual to provide them with very difficult levels (see the +Vengeance of Peter W game). Of course you can also use +the Edit Name/Hint icon, at any time, to add or edit a +name and hint. + + + + +Testing a Level + +After saving an edited level, you can test it by using +Play Any Level in the +Game menu. By default the game and level number are +remembered and you can get straight into the level with just a few +clicks. If you then want to edit some more, the same is true when you +use Edit Any Level again. + + + + + +Reorganizing Games and Levels + + +Moving a Level + +You can use Move Level on the +Editor menu to re-order or re-number the levels in +a game or to move a level from one game to another. Move +Level is a two-part operation. You must first load a +level by selecting it for editing or play, then when you use +Move Level, the Level Selection Dialog box +appears and you can select the new level number and game (as +required). + +Moves leave no gaps in the sequence of level numbers in a +game. For example, if you move level 10 of game A to level 3 of game +B, levels 11 and above in game A are re-numbered down by one (to close +the gap) and levels 3 and above in game B are re-numbered up by one +(to make room for the newcomer). + + + + +Deleting a Level + +You can use Delete Level on the +Editor menu to remove an unwanted level. The other +levels are re-numbered so as to close the gap in the sequence. + + + + + + + +Questions, Answers, and Tips + + +Overview + +This chapter presents some general hints for playing &kgoldrunner;, +as opposed to the specific hints you will find on the levels in tutorial and +championship games (see The Help Menu). +The hints are grouped as follows: + + + + + +Dealing with Enemies + + + + +Digging Puzzles + + + + +Tricks with Gold + + + + +Solving Difficult Levels + + + + + + +Dealing with Enemies + + + +You can always trap a single oncoming enemy in one hole, get his +gold and run on over him without killing him. It is often a good idea +to dig behind you and delay him further, but that might kill him. In +some levels it is not a good idea to kill enemies. + + +You can usually trap two oncoming enemies in two holes, get +their gold and run on over them, but this is dangerous if the enemies +are separated by two or three spaces. Sometimes two holes kill one of +the enemies and the other escapes and sometimes both escape. + + +It is nearly always impossible to trap three oncoming enemies +and run on over them. You will have to kill them, dig and drop through +the floor or simply run away. + + +Try to get all the enemies running together in one group. That +way you will not get surrounded and you can do such fun things as +collect gold while they run along behind you. + + +Sometimes you can kill an enemy with two holes, but it often +takes three to nine holes to kill one to five enemies. + + +It is possible to kill an enemy with one hole if he is far +enough away when you dig it. + + +In the Traditional game, you can trap an enemy +permanently in a pit of brick or concrete. Stand near the edge of the +pit on the opposite side to the enemy. As he approaches the pit, move +down one square (to the level of the floor of the pit) and the enemy +will usually run into the pit. + + +In some levels in the Traditional game, you can +force an enemy to climb a ladder ahead of you if you stand somewhere +above the bottom of the ladder. + + + + + + +Digging Puzzles + + + +To dig through more than one layer of bricks, start by digging +as many bricks as there are layers, jump in, dig one brick less and so +on. + + +If you dig next to a ladder or a succession of bars, you can dig +down through any number of layers and up to five or six bricks +horizontally. Just return to the ladder or bars after digging each +layer, then move down one square, run out and dig the next +layer. + + +If a digging puzzle looks impossible, consider that there might +be some false bricks in it. Maybe the bottom layer is false or maybe +you can stand in a false brick and dig next to it. + + +Think about the order in which you will collect the gold. If you +pick the right order, the digging might be easier. + + +Some levels require you to dig away a brick walll, run in, +collect some gold and run out again before the bricks close up and +trap you. + + + + + + +Tricks with Gold + + + +If you cannot get to a piece of gold, think about getting an +enemy to go there and get it. Even think about using an enemy's head +as a bridge to get across a pit or precipice. + + +In some levels there is a nugget that must be collected last of +all, because you will need the hidden ladders to get away from that +position. + + +In some situations an enemy can be killed without giving up the +gold he is carrying. The gold becomes a lost +nugget. You score no points for making an enemy lose a nugget, +but at least you can finish the level. + + +If there is nowhere to dig, keep the enemies running over +concrete and the tops of ladders and so make them release their gold +at random. + + + + + + +Solving Difficult Levels + + + +Try changing the speed settings to Beginner or even +lower. + + +Use the Editor feature to peek at where the false +bricks and hidden ladders are. + + +Use the Editor feature to save the level in one of your own +games, then you can edit it so that you do not always have to start at +the beginning when you die. This allows you to develop the solution in +easier stages. + + +In digging puzzles, look for false bricks you can fall into and +thus have one less brick to dig. Or consider using an enemy to go +ahead of you, so that you can stand on his head and dig. + + +Look for a theme or trick in the level, such as riding down on +an enemy's head, trapping all the enemies in a pit, luring the enemies +to a corner where they will stay and not chase you, luring the enemies +into pits you must cross to get to some gold, getting the enemies to +fetch gold for you or finding spots where you can stand and make an +enemy move to where you want him. + + + + + + + + + +Credits and Licenses + +&kgoldrunner; copyright 2003 Ian Wadham and Marco Krüger. + +&kgoldrunner; is inspired by an early computer game called +Lode Runner which was written in the USA +by Doug Smith and first released +in 1983 by Broderbund Software. It +appeared originally on the Apple II and Commodore 64 computers, which was +where the Wadham family and Marco Krüger got hooked. It was a major +best-seller in its day and is one of the all-time great computer games. + +You can find out more about Lode Runner and +the various versions that have come out since 1983, on the +website Jason's Lode Runner +Archive (http://entropymine.com/jason/lr/). +That site also has the story of the game's original development, in the file + +misc/ldhist.html. + +&kgoldrunner; is an attempt to preserve the spirit of the original +classic game on a platform that will be portable and will last more than +a few years. It is available as free software in source code form. Copies +of the original game are unobtainable now and the machines it ran on are +going into museums. + +Marco Krüger developed &kgoldrunner; as far as v0.3, out of +nostalgia for the original Commodore 64 game. Ian Wadham added +several features and levels and created the current version. Ian's +elder son Peter composed the 100 levels in the "Initiation" game and +the 20 tricky levels in "Vengeance of Peter W". The other levels +were composed by Ian, Peter, Marco Krüger and friends and Ian's other +children, Simon and Genevieve. + + +&underFDL; +&underGPL; + + + + +Installation + + +How to Obtain &kgoldrunner; +&install.intro.documentation; + + + +Compilation and Installation +&install.compile.documentation; + + + + + +Porting &kgoldrunner; to Other Platforms + +&kgoldrunner; is written in C++, using &Linux; and the free-software +version of the portable &Qt; object and &GUI; library. The current version +can be compiled and run either with &kde; 3 and &Qt; 3 or with &Qt; 3 alone. + +The &Qt;-only version of &kgoldrunner; is intended to be almost +independent of &Linux; and the &kde; desktop. That means it should be +fairly easy to port to other operating systems, such as &Windows; +and Macintosh, because the &Qt; library and C++ language are portable +to other operating systems and the &kgoldrunner; source code is +available as free software. + +If you would like to port &kgoldrunner;, please email +Ian Wadham at ianw@netspace.net.au, to obtain +the latest version of the &Qt;-only code and the Technical Details +document. Of course, you must have your own C++ development +setup on your target machine. Also, the &Qt; library is +not free software on &Windows; and Macintosh, +so you will need to have access to a system with a paid +&Qt; Development licence. + + + +
+ + diff --git a/doc/kgoldrunner/kgoldrunner.png b/doc/kgoldrunner/kgoldrunner.png new file mode 100644 index 00000000..23429eb4 Binary files /dev/null and b/doc/kgoldrunner/kgoldrunner.png differ diff --git a/doc/kgoldrunner/level010.png b/doc/kgoldrunner/level010.png new file mode 100644 index 00000000..06126f83 Binary files /dev/null and b/doc/kgoldrunner/level010.png differ diff --git a/doc/kgoldrunner/select.png b/doc/kgoldrunner/select.png new file mode 100644 index 00000000..b2c50766 Binary files /dev/null and b/doc/kgoldrunner/select.png differ diff --git a/doc/kgoldrunner/tute008.png b/doc/kgoldrunner/tute008.png new file mode 100644 index 00000000..ee5b5c8b Binary files /dev/null and b/doc/kgoldrunner/tute008.png differ diff --git a/doc/kjumpingcube/Makefile.am b/doc/kjumpingcube/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/kjumpingcube/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/kjumpingcube/index.docbook b/doc/kjumpingcube/index.docbook new file mode 100644 index 00000000..55810a5a --- /dev/null +++ b/doc/kjumpingcube/index.docbook @@ -0,0 +1,368 @@ + + + + + +]> + + + +The &kjumpingcube; Handbook + + + +Matthias +Kiefer + +
&Matthias.Kiefer.mail;
+
+
+ +
+ + +1999 +2000 +&Matthias.Kiefer; + + + +&FDLNotice; + +2005-12-10 +1.1 + +&kjumpingcube; is a simple tactical game you can play against a friend +or the computer. + + + +KDE +KJumpingCube +kdegames +game +strategy + + +
+ + + +Introduction + +&kjumpingcube; is a simple tactical game. You can play it against the computer +or against a friend. The playing field consists of squares that contains +points. By clicking on the squares you can increase the points, and if the +points reach a maximum the points will jump to the squares neighbors and take +them over. The winner is the one who owns all squares. + + + + + +Playing &kjumpingcube; + + +Rules + + +A move consists of increasing the points of a square by clicking +on it. You can only increase a square that doesn't belong to your opponent. By +clicking a square that has no owner, you become the owner. +If a square has more points than it has neighbors, the points +jump to its neighbors and take them over. +Neighbor-Fields are only direct neighbors, not the diagonal +neighbors. +The winner is the one who owns all squares. + + + + + + + +Command Reference + + +The Menu Bar + + +The <guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N +GameNew + +Start a new game. + + + + +&Ctrl;O +GameLoad... + +Open a previously saved game. + + + + +&Ctrl;S +GameSave + +Save the current game. + + + + +GameSave As... + + +Save the current game with a different +name. + + + + +Escape +GameStop Thinking +Stop the computer opponent thinking about its next +move. The computer opponent will then make the best move it has found +up to the moment you told it to stop. + + + + + +&Ctrl;Q +GameQuit +Quits &kjumpingcube;. + + + + + + +The <guimenu>Move</guimenu> Menu + + + +&Ctrl;Z + +MoveUndo +Undo the last move you made. + + + + +H +MoveHint +Get a hint as to the best next +move. + + + + + + + +The <guimenu>Settings</guimenu> Menu + + + +SettingsShow +Toolbar +Toggle the display of the toolbar. + + + +SettingsShow + Statusbar +Toggle the display of the statusbar. + + + +SettingsConfigure Shortcuts... +Open a dialog where you can configure the +shortcuts for &kjumpingcube; + + + +SettingsConfigure Toolbars... +Open a dialog where you can configure the toolbars for &kjumpingcube; + + + +Settings +Configure &kjumpingcube;... + + +Open a dialog where you can configure the following items: + + + +Computer Skill +Lets you choose your playing skill from a +slider. This decides how clever your computer opponent is, if you are +playing against the computer. +You can choose from: + +Beginner +Average +Expert + + + + +Board Size +Lets you choose the playfield +sizes. +Use the slider to select a value between +5x5 squares and 10x10 +squares in size. + + + + +Computer Plays As +Sets the computer opponent to be Player +1, Player 2 or both. Normally you +would set the computer to play one player, and you would play the +other. Player 1 always starts first. + + + +Board Color +Choose a color for each player + + + + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + +The &kjumpingcube; Toolbar + + + +New +Starts a new game + + + +Save +Save the current game. + + + +Stop Thinking +Stop the computer opponent thinking about it's next +move. The computer opponent will then make the best move it has found +up to the moment you told it to stop. + + + +Undo +Undo the last move you made. + + + + + + + + +Questions, Answers, and Tips + + +Strategy tips + + +Try to avoid increasing a square when your opponent owns a +neighbor square, that reaches its maximum earlier than your +square. + + + + + + + +Credits and License + +&kjumpingcube; is Copyright 1998,1999 &Matthias.Kiefer; +&Matthias.Kiefer.mail; + +&kjumpingcube; was inspired by a game that came out for the Commodore64 and for +other home computers, too. Sorry, that I don't know the original author's +name. + +Documentation Copyright 1999 &Matthias.Kiefer; +&Matthias.Kiefer.mail; + +Documentation updated for &kde; 2 and 3 by &Lauri.Watts; +&Lauri.Watts.mail; + + + +&underFDL; +&underGPL; + + + + +Installation + + +How to obtain &kjumpingcube; + +&install.intro.documentation; + + + + +Requirements + +In order to successfully compile &kjumpingcube;, you need &kde; 2.0. +All required libraries as well as &kjumpingcube; itself can be found on +&kde-ftp;. + + + + + +Compilation and Installation + +&install.compile.documentation; + +That should do it! Should you run into any problems, please report them +to the author, at &Matthias.Kiefer.mail; + + + + +
+ + diff --git a/doc/klickety/Makefile.am b/doc/klickety/Makefile.am new file mode 100644 index 00000000..085981d9 --- /dev/null +++ b/doc/klickety/Makefile.am @@ -0,0 +1,4 @@ + +KDE_LANG = en +KDE_DOCS = AUTO + diff --git a/doc/klickety/index.docbook b/doc/klickety/index.docbook new file mode 100644 index 00000000..b38f81c6 --- /dev/null +++ b/doc/klickety/index.docbook @@ -0,0 +1,425 @@ + + + Klickety"> + + + + +]> + + + + +The &klickety; Handbook + + + + +Thomas +Davey + +thomas.davey@gmail.com + + +&Philip.Rodrigues; +&Philip.Rodrigues.mail; + + + + + + +2005 +Thomas Davey + +&FDLNotice; + +2006-01-21 +3.5 + + + +&klickety; is a strategy game for &kde;, an adaption of the Clickomania game. + + + + +KDE +kdegames + + + + + + +Introduction + +The objective of &klickety; is to clear the game board of all +the colored blocks in the least possible time (although having a short time is +only a secondary objective). The overall aim is to get the lowest score +possible. It will provide entertainment for all abilities, but a +challenge in logical thought if you want to get a really low score. + + + + +The Game Screen + + + + + + + + + + + + + + + + + + + +This is a colored block, you click on these to play the game. + + + +This is what was referred to in the introduction as the game +board, this is where the game actually takes place. + + + + +This is the game timer, it starts the first time that you click on a +removable block. + + + + +This the remaining block counter, which also doubles up the score +counter. It is usually black at the start of a game, it changes to +blue if the score is good enough to get onto your local highscore +table, and red if it would make the top spot. + + + + + + +Playing the game + +When you start the program you will get a screen that looks +something like the screenshot in the previous section. The idea of the +game is to remove blocks, which you do by clicking on them. + +However, you can only remove a block if it is adjacent to one or +more other blocks of the same color on one or more of its +sides. Diagonal connections do not count. You get a random board every +time you start a new game. + +If you click on a block which is connected as described, it will +disappear. Any adjacent blocks of the same color will also disappear, +along with all of the same color adjacent to them, and so +on. Any blocks above them drop down to fill the gaps left by the +disappeared pieces. If you clear an entire column, all the columns to +the right move across to fill the gap. + + +The game ends when there are no blocks adjacent to other blocks +of the same colour + + + +Scoring + +Scoring is very simple. Your score is the number of remaining +blocks when the game ends. In the case of having two high scores of +the same score, the one completed in the shortest time is listed in +the higher position. + + + + +Menu Reference + + +The <guimenu>Game</guimenu> Menu + + + +GameNew Game + (&Ctrl;N +) +Starts a new game before the previous one has finished +(once you end a game, a button appears on the game board labeled +'start' which does the same thing). + + + + + +GamePause + (P +) +Not yet implemented. + + + + +GameShow +Highscores (&Ctrl;H ) + +Shows the Highscore Table for checking a target or for +bragging purposes. (This is the same highscore table as is displayed +if you get a new highscore at the end of a game.) There are also tabs +for seeing all the players who have turned in scores and for looking +at some statistical analysis of the performance of individual +players. There are also links to the worldwide highscore and player +lists on the web. + + + + +GameQuit (&Ctrl;Q ) + +Ends the program. + + + + + + + +The <guimenu>View</guimenu> Menu + + + +ViewZoom In + (&Ctrl;+ +) + +Zooms the game board in, increases the window size as +necessary. + + + + +ViewZoom Out + (&Ctrl;- +) +Zooms the game board out. Window size will decrease around it. + + + + + + +The <guimenu>Settings</guimenu> Menu + + + +SettingsHide Menu Bar + (&Ctrl;M +) + +Hides the menu bar. It can be brought back with the +right-click menu or by pressing &Ctrl;M . + + + + +SettingsConfigure Shortcuts... + +Allows you to change the shortcut keys listed above. + + + + +SettingsConfigure Notifications... + + +Allows you to change the notifications for Game +over and the removal of a column (listed as Line +removed), such as playing a sound when you remove a column, or +starting your favorite IRC client when you finish the game so that you +can tell all your friends about your &klickety; skills. + + + + +SettingsConfigure Highscores... + + +Displays the highscores configuration dialog, which has two +tabs: + +On the Main tab, you can change the default +nickname that is entered onto the highscore table if you get a +highscore, add a comment to go with your entry on the players tab of +the highscore table. You can also activate the World-wide highscores +enabled feature so that you can compare scores with players +from around the globe. If you are connected to the +Internet, &klickety; will send your score automatically at the end of the +game to the highscore web server (klickety.sf.net). + +The Advanced tab displays your Registration Data +on klickety.sf.net. If worldwide highscores have been activated, +this tab shows the Nickname: from the Main +tab with which you are registered and the cryptographic Key: +used to prevent any attempts at cheating. This key was generated when you +registered on klickety.sf.net by selecting +World-wide highscores enabled the first time. +The registration key is used in conjunction with the +nickname to identify uniquely users, but users cannot have the same nickname. +Click on the Remove button to delete you from the world highscores +list. + + + + +SettingsConfigure &klickety;... + + +Brings up the main configuration dialog, this will be +dealt with in the next chapter. + + + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + +Configuring &klickety; + +Each page of the &klickety; configuration dialog has buttons +labeled Help and +Defaults.The Help button +brings up this handbook, and the Defaults +button restores all the settings to their default values. + +The individual options are as follows: + + +The <guilabel>Appearance</guilabel> tab + + + +Enable Animations +If checked, pieces are shown with an +animation (a small rebound effect), when they touch the bottom. + + + + +Background + +Allows you to change the background color and change +its opacity. Trivia: if it has some transparency it doesn't actually +show what's directly beneath &klickety;, but rather it shows the +desktop wallpaper. + + + + + + + + +The <guimenu>Colors</guimenu> tab + +Colors #1 through #5 Allow you to change the colors of the +colored blocks. For an extra challenge, make them all the same +color. N.B. You must restart &klickety; for these changes to take +effect. + + + + + +Tips + + +Try to remove sets of two blocks to allow single blocks to join +up with other ones of the same color, it's your only way to get rid of +them. + +Spend time thinking about your move: you primarily get higher places on the highscore table by getting rid of more pieces than doing it quickly. + + + + + + + +Credits and License + +&klickety; Program Copyright 1995 Eirik Eng and 1996-2004 Nicolas Hadacek + +&klickety; is based on clickomania. + +The &klickety; website can be found at http://klickety.sourceforge.net. + +First version of documentation by Thomas Davey, Copyright 2005. + +Reviewed by &Philip.Rodrigues; &Philip.Rodrigues.mail;. + +Further markup and reviewing by &Francis.Giannaros; +&Francis.Giannaros.mail;. + + + +&underFDL; + +&underGPL; + + + + +Installation + + +How to obtain &klickety; + +&install.intro.documentation; + + + + +Compilation and Installation + +&install.compile.documentation; + + + + + +&documentation.index; + + + diff --git a/doc/klickety/screenshot.png b/doc/klickety/screenshot.png new file mode 100644 index 00000000..c59d09f9 Binary files /dev/null and b/doc/klickety/screenshot.png differ diff --git a/doc/klines/Makefile.am b/doc/klines/Makefile.am new file mode 100644 index 00000000..8c761fb4 --- /dev/null +++ b/doc/klines/Makefile.am @@ -0,0 +1,5 @@ + +KDE_LANG = en +KDE_DOCS = klines + + diff --git a/doc/klines/index.docbook b/doc/klines/index.docbook new file mode 100644 index 00000000..d86d25bf --- /dev/null +++ b/doc/klines/index.docbook @@ -0,0 +1,328 @@ + + + + + +]> + + + + +The &kolorlines; Handbook + + + +Roman +Razilov + +
Roman.Razilov@gmx.de
+
+
+ +Roman +Merzlyakov + +
roman@sbrf.barrt.ru
+
+
+ + +
+ + +2000 +2001 +Roman Razilov, +Roman Merzlyakov + +&FDLNotice; + +2001-05-20 +1.00.00 + +&kolorlines; is a nice little game meant to amuse you for +anywhere from 3 minutes to 6 days(straight). + + +KDE +kdegames +klines + + +
+ + +Introduction + +&kolorlines; is a simple game. It is played by one player, so there is +only one winner :-) You play for fun and against the high score. It has +been inspired by well known game - Color +lines, written for DOS by Olga Demina, Igor Ivkin and +Gennady Denisov back at 1992. + +In 2000 Roman Merzlyakov wrote a original version for &kde;. Roman +Roazilov added to it a cool povray generated +animation. Later in 2001 he ported it to &kde; 2 and made it conform to +the &kde; games standard. + + + + + +Using &kolorlines; + +The main rules of game is as simple as possible: you move (using +mouse) marbles from cell to cell and build lines (horizontal, vertical +or diagonal). When a line contains 5 or more marbles - they are removed +from the field and your score grows. After each of your turns computer +drops three more marbles onto the field. + + The increase in score depends on the amount of erased marbles +and if &kolorlines; shows the next balls. The increase will be smaller if +you use the information of what's next. + + +More &kolorlines; features + +&kolorlines; is wonderful in that it will allow you to kill 5 minutes +while you wait for that program to compile. Or, if nothing +else, it will allow you to kill 5 minutes. + + + + + +Command Reference + + +The main &kolorlines; window + +There is a large area with marbles. At the right side a bar +showing the next three marbles to be dropped on the field. Underneath +the field is the status bar. Above it is the menu bar. + + +The <guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New game + +Starts a new game + + + + + +&Ctrl;H + +Game +Show Highscores + +Displays the high score table + + + + + +&Ctrl;Q + +Game +Quit + +Quits &kolorlines; + + + + + + +The <guimenu>Move</guimenu> Menu + + + + + +&Ctrl;Z + +Move +Undo + +This will undo the last move. + + + + +MoveEnd Turn + + +Skip your move, immediately play the next marbles without you moving +any. + + + + + +D +Move +Start Tutorial + +Show a short, animated, non-interactive demonstration of how +to play the game. If the tutorial is running, the statusbar will show +Level: Tutorial, and this menu entry will now show +Stop Tutorial. + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +Settings +Show Statusbar + +Toggles the display of the statusbar. + + + + +&Ctrl;P + +Settings +Show Next + +Toggles the information on next +marbles. + + + +SettingsUse Numbered Balls + + +Number the balls according to color. This may be of assistance to +players who are color blind, have other vision impairments that make the +colors hard to distinguish, or those of you who simply like the look. + + + +SettingsChoose Game +Type + +Choose a difficulty level, from Very Easy +through Very Hard. + + + +SettingsConfigure +Shortcuts + +Displays a standard &kde; shortcut configuration dialog, allowing you +to customize the keys used in the game. + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + + + + +Credits and License + +&kolorlines; + +Program copyright 2000 Roman Merzlyakov +roman@sbrf.barrt.ru +Contributors: + +Roman Razilov Roman.Razilov@gmx.de +Redesign, Graphik, animation, partial re-write. Special thanks to my wife Larissa Juschkin for +testing and discussions. + + + + +Documentation copyright 2001 Roman Razilov +Roman.Razilov@gmx.de + + + +&underFDL; + + +&underGPL; + + + + +Installation + + +How to obtain &kolorlines; + +&install.intro.documentation; + + + + +Compilation and Installation + +&install.compile.documentation; + + + + +Generating marbles with <application>Povray</application> + +All pictures (klines/*.jpg) are +rendered with Povray 3.0 and manipulated +with ImageMagick. +Povray & bash scripts are in the folder >klines/povray. +Customize balls.pov as you want, +render and install it. + +You need Povray 3.0 to render. In newer Versions &ie; 3.1 +halo is suppressed. + +In order to generate marbles on your system, type the following +in the base folder of the &package; distribution: + + + +% cd klines +% make install + + + + + + +&documentation.index; + +
+ + diff --git a/doc/kmahjongg/Makefile.am b/doc/kmahjongg/Makefile.am new file mode 100644 index 00000000..085981d9 --- /dev/null +++ b/doc/kmahjongg/Makefile.am @@ -0,0 +1,4 @@ + +KDE_LANG = en +KDE_DOCS = AUTO + diff --git a/doc/kmahjongg/background.png b/doc/kmahjongg/background.png new file mode 100644 index 00000000..f4ba2700 Binary files /dev/null and b/doc/kmahjongg/background.png differ diff --git a/doc/kmahjongg/boardeditor.png b/doc/kmahjongg/boardeditor.png new file mode 100644 index 00000000..bd146782 Binary files /dev/null and b/doc/kmahjongg/boardeditor.png differ diff --git a/doc/kmahjongg/config.png b/doc/kmahjongg/config.png new file mode 100644 index 00000000..d6923171 Binary files /dev/null and b/doc/kmahjongg/config.png differ diff --git a/doc/kmahjongg/gamescreen.png b/doc/kmahjongg/gamescreen.png new file mode 100644 index 00000000..22d6c603 Binary files /dev/null and b/doc/kmahjongg/gamescreen.png differ diff --git a/doc/kmahjongg/highscore.png b/doc/kmahjongg/highscore.png new file mode 100644 index 00000000..4d5667d2 Binary files /dev/null and b/doc/kmahjongg/highscore.png differ diff --git a/doc/kmahjongg/index.docbook b/doc/kmahjongg/index.docbook new file mode 100644 index 00000000..37173b49 --- /dev/null +++ b/doc/kmahjongg/index.docbook @@ -0,0 +1,1012 @@ + + + + + + + + JohnHayes'> + justlinux@bellsouth.net'> + + MathiasMueller'> + in5y158@public.uni-hamburg.de'> + AlbertAstalsCid'> + astals11@terra.es'> + DavidBlack'> + david.black@lutris.com'> + MichaelHaertjens'> + mhaertjens@modusoperandi.com'> + OsvaldoStark'> + starko@dnet.it'> + BenjaminMeyer'> + ben+kmahjongg@meyerhome.net'> + +]> + + + + + +The &kmahjongg; Handbook + + + +&John.Hayes; +&John.Hayes.mail; + + + +&Mathias.Mueller; &Mathias.Mueller.mail; + + + +&Albert.Astals.Cid; &Albert.Astals.Cid.mail; + + + +&David.Black; &David.Black.mail; + + + +&Michael.Haertjens; &Michael.Haertjens.mail; + + + +&Osvaldo.Stark; &Osvaldo.Stark.mail; + + + +&Benjamin.Meyer; &Benjamin.Meyer.mail; + + + + + + + +2005 +John Hayes + + +&FDLNotice; + +2006-01-23 +0.7.6 + + + + +&kmahjongg; is a game similar to famous oriental game Mahjongg. + + + + + +KDE +kdegames +KMahjongg +Mahjongg + + + + + +Introduction + + +&kmahjongg; is a game similar to the famous oriental game Mahjongg. The purpose of the game +is to remove all of the tiles from the gameboard. Tiles are removed by matching tiles in +pairs. The matched tiles must have a free side on the left or on the right. Please report +any problems or feature requests to the &kde; mailing lists. + + + + +Gameplay + + + + +Starting a New Game + +When &kmahjongg; is started, the board is setup and the game timer is started, waiting +for your first move. A new game can be started from selecting + +GameNew + + or pressing +&Ctrl;N +. + + + +There is also the option of starting a specific number games. All of the games are numbered and they are randomly selected from + +GameNew + +. + +A specific number game can be started by selecting + +GameNew Numbered Game + + + + + + +Aim of the Game + +The purpose of the game is to remove all of the tiles from the gameboard. Tiles are +removed by matching tiles in pairs, which then disappear. The matched tiles must have a +free side on the left or on the right. + + + + + +Playing the Game + + +Locate and select with the mouse, matching tiles that have a free side on the left or right side. +The tiles disappear as they are selected and matched. + + + +There are two options to help find matching tiles that have a free side. +One option is to get a hint by selecting + +MoveHint + + or pressing H +which will flash a pair of matching tiles. +The second option is to activate + +SettingsShow Matching Tiles +, +which will highlight the selected tile and flash any matching tiles that have a free side +with each move. No additional tiles will be highlighted if there are no matching tiles with a free side. + + + + + +The Game Screen + + + + +A screenshot of &kmahjongg; + + + + + + Screenshot + + + + +A quick explanation of the parts of the game screen... + + + + + +Game Screen + + +The Game screen is in the center of the screen and is the largest section of the window. +The background is populated with the game tiles. + + + + + +Game Timer + + +The Game Timer is located on the top right of the screen. The timer keeps track of the +elapsed time from the start of the game until the game is finished. + + + + + + +Statusbar + + +The Statusbar is located at the bottom of the screen, and contains three panes. The first +pane lists the removed/total tiles and the number of matching tiles that have free sides. +The second pane gives the sequence number of the game you are playing. In the last pane +lets the player know when the game is ready. + + + + + + + + + + + +Configuration and Default Keybindings + +The configuration options are as follows: + + + +Configure &kmahjongg; + + + +Configure Dialog + + + + + + Configure Dialog + + + + + +SettingsConfigure &kmahjongg;... +opens the Configure &kmahjongg; Dialog. + +<guilabel>General</guilabel> options + + + +Show removed tiles + + +Places a grid to the right of the gameboard where matching tiles are stacked after being removed from the gameboard. + +Here's a screenshot of &kmahjongg; showing removed tiles. + + + + + + Removed tiles. + + + + + + + + +Generate solvable games + + +Selects only games that can be solved when you start a new game. + + + + + +Play winning animation + + +Shows a replay of all moves when you win a game. + + + + + + + +<guilabel>Tiles</guilabel> options + + + + +Draw shadows + + +Draws shadows on the tiles to give a 3D appearance. + + + + + +Use mini-tiles + + +Sets the tile size to small. + + + + + + + +<guilabel>Background</guilabel> options + + + + +Scaled + + +Scales the background image. + + + + + +Tiled + + +Tiles the background image. + + + + + + + + + + + + + +Default Keybindings + +SettingsConfigure Shortcuts... allows you to change the default keyboard bindings. + + +Keybindings Configure Dialog + + + + + + + Keybindings Configure Dialog + + + + + +The default kebindings are as follows: + + + + + + +D + + +Playes the game in Demo mode. + + + + + +H + + +Gives you a hint by flashing two matching tiles. + + + + + +F1 + + +Displays this handbook. + + + + + +&Ctrl;O + + +Loads a previously saved game. + + + + + +&Ctrl;N + + +Starts a new game. + + + + + +P + + +Pauses or resumes a game. + + + + + +&Ctrl;Q + + +Quits the game. + + + + + +&Ctrl;&Shift;Z + + +Move redo. + + + + + +F5 + + +Restart the game. + + + + + +&Ctrl;S + + +Saves the game. + + + + + +&Ctrl;H + + +Show the highscores. + + + + + +&Ctrl;Z + + +Move undo. + + + + + +&Shift;F1 + + +What's this? Help + + + + + + + + + + + + +Command Reference + + + + +The main &kmahjongg; window + + +A screenshot of &kmahjongg; + + + + + + Screenshot + + + + + +The <guimenu>Game</guimenu> Menu + + + + + + + + +&Ctrl;N + +Game +New + +Starts a new game. + + + + + +&Ctrl;O + +Game +Load... + +Loads a saved game. + + + + + +F5 + +Game +Restart Game + +Restarts the numbered game being played. + + + + +Game +New Numbered Game... + +Starts a specific numbered game. + +Start a Numbered Game Dialog + + + + + + Start a Numbered Game Dialog + + + + + + + + +Game +Open Theme... + +Allows you to load a theme with different backgrounds and tile styles. + +Load Theme Dialog + + + + + + Load Theme Dialog + + + + + +The available options: + +default (shown) +pirates + + + + + + + +Game +Open Tileset... + +Allows you to load a different tileset. + +Open Tileset Dialog + + + + + + Open Tileset Dialog + + + + + +The available options: + +default (shown) +pirates +traditional + + + + + + + +Game +Open Background... + +Allows you to load a different background image. + +Load Background Dialog + + + + + + Load Background Dialog + + + + + +The available options: + +default (shown) +haze +pirates +slate +wood + + + + + + + +Game +Open Layout... + +Allows you to load a different tile layout. + +Load Layout Dialog + + + + + + Load Layout Dialog + + + + + +The available options: + +default (shown) +cross +pirates +pyramid +stax +tower +triangle + + + + + + + + +&Ctrl;S + +Game +Save + +Saves the current game. + + + + +Game +Save Theme... + +Saves the current theme. + + + + + +P + +Game +Pause + +Pauses or resumes the game. While the game is paused, all tiles are hidden. + + + + + +&Ctrl;H + +Game +Show Highscores + +Shows the highscore list. + +Show Highscores + + + + + + Show Highscores + + + + + +When your game scores exceed the top ten scores listed, you are prompted to enter your name. + + + + + + + + + +&Ctrl;Q + +Game +Quit + +Quits &kmahjongg;. + + + + + + + + +<guimenu>Edit</guimenu> Menu + + + + +Edit +Board Editor + +Opens the gameboard editor. + +Gameboard Editor + + + + + + Gameboard Editor + + + + + +The gameboard editor lets you create your own tile layouts for gameplay. + + + + + + + + + +<guimenu>Move</guimenu> Menu + + + + + + +&Ctrl;Z + +Move +Undo + +Undo your last move. + + + + + +&Ctrl;&Shift;Z + +Move +Redo + +Redo a move the was undone. + + + + + +H + +Move +Hint + +Gives a Hint by flashing a matching pair of tiles. + + + + + +D + +Move +Demo + +Starts the game Demo mode. + + + + +Move +Shuffle + +Shuffles the tiles on the board. + + + + + + + + +<guimenu>Settings</guimenu> Menu + + + + +SettingsShow/Hide Toolbar + +Shows or hides the Toolbar. + + + + +SettingsShow/Hide Statusbar + +Shows or hides the Statusbar. + + + + +SettingsShow/Hide Matching Tiles + +Shows or hides matching tiles by not flashing the matching free tiles when a tile is selected. + + + + +SettingsConfigure Shortcuts... + +Displays a standard &kde; shortcut configuration dialog to change the +keyboard shortcuts for &kmahjongg;. + + + + +SettingsConfigure Toolbars... + +Displays a standard &kde; dialog where you can configure the toolbar icons. + + + + +SettingsConfigure &kmahjongg;... + +Opens a dialog to configure &kmahjongg;. For details, +see here. + + + + + + + + + + +The <guimenu>Help</guimenu> Menu + + + + + + +&help.menu.documentation; + + + + + + + + + + + + +Credits and License + + +&kmahjongg; for KDE + + + +Program Copyright © 1997 &Mathias.Mueller; &Mathias.Mueller.mail; + + + +Contributors: + +&David.Black; &David.Black.mail; +&Michael.Haertjens; &Michael.Haertjens.mail; +&Osvaldo.Stark; &Osvaldo.Stark.mail; +&Benjamin.Meyer; &Benjamin.Meyer.mail; +&Albert.Astals.Cid; &Albert.Astals.Cid.mail; + + + + +Documentation Copyright © 2005 &John.Hayes; &John.Hayes.mail; + + + + +&underFDL; + +&underGPL; + + + + + diff --git a/doc/kmahjongg/layout.png b/doc/kmahjongg/layout.png new file mode 100644 index 00000000..38be51e4 Binary files /dev/null and b/doc/kmahjongg/layout.png differ diff --git a/doc/kmahjongg/numbered.png b/doc/kmahjongg/numbered.png new file mode 100644 index 00000000..142d402e Binary files /dev/null and b/doc/kmahjongg/numbered.png differ diff --git a/doc/kmahjongg/shortcuts.png b/doc/kmahjongg/shortcuts.png new file mode 100644 index 00000000..32a941cf Binary files /dev/null and b/doc/kmahjongg/shortcuts.png differ diff --git a/doc/kmahjongg/showremoved.png b/doc/kmahjongg/showremoved.png new file mode 100644 index 00000000..33b49957 Binary files /dev/null and b/doc/kmahjongg/showremoved.png differ diff --git a/doc/kmahjongg/theme.png b/doc/kmahjongg/theme.png new file mode 100644 index 00000000..693373ca Binary files /dev/null and b/doc/kmahjongg/theme.png differ diff --git a/doc/kmahjongg/tileset.png b/doc/kmahjongg/tileset.png new file mode 100644 index 00000000..b2ff3854 Binary files /dev/null and b/doc/kmahjongg/tileset.png differ diff --git a/doc/kmines/Makefile.am b/doc/kmines/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/kmines/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/kmines/index.docbook b/doc/kmines/index.docbook new file mode 100644 index 00000000..423a60a8 --- /dev/null +++ b/doc/kmines/index.docbook @@ -0,0 +1,617 @@ + + + + + +]> + + + + +The &kmines; Handbook + + + +Nicolas +Hadacek + +
&Nicolas.Hadacek.mail;
+
+
+ +Michael +McBride +
mmcbride@ematic.org
+
+ +Nicolas +Hadacek + +
&Nicolas.Hadacek.mail;
+
+Developer +
+ +Lauri +Watts +
&Lauri.Watts.mail;
+Reviewer +
+ +
+ + +2000 +&Nicolas.Hadacek;, Michael McBride + + +&FDLNotice; + +2005-12-28 +2.1.10 + +&kmines; is the &kde; version of the classic minesweeper +game. + + +KDE +game +minesweeper + + +
+ + +Introduction + +&kmines; is the classic Minesweeper game. You must uncover all the empty +squares without blowing up a mine. + +When you uncover a square, a number appears: it indicates how many mines +surround this square. If there is no number the neighboring squares are automatically +uncovered. When you find a square that you are sure contains a mine, it is +very useful to put a flag on it (by right-clicking). + + + + +How to Play + +You have to use the mouse and its three buttons to uncover or to flag the +squares (with two-buttoned mice, clicking the +middle button is generally achieved by +simultaneously pressing the left and the +right buttons). Here are the details: + + +The left button will +uncover a square. If there is a bomb below that square, the bomb will +explode, and the game will be over. +If there is not a bomb under the square, a number will appear. +That is the number of neighboring squares that contain bombs. +Which ones? you ask: That's the point of the game. +For each square (excluding edge and corner squares), there are 8 +neighboring squares. +If none of the neighboring squares has a bomb, then a blank will +be shown under the square, and all neighboring squares will be +automatically uncovered. +Left clicking a flagged square is +safe and does nothing. +The right button will mark +a square as containing a mine (by drawing a red flag on it) or, if the option is +set, as being uncertain (by drawing a question mark on it). The uncertain tag can +be useful when you are puzzled about the positions of +mines. +The middle button will +clear the surrounding squares if the right number of squares is +already flagged. It is very useful since it is much quicker than +uncovering all individual squares. +If your flags are not properly positioned, you will +explode a bomb. +Pressing the yellow smiley will start a new +game. + + +These are the default settings. The mouse buttons can be +reprogrammed. For more information, see the section entitled Game Options. + +The &kmines; Screen + + +The &kmines; Screen + + +The &kmines; Screen + + + +The &kmines; screen consists of: + + +A Box showing you the number of mines still left to be marked. +Every time you flag a bomb site, this number will decrease by one. +This box does not determine if you are right or wrong, but only how +many mines you need to theoretically mark before you are done with this +game. +In this example, there are 40 mines to be marked. +A Yellow Smiley Face. Clicking this will start a new game. If +you lose the game, it will become a frown. +The box on the right shows how much time this round has taken. +The shortest time for each level is listed as the high +score. +The playing area. This will vary in size depending on the +difficulty level of the game. In this example, it consists of 256 squares. This is where you play the game. + + + + + +Game Options +The options to &kmines; are set by selecting +Settings Configure &kmines; + from the menubar. This will bring up a dialog box. + + +&kmines; Preferences + + +&kmines; Preferences + + + +This dialog box is divided into three sections. + + + +Game + +The first option, labeled Enable ? mark, +determines whether you can mark squares as +questionable. If this option is checked, then right clicking the mouse will first cause a square to be +flagged. If you right click again on the same square, the flag will change to +a ?, to indicate you don't know if there is a bomb +there or not. If you click again, the square will change back to +blank. + +If there is not a mark in front of this option, the first +right mouse click will change the square to a +red flag. The second will make it blank again, thus skipping the ? +symbol. + +The next option down, labeled Enable +keyboard, determines if the keyboard shortcuts will work +while playing the game. The default keyboard shortcuts are given in , and can be changed by selecting +SettingsConfigure Shortcuts.... + +The only keyboard shortcuts affected are those +responsible for playing the game. Keyboard commands such as +New Game and +Quit, still work, regardless of this +option. + +The Pause if windows loses focus option will +automatically pause the game if the &kmines; window loses focus. When the game +is paused, the gameboard disappears, so you cannot use pausing the game as a way +to cheat! + +The "Magic" reveal option switches on Magic +reveal mode. In this mode, &kmines; does most of the gameplay for you: +If you have uncovered enough squares to be able to tell with certainty that a +square contains a mine, &kmines; will automatically mark it as a mine, and +perform an autoreveal on squares for which it has marked the correct number of +mines. Note that when you have this mode switched on, your scores do not count +as highscores. + + +The last section of this tab allows you to change the action of each of +the three mouse buttons. Your options are: + + + +Reveal +This will reveal the contents of the square. If there is no bomb, +then a number will appear. If there is a bomb under that square, then the bomb +will explode, and the game will be over. + + + +Autoreveal +Clicking on a square with this button will automatically reveal +all neighboring squares that are not marked with red flags. If one of those +neighboring squares has a bomb under it, the bomb will explode, and the game will +be over. + + + +Toggle Flag +Clicking with this button on a square will cycle through blank, +red flag, and question mark. + + + +Toggle ? Flag +Clicking with this button on a square will mark/unmark with a +question mark. + + + + + + +Appearance + +The top three color buttons determine the +Flag Color, Explosion Color +and Error Color. + +The rest of this page is concerned with mines colors. To adjust, +simply click the color button beside each description, and select the +new color. + + + + +Custom Game + +Finally, if you are not finding the existing configurations +challenging enough, you can set up a custom game here. Use the +sliders to change the Width, +Height, and the percentage of the board covered +with Mines. The default game has 15 percent of +the board covered, so there is plenty of room to make the game +harder. + + + + +Saving your changes +Once your changes are complete, click +OK to make them permanent. + +If you want to abandon your changes, click +Cancel to abort the changes. + +If you click on Defaults, the default +settings will be restored. Click OK to make +these default changes permanent, or edit the settings some +more. + + + + + + + +Commands/Keyboard Shortcuts + +The following sections briefly describe each menubar option. + + +<guimenu>Game</guimenu> Menu + +The Game menu consists of 4 options. + + + + + +&Ctrl;N + +Game +New +Starts a new game. + + + + + +P + +Game +Pause +Pauses or resumes the game. This will hide the board (no cheating), and +give you a button to press when you want to continue this +game. + + + + + +&Ctrl;H + +Game +Show Highscores +This shows you the high score (the shortest time) for each +difficulty level. + + + + + +&Ctrl;Q + +Game +Quit +Quits &kmines; + + + + +<guimenu>Move</guimenu> Menu + + + + + +H + +Move +Hint +If you use this item, you will get a hint about where +you should click next. If you do that your score will not be added to the high-scores. + + + + + +Move +Solve +This item tries to solve the current game. + + + + + +Move +Solving Rate... +This item shows a dialog that calculates the solving rate for the current game type. + + + + + +Move +View Log +This item shows a dialog with the log of the last game. + + + + + +Move +Replay Log +This item replays the current log. + + + + + +Move +Save Log... +This item shows a dialog to save the current log. + + + + + +Move +Load Log... +This item shows a dialog to load a log. + + + + + + +<guimenu>View</guimenu> Menu + + + + + +&Ctrl;+ + +View +Zoom In +Increase the size of the gameplay canvas. + + + + + + +&Ctrl;- + +View +Zoom Out +Decrease the size of the gameplay canvas. + + + + + + +<guimenu>Settings</guimenu> Menu + + + +Settings +Show Menubar +If there is a check in front of this option, then the menubar +will be visible. Selecting this option, hides the menubar. +To restore a hidden menubar, click outside the game play area, with +the right mouse button. This will bring up a small sub-menu. Simply select +Show Menubar from this sub-menu, and the menubar +will be visible again. + + + + +Settings +Choose Game Type + + +Lets you set the difficulty level from a sub-menu. +There are three default levels of difficulty: +Easy (64 squares, 10 mines), +Normal (256 squares, 40 mines), and +Expert (480 squares, 99 mines). There is also a +Custom... level settings. +If you select Custom..., then the +settings you have configured in the Configure +&kmines; dialog will be used. + + + +SettingsConfigure +Shortcuts... +This item lets you change the keyboard shortcuts used by &kmines;. + + + +SettingsConfigure +Notifications... +This item displays a standard &kde; notifications configuration +dialog, where you can change the notifications (sounds, visible messages, +&etc;) used by &kmines;. + + + +SettingsConfigure +Highscores... +On the Main tab you can change your +Nickname and add a +cute Comment about yourself, to show in the high +score table. You can also activate the World-wide highscores +enabled checkbox, and share your skill at &kmines; across +the world with an online highscore server. If you are connected to the +Internet, &kmines; will send your score automatically at the end of the +game to the highscore web server (kmines.sf.net). +The Advanced tab displays your Registration Data +on kmines.sf.net: Your Nickname: from the Main +tab and the Key:. This key was generated when you registered on kmines.sf.net by +selecting World-wide highscores enabled the first time. +The registration key is used in conjunction with the +nickname to identify uniquely users, but users cannot have the same nickname. +Click on the Remove button to delete you from the world highscores +list. + + + + +Settings +Configure &kmines;... +Allows you to adjust game options. For more detailed +information, see the section entitled Game +Options. + + + + + + +<guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + +Default Keyboard Shortcuts +The following table shows you the default keyboard shortcuts. + + + +Keyboard Shortcuts + +Key ComboAction + +ArrowsMove cursor in playing area. +PageDownMove to bottom +edge +PageUpMove to top +edge +HomeMove to left edge +EndMove to right edge +SpaceReveal square. +WMark square as mine +ReturnAutomatically reveal all +surrounding squares not marked by a flag. +&Ctrl;NNew Game +PPause Game +&Ctrl;QQuit &kmines; +F1&kmines; Handbook +&Shift;F1What's This Help +&Ctrl;HShow Highscores +&Ctrl;MShow Menubar +&Ctrl;+Zoom In +&Ctrl;-Zoom Out + + + +
+ +These shortcuts can be changed by selecting +Settings Configure +Shortcuts... from the menubar. + +
+ +
+ +Credits and License + + +&kmines; + + +Program copyright 1996-2000 &Nicolas.Hadacek; &Nicolas.Hadacek.mail; +Documentation copyright 2000 &Nicolas.Hadacek; &Nicolas.Hadacek.mail; +Documentation updated for &kde; 2.0 by &Mike.McBride; +&Mike.McBride.mail; +Some changes for &kde; 3.2 by &Philip.Rodrigues; &Philip.Rodrigues.mail; + + +&underFDL; +&underGPL; + + + + +Installation + + +How to obtain &kmines; + +&install.intro.documentation; + + + + +Compilation and Installation + +&install.compile.documentation; + + + + + +
+ + + diff --git a/doc/kmines/kmines1.png b/doc/kmines/kmines1.png new file mode 100644 index 00000000..d872f735 Binary files /dev/null and b/doc/kmines/kmines1.png differ diff --git a/doc/kmines/kmines2.png b/doc/kmines/kmines2.png new file mode 100644 index 00000000..961acdbd Binary files /dev/null and b/doc/kmines/kmines2.png differ diff --git a/doc/kolf/Makefile.am b/doc/kolf/Makefile.am new file mode 100644 index 00000000..171f575c --- /dev/null +++ b/doc/kolf/Makefile.am @@ -0,0 +1,2 @@ +KDE_LANG = en +KDE_DOCS = AUTO diff --git a/doc/kolf/index.docbook b/doc/kolf/index.docbook new file mode 100644 index 00000000..9da81ae3 --- /dev/null +++ b/doc/kolf/index.docbook @@ -0,0 +1,833 @@ + + + + + +]> + + + + +The &kolf; Handbook + + + +Jason +Katz-Brown + +
jasonkb@mit.edu
+
+
+ +
+ +&FDLNotice; + +2005-12-18 +1.1 + + + +&kolf; is a miniature golf game for &kde;. + + + + + +KDE +golf +miniature +arcade + + +
+ + Introduction +&kolf; is a miniature golf game with block graphics and a 2d top-down view. Courses are dynamic, and up to 10 people can play at once in competition. +&kolf; comes with a tutorial course. Playing this course is the easiest way to get started. + + + + +Tutorial + +&kolf;'s game-play is simple. It is easiest to learn how to play hands-on, and thus a tutorial course is included to guide you through the basics. This chapter will guide you through this tutorial. To start the tutorial, choose HelpTutorial. + + +Hitting the Ball +To aim the ball, either line up the mouse behind the ball so the +putter aims in the direction you want to hit it or press one of the +Left Arrow (counterclockwise) or Right Arrow (clockwise) keys, to rotate the putter. + +To rotate the putter more quickly with the keyboard, hold down Shift while pressing or holding the arrow keys. To rotate more slowly, hold down Control. + +To hit the ball, press and hold either the &LMB; or Down +Arrow key. The longer you hold down the key or button, the +more strength the putt will have. It will take practice to get a good +feel for speed. + +To stop abort the putt while you are still holding moving the putter back in your stroke, press Escape. + + +Hit the ball into the hole (gray circle) to move on the next. + + + +Slopes +Slopes are areas of slanted down that the golf ball rolls down in an expected manner. The light of the golf course is from the upper-left, so the brightest slopes will push the ball towards the upper-left corner of the hole. Choose HoleShow Info to see an arrow that points in the direction the slope will roll the ball. +On this tutorial hole, hit the ball onto the upward-pointing +slope and let it roll into the cup to move onto the next hole. + + Directions +On the next hole, experiment with the other kinds of slopes. There are elliptical, vertical, horizontal, and diagonal slopes in &kolf;. Hit the ball into the hole. + + Steepness +The next tutorial hole has two slopes of differing steepness. If you choose HoleShow Info, the steepness of the slope will appear as a number from 1-8, where 8 is steepest and 1 is shallowest. Also, the more extreme shading the slope has, the steeper it is. Hit the ball into the hole and continue to the next hole. + + + + Walls +Balls bounce of walls as one would expect them to, with a little bit of dampening. Deflect the ball off the walls and into the cup to advance to the next hole. + + + Puddles and Sand + Puddles +Puddles are blue. When your ball rolls into a puddle, a penalty stroke is added to your score and your ball is placed outside the puddle. + + Sand +Sand is yellow. Balls roll through sand very slowly. +Hit the ball around the puddles and through the sand into the hole to continue to the next tutorial hole. + + + + Windmills +Windmills are compound objects with brown border walls and a moving arm at either the top or bottom of the object. Half-walls create the opening to the windmill, which a black arm swings across. It takes practice to be able to time stroke of the ball to pass through the opening. Hit the ball through the windmill into the hole to continue. + + + Black Holes +When you hit your ball into a black hole, it is transported to the exit and ejected at the angle of the exit at a speed directly relational to the speed your ball was going. Choose HoleShow Info to see which Black Hole goes to which exit and which direction the ball will come out of the exit. The rim around Black Holes and their corresponding exits are also the same color. Hit the ball into the black hole, which will then eject the ball into the cup so you can go to the next hole. + + + Floaters +Floaters are moving platforms that carry a ball that lands on it. Floaters' speeds vary. This one is a pretty slow floater, but you still must time your shot so you can go over the wall and into the cup. + + + Bridges +Bridges are simple - they just lift the ball above the things below them, like slopes, sand, and puddles. They can have brown walls on their perimeter. + + + + Basics +Here's in-depth description of &kolf;'s basic features. + + +Starting a Game + +To start a game, choose +GameNew. +This opens a dialog where you can configure your new game. + + +More third-party &kolf; courses are available at the &kolf; website. See the &kolf; user-uploaded courses page. + + + +<guilabel>Players</guilabel> Page +To add a player to your game, click the New +Player button. + +The text boxes in the grass area are the names of the players in +this game. To change the name of the players, change the text in the +text boxes. To change the ball color of the players' balls, click the +middle button that has a color rectangle, and a dialog will open where +you can select the new ball color. The rectangle on the button is the +current player's ball color. + +To remove a player, click the Remove +button next to the player's name. + + + + <guilabel>Course</guilabel> Page +To choose the course that you want to play, click it's entry in the list box on the left of this page. &kolf; comes with many built-in courses that you can play. +To add a course that you have downloaded or made, click on the Add... button on the bottom of this page, and choose the course file in the file dialog. + + <guilabel>Options</guilabel> Page +On this page you can choose whether or not this game will be played in Strict mode. In strict mode, there is no undo, moving holes, or editing of the course. This mode is generally for competition. Only in strict mode are high scores kept. + + + + Saved Games +&kolf; can keep a history of your rounds that are still in progress and save them to disk so you can load them and play them later. Saved games include the current hole, names and colors of all players in game, and the scores of the players. + Saving Your Game +To save your game while still playing, choose GameSave Game +. If you have not saved a game on this game before, you will be prompted for the file to save to. + + Loading Your Game +To load the game that you saved, choose GameLoad Saved Game... +. You can then choose the saved game to open. + + + + Practicing +There are a few features that make practicing your golf easier. These features are not enabled during strict mode. + Undo +To undo a bad shot, choose HoleUndo Shot. + + Switching Holes +The Go menu contains many menu items for switching to different holes that are quite self explanatory. See the Go menu reference. + + + + Putting Options +&kolf; has a few ways to putt. The basic putting method is described above. +The mouse is enabled by default. This means that whenever the mouse is moved, the putter aligns to the line between the mouse and ball. +If you prefer to only use the keyboard and arrow keys, you can disable mouse putting by unchecking SettingsEnable Mouse for Moving Putter. + + Advanced Putting +Advanced putting can be enabled by checking SettingsEnable Advanced Putting. This putting mode is slightly more challenging than the regular click-and-hold mode, and can be used with either the mouse or keyboard. + +To putt in this mode: + + +Click or press the Down Arrow key once to begin the swing. The putting indicator will appear, and the strength gauge will start filling up with a color, which changes as the gauge fills up. + + +Click or press the Down Arrow key when the strength gauge has filled up the amount that you want. The farther around the circle the strength gauge has filled up, the stronger the putt will be. It takes practice to be able to get the correct strength on putts. +After this, the color in the putting indicator will start to wind down back to where it started. + + +Click or press the Down Arrow key again when the strength-indicator is as close as possible to the center line of the putting indicator. + + +The closer the final click (or key press) is, the more true the putt is to the original line. + + + + + Editing Courses +&kolf; comes with a versatile editor for its course file format. Courses can have any number of holes, and there is no limiting the creativity of the course designer. + + Creating a New Course +To create a new course, choose GameNew. Go to the Course page of the dialog, and choose the Create New course. Make sure that, under the Options page of the dialog, Strict mode is unchecked. + + +To play this new course later, click the Add... button to add the course to your list of courses. + + + + Adding Holes +Choose HoleNew to add a new hole at the end of the course. + +You can restore a hole to a blank state, similar to when it was first created, by choosing HoleClear. + +Two items will appear on new holes: a cup, and a white ball. The white ball marks where players' balls start the hole. + + + Editing Basics +The basic building block of &kolf; courses is the object. Holes are made up of a bunch of objects. Objects can be moved to any location, and the different kinds of objects each have different properties you can set. +To start editing a hole, toggle the HoleEdit menu item, or press the pencil icon on the toolbar. To leave editing mode, uncheck this menu item (or the toolbar icon). + + Adding Objects +To add a new object to the course, choose it's name in the list box labeled Add object:. The object will appear in the center of the course. + +Bridges and signs will cover up new objects if they were already in the center of the hole. + + +You can add more than one cup to a hole! Doing so can create interesting hole designs. + +Some &kolf; objects are available as plugins. If you add these objects to your course, course players will need to download the plugin. +Third-party &kolf; plugins are available at the &kolf; website. See the &kolf; plugins download page. + + + Moving and Resizing Objects +Move your mouse cursor over the object that you want to move, and the mouse cursor will change to a hand cursor. Then click and drag the object to where you want it. +Puddles, sand, floaters, bridges, and signs can be resized by dragging the small circle at their lower-right corner. + + +Walls can be moved by clicking and dragging their endpoints, or you can move the whole wall by clicking and dragging on the middle part of the wall as normal. + + + + Objects' Settings +Objects' properties can be changed in the area at the lower-right corner of the &kolf; window. To edit an object, move your mouse cursor over it until the cursor changes to the hand cursor. Then click. Various controls for that object will appear in the lower-right corner of the &kolf; window. + General Hole Settings +To edit general hole settings, click an area of the hole that is not covered by an object. When this is possible, the cursor will be the normal pointer cursor. + + + Course name: + +This is the name that is shown in the new game dialog and in the About Course dialog. + + + Course author: + +This is the author name that is shown in the About Course dialog. + + + Par: + +The par of the hole. + + + Maximum: + +The maximum number of strokes a player can take on the hole. + + + Show border walls + +Whether or not to show walls around the border of the hole. + + + + + + Slopes + + + Type + +Choose the type of the slope - Vertical, Horizontal, Diagonal, Opposite Diagonal, Circular. Diagonal and opposite diagonal slopes are triangles, while circular slopes are circles. Vertical and horizontal slopes are rectangles of course. + + + Reverse direction + +Whether or not to reverse the slant of the slope. For example, consider an elliptical slope (a circular slope). When it is not reversed, it pushes balls outward like a mound. When it is reversed, it sucks balls inward like a hole. + + + Grade: + +Choose the grade (steepness) of the slope by moving the slider so the grade moves towards 0 or 8, where 8 is steepest and 0 is flat. Steeper slopes push the ball more. + + + Unmovable + +Whether or not this can be moved by other objects, like floaters. + +If a floater overlaps with this object at any point on this path, and the slope is large and thus won't move the floater, you must make the slope Unmovable, or &kolf; will suffer poor performance! + + + + + + + Puddles and Sand +Puddles and Sand have the same configuration options as each other. + + + Enable show/hide + +Checking this enables the flashing of this puddle or sand. + + + Show/Hide speed + +The more towards Fast the slider is, the faster the puddle or sand flashes. + + + + + + Bridges, Windmills, Floaters, and Signs +Bridges, windmills, floaters, and signs all have similar settings for configuring on which sides there are border walls. To show the wall on a side, check the check box for that side. +There are also some more specific options for windmills, floaters, and signs. + + Windmills + + Windmill on bottom + +Whether or not the black windmill arm is on bottom or top - the default is that it is on the bottom. The two half-walls will always be with the arm. Note that you cannot enable a border wall on the side that the arm is. + + + Speed + +The more towards Fast the slider is, the faster the black windmill arm moves. + + + + + + Floaters +The path the floater moves along is shown by the wall it's connected to. + + Moving speed + +The more towards Fast the slider is, the faster the floater moves. If the slider is all the way at the Slow end, the floater will stop. + + + + + + Signs + + Sign HTML: + +You can enter any valid &HTML; here. For example, entering Hit it <i>softly</i> towards the slope! will create the text Hit it softly towards the slope!. + + + + + + + + + + + +Command and Menu Reference + + +The <guimenu>Game</guimenu> Menu + + + + +&Ctrl;N + +Game +New... + + +Start a new game + + + + + +&Ctrl;O + +Game +Load Saved Game... + + +Load a previously saved game. + + + + + +&Ctrl;S + +Game +Save Course + + +Save the current course using it's current file +name. + + + + + +GameSave Course As... + + +Open a file dialog allowing you to save the current +course using a new file name. + + + + + +GameSave Game + + +Save the current game with the last saved game filename, or under a filename you specify if you have not saved the game yet. + + + + + +Game +Save Game As... + + +Opens a file dialog allowing you to save the current +game with a filename of your choice. + + + + + +&Ctrl;End + +Game +End Game + + +End the current game, without closing &kolf; + + + + + +&Ctrl;H + +Game +Show Highscores + + +Display the high score table. + + + + + +&Ctrl;P + +GamePrint... + + +Print the current screen. + + + + + +GameAbout Course + + +Show name, author, and par of course. + + + + + +&Ctrl;Q + +GameQuit + +Quit &kolf; + + + + + + +The <guimenu>Hole</guimenu> Menu + + + + +&Ctrl;E + +Hole +Edit + + +Switch to editing mode to edit the current hole. + + + + + +&Ctrl;&Shift;N + +Hole +New + + +Create a new hole. (Only while editing.) + + + + + +&Ctrl;Delete + +Hole +Clear + + +Remove all objects from the current +hole. (Only while editing.) + + + + + +&Ctrl;R +Hole +Reset + + +Reset the current hole to it's starting +position, so that you can start over. + + + + + +&Ctrl;I + +Hole +Show Info + + +Show information about objects on the current hole. + + + + + +&Ctrl;Z + +Hole +Undo Shot + + +Undo the last shot you made. + + + + + + + +The <guimenu>Go</guimenu> Menu + + + + +Go +Switch to Hole + + +Go directly to another hole within the +course. A list of the hole numbers allows you to choose +which hole you wish to go to. + + + + + +&Alt;Right Arrow +Go +Next Hole + + +Go the the next hole. + + + + + +&Alt;Left Arrow +Go +Previous Hole + + +Go to the previous hole. + + + + + +&Ctrl;Home +Go +First Hole + + +Go to the first hole in the course. + + + + + + +&Ctrl;&Shift;End + + +Go +Last Hole + + + +Go to the last hole in the course. + + + + + +Go +Random Hole + + +Go to a random hole from the current course. + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +Settings +Show Toolbar + + +Toggle on and off the main toolbar. + + + + + +Settings +Show Statusbar + + +Toggle on and off the statusbar. + + + + + +Settings +Enable Mouse for Moving Putter + + +Allow the use of the mouse to move the +putter. + + + + + +Settings +Enable Advanced Putting + +Enable the advanced putting mode, as +described in its own +section. + + + + + +Settings +Show Putter Guideline + + +Toggle on and off the putter guideline. + + + + + +Settings +Play Sounds + + +Toggle on and off the sound effects. + + + + + +Settings +Enable All Dialog Boxes + + +Re-enables any dialog boxes that you disabled by clicking on +Do not show this message again or similar. + + + + + +Settings +Reload Plugins + + +Reload plugins. + + + + + +Settings +Show Plugins + + +Display the currently loaded plugins. + + + + + +Settings +Configure Shortcuts... + + +Customize the keyboard shortcuts. + + + + + +Settings +Configure Toolbars... + + +Open a dialog where you can configure the toolbars for &kolf;. + + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + +Credits and Licenses + +&kolf; copyright 2001, 2002 &Jason.Katz-Brown; + + +Developers + +&Jason.Katz-Brown; jasonkb@mit.edu + + +Niklas Knutsson + + +Ryan Cumming + + +Daniel Matza-Brown + + + +Documentation copyright 2002, &Jason.Katz-Brown; + + + +&underFDL; +&underGPL; + + + + +Installation + +&install.intro.documentation; +&install.compile.documentation; + + + +&documentation.index; + +
diff --git a/doc/konquest/Makefile.am b/doc/konquest/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/konquest/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/konquest/index.docbook b/doc/konquest/index.docbook new file mode 100644 index 00000000..34a70b66 --- /dev/null +++ b/doc/konquest/index.docbook @@ -0,0 +1,295 @@ + + + + + +]> + + + + +The &konquest; Handbook + + + +Nicholas +Robbins + +
&Nicholas.Robbins.mail;
+
+
+ +
+ + +Russ +Steffen + +
&Russ.Steffen.mail;
+Developer +
+ + +Lauri +Watts + +
&Lauri.Watts.mail;
+
+Reviewer +
+ + +2000 +Nicholas Robbins + + +&FDLNotice; + +2005-12-16 +1.1 + +&konquest; is a wonderfully complex game for universal +domination, or konquest, if you like. :) + + +KDE +Konquest +kdegames + + +
+ + +Introduction + +This the &kde; version of Gnu-Lactic +&konquest;, a multi-player strategy game. The goal of the +game is to expand your interstellar empire across the galaxy and of +course, crush your rivals in the process. + + + + +Using &konquest; + + +More &konquest; features + +A really entertaining game of universal domination. + + + + + +Configuration + +When you start a &konquest; game, a settings window pops up. +From here you need to enter player names, decide the number of +planets, and how many turns are allowed. (Don't worry, you can add +more later) You can also reject the map, until a you find a galaxy +layout you like. After this, just click OK +to start the game. + + + + + + +Command Reference + + +The Main &konquest; Window + +The Planet Status indicator on the right is +your source for intelligence on the state of the Galaxy. Your spies +will report on the status of your rivals, but not on the +native-controlled planets. The information present in the display is: +Planet name, Owner, +Production rate, fleet strength +(Ships and Kill +percent). + +The Production rate is the number of ships +that planet will construct in one turn. The fleet strength is the +number of Ships currently in place at the +planet. And, the Kill percent is a measure of the +effectiveness of the ships produced at that planet. Attack fleets take +the kill percentage of their planet of departure, and defense fleets +use the kill percentage of the planet they are defending. + + +The <guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New + +Starts a new game. + + + + + +&Ctrl;End + +Game +End Game + +End the current game, without closing &konquest; + + + + +Game +Measure Distance + +Click with the &LMB; on two planets to see their distance. + + + + +Game +Show Standings + +Opens a window to display the detailed current +standings for all players + + + + +Game +Fleet Overview + +Opens a window to display detailed informations +for all fleets + + + + + +&Ctrl;Q + +Game +Quit + +Quits the game. + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +Settings +Show Toolbar + + +Toggle the toolbar display on and off. + + + + + +Settings +Configure Shortcuts... + + +Opens a standard &kde; shortcut configuration dialog to +change the keyboard shortcuts used by &konquest;. + + + + + +Settings +Configure Toolbars... + + +Brings up the standard &kde; toolbar configuration dialog to customize the &konquest; toolbar icons. + + + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + + + + +Credits and License + +&konquest; + +&GNU;-Lactic Conquest Project, &kde; version by &Russ.Steffen;, +&Russ.Steffen.mail;. + +Copyright © 1998 by the &GNU;-Lactic Conquest Project + +Documentation copyright 2000 Nicholas Robbins +&Nicholas.Robbins.mail; + + + +&underFDL; + +&underGPL; + + + + +Installation + + +How to obtain &konquest; + +&install.intro.documentation; + + + + +Requirements + +In order to successfully use &konquest;, you need &kde; 3.0. It +is also reccomended that you have your X server set to over 8bpp, as +the plants are scanned and require a rather high color depth. + + + + +Compilation and Installation + +&install.compile.documentation; + + + + + +&documentation.index; + +
+ + diff --git a/doc/kpat/Makefile.am b/doc/kpat/Makefile.am new file mode 100644 index 00000000..60062a5e --- /dev/null +++ b/doc/kpat/Makefile.am @@ -0,0 +1,5 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + +KDE_MANS = AUTO diff --git a/doc/kpat/clubs.png b/doc/kpat/clubs.png new file mode 100644 index 00000000..55a6f6ed Binary files /dev/null and b/doc/kpat/clubs.png differ diff --git a/doc/kpat/diamonds.png b/doc/kpat/diamonds.png new file mode 100644 index 00000000..b57215a8 Binary files /dev/null and b/doc/kpat/diamonds.png differ diff --git a/doc/kpat/hearts.png b/doc/kpat/hearts.png new file mode 100644 index 00000000..9769cd2a Binary files /dev/null and b/doc/kpat/hearts.png differ diff --git a/doc/kpat/index.docbook b/doc/kpat/index.docbook new file mode 100644 index 00000000..3fcda113 --- /dev/null +++ b/doc/kpat/index.docbook @@ -0,0 +1,811 @@ + + + + + +]> + + + +The &kpatience; Handbook + + + +Paul Olav +Tvete + + +Maren +Pakura + + +Stephan +Kulow + + + +MikeMcBride +Reviewer +
mpmcbride7@yahoo.com
+
+ + +Paul Olav +Tvete +Developer + + + +Stephan +Kulow +Developer + +
+ + + + +2000 +Paul Olav Tvete + + +20012004 +Maren Pakura + + +&FDLNotice; + +2004-02-07 +3.2 + + +&kpatience; is a highly addictive card game for &kde; + + + + +KDE +game +patience +solitaire + +
+ + +Introduction + + +To play a patience you need, as the name suggests, patience. For simple games, +where the way the game goes depends only on how the cards fall, your patience is +even the only thing you need. + + + +But there are also patience where you also need to plan your strategy and think +ahead to win. + + + +All the games have in common that you have to put the cards in a special order +in moving, turning and reordering them. + + + + + +Rules + +suits +A card game contains one or more card decks of 52 cards each. Each deck contains +the four suits: + + + + + + + + + + + + + + + + + +Clubs + + + + + + + + + + + + +Spades + + + + + + + + + + + + +Hearts + + + + + + + + + + + + +Diamonds + + + + + + + + +Each of these suits contains itself the following cards types: ace, two, three, +four, five, six, seven, eight, nine, ten, jack, queen and king. + + +family +This complete order of cards is called a family. There are real families and +alternating ones. The real families are all of one suit (⪚ Hearts) while the +alternating families the card color changes from one to another between a black +color (Clubs and Spades) and a red color (Hearts and Diamonds). + + + +If the family is not complete, it's called a sequence. + + + + + +The game field of &kpatience; + + + + +There are only a few different elements in typical patience game. These should +be introduced in the following. + + + +1. Talon + +talon +In the beginning of each game all cards are mixed in the deck. But not in every +game all cards are dealt out, but some reside in the deck. These cards are put +down on the so called talon, which you can find quite easy as it is in most +games the only pile showing the reverse. + + + +If no card shows the reverse side, all cards are dealt out and there is no +talon. These card games are the hardest as there is no randomness involved after +the start of the game, so there is nothing to blame + + + + + +2. Waste Pile + +waste pile +Many games put cards from the talon first on a waste pile when you click on the +reverse of the top card on the talon. + + + +From there you can take the card (which is then face up) and put +it into the game. + + + + + + +3. Foundation + +foundation +The foundation piles is where you want to have your cards in the end. If all +cards end up there in the right order, you've won. + + + +In most card games these piles are empty in the beginning, but &kpatience; drops +cards there when it sees them fitting to save you this most often boring part. + + + + + +4. Playing Piles + +playing piles +These piles are the ones where the actual reordering happens. The rules for +these piles vary a lot between the games. Some show all cards on them, some +not. Some allow only card to be removed, some allow every card to be removed, +etc. See the following chapters for details. + + + + + + +General Use + + +Use of the menu is too easy to be described now. + + + + + +Klondike + + +Klondike +Klondike is the most famous patience - most likely because an often used +operating system comes with it. It is played with one deck. + + + +Goal of Klondike is to put all cards as real families ascending on the +foundation. This works usually pretty well as soon as all cards are lying face +up in the playing piles. + + + +The sequences on the playing piles have to be put there descending while the cards +should alternate in colors (red and black). You can move whole sequences or +parts of it if the first card fits on another pile. + + + +On a free pile you can put a king of any color or a sequence starting with a +king. + + + +When you click on the talon, one card from will be moved to the waste pile, +from where you can move it to the playing piles or the foundation. +Is the talon empty, you can move the complete waste pile on the talon +in clicking on the empty talon. + + + +You can look through the cards on the talon without any limit, but you should +learn fast that you've lost if you can't find one card to move and that a new +shift doesn't help you in finding new solutions. + + + + +Grandfather + + +Grandfather +This game has been introduced to Paul by his grandfather, so it got this +name. There aren't any other patience games known implementing this patience +game. + + + +Grandfather deals one deck to seven playing piles while some cards on each pile +are face down on initial deal. + + + +The goal is to put all cards as real families ascending on the foundations. + + + +You can move every card on every pile if it fits on another card to build a real +sequence with descending order. For example you can move the five of spades on +top of the six of spades, no matter how many cards on are on top of the five of +spades. Just the six of spades has to be on top of its pile. + + + +On a free pile you can place a king (again no matter how many cards are on top of +it) + + + +If no more cards can be moved, you can re-deal up to two times. All cards already +in the foundations stay there, but the cards in the playing piles are reordered +to give you a new chance to find a solution. + + + +Even though the rules are simple and allow quite some moves, the game is still +hard to win, but is still a joy to play (or because of that). + + + + +Aces Up + + +Aces Up +This patience has very simple rules, but is still hard to win. It is played +with one deck and the goal is to put all cards beside the aces to the +foundation. After that there should be an ace on every playing pile left. + + + +Each top card that is of the same suit (⪚ spades) and has a lower +value than another top card (⪚ six of spades and four of spades) can be +put on the foundation by clicking on it. + + + +If you can't move any more cards to the foundation, you can get a new card for +each playing pile in clicking on the talon. + + + +On a free pile you can move every other card on top of a pile. You should choose +these moves to free piles the way that new cards can be moved to the foundation +after that. + + + +The auto drop feature is disabled for this patience. + + + + + +Freecell + + +Freecell +Freecell is playing with one card deck. You have four free cells in the left top +corner. Beside that there are the four foundation piles and below it there are +eight playing piles. + + + +The goal of the game is to have all cards as real families ascending on the +foundation. This works quite often if you know how to play as Freecell is +solvable at a rate of 99.9% approximately (of the first 32000 deals there is only +one unsolvable - 11982 if you want to know). + + + +In the playing piles you have to build descending sequences, where red and black +cards alternate. In a free cell you can put any card. + + + +You can only move one single card that lays on top of a pile or a free cell. +Sequences can only be moved if you have enough free room (free cells or free +playing piles) to put the cards in between you move. + + + +The moving of sequences will be supported by &kpatience; if there is enough room. The +maximum of cards you can move is calculated by: + + +(#{free cells} + 1) * 2#{free piles} + + + +To solve this game it is recommended to grab the cards out of the playing +sequences in the same order they have to be put into the foundation (first the +aces, then the twos, &etc;) + + + +You should try to keep as many free cells and/or playing piles empty, so you can +build as long sequences as possible. + + + + + +Napoleon's Tomb + +Napoleon's Tomb +Napoleon's Tomb is played with one deck. Goal is to put all cards on the +foundations as ascending families without caring about the card colors or suits. + + + +The foundation is made out of five piles, that are put as an X. On the four +corners of this X you have to build sequences starting with seven and ending +with king. + + + +The pile in the middle takes four times the sequence from six down to ace. The +color doesn't matter for all five piles. + + + +The other four piles on the sides of the pile in the middle (or in between the X +- however you want to put it) can take one card each to place cards temporarily. +You can go through the talon only once. + + + +[auto drop] is disabled for this patience. + + + + +Mod3 + + +Mod3 +Mod-3 is played with two card decks. Goal of the game is to put all +cards on the top three rows. In those you have to build sequences of the same +color. In the first row you have to create the sequence 2-5-8-B, in the second +row the sequence 3-6-9-D and in the third row the sequence 4-7-10-K. The suit of +the cards has to be the same in each sequence, so you can put a five of hearts +only on top of a two of hearts. + + + +The fourth row is waste pile and playing pile at once. On an empty slot you can +put any card from the first three rows or one of top of the fourth row. + + + +Aces you can put on the aces piles on top of the talon. They are in the game +so you have a starting point for creating free slots. + + + +If you can't move any more cards, you can get new cards on the fourth row in +clicking on the talon. + + + +[auto drop] is disabled for this patience. + + + + +Calculation + + +Calculation +Calculation is surely a patience you have to get used to. It is played with one +card deck and the goal of the game is to put all cards in the four foundation +piles as ascending families without caring the color of the cards. + + + +For the foundation you have to follow these orders: + + +
+ + + + + +1st pile +A-2-3-4-5-6-7-8-9-10-J-Q-K + + + +2nd pile +2-4-6-8-10-Q-A-3-5-7-9-J-K + + + +3rd pile +3-6-9-Q-2-5-8-J-A-4-7-10-K + + + +4th pile +4-8-Q-3-7-J-2-6-10-A-5-9-K + + + + +
+ + +The card on top of the talon can be put on any of the four playing piles. +You should do this, so you can put following cards easier on the foundation. + +
+ + +Gypsy + +Gypsy +Gypsy is played with two card decks. Goal of the game is to put all cards in +real families ascending on the foundation. + + + +The playing piles have to be descending while red and black cards have to +alternate. You can only move sequences or single cards. On a free slot you can +put any card or sequence. + + + +If you can't move any more cards, you can click on the talon to get a new bunch +of cards on each playing pile. + + + +In using the undo feature you can ease the game quite a lot as you have to take +quite some decisions and some of them might turn out to be wrong after you +clicked the talon. + + + + + +Forty And Eight + + +Forty And Eight +Forty and Eight is played with two card decks. Goal of the game is to put all +cards as real families on the foundation. + + + +The playing piles have to be descending while you have to care about colors. So +you can only put a five of hearts on a six of hearts. + + + +You can only move one single card on top of a pile. In a free slot you can put +any card. + + + +In clicking on the talon you can put a card on the waste pile, from where you +can put it on a playing pile or the foundation (this &kpatience; will do for you). +Is the talon empty you can put all cards on the waste pile back on the talon. +This works only once, after the second time the talon empties, the game is over. + + + +This patience isn't really easy to solve, but with some experience, you can +solve many deals, especially if you use the undo feature from time to time to +correct your decisions, and the decisions &kpatience; does in putting cards on the +foundation. + + + + +Simple Simon + + +Simple Simon +Simple Simon is played with one card deck. Goal of the game is to put all cards +as real families on the foundation. + + + +In the playing piles you can build sequences. In general you don't have to care +about the suits of the cards, but sequences can only be moved if they are part +of a real sequence. (⪚ you can move the six of spades only if on top of it is +the five of spades and not move it if on top of it is the five of clubs). + + + +The cards can only be moved to the foundation if all 13 cards of one family lay +on top of each other in the playing piles. + + + +Suggestion + +You should try as soon as possible to move away the cards on the right piles to +get free piles to place cards temporarily as you can put on those any card. + + + +With enough free room you can build families on free slots independently of the +color. If you have all cards in such families you can sort them after their +color, so they can be moved to the foundation. + + + + + + +Yukon + + +Yukon +Yukon is played with one card deck. Goal of the game is to put all cards as real +families ascending on the foundation. + + + +The sequences on the playing piles have to be descending with alternating red and +black cards. You can move every face up card no matter how many cards are on top +of it. So you can put a five of hearts on a six of spades if this one is on top +of its pile. + + + +In a free slot you can put a king of any color (again, no matter how many cards are on top +of it) + + + + +Grandfather's Clock + + +Grandfathers Clock +Grandfather's clock is a simple patience and after some experiments you should +be able to solve most deals. It is played with one card deck and goal of the +game is put the cards as real ascending sequences on the foundation. + + + +The foundation is on the right side and consists of 12 piles that form the shape +of a clock. The ace is on one o'clock, jack is on 11 o'clock and queen on 12 +o'clock. + + + +There are 8 playing piles beside the clock and on each are 5 cards. On the +playing piles you can build descending sequences without caring on the color of the +cards. You can only move one single card at a time. + + +[auto drop] is disabled for this patience. + + + + +Kings + + + +Kings +The patience Kings is played with two card decks. The cards are dealt in a way +that below each playing pile is a king. The cards between two kings are put on +of them (the first cards are obviously put on the first king). This way you can +get piles with very different lengths. + + + +Goal of the game is to put all cards as real families ascending on the +foundation (on the right side of the playing piles). + + + +The sequences on the playing piles have to be descending while red and black +cards have to alternate. Several cards can only be moved when they are part of +an alternating sequence. + + + +In the 8 free cells on the top of the playing field you can put a single card +temporarily. On a free playing pile you can put any card or sequence. + + + + + +Spider + + +Spider +Spider is played with two card decks. The cards are dealt out into 10 playing +piles, 4 of 6 cards and 6 of 5 cards each. This leaves 50 cards that can be +dealt out 10 at a time, one on each playing pile. + + + +In the playing piles, a card can be placed on another card of any suit and +of one higher value. A sequence of ascending cards of the same suit may be +moved from one playing pile to another. + + + +The goal of spider is to put all cards as real families descending from +Kings anywhere in the playing piles. When such a family is built in a +playing pile, it is removed to the lower-left corner of the window. + + + +The different levels determine how many suits are dealt - Easy uses 1 suit, +Medium uses 2 suits, and Hard uses all 4 suits. The game is fairly easy to +win at Easy, and very difficult to win at Hard. + + + + + +Golf + +Golf +Golf is played with one card deck. The goal of Golf is to move all the cards on the tableau to the foundation. + +The layout of golf solitare is straight forward. At the beginning of the game you will see, the tableau, on it are seven columns each containing five cards. Below that is the talon and the foundation. + +Playing golf solitare is simple, but does require strategy to win. The cards at the base of each column on the tableau are available for play. Available cards are built upon the top foundation card in ascending or descending sequence regardless of suit. If there are no moves available a card may be dealt from the talon to the foundation. A game is over when all the cards in the talon have been dealt and there are no more possible moves. + + + +
+ + +Credits and License + + +&kpatience; Copyright 1995-2000 Paul Olav Tvete + + + +&kpatience; Copyright 2001 Stephan Kulow +coolo@kde.org + + + +Freecell Solver by Shlomi Fish +shlomif@vipe.technion.ac.il + + + +Documentation Copyright 2000 Paul Olav Tvete + + + +Documentation updated for KDE 2.0 by Michael McBride +mpmcbride7@yahoo.com + + + +Documentation rewritten for &kpatience; 2.0 (KDE 2.1) by Maren Pakura +maren@kde.org + + + + +&underFDL; +&underX11License; + + + +&documentation.index; + +
+ + + diff --git a/doc/kpat/man-kpat.6.docbook b/doc/kpat/man-kpat.6.docbook new file mode 100644 index 00000000..7ea3ce27 --- /dev/null +++ b/doc/kpat/man-kpat.6.docbook @@ -0,0 +1,340 @@ + + + +]> + + + +KPAT +6 +January 24, 2002 +kde-games +KDE User's Manual + + + +&kappname; +A highly addictive &kde; card game. + + + + +kpat +Qt-option +KDE-option + + + + +DESCRIPTION + +&kpat; is a compendium of several well known patience card games, +ranging from the well known Klondike and Freecell, to lesser known +games such as Grandfather's Clock and Mod3. In all there are 13 +variations for you to while away time. + + + +OPTIONS + +This program follows the usual GNU command line syntax, +with long options starting with two dashes (`--'). A +summary of the options supported by kpat +is included below. + + + +Generic Options + + + + + +Show author information + + + + +--help + +Show help about options + + + + + + +Show Qt specific options + + + + + + +Show &kde; specific options + + + + + + +Show all options + + + + + + +Show license information + + + + +, + +Show version information and exit. + + + + + + + + +Qt Options + + + + + +Use the X-server display displayname. + + + + + + + +Restore the application for the given sessionId. + + + + + + + + +Causes the application to install a private +color map on an 8-bit display. + + + + + + + + +Limits the number of colors allocated in the +color cube on a 8-bit display, if the application +is using the QApplication::ManyColor +color specification. + + + + + + +tells Qt to never grab the mouse or the keyboard. + + + + + + +running under a debugger can cause an implicit +, use +to override. + + + + + + +switches to synchronous mode for debugging. + + + +, + +defines the application font. + + + +, + + +sets the default background color and an +application palette (light and dark shades +are calculated). + + + + +, + +sets the default foreground color. + + + +, + +sets the default button color. + + + + + +sets the application name. + + + + + +sets the application title (caption). + + + + + + +forces the application to use a TrueColor +visual on an 8-bit display. + + + + + + + +sets XIM (X Input Method) input style. Possible +values are onthespot, overthespot, offthespot +and root. + + + + + + +set XIM server. + + + + + +disable XIM. + + + + + + +KDE Options + + + + +Use caption as name in the titlebar. + + + + + + +Use icon as the application icon. + + + + + + +Use icon as the icon in the titlebar. + + + + + + + +Use the DCOP Server specified by server. + + + + + + + +Disable crash handler, to get core dumps. + + + + + + + +Waits for a WM_NET compatible windowmanager. + + + + + + + +sets the application &GUI; style. + + + + + + + +sets the client geometry of the main widget. + + + + + + + + + + + +SEE ALSO + +&kpat; is documented in detail in The &kpat; +Handbook +($KDEDIRshare/doc/HTML/en/kpat/index.html +or enter help://kpat/index.html in +&konqueror;). + +&kde-http; + + + +AUTHOR + +&kpat; is by: + +Paul Olav Tvete +Mario Weilguni mweilguni@kde.org +Matthias Ettrich ettrich@kde.org +Rodolfo Borges barrett@labma.ufrj.br +Peter H. Ruegg kpat@incense.org +Michael Koch koch@kde.org +Marcus Meissner mm@caldera.de +Shlomi Fish shlomif@vipe.technion.ac.il +Stephan Kulow coolo@kde.org + + + diff --git a/doc/kpat/playfield.png b/doc/kpat/playfield.png new file mode 100644 index 00000000..ad16caab Binary files /dev/null and b/doc/kpat/playfield.png differ diff --git a/doc/kpat/spades.png b/doc/kpat/spades.png new file mode 100644 index 00000000..5f9dea1f Binary files /dev/null and b/doc/kpat/spades.png differ diff --git a/doc/kpoker/Makefile.am b/doc/kpoker/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/kpoker/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/kpoker/index.docbook b/doc/kpoker/index.docbook new file mode 100644 index 00000000..d63af910 --- /dev/null +++ b/doc/kpoker/index.docbook @@ -0,0 +1,582 @@ + + + + + +]> + + + +&kpoker; Handbook + + +Jochen +Tuchbreiter + +
&Jochen.Tuchbreiter.mail;
+
+
+ +Andreas +Beckermann + +
&Andreas.Beckermann.mail;
+
+
+ +
+ + +19972001 +&Jochen.Tuchbreiter;, &Andreas.Beckermann; + + +&FDLNotice; + +2005-12-14 +1.1 + + +This is the helpfile of &kpoker;, a little poker game. + + + + +KDE +kpoker +kdegames +games +game +poker +play cards + +
+ + +Introduction + + +What is &kpoker;? + +&kpoker; is a &kde; compliant clone +of those highly addictive pocket video poker games which are sometimes called +Videopoker as well. + + + +Where do I get the latest version of &kpoker;? + +The latest version of &kpoker; is always available at + + +The &kpoker; homepage: http://kpoker.sourceforge.net/ + + +The &kde; homepage: &kde-http; + + + + + + + + +Starting a new game + +You can start a new game two ways: + + + +By selecting Game +New from the +menubar. + +Selecting &Ctrl;N + + +Either way, you will be presented with a dialog box. + + +AI Configure Dialog + + +AI Configure Dialog + + + + + +How many players do you want? +Will determine if you play a one +player game (against the house), or a two +player game. + + + +Your name: +Lets you personalize &kpoker; so that it calls you by name +(instead of Youyou>). + + + +Player's starting money: +Lets you set the money you start each game +with. + + + +The names of your opponents: + lets you set the name of the other player. + + + +Show this dialog every time on +startup +If this is checked, the first game after you start +&kpoker; will show this menu. If this option is not checked, then the +game will not show this menu for the first game. + + + +When you are satisfied with your settings, click +OK, to start the game. Clicking +Cancel, will cancel the start of a new +game. + + + + +One player game + + +How do I play ? + +At the beginning of the game you get $100. You draw five cards +and decide which ones you want to keep. You indicate which cards you +want to keep by clicking on the face of the card. The word +Held will appear above the card. + +After doing this you draw new cards for those you did not want +to keep. This is accomplished by clicking on +Draw New Cards. + + The game now looks if you have any poker +hand and gives cash according to the kind of hand you got (see +scoring). + +Now you start over, drawing five cards, deciding which ones you want to +keep ... + +The game ends if you can not pay for another round or if you close the +&kpoker; window. + + + + +What are possible poker hands ? + +Possible poker hands are: + + + +Jacks or higher +Two cards of the same rank, both Jacks or +higher + + + +Two pairs +Two cards of one rank and two cards of another +rank + + + +Three of a kind +Three cards of the same rank + + + +Four of a kind +Four cards of the same rank + + + +Full House +Three cards of one rank and two cards of +another + + + +Straight +Five cards of consecutive rank including the combo +ace-2-3-4-5 + + + +Flush +Five cards of the same suit + + + +Straight flush +Five cards of the same suit and of consecutive +rank + + + +Royal flush +Ace, king, queen, jack, and ten of the same +suit + + + + + + + +How many bucks do I get for what hand ? + + + + + + +A pair of Jacks or higher cards$5 + Two Pairs $10 + Three of a kind $15 + Straight $20 + Flush $25 + Full House $40 + Four of a kind $125 + Straight Flush $250 + Royal Flush$2000 + + + + + + + + +Two player game + +The two player game is very different from a one player game. + + +Starting a two player game + +To start a two player game, select Game +New on the menubar. This will +bring up a dialog. Simply select the number of players on the slider or +in the spinbox labeled How many players do you want?. +Then click OK. + + + +Playing a two player game + +There are four phases to a two-player game: + + +you begin to draw cards +then you bet some money +after that you exchange your cards +then you raise and finally you will see the cards of the other +player and the winner will get all the +money. + + + +Draw +You will begin the game with $100. After clicking on the +Draw New Cards +button you will get five cards. That's all, here. + + + +Bet +Now you decide if your cards are good or +not. If they are, you bet some money - at least $5 and maximal $20. Just click +on the +$5 and similar buttons. + + +Exchange +Now you decide which cards you want to keep. Click on +them. If you have a royal flush then don't exchange any cards! When +you think you are ready then click again on the Draw New Cards +button - you will probably get new cards. + +Perhaps you do not get new cards - then the computer player has +quite good cards (or at least not very bad cards) and he has +raised. You have to decide to adjust your bet or to get out of the +round. By default you will adjust your bet. Click again on the +Draw New Cards button when ready. + + + +Raise + + After you got new cards you are allowed to raise a +little bit. It is the same as the bet phase so I don't explain it +again. Click on the draw button (which is now labeled with +See!) when ready. + + + +See / Draw +I know I said there are four phases and this one is the +fifth. But it is nearly the same as the draw phase. You will also see the cards +of the computer player and the winning cards are blinking. The winner gets all +money. Click on draw to begin a new round! + +The blinking cards are not necessarily the best cards of +that player. Only the cards which caused the win are blinking. So if +you have two pairs (⪚ 2 * 2 and 2 * 3) and an ace, but only the ace +is blinking, then the computer player also has these two pairs (2 * 2 +and 2 * 3) but not an ace. + + + + + + + + +Commands and Keyboard Shortcuts + +The following sections briefly describe each menubar option. + + +<guimenu>Game</guimenu> Menu + +The Game menu consists of three options. + + + + + +&Ctrl;N + +Game +New +Starts a new game of &kpoker;. For more +information, see the section entitled Starting a new +game. + + + + +&Ctrl;S + +Game +Save +Saves your current game to disk. This will +replace any previously saved games. + + + + +&Ctrl;Q + +Game +Quit +Quits &kpoker; + + + + + +<guimenu>Settings</guimenu> Menu + +The settings menu is used to adjust the sound, look and behavior of &kpoker;. + + + + + + +&Ctrl;M + +SettingsShow Menubar +This toggles the menubar on or off. If it is off, and you need to use the menubar, you can right click in the playing area of &kpoker; and a menu will appear. You can then select Show Menubar to turn it back on. + + + + +SettingsShow Statusbar +This option will toggle the status bar on or +off. The status bar is located at the bottom of the &kpoker; window, and contains instructions for play, and how much you won on the previous hand. + + + + +SettingsSound +This option will toggle the sound on or +off. + + + +Settings +Blinking Cards +If this option has a check beside it, then when you win a hand, +the cards which won you the money will blink. If this option +is not checked, no cards blink. + + + +SettingsAdjust Bet is +Default. +If this option does not have a check in front of it, and you are +playing a 2 person game, you will be responsible for clicking Adjust +Bet, if your opponent raises the stakes, because the default action +will be to fold your hand in defeat. +If, on the other hand, the option does have a check in front of it, and +you are in the same situation, the default action of the game, is to match your +opponents bet, and you will be responsible for folding your +hand. + + + +SettingsSave +Settings +This saves all your options to your hard drive. These options +will be restored automatically when you restart &kpoker;. + + + + +Settings +Configure Shortcuts... + + +Customize the keyboard shortcuts. + + + + + +SettingsConfigure Carddecks... +This will open a new window where you can select the front and back of the cards for &kpoker;. + + + + +SettingsConfigure KPoker... +This opens a dialog box. + +AI Configure Dialog + + + + +There are either one or three options to adjust: + +The top text box determines in milliseconds how long to delay +before showing the next card. This can be used to speed up the deals, +if you are not interested in the more realistic default speed. + +The text box labeled Maximal bet:, +determines the maximum bet for each hand. + +The text box labeled Minimal bet: determines +the smallest allowable bet for each hand. + +The Maximal bet: and Minimal +bet: lines are not shown in one player mode. In single +player mode, only the card delay can be adjusted. + +As you can see, the changes will not go into effect until a new +round is started. + + + + + + + + +<guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + +Default Keyboard Shortcuts + + + +Key ComboAction + +&Enter;Draw +1Exchange Card 1 +2Exchange Card 2 +3Exchange Card 3 +4Exchange Card 4 +5Exchange Card 5 +&Ctrl;QQuit &kpoker; +&Ctrl;NNew Game +&Ctrl;SSave Game +&Ctrl;MShow Menubar +&Ctrl;F1What's This Help +F1Help Contents + + + + + +Credits and License + +&kpoker; + + +Program copyright 1997-2000 &Jochen.Tuchbreiter;&Jochen.Tuchbreiter.mail;, &Andreas.Beckermann; &Andreas.Beckermann.mail; +Persons helping me: + +Chris Holmes - idea of writing this game and for parts of the +visual appearance +John Fitzgibbon - provided the card images +Nico Schirwing - drew the backs of the cards +&Andreas.Beckermann; - currently maintaining the game + + +Documentation updated for &kde; 2.0 by &Mike.McBride; +&Mike.McBride.mail; + + + +&underFDL; +&underGPL; + + + + +Installation + + +How to obtain &kpoker; + +&install.intro.documentation; + + + + +Compilation and Installation + +&install.compile.documentation; + + + + +
+ + diff --git a/doc/kpoker/kpoker1.png b/doc/kpoker/kpoker1.png new file mode 100644 index 00000000..bd409a6e Binary files /dev/null and b/doc/kpoker/kpoker1.png differ diff --git a/doc/kpoker/kpoker2.png b/doc/kpoker/kpoker2.png new file mode 100644 index 00000000..c03cd62a Binary files /dev/null and b/doc/kpoker/kpoker2.png differ diff --git a/doc/kreversi/Makefile.am b/doc/kreversi/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/kreversi/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/kreversi/index.docbook b/doc/kreversi/index.docbook new file mode 100644 index 00000000..90a8a77e --- /dev/null +++ b/doc/kreversi/index.docbook @@ -0,0 +1,533 @@ + + + + + +]> + + + +The &kreversi; Handbook + + + + +Clay +Pradarits + +
&Clay.Pradarits.mail;
+
+
+ + +Mario +Weilguni + +
&Mario.Weilguni.mail;
+Developer +
+ + +Lauri +Watts +Reviewer +
+&Lauri.Watts.mail; +
+
+ + + +
+ +2005-12-14 +1.7 + +&kreversi; is a simple one player strategy game. + + + +KDE +kdegames +game +KReversi + +
+ + +Introduction + + +What is &kreversi;? + +&kreversi; is a simple one player strategy game played against the +computer. The playing field is an 8 by 8 square board divided into 64 +squares. The game piece used is a colored stone - one side of it is red, the +other blue. If a piece is captured by an opposing player, that piece is turned +to reveal the color of that player. A winner is declared when one player has +more pieces of his own color on the board and if there are no more possible +moves. + + + + +Object of the Game + +The object of the game is to control the majority of squares on the +board. + + + + + + + +Playing + + +Basics + +A move consists of outflanking your opponents disc(s), then flipping the +outflanked disc(s) to reveal your color. A move is performed by placing the +mouse pointer over the desired square then &LMB; click. + +To outflank means to place a disc on the board so that your opponent's +row(s) of disc(s) is bordered at each end by a disc of your color. A row may be +made up of one or more discs. + +The game starts with each player having two discs automatically placed +in the center four squares of the board in the following pattern: + + +&kreversi; opening position + + +&kreversi; opening position +Board Layout + + + + + +Menu Bar + +The Menu Bar contains several choices of +drop-down menus. These are Game, Move, View, Settings, and Help. Click with the &LMB; or +&Alt;the underlined +letter to show the drop-down menu. + + + + + + +Toolbar +Provides the player with icon shortcuts for commonly +used actions. These are New, +Stop Thinking, Continue Thinking, +Undo, Show Menubar, Hint, +Show Last Move, and Show Legal Moves. +Place the mouse pointer over any of these icons and click with the &LMB; to +activate. + + + + + +Game Board +The game board consists of a 8 by 8 square board +divided into 64 squares. + + + + + +Status Bar +The status bar displays whose turn it is. + + + +The field on the right side of the game board contains useful information such as +which color represents each player, the number of +squares each player controls on the board and displays all moves in the current game. + + + +Rules + +Blue always moves first. + +If on your turn you cannot outflank and flip at least one +opposing disc, your turn is forfeited and your opponent moves +again. However, if a move is available to you, you may not forfeit +your turn. + +A disc may outflank any number of discs in one or more rows in +any number of directions at the same time - horizontally, vertically +or diagonally. A row is defined as one or more discs in a continuous +straight line. + +You may not skip over your own color disc to outflank an +opposing disc. + +Discs may only be outflanked as a direct result of a move and +must fall in the direct line of the disc placed down. + +All discs outflanked in any one move must be flipped, even if it +is to the player's advantage not to flip them. + +Once a disc is placed on a square, it can never be moved to +another square later in the game. + +When it is no longer possible for either player to move, the +game is over. Discs are then counted and the player with the majority +of his or her color discs on the board is declared the winner. + +It is possible for a game to end before all 64 squares are +filled. + + + + +Tips + +Try to place pieces on the edges whenever possible. Your +opponent cannot outflank these pieces. + +Avoid placing pieces on one of the three neighbor fields of a +corner unless you are absolutely sure that your opponent will not be +able to put a piece on a corner. + +Sometimes it is better to offer some pieces to your +opponent + +Try to put pieces on fields which prevent your opponent from +moving. + +Try to force your opponent to put a piece in a neighboring field +of a corner. + + + + + + +User Interface + + +<guimenu>Game</guimenu> Menu + + + +&Ctrl;N +GameNew +Starts a new game. + + + + +&Ctrl;O +GameLoad... +Loads a saved game. + + + + +&Ctrl;S +GameSave +Saves the current game. + + + + +&Ctrl;H +GameShow Highscores +Opens a dialog that displays different high score tables. +Export the high scores into a file or click on +Configure to open a dialog to customize your +Nickname and add a Comment. + + + +&Ctrl;Q +FileQuit +Quits &kreversi;. + + + + + +<guimenu>Move</guimenu> Menu + + + +&Ctrl;Z +MoveUndo +Removes your last move as well as the computer's last +move from the board. + + + + +HMoveHint + +The computer will provide a hint for your next +move. + + + +MoveSwitch +Sides +Will make you the opposite color. + + + +Escape +MoveStop +Thinking +Stops the computer's depth search, then you choose the +computer's next move, or select the Continue Thinking +menu item to continue the computer's thinking process. + + + + +Move +Continue Thinking +Continue the computer's thinking process if it was +previously stopped using the Stop Thinking menu +item. + + + + + + +<guimenu>View</guimenu> Menu + + + +&Ctrl;+ +ViewZoom In +Enlarges the game board. + + + +&Ctrl;- +ViewZoom Out +Shrinks the game board. + + + + + +<guimenu>Settings</guimenu> Menu + + + +&Ctrl;MSettings +Hide Menubar +Toggles whether the menubar is visible. + + + + +Settings +Toolbars +Main Toolbar (&kreversi;) + +Toggle the Main Toolbar + + + + +Settings +Toolbars +View Toolbar (&kreversi;) + +Toggle the View Toolbar + + + +Settings +Configure Shortcuts... +Opens a dialog which lets you configure all the keyboard shortcuts which +are available in &kreversi;. + + + + +Settings +Configure Toolbars... + + +Open a dialog where you can configure the toolbars for &kreversi; + + + + +Settings +Configure &kreversi;... +Opens the configuration +dialog which lets you tweak a lot of &kreversi;'s +options. + + + + + +<guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + +Configuration + +Selecting the Settings +Configure &kreversi;... menu item will +open a configuration dialog which lets you adjust many of &kreversi;'s +options. + + +&kreversi; configuration dialog + + +&kreversi; configuration dialog + + + + + +Grayscale chips +Check this box to use grayscale chips instead of blue and +red ones; this might improve the look on very low color +displays. + + + +Play Game +Check a radiobutton whether to play Casually +or Competitively. + + + +Animation +If this option is checked, a short animation will be shown +when a disc changes it's color. + + + +Animation Speed +If the Animation option is enabled, you +can use this slider to define how fast the animation should be played; move the +slider to the left for a slower animation, moving the slider to the right will +play the animation faster. + + + +Computer Skill +This slider lets you define the skill level of the computer +player. There are seven different skill levels available, ranging from beginner +to expert. Move the slider to the left for an easier opponent, or to the right +to get a more difficult opponent. + + + +Background +Here you can define the appearance of the playground. Select +the Color: option and press the colored button at the right +if you want the background to have a single color. Select the +Image: option and enter the path to a picture in the input +field at the right in case you want to use a specific image as the background +wallpaper. + + + +Help +Opens &kreversi; help. + + +Defaults +Reverts settings to defaults. + + +OK +Saves changes and closes dialog. + + +Apply +Saves changes but doesn't close dialog. + + +Cancel +Cancels all your changes and closes the dialog. + + + + + +Credits and License + +Program Copyright 1998-2000 &Mario.Weilguni; +Mats Luthman - Designer of the move engine. + +Original documentation by &Mario.Weilguni; +Edited by Robert Williams + +Documentation re-written and updated for &kde; 2.0 by &Clay.Pradarits; +&Clay.Pradarits.mail; + + + +&underFDL; +&underGPL; + + + +Installation + +&install.intro.documentation; + + +Compilation and Installation + +&install.compile.documentation; + + + + +&documentation.index; +
+ + + + + + + + + + diff --git a/doc/kreversi/kreversi-configuration.png b/doc/kreversi/kreversi-configuration.png new file mode 100644 index 00000000..9c8df2d0 Binary files /dev/null and b/doc/kreversi/kreversi-configuration.png differ diff --git a/doc/kreversi/kreversi1.png b/doc/kreversi/kreversi1.png new file mode 100644 index 00000000..fe32be35 Binary files /dev/null and b/doc/kreversi/kreversi1.png differ diff --git a/doc/ksame/Makefile.am b/doc/ksame/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/ksame/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/ksame/index.docbook b/doc/ksame/index.docbook new file mode 100644 index 00000000..4e9cb5e9 --- /dev/null +++ b/doc/ksame/index.docbook @@ -0,0 +1,295 @@ + + + + + +]> + + + + +The &ksame; Handbook + + + +Nicholas +Robbins + +
&Nicholas.Robbins.mail;
+
+
+ +
+ + +2000 +Nicholas Robbins + +&FDLNotice; + +2005-12-17 +0.5.1 + +&ksame; is a nice little program meant to amuse you for +anywhere from 3 minutes to 6 days (straight). + + +KDE +kdegames +ksame + + +
+ + +Introduction + +&ksame; is a simple game. It is played by one player, so there is only one +winner :-) You play for fun and against the high score. It has been inspired by +SameGame, which is only really famous on the Macintosh. + +In 1997 &Stephan.Kulow; and Marcus Kreutzberger wrote Probiere (a German pun). It was their first attempt to +write a X11 application. They decided that it would be a good idea to port it to +&kde;, and here we are. + + + + + +Using &ksame; + +A very simple premise. There are a bunch of marbles. Get rid of them +all. ;-) + +You can erase same marbles when they are connected vertically or +horizontally when you click them. If there are pieces over the erased ones, +they will drop down. If all of the pieces on the vertical line are erased, all +pieces on the right side will slide to the left. + +The score is calculated as follows: +Subtract 2 from the number of marbles erased, and square the result. +As an example, if you erase 7 balls, then you get 25 points (7 minus 2 = 5. 5 squared is 25 points).If you erase 8 +balls, then you get 36 points (8 minus 2 = 6. 6 squared is 36 points. + +Get as many of the +same pieces as you can, and then erase them in one click. That way you will get +a higher score. The game is over when there are no pieces that can be +erased. + +The score will then be decreased according to the number of remaining +pieces. If you erase all pieces 1,000 bonus points will be added to the final +score. + + + +More &ksame; Features + +&ksame; is wonderful in that it will allow you to kill 5 minutes while you +wait for that program to compile. + + Or, if nothing else, it will allow you to kill 5 minutes. + + + + + +Command Reference + + +The Main &ksame; Window + +There is a large area with lots of marbles. Underneath is the status +bar. Above is the menu bar. + + +The <guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New + +Starts a new game. If Settings +Random Board is enabled, a random game is started. +Otherwise you are presented a dialog in which you can choose a specific board. + + + + + + +&Ctrl;R + +Game +Restart This Board + +Restarts the current board. + + + + + +&Ctrl;H + +Game +Show Highscores + +Displays the high score table. + + + + + +&Ctrl;Q + +Game +Quit + +Quits &ksame;. + + + + + + +The <guimenu>Edit</guimenu> Menu + + + + + +&Ctrl;Z + +Edit +Undo + +This will undo the last move. + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +Settings +Random Board + +Enabling this will make the Game +New start with a random board instead of letting +you choose a specific board. + + + + +Settings +Show Number Remaining + +Displays the remaining numbers of marbles for each color in the status bar. + + + + +Settings +Configure Shortcuts... + + +Configure the keyboard keys you use to access the +different actions. + + + + + +Settings +Configure Notifications... + +Displays a standard &kde; notifications +configuration dialog to change the audio and visual notifications for &ksame;. + + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + + + + +Credits and License + +&ksame; + +Program copyright 1997 Marcus Kreutzberger +kreutzbe@informatik.mu-luebeck.de +Contributors: + +&Stephan.Kulow; &Stephan.Kulow.mail; + + + +Documentation copyright 2000 Nicholas R. Robbins +logik9000@home.com + + +&underFDL; + + +&underGPL; + + + + +Installation + + +How to obtain &ksame; + +&install.intro.documentation; + + + + +Requirements + +In order to successfully use &ksame;, you need &kde; 3.0. Or at least the +kdelibs package. + + + + +Compilation and Installation + +&install.compile.documentation; + + + + + + +&documentation.index; +
+ + diff --git a/doc/kshisen/Makefile.am b/doc/kshisen/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/kshisen/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/kshisen/index.docbook b/doc/kshisen/index.docbook new file mode 100644 index 00000000..483c4eb7 --- /dev/null +++ b/doc/kshisen/index.docbook @@ -0,0 +1,423 @@ + + + + + +]> + + + +The &kshisen; Handbook + + + +Dirk +Doerflinger + +
&Dirk.Doerflinger.mail;
+
+
+ +FrerichRaabe +Reviewer +
&Frerich.Raabe.mail;
+
+ +
+ + +2006-06-17 +1.5.1 + + +&kshisen; is a game similar to Mahjongg for &kde;. + + + + KDE + kdegames + kshisen + game + shisen-sho + + +
+ + + +Introduction + +&kshisen; is a single-player-game similar to Mahjongg and uses the same +set of tiles as Mahjongg. + +The object of the game is to remove all tiles from the field. + + + + +Playing &kshisen; + + +Rules + +The aim of the game is to remove all tiles from the board. Only two +matching tiles can be removed at a time. Two tiles can only be removed if they +can be connected with a maximum of three connected lines. Lines can be +horizontal or vertical, but not diagonal. + +You don't have to draw the lines by yourself, the game does this for +you. Just mark two matching tiles on the board, if they can be connected with a +maximum of three lines, the lines will be drawn and the tiles are +removed. + +Remember that lines only may cross the empty border. If you are stuck, you +can use the Hint feature to find two tiles which may be removed. Clicking a +tile with the &RMB; will show you all corresponding tiles, no matter if they are +removable at the moment or not. + +The game is over when no moves are possible. This could be due to +clearing all the tiles from the field or reaching a point in the game +where no two matching tiles can be connected by three lines. +Some games are unsolvable, if you would like to avoid unsolvable +games uncheck the option Allow unsolvable +games in the configuration dialog. + +The resulting score S is calculated using the +following formula, assuming that n represents the +number of tiles present when the game started and t +standing for the time (in seconds) it took to clear the +field: + + + + +S=(n/t)*sqrt(n/84)/0.0014 + + +If you played with the Gravity setting enabled, this score +will additionally get multiplied by two. + + + + +Menu Reference + + +The <guimenu>Game</guimenu> Menu + +The Game menu lets you control the status of the +current game: + + + + +&Ctrl; +N +GameNew +Finish the actual game set and start a new +session with new tiles. + + + + +F5 +GameRestart +Game +Restart the current game with the same +tiles. + + + + +P +Game +Pause +Pauses the entire game, especially the timer +which affects the scoring. The menu entry is also used for resuming the +game. + + + + +&Ctrl;H + +GameShow +Highscores +Shows the (local) Top-Ten charts of &kshisen;. + + + + +&Ctrl; +Q +GameQuit +Quits &kshisen;. + + + + +Some of the menu items can also be controlled by keykoard shortcuts. +See for a list. + + + + +The <guimenu>Move</guimenu> Menu + + + + &Ctrl;Z +Move +Undo +Undo the last step. Same as the +Undo button in the toolbar. + + + +&Ctrl;&Shift;Z +Move +Redo +Redo the last step. Same as the +Redo button in the toolbar. + + + + +H +Move +Hint +Show a tip, which two tiles to remove +next. + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +Settings +Show Toolbar + + + +Toggle on and off the display of the toolbar. + + + + + + +Settings +Show Statusbar + + + +Toggle on and off the display of the status bar. + + + + + + +Settings +Configure Shortcuts... + + +Open a dialog which lets you redefine all the keyboard shortcuts. + + + + + +Settings +Configure Toolbars... + +Displays a &kde; standard dialog where you can configure the toolbar icons. + + + + +Settings +Configure &kshisen;... + + +Opens the configuration +dialog to change &kshisen; settings. + + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + +Shortcuts + +Default shortcuts are: + + + + + +New&Ctrl;N +Restart GameF5 +PauseP +Show Highscores&Ctrl;H +Quit&Ctrl;Q +Undo&Ctrl;Z +Redo&Ctrl;&Shift;Z +HintH +&kshisen; HandbookF1 +What's This&Ctrl;&Shift;F1 + + + + + + + + +Configuration + +Selecting the Settings +Configure &kshisen;... menu item will +open a configuration dialog which lets you alter the behavior of the game. + + +&kshisen; configuration dialog + + +&kshisen; configuration dialog + + + + + +Gravity +Checking this makes the game even harder: If a tile is +removed, all tiles lying above it will fall down one step. + + + +Allow unsolvable games +If checked, only solvable games will be +created when a new game is started. Note: You may still choose the wrong way and +have to try again if you didn't solve the game, but if it's checked, the game +will be solvable. + + + +Board Difficulty +The slider controls the difficulty of the board. There are three +options Easy, Medium and +Hard. + + + +Piece Removal Speed +Adjusting this slider alters the speed at which the pieces are +removed from the screen after a match has been made. + + + +Prefer Unscaled Tiles +Resizing the window causes the tiles on the board to be scaled to +match the window size. When this option is checked, the window is resized to fit +the tiles' natural size. You can still adjust the size of the window. Doing so +causes this mode to be disengaged. + + + +Tile Size +This slider allows you to can change the number of tiles +on the board. The more tiles you have, the harder (and longer) +the game will be. + + + +Help +Opens &kshisen; help pages. (this document). + + +Defaults +Reverts all settings to defaults. + + +OK +Saves your changes and closes the dialog. + + +Apply +Saves your changes but doesn't close the dialog. + + +Cancel +Cancels all your changes and closes the dialog. + + + + + +Credits and License + + +&kshisen; Copyright 1999 &Mario.Weilguni; +&Mario.Weilguni.mail; + + + + +Documentation Copyright 2000 &Dirk.Doerflinger; +ddoerflinger@gmx.net + + + + +&underFDL; +&underGPL; + + + + +Installation + + +How to obtain &kshisen; + +&install.intro.documentation; + + + + +Requirements + + +In order to successfully compile &kshisen;, you need &kde; 3.x. +All required libraries as well as &kshisen; itself can be found on +&kde-ftp;. + + + + +Compilation and Installation + +&install.compile.documentation; + + + + +
+ diff --git a/doc/kshisen/kshisen-configuration.png b/doc/kshisen/kshisen-configuration.png new file mode 100644 index 00000000..cbcfb109 Binary files /dev/null and b/doc/kshisen/kshisen-configuration.png differ diff --git a/doc/kshisen/score-formula.png b/doc/kshisen/score-formula.png new file mode 100644 index 00000000..0dceb581 Binary files /dev/null and b/doc/kshisen/score-formula.png differ diff --git a/doc/kshisen/score-formula.tex b/doc/kshisen/score-formula.tex new file mode 100644 index 00000000..a50d233d --- /dev/null +++ b/doc/kshisen/score-formula.tex @@ -0,0 +1,18 @@ +\documentclass{article} +\usepackage[psamsfonts]{amssymb} +\usepackage{amsmath} +\newcounter{afg} +\newcommand{\aufgabe}{\stepcounter{afg}\subsubsection*{Aufgabe \arabic{afg}}} + +\renewcommand{\labelenumi}{\alph{enumi})} + +\begin{document} + +\begin{align*} + \text{S} & = \frac{n}{t} \cdot \sqrt{\frac{n}{84}} / 0.0014 \\ + & = \frac{n}{t} \cdot \frac{\sqrt{\frac{n}{84}}}{0.0014} \\ + & = \frac{n}{t} \cdot \frac{\sqrt{n}}{\sqrt{84}\cdot 0.0014} \\ + & = \frac{n}{t} \cdot \frac{\sqrt{n}}{0.012831211946} \\ + & = \frac{\sqrt{n^3}}{0.012831211946\cdot t} \\ +\end{align*} +\end{document} diff --git a/doc/ksirtet/Makefile.am b/doc/ksirtet/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/ksirtet/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/ksirtet/index.docbook b/doc/ksirtet/index.docbook new file mode 100644 index 00000000..524123c0 --- /dev/null +++ b/doc/ksirtet/index.docbook @@ -0,0 +1,586 @@ + + + + + +]> + + + +The &ksirtet; Handbook + + +&Nicolas.Hadacek; + + + + + + +19992000 +&Nicolas.Hadacek; + + +&FDLNotice; + +2005-12-17 +2.1.11b + + +&ksirtet; is a clone of the well known game Tetris. You must fit the +falling pieces to form full lines. + + + +KDE +game +tetris + + + + +Introduction + + +&ksirtet; is a clone of the well known game Tetris. You +must fit the falling pieces to form full lines. You can rotate and translate +the falling piece. The game ends when no more pieces can fall, &ie; when your +incomplete lines reach the top of the board. + + + +Every time you have destroyed 10 lines, you advance to the next level and the +pieces fall quicker (to be precise, the pieces fall from the top of the screen each +1/(1+level) second). + + + + +How to Play + + +Handling the Falling Piece + + +You can use the keyboard to rotate, translate or drop down the falling piece. The +keys are configurable via the menu item Settings +Configure Shortcuts.... + + + + + +Game Types +&ksirtet; supports two types of single player game: Normal and Arcade. In +Normal mode, points totals are kept, and high scores recorded. Also, when advancing +to the next level, play continues without clearing the gameboard. In Arcade +mode, no points totals are kept, although an Elapsed time +counter is shown. When you advance to the next level in Arcade mode, the +gameboard is cleared of pieces. +You can choose the game mode in the Mode menu. + + + +How to gain Points + +There are two sources of points: + + + + +Dropping down a piece using the Down Arrow gives a small +number of points which is equal to the drop height. It is not much each time +but is particularly relevant in the earlier levels, when the pieces fall more +slowly. + + + + + +The biggest part of the score comes from destroying full lines. It is very +important to note that destroying multiple lines at the same time gives you a +lot more points. A four-lines destruction is called a tetris: +it gives a maximum amount of points. + + + +The points gained are 40 for a one-liner, 100 for two lines, 300 for three lines +and 1200 for a tetris. These numbers are multiplied by the current +level. For example, destroying four lines in level 10 gives 12,000 points. + + + + + + + +Configuring &ksirtet; + + +The &ksirtet; configuration is accessed from the menu item +Settings +Configure &ksirtet;.... The +options are as follows: + + + +Game Configuration + + + + +Initial level: +Set the level which you will play at startup, from 1 (easiest) +to 20 (hardest). Default is level one. + + + +Direct drop down +If checked, pressing the Down Arrow will cause +a tile to immediately fall to the bottom of the screen. If unchecked, pressing +the Down Arrow only causes the piece to fall until the key is released. + + + +Old rotation style +&ksirtet; has two ways of rotating each piece: +Old style and New style. Each style uses a +different point to rotate the piece around. Try both, and see which one you prefer. + + + + + + + + +Appearance Configuration + + + + +Enable animations +If checked, pieces are shown with an +animation (a small rebound effect), when they touch the bottom. + + +Show piece's shadow +If checked, a shadow is placed beneath the game board +showing where the piece will fall. + + + +Show next piece +If checked, shows the next tile that will fall onto the +game board. + + +Show detailed "removed lines" +field If checked, the Removed +Lines counter on the left of the screen shows how many times each +number of lines have been removed. If unchecked, just the total number removed +is shown. + + + + +Background +Select the color and the opacity for &ksirtet; background. +An opacity of zero makes the &ksirtet; background +completely transparent, and a setting of one makes the &ksirtet; background +completely opaque. + + + + + + + +Colors Configuration +Here you can select the colors used for the tiles in &ksirtet;. + + + +A.I. Configuration +Here you can configure the A.I. in &ksirtet;. + + + + + +Configuring Highscores + +The configuration is accessed from +Settings +Configure Highscores.... The +options are as follows: + + + +The <guilabel>Main</guilabel> Tab + + + + +Nickname: +Displays your current nickname and allows you to change it. + + + + +Comment +A comment about yourself. You choose... + + + + +World-wide highscores enabled +If checked and if you are connected to the Internet, +&ksirtet; will send your score automatically at the end of the game +to the highscore web server (ksirtet.sf.net). + + + + + + +The <guilabel>Advanced</guilabel> Tab + +This tab displays your Registration Data +on ksirtet.sf.net: + + + +Nickname: +Displays your current nickname from the Main tab. + + + + +Key: +This key was generated when you registered on ksirtet.sf.net by +selecting World-wide highscores enabled the first time. +The registration key is used in conjunction with the +nickname to identify uniquely users, but users cannot have the same nickname. +Click on the Remove button to delete you from the world highscores +list. + + + + + + + + + + + + +Multiplayer + + +The multiplayer option allows you to play with others players on the same computer. +You can also play against an AI (artificial intelligence) player which is currently not +very good. + + + +Basically when destroying lines, a player sends garbage at the +bottom of the game of his next opponent: nothing for one line destroyed, one +garbage line for two lines destroyed and so on. + + + + + +Command Reference + + +The Main &ksirtet; Window + + +The <guimenu>Game</guimenu> Menu + + + + + + +&Ctrl;N + +Game +New + +Starts a new game. + + + + + +P + +Game +Pause + +Pauses or resumes the game + + + + + +&Ctrl;H + +Game +Show Highscores + +Opens a dialog that displays different high score tables. +Clicking on the links below the tables downloads world-wide high scores. +Export the high scores into a file or click on +Configure to open a dialog to customize your Nickname and add a Comment. + + + + + + +&Ctrl;Q + +Game +Quit + +Quits &ksirtet; + + + + + + + +The <guimenu>View</guimenu> Menu + + + + + +&Ctrl;+ +View +Zoom In + +Enlarges the game board + + + + + +&Ctrl;- +View +Zoom Out + +Reduces the game board size + + + + + + + + +The <guimenu>Mode</guimenu> Menu + + + + + +Mode +Single Human (Normal) + +Sets the multiplayer mode to single +player normal. See . + + + + +Mode +Single Human (Arcade) + +Sets the multiplayer mode to single +player arcade. See . + + + + + +Mode +Human vs Human + +Sets the multiplayer mode to two player with two human +players. + + + + +Mode +Human vs Computer + +Sets the multiplayer mode to two player with one human +and one computer player. + + + + +Mode +More... + +Displays the multiplayer +options dialog. + + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + + +&Ctrl;M + +Settings +Show Menubar + +Shows or hides the Menubar.To return the +menubar, right-click anywhere on the gameboard and select Show +Menubar. + + + + +Settings +Configure Shortcuts... + +Displays a standard &kde; key bindings +configuration dialog to change the keyboard shortcuts for &ksirtet;. + + + + +Settings +Configure Notifications... + +Displays a standard &kde; notifications +configuration dialog to change the audio and visual notifications for &ksirtet;. + + + + +Settings +Configure Highscores... + +Displays the high score configuration dialog, +in which you can change several settings that affect how &ksirtet; treats highscores. + + + + +Settings +Configure &ksirtet;... + +Opens the &ksirtet; +configuration dialog, where you can change the options of the game. + + + + + + + +The <guimenuitem>Help</guimenuitem> Menu + +&help.menu.documentation; + + + + + + + + +Credits and Licenses + + +Developers + +Erik Eng - Wrote the generic tetris code +&Nicolas.Hadacek; &Nicolas.Hadacek.mail; + + + +Authors +&Nicolas.Hadacek; &Nicolas.Hadacek.mail; +Robert Williams rwilliams@kde.org - Editor +Documentation updated for &kde; 3.2 by &Philip.Rodrigues;. + + + +&underFDL; +&underGPL; + + + + + + + + + + diff --git a/doc/ksmiletris/Makefile.am b/doc/ksmiletris/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/ksmiletris/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/ksmiletris/gamescreen.png b/doc/ksmiletris/gamescreen.png new file mode 100644 index 00000000..9cb50cf6 Binary files /dev/null and b/doc/ksmiletris/gamescreen.png differ diff --git a/doc/ksmiletris/icon.png b/doc/ksmiletris/icon.png new file mode 100644 index 00000000..9bdec8a4 Binary files /dev/null and b/doc/ksmiletris/icon.png differ diff --git a/doc/ksmiletris/index.docbook b/doc/ksmiletris/index.docbook new file mode 100644 index 00000000..53f330f0 --- /dev/null +++ b/doc/ksmiletris/index.docbook @@ -0,0 +1,456 @@ + + + KSmiletris"> + + + + + JohnHayes'> + justlinux@bellsouth.net'> + SandroSigala'> + ssigala@globalnet.it'> + Tetris"> +]> + + + + +The &ksmiletris; Handbook + + + +&John.Hayes; &John.Hayes.mail; + + + +&Sandro.Sigala; &Sandro.Sigala.mail; + + + +&Frerich.Raabe; &Frerich.Raabe.mail; + + + + + + +2005 +&John.Hayes; + + +2005-12-14 +3.5.0 + +&FDLNotice; + + + + + +&ksmiletris; is a &Tetris; like game. + + + + +KDE +kdegames +KSmileTris +Tetris + + + + + +Introduction + +&ksmiletris; is a game similar to the &Tetris; game. If you have ever played &Tetris;, you will find &ksmiletris; easy to learn and play. + + + + + +Game Play + + +Starting a New Game + +When you start &ksmiletris;, the game shows a blank game area. To start playing either select New from the Game menu or press the key combination &Ctrl;N. + + + + +Aim of the Game + +The aim of the game is to complete each level and get the highest score possible. You score by stacking or moving the similar tiles together as they fall, and then disappear. As the tiles disappear, your score increases. + + + + +Playing the Game + +By default, you can use the Left Arrow and Right Arrow keys to position the falling tiles where you want them to stack, and the Up Arrow or Down Arrow keys to rotate the tiles left or right respectively. The Spacebar drops the tile all the way to the bottom - use it as a way to save time. + +All of the shortcuts can be customized by selecting SettingsConfigure Shortcuts from the menu. + +Four of more of the same type of connected tiles will change to a broken or shattered look. When four or more of the connected tiles are shattered, they will disappear and all pieces above them will settle down to the rows below them. + +Every time a piece shatters, you get 10 points for each tile, you get 20 more points for each tile when it disappears. There are 1000 points per level. As the level increases, more types of tiles will be introduced which makes it more challenging to find fits and the speed at which the tiles fall increases as well. + +The game ends when the center column fills to the top with tiles, and no more can fall. + + + + +The Game Screen + + + + + + + + + &ksmiletris; in Action + + + + +A quick explanation of the parts of the game screen... + + + + + +Game Screen +The Game screen is at the top left and is the largest section of the window. + + + +Preview Screen +The Preview screen at the top right, gives you a preview of the block that will drop out next. + + + +Alignment Screen +The Alignment screen is located just below the game screen. As the tiles are falling, the two tiles that will be on the bottom are shown in the alignment screen in the columns that they are located in. This screen helps you see where the tiles will be located when they get to the bottom. + + + +Statusbar +The status bar is located at the very bottom of the screen, and shows the current level you are on and your score. Display of the Statusbar can be toggled on or off by the menu SettingsHide Statusbar. + + + + + + + + + +&ksmiletris; Configuration and Default Shortcuts + + +Configuring Gameplay + +The configuration options are as follows: + + +Hide/Show Statusbar + + + + +SettingsHide Statusbar + +Hides the Statusbar. + + + + +SettingsShow Statusbar + +Shows the Statusbar. + + + + + + + + +Appearance Configuration + +The game appearance configuration can be set from SettingsPieces. Your options are: + + + + +SettingsPiecesSmiles +Sets the tile appearance to Smileys. + + + + + + + + + + + +SettingsPiecesSymbols +Sets the tile appearance to Symbols. + + + + + + + + + + + +SettingsPiecesIcons +Sets the tile appearance to Icons. + + + + + + + + + + + + + + + +Sound Configuration + + + + +SettingsSounds +Toggles sound effects on or off. + + + + + + + + +Default Shortcuts + +SettingsConfigure Shortcuts +allows you to change the default keyboard shortcuts. The default shortcuts are as follows: + + + + +Spacebar +Drops the tile to the bottom fast. + + + +Up Arrow +Rotate tile to the left. + + + +Down Arrow +Rotate tile to the right. + + + +Left Arrow +Move tile to the left. + + + +Right Arrow +Move tile to the right. + + + +P +Pause or resume the game. + + + +&Ctrl;N +Start a new game. + + + +&Ctrl;End +End the game. + + + +&Ctrl;Q +Quit the game. + + + +&Ctrl;H +Show the high scores. + + + + + + + + + + + +Menu Reference + + +The Main &ksmiletris; Window + + +The <guimenu>Game</guimenu> Menu + + + + + + +&Ctrl;N + +Game +New + +Starts a new game. + + + + + +&Ctrl;End + +Game +End + +Ends the current game. + + + + + +P + +Game +Pause + +Pauses or resumes the game + + + + + +&Ctrl;H + +Game +Show Highscores + +Displays the high scores. + + + + + +&Ctrl;Q + +Game +Quit + +Quits &ksmiletris; + + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + + +Settings +Show/Hide Statusbar + +Shows or hides the statusbar. + + + + +Settings +Pieces + +Lets you select from three different appearances for the tiles. Your options are Smileys, Symbols or Icons. + + + + +Settings +Sounds + +Toggle effects on or off. + + + + +Settings +Configure Shortcuts... + +Allows you to change the default shortcuts. + + + + + + + +The <guimenuitem>Help</guimenuitem> Menu + +&help.menu.documentation; + + + + + + + + + +Credits and License + +&ksmiletris; + +Program Copyright (c) 1998 &Sandro.Sigala; &Sandro.Sigala.mail;. + + +All rights reserved. + +Documentation copyright 2005 &John.Hayes; &John.Hayes.mail;. + + + +&underFDL; + +&underGPL; + + + + diff --git a/doc/ksmiletris/smiley.png b/doc/ksmiletris/smiley.png new file mode 100644 index 00000000..a83c133e Binary files /dev/null and b/doc/ksmiletris/smiley.png differ diff --git a/doc/ksmiletris/symbol.png b/doc/ksmiletris/symbol.png new file mode 100644 index 00000000..abd92813 Binary files /dev/null and b/doc/ksmiletris/symbol.png differ diff --git a/doc/ksnake/Makefile.am b/doc/ksnake/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/ksnake/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/ksnake/index.docbook b/doc/ksnake/index.docbook new file mode 100644 index 00000000..608d3132 --- /dev/null +++ b/doc/ksnake/index.docbook @@ -0,0 +1,346 @@ + + + + + +]> + + + +The &ksnake; Handbook + + +Michel +Filippi + + + + + +2000 +Michel Filippi + + +&FDLNotice; + +2005-12-17 +0.4.0 + + + +Snake Race is a game of speed and agility. You are a hungry snake and are trying +to eat all the apples in the room before getting out! + + + + +KDE +game +linux +race + + + + + +Introduction + + +&ksnake; is a game of speed and agility. You are a hungry snake and are trying +to eat all the apples in the room before getting out! + + + + + +Rules of the Game + + +To Win + + +To win &ksnake; you must eat all the apples in the room and leave through the +exit that opens at the top. + + + + + +Each apple you eat makes you grow longer. + + + + +If you hit a wall, you die. + + + + +If you hit yourself, you die. + + + + +If you are hit in the head by a bouncing ball, you die. + + + + +If you take too long to eat the apples, more of them appear. + + + + + + + +Scoring + + +Before the timer has run out + + + +Before the timer has run out, you score: + + + + + +1 point for a red apple + a bonus depending on your skill level. + + + + +5 points for a gold apple + a bonus for your skill level + 2 points for each +computer snake + 2 points for each bouncing ball. + + + + +Twice your level number when you exit the room + a bonus depending on your skill +level. + + + + + + + +After the timer has run out + + +After the timer has run out, you score: + + + + + +1 point for a red apple. + + + + +2 points for a gold apple. + + + + +Points equal to your level number when you exit the room + a bonus depending on +your skill level. + + + + + + + +When the computer snake eats an object + + +When the computer snake eats an object you lose: + + + + + +2 points for a red apple. + + + + +5 points for a gold apple. + + + + + + + +Other points + + +You score 20 points for killing a computer snake. + + + + + + + + +Customization + + +Create Your Own Levels + + +To create your own level, copy one of the levels from $KDEDIR/share/apps/ksnake/levels to +$HOME/.kde/share/apps/ksnake and +edit it with a bitmap editor. You can then select it in +Settings Configure &ksnake;... + in the First Level tab. + + + + + + + +Menu Reference + + +The <guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New + + +Start a new game + + + + + + +P + +Game +Pause + + +Pause the game. + + + + + + +&Ctrl;H + +Game +Show Highscores... + + +Show the High Score dialog. + + + + + + +&Ctrl;Q + +Game +Quit + + +Quits &kappname;. + + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +SettingsShow Statusbar + + +Shows or hides the &ksnake; status bar at the bottom of the screen. + + + + +Settings +Configure Shortcuts... + +Select this to open a dialog which lets you define shortcuts +for all menu items in &ksnake;. + + + + +SettingsConfigure &ksnake;... + + +Opens the &ksnake; configuration dialog to change &ksnake; +settings. +On the General tab you can set the speed of snakes, +choose the number of snakes and balls and their behaviour. +Select a background color or an image on the Appearance tab. +Choose a starting level from 1 to 25 on the First Level tab. + + + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + + +Credits and Licenses + + +&ksnake; copyright 2000, Michel Filippi + + + +Documentation copyright 2000, Michel Filippi and Robert Williams. + + + + +&underFDL; +&underGPL; + + + + diff --git a/doc/ksokoban/Makefile.am b/doc/ksokoban/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/ksokoban/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/ksokoban/index.docbook b/doc/ksokoban/index.docbook new file mode 100644 index 00000000..93fbfbea --- /dev/null +++ b/doc/ksokoban/index.docbook @@ -0,0 +1,429 @@ + + + + + +]> + + + +The &ksokoban; Handbook + + + +Anders +Widell + +
&Anders.Widell.mail;
+
+
+ +
+ + +2000 +&Anders.Widell; + + +&FDLNotice; + +2006-06-19 +0.4.2 + + + +&ksokoban; is a &kde; implementation of the Japanese warehouse keeper +game sokoban. + + + + +KDE +kdegames +game +sokoban +ksokoban + +
+ + +Introduction + + +The first sokoban game was created in 1982 by Hiroyuki Imabayashi at +the Japanese company Thinking Rabbit, Inc. Sokoban is +japanese for warehouse keeper. The idea is that you +are a warehouse keeper trying to push crates to their proper locations +in a warehouse. + + + +The problem is that you cannot pull the crates or step over them. If +you are not careful, some of the crates can get stuck in wrong places +and/or block your way. + + + +It can be rather difficult just to solve a level. But if you want to +make it even harder, you can try to minimize the number of moves +and/or pushes you use to solve the level. + + + +To make the game more fun for small kids (below 10 years or so), some +collections with easier levels are also included in &ksokoban;. These +are marked (easy) in the level collection menu. +Of course, these levels can be fun for adults too, for example if you +don't want to expose yourself to too much mental strain. + + + + +The Game + + +The objective of the game is to push all the red gems to the goal +squares, which are marked with green glassy round things on the floor. + + + +Use the cursor keys or the mouse to move about. If you move onto a gem +using the cursor keys or the middle mouse +button, and there is nothing blocking it on the opposite side, then you +will push the gem. It isn't possible to pull the gems, so if you push +a gem into a corner it will be stuck there. You cannot step over the +gems either, so you can also get trapped in a part of the maze by +blocking the way with gems. Also note that it isn't possible to push +more than one gem at a time. + + + +If you notice that you have moved the gems in such a way that the +level is impossible to solve, then you can always use the undo feature +to go back to a position where the mistake has not yet been made. You +can of course also restart the level from the very beginning. + + + +Controls + + + + + +KeyAction + + + +Cursor keys Move one square in a direction +&Ctrl;Cursor keys Move as far as possible in a direction without pushing any gems +&Shift;Cursor keys Move as far as possible in a direction, pushing any gem in the way +Left mouse button Move to any place in the maze that can be reached without pushing any gems +Middle mouse button or &Ctrl;ZUndo the last move +Right mouse button Move up/down/left/right in a straight line, pushing any gem in the way +Mouse wheelTraverse the history +&Ctrl;&Shift;ZRedo the last undone move +N Go to the next level in the current level collection +P Go to the previous level in the current level collection +&Esc; Restart the current level +&Ctrl;QQuit the game + + + + + + + +Loading external levels + + +&ksokoban; has the ability to load external sokoban levels from text +files. You can load levels using the menu entry +Game Load +Levels... , or by specifying the level file +&URL; as a command line argument when starting &ksokoban; from a +shell. + + + +The external levels must be defined using the standard characters +shown in the table below. If the file contains more than one level, +the levels should be separated by blank lines. The file may also +contain text between the levels. + + + + + +CharacterMeaning + + + +#Wall +(space)Empty square +.Goal square +$Object on an empty square +*Object on a goal square +@Start position on an empty square ++Start position on a goal square + + + + + +As an example, below is a text representation of the first level in the +Microban level collection: + + + + +#### +# .# +# ### +#*@ # +# $ # +# ### +#### + + + + + + +Menu Reference + +This is a complete guide to the menus of &ksokoban;. + + +The <guimenu>Game</guimenu> Menu + + + + +Game +Load Levels... + + +Load an external level. See the section Loading External Levels for +more information. + + + + + +N +Game +Next Level + + +Load the next level. + + + + + +P +Game +Previous Level + + +Go back to the previous level. + + + + + +&Esc; +Game +Restart Level + + +Restart the current level. + + + + + +Game +Level Collection + + +Change to a different set of levels. &ksokoban; +comes with several level sets, and you can load more that you can find +on the internet. + + + + + +&Ctrl;Z +Game +Undo + + +Undo the last move + + + + + +&Ctrl;&Shift;Z +Game +Redo + + +Redo the last move you undid with the menu item +above. + + + + + +&Ctrl;Q +Game +Quit + + +Exit &ksokoban;. + + + + + + +The <guimenu>Animation</guimenu> Menu + + +The Animation Menu allows you to specify the speed that movement replay animations are shown at. + + + + + +Animation +Slow + + +Show replay animations at a slow pace. + + + + + +Animation +Medium + + +This is the default setting, and shows replay animations at a slightly faster pace. + + + + + +Animation +Fast + + +Show replay animations at the fastest pace. + + + + + +Animation +Off + + +Do not animate replay animations. + + + + + + + + +The <guimenu>Bookmarks</guimenu> Menu + + + + +Bookmarks +Set Bookmark + + +&ksokoban; allows you to set bookmarks with a level at a +particular state. You might use this to save yourself +repeating the same initial steps in a level. +You can have up to ten bookmarks at a time, and access them via the +hotkey &Ctrl;X, +where X is any digit between 0 and 9. + + + + + +Bookmarks +Go to Bookmark + + +Jump to a state you have previously saved as a +bookmark. + + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + +Credits and License + + +&ksokoban; is copyright © 1998-2000 by &Anders.Widell; +&Anders.Widell.mail;. For +the latest info on &ksokoban;, see the &ksokoban; home page at + +http://hem.passagen.se/awl/ksokoban/ + + + +The background graphics starfield was taken from the +Gimp. All other graphics +were created by &Anders.Widell; &Anders.Widell.mail; using +the Povray ray tracer. + + + +All the currently included sokoban levels were created by David W. +Skinner sasquatch@bentonrea.com. See his sokoban page +at +http://users.bentonrea.com/~sasquatch/sokoban/ + + + +&underFDL; +&underGPL; + + + +Installation + +&install.intro.documentation; + +&install.compile.documentation; + + + +&documentation.index; +
+ diff --git a/doc/kspaceduel/Makefile.am b/doc/kspaceduel/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/kspaceduel/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/kspaceduel/index.docbook b/doc/kspaceduel/index.docbook new file mode 100644 index 00000000..2d3cc2b7 --- /dev/null +++ b/doc/kspaceduel/index.docbook @@ -0,0 +1,659 @@ + + + + + +]> + + + +The &kspaceduel; Handbook + + + +Andreas +Zehender + +
az@azweb.de
+
+
+
+ + + +19992000 +Andreas Zehender + + +&FDLNotice; + +2003-09-17 +1.1 + + +&kspaceduel; is an space arcade game. +Two ships fly around the sun and try to shoot eachother. + + + + +Space duel +KDE +game +ships +shooting + + +
+ + +Introduction + +&kspaceduel; is an space arcade game for two players. + +Each player controls a ship that flies around the sun and tries to shoot +at the other ship. You can play &kspaceduel; with another person, against the +computer, or you can have the computer control both ships and play each +other. + + + + +The Game +The idea of the game is simple (but addictive). You try to destroy your opponent before he destroys you. +Be careful not to hit the sun. + +Rules of the Game + +Ship Movement +Each player controls one ship. +The ships can rotate, accelerate, shoot and lay mines. + + + +Energy + +Each ship has an amount of energy (refer to the game +options for values). + +Ships need energy for rotation, acceleration, shooting and laying mines. A +ship gets energy from its solar panels. The amount of energy a ship gets depends +on the distance from and direction to the sun. A ship gets more energy near the +sun and less energy near the border. It gets the full amount of energy if the +sun shines directly on the panels and less or even no energy if the sun shines +at an angle to the panel or to the side of the panel. + +If a ship has no energy it can't navigate or shoot. + + + + +Hit points + +Collisions with own or other bullets or mines decrease the hit points of a +ship. If two ships collide, the weaker ship is destroyed and the hit points of +the stronger ship are decreased by the hit points of the weaker ship plus an +amount (Crash Damage). A ship is destroyed when it flies into the sun. + + + + +Bullets and Mines + +Bullets fly around the sun like a ship. + +Mines have an amount of energy to stay at the same position. When the +energy is spent, the mine falls into the sun. Mines near the sun need more +energy than those which are further away. + +Mines can be destroyed with bullets. + +By default a ship can have 5 bullets and 3 mines on the screen. + + + + +Powerups + +From time to time powerups appear on the play field. + +There are four different powerups: + + + +Mine +The maximum number of mines is increased for the +player. + + +Bullet +The maximum number of bullets is increased for the +player. + + +Energy (yellow sphere) +The player gets energy. + + +Shield (blue sphere) +The player gets hit points + + + + + + +The &kspaceduel; screen + + +&kspaceduel; Main Screen + + +&kspaceduel; Main Screen + + + +You can see that the central part of the &kspaceduel; screen is the playing +area. To the left and the right are the Red Player's and the Blue Player's +stats. +The top box on the player stats, represents the Hit Points of the +craft. +The middle box on the player stats, represents the battery power. +The bottom box on the player stats, shows the number of wins. +The top of the playing area is occupied by the menubar and toolbar. +The bottom of the playing area is the status bar. + + + + + +Game Options + +&kspaceduel; has many, many options you can adjust to increase the enjoyment +of this game. + +Configuration is divided into two sections: + + +Player Keys +Game Settings + + + +Keys + +&kspaceduel; has a default set of keys to control the game. For a list of +default keys see the section entitled Default key +bindings. + +Keys can be configured by: + +Selecting SettingsConfigure +Shortcuts... from the menubar. + +This will bring up a dialog box which lets you configure all hotkeys, +including the keys used for steering the space ships, for firing bullets, and +for laying mines. + +You can see that each player (Red and Blue), have 5 keystrokes +corresponding to Rotate Left, Rotate Right, Accelerate, Shot, and Mines. + +When you are finished configuring your keys, you should click on +OK to apply your changes. + +If you want to restore the default keys, simply click on the +Default button once. To make these changes permenant, +press OK. + +If you want to abandon the changes you made, and return to your previously +selected keys, simply click Cancel, and your changes +will be lost. + + + +Game Configuration + +All game settings of &kspaceduel; are configurable. + +If you want to change the game settings, simply select +SettingsConfigure &kspaceduel;... + from the menubar. + +This will bring up a dialog box with two pages, labelled +General and Game. The first page +is rather simple, the second one has seven tabs across the top. + +Settings are collected in different configurations. You can choose from +several pre-defined configurations. + +If you choose the Custom configuration, you can +define all settings for yourself. + +If you have not selected Custom, you will not +be able to make any changes to these options. + + If you have found an interesting configuration, mail it to the +author of the game. It can be implemented in a future version (send the part +[Game] in the file +~/.kde/share/config/kspaceduelrc). + +When you are finished altering the options, you should click on +OK to apply your changes. + +If you want to restore the default, simply click on the +Default button once. To make these changes permenant, +press OK. + +If you want to abandon the changes you made, and return to your previous +options, simply click Cancel, and your changes will be +lost. + +The settings are: + + +<guilabel>General</guilabel> + + + +Hit Points +These two sliders allow you to define the hitpoints for each +of the two players; you might want to decrease the hitpoints for a player +to give that player a handycap. + + + +Refresh time +Time between two screen refreshes in milliseconds. All other +settings are independent of the refresh time. + + + +Red Player +Here you can define whether the red player gets controlled by +the AI, and also choose the skill level of the AI for this player. Just +experiment with the different skill level to find one which fits you. + + + + +Blue Player +This works the same as the options for the Red +Player described above. + + + + + + +<guilabel>Game</guilabel> + + + +Game speed +Controls the speed of the whole game. + + + + + + +<guilabel>Bullet</guilabel> + + + +Shot speed +The speed of bullets + + +Energy need +The amount of energy needed for one shot. + + +Max number +The maximum number of bullets a player can have on the +screen. + + +Damage +The number of hit points damage done when a bullet hits a +ship. + + +Life time +The maximum life time of a bullet. + + +Reload time +The time a ship needs to reload a bullet. + + + + + +Mine + + + +Mine fuel +The amount of fuel on a mine. + + +Energy need +The amount of energy needed for laying a mine. + + +Activate time +The time a mine is inactive. + + +Damage +The number of hit points of damage done when a ship hits a +mine. + + +Max number +Maximum number of mines a player can have on the +screen. + + +Reload time +The time a ship needs to reload a mine. + + + + + + +<guilabel>Ship</guilabel> + + + +Acceleration +Acceleration of the ships + + +Energy need +The energy needed to accelerate a ship. + + +Rotation speed +The speed at which a ship rotates. + + +Energy need +The energy needed to rotate a ship. + + +Crash damage +The number of hit points damage done when two ships +collide. + + + + + + +<guilabel>Sun</guilabel> + + + +Sun energy +The strength of the sun. The higher the value, the quicker +ships will be recharged. + + +Gravity +The strength of the gravitational pull of the +sun. + + + + + + +<guilabel>Start</guilabel> + + + +Position X and Position Y +The ships position at the beginning of a new round. The ships +start at opposite sides of the sun. + + +Velocity X and Velocity Y +Velocity at the beginning of a new round. + + + + + + +<guilabel>Powerups</guilabel> + + + +Appearance time +Maximal time between the appearance of two +powerups. + + +Life time +Maximal life time of a powerup. + + +Energy amount +Amount of energy a player gets from an energy +powerup. + + +Shield amount +Amount of hit points a player gets from a shield +powerup. + + + + + + + + + +Commands/Keyboard Shortcuts + +The following sections briefly describe each menubar option. + + +The <guimenu>Game</guimenu> Menu + +The Game menu is used to start and pause the game. + + + + + +&Ctrl;N +GameNew +Starts a new game of &kspaceduel;. + + + + + +&Ctrl;N +GameNew Round +Starts a new round, at the current level. + + + + +P +GamePause +Pauses and unpauses the game. + + + + +&Ctrl; +Q +GameQuit + +Quits &kspaceduel; + + + + + + +<guimenu>Settings</guimenu> Menu + + + + +SettingsShow Toolbar +When selected, the toolbar will be visible. When not selected, +the toolbar will be hidden. + + + + +SettingsShow Statusbar + +When selected, the status bar (the bar along the bottom of the +screen which gives textual information) will be visible. When not selected, the +status bar will be hidden. + + + + +Settings +Configure Shortcuts... + +Allows you to change the keyboard shortcuts for &kspaceduel;, +including the keys for steering the space ship, firing bullets &etc;. + + + + + +Settings +Configure Toolbars... + +Display the standard KDE Toolbar Configuration Dialog. + + + + + +Settings +Configure &kspaceduel;... + +Opens a configuration dialog which lets you define many +settings of the game, refer to the chapter Game +Options for further information. + + + + + + +<guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + +Default Key Bindings + +The following tables show you the default key bindings. + +Menu bindings + + + +Key ComboAction + +&Ctrl;QQuit &kspaceduel; +&Ctrl;NNew Game +&Ctrl;NNew Round +PPause Game +F1Help Contents +&Shift;F1Whats This Help +SpaceStart Game + + + + +The Menu Key Bindings can be changed by selecting +Settings Configure Key Bindings + + +Game Play + + + + + + +ActionRed PlayerBlue Player + +Rotate LeftSLeft Arrow +Rotate RightFRight Arrow +AccelerateEUp Arrow +ShotDDown Arrow +MinesAInsert + + + + +To change these keys, refer to the section entitled Player Keys.... + + + + + +Credits and License + + +&kspaceduel; + + +Program copyright 1999-2000 Andreas Zehender az@azweb.de +Documentation copyright 2000 Andreas Zehender az@azweb.de +Documentation updated for &kde; 2.0 by Mike McBride mpmcbride7@yahoo.com + + +&underFDL; +&underGPL; + + + + +Installation + + +How to obtain &kspaceduel; + +&install.intro.documentation; + + + + +Compilation and Installation + +&install.compile.documentation; + + + + +
+ + diff --git a/doc/kspaceduel/kspaceduel3.png b/doc/kspaceduel/kspaceduel3.png new file mode 100644 index 00000000..a23bc50e Binary files /dev/null and b/doc/kspaceduel/kspaceduel3.png differ diff --git a/doc/ktron/Makefile.am b/doc/ktron/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/ktron/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/ktron/index.docbook b/doc/ktron/index.docbook new file mode 100644 index 00000000..52a76da6 --- /dev/null +++ b/doc/ktron/index.docbook @@ -0,0 +1,575 @@ + + + + + +]> + + + +The &ktron; Handbook + + + +Fabian +Dal Santo + +
&Fabian.DalSanto.mail;
+
+
+ + +Matthias +Kiefer + +
&Matthias.Kiefer.mail;
+
+Developer +
+ + +Lauri +Watts + +
&Lauri.Watts.mail;
+
+Reviewer +
+ +
+ + +1999 +2000 +&Matthias.Kiefer; + + + +2001 +&Fabian.Dal.Santo; + + +&FDLNotice; + +2005-12-16 +1.1 + + + +&ktron; is a simple Tron clone for &kde;, which you can +play alone or against a friend. + + + + +KDE +kdegames +KTron +game +tron + +
+ + +Introduction + + +&ktron; is a simple Tron-Clone for the +K Desktop Environment. You can play +&ktron; against the computer or a friend. + + + +The aim of the game is to live longer than your opponent. To do that, +avoid running into a wall, your own tail and that of your opponent. + + + + + +Playing &ktron; + + +Rules + + +Once a round is started, the players do not stop moving forward (unless the game +is paused). All you have to do is avoid crashing by changing your players +direction. Additionally you can try to hinder your opponent. For this it is +possible to increase the velocity by pressing your accelerator key. + + + +A round starts when all human players press a direction key. The initial moving +direction is then in this direction. + + + +If you want to interrupt playing, select +Pause from the Game menu +or hit the keyboard shortcut (see section Default +Shortcuts). Additionally the game is paused when the game loses the +keyboard focus, ⪚ when switching to an other window. + + +To continue the game, select the menu item +Pause again, or the keyboard shortcut. The +game will also continue if the human players press one of their direction keys. +But be careful, your player switches to this direction immediately. + + + +A game consists of several rounds, and ends if a player has at least +nine points, and additionally two more points than the opponent. +The current score is always displayed in the status bar. + + + + + +The Computer Player + + +You can let the computer be Player One, Player Two or both players. +There are three difficulty levels: Beginner, Average and Expert. + + + +If you choose Beginner, the computer doesn't care about +the opponent and just moves around. This is the same algorithm as used by +xtron-1.1. + + +At skill levels Average and Expert, the computer tries to hinder the +opponent when he comes near. + + + +See the Settings +Menu section for information on how to configure the computer player. + + + + + +Using the Keyboard + + +Each player has five keys. Four keys for changing the direction and one to +accelerate. + + + +The direction keys do not have to be held down. Simply press them once to change +the direction of your player. + + + +Acceleration only occurs while the acceleration key remains depressed. When you +release the acceleration key, velocity returns to normal. + + + +See section Settings +Menu for information how to change the default keys. + + + + + +Command Reference + +The following sections briefly describe each menubar option. + + +The <guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New + +Starts a new game. + + + + + +P + +Game +Pause + +Toggles whether the game is paused. + + + + + +&Ctrl;Q + +Game +Exit + +Quits. + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +Settings +Show Statusbar + +Shows or hides the Statusbar. + + + + +Settings +Configure Shortcuts... + +Displays a standard &kde; shortcuts configurator. + + + + +Settings +Configure &kappname;... + +Opens a comprehensive dialog +to configure various options. + + + + + + +The <guimenuitem>Help</guimenuitem> Menu + +&help.menu.documentation; + + + + +Default Shortcuts + +The following tables show you the default shortcuts. + + + +Player 1 Shortcuts + + + +Key Combo +Action + + +R +Up + + +F +Down + + +G +Right + + +D +Left + + +A +Accelerate + + + +
+ + +Player 2 Shortcuts + + + +Key Combo +Action + + + + +Up Arrow +Up + + +Down Arrow +Down + + +Right Arrow +Right + + +Left Arrow +Left + + +0 +Accelerate + + + +
+ + +General Shortcuts + + + +Key Combo +Action + + + + +P +Pause/Resume Game + + +&Ctrl;N +New Game + + +&Ctrl;Q +Quit &ktron; + + +F1 +Help Contents + + +ShiftF1 +What's This Help + + + +
+ + +These shortcuts can be changed by selecting +Settings Configure Shortcuts... + from the menubar. + + +
+ +
+ + + +The Configuration Dialog + +Selecting the Configure &kappname;... option +in the Settings menu will open a further dialog which lets +you tweak &kappname;'s behavior. + +This dialog is divided into three pages. + + +General Configuration + + + +Show winner by changing color +Enable this box to improve visualizing that the game is over +by making &ktron; change the color of the loser's trail to the color of +the winner. + + + +Disable acceleration +Checking this box will disable the acceleration feature - +pressing the acceleration key will have no effect, both vehicles will always +travel with constant velocity. + + + +Crash when moving in the opposite direction +Enable this to make a vehicle crash into itself as soon as a +player attempts to move into the opposite direction (&ie; a vehicle moves left, +and the player tries to move to the right in one step). If this box is +not checked, nothing happens when a player attempts to move into the opposite +direction. + + + +Player Names +Specify custom names to use for the players to override the use of default ones. + + + +Speed +Use this slider to define how fast the vehicles move; moving the +slider to the left will make the vehicles move slower, moving it to the right +will make them move faster. + + + + + +<acronym>AI</acronym> Configuration + + + +Computer Controls +Use these two checkable boxes to define which players should +be controlled by the computer. + + + +Intelligence: +Selects the skill level of the computer player, +Beginner, Average or +Expert +Beginner ignores the opponent, and just moves +randomly. Average or +Expert causes the computer to actively hinder the +opponent. + + + + + +Appearance Configuration + + + +Line style: +Here you can choose one of four different line styles to be +used for drawing the vehicles. The names of the styles are self-explanatory: +3D Line, 3D Rectangles, +Flat and Circles. + + + + +Line Size +Use this slider to define how broad the trail of a vehicle should +be. Moving the slider towards the left will make the trail more narrow, moving +it to the right will result in a wider trail. + + + +Background +Here you can define the appearance of the playground's +background. If you prefer a simple solid color, select the +Color: option and click on the button at the right of it +to open a convenient color-selection dialog. +You can also choose a background image for the playground. To do so, +select the Image: option, and then provide the filename +of a wallpaper image to use in the input field at the right. Alternatively you +can click on the little button at the very right to open the well-known +file-selection dialog. + + + + +Player 1 color: +Click on the colored rectangle to open a color-selection dialog +which lets you define the color to be used for the vehicle of the first +player. + + + +Player 2 color: +Click on the colored rectangle to open a color-selection dialog +which lets you define the color to be used for the vehicle of the second +player. + + + + + + + +Credits and License + + +&ktron; + +Program Copyright 1999 &Matthias.Kiefer; +&Matthias.Kiefer.mail; + + + +Parts of the code are from xtron-1.1 by Rhett D. Jacobs +rhett@hotel.canberra.edu.au> + + + +Documentation Copyright 1999 &Matthias.Kiefer; +&Matthias.Kiefer.mail; + + +Documentation updated for &kde; 2.0 by &Fabian.Dal.Santo; +&Fabian.DalSanto.mail; + + +&underFDL; +&underGPL; + + + + + +Installation + + +How to obtain &ktron; + +&install.intro.documentation; + + + + +Requirements + + +In order to successfully compile &ktron;, you need &kde; 3.0. All +required libraries as well as &ktron; itself can be found on &kde-ftp;. + + + +Compilation and Installation + +&install.compile.documentation; + +Should you run into problems, please report them to the the +author at &Matthias.Kiefer; + + + + + +
+ + diff --git a/doc/ktuberling/Makefile.am b/doc/ktuberling/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/ktuberling/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/ktuberling/button-new.png b/doc/ktuberling/button-new.png new file mode 100644 index 00000000..4e744438 Binary files /dev/null and b/doc/ktuberling/button-new.png differ diff --git a/doc/ktuberling/button-open.png b/doc/ktuberling/button-open.png new file mode 100644 index 00000000..c62d21d6 Binary files /dev/null and b/doc/ktuberling/button-open.png differ diff --git a/doc/ktuberling/button-print.png b/doc/ktuberling/button-print.png new file mode 100644 index 00000000..8a48c035 Binary files /dev/null and b/doc/ktuberling/button-print.png differ diff --git a/doc/ktuberling/button-redo.png b/doc/ktuberling/button-redo.png new file mode 100644 index 00000000..67733a91 Binary files /dev/null and b/doc/ktuberling/button-redo.png differ diff --git a/doc/ktuberling/button-save.png b/doc/ktuberling/button-save.png new file mode 100644 index 00000000..49e4dae5 Binary files /dev/null and b/doc/ktuberling/button-save.png differ diff --git a/doc/ktuberling/button-undo.png b/doc/ktuberling/button-undo.png new file mode 100644 index 00000000..fb918436 Binary files /dev/null and b/doc/ktuberling/button-undo.png differ diff --git a/doc/ktuberling/gameboard.png b/doc/ktuberling/gameboard.png new file mode 100644 index 00000000..0d1fd1c4 Binary files /dev/null and b/doc/ktuberling/gameboard.png differ diff --git a/doc/ktuberling/index.docbook b/doc/ktuberling/index.docbook new file mode 100644 index 00000000..33b87804 --- /dev/null +++ b/doc/ktuberling/index.docbook @@ -0,0 +1,811 @@ + + + + + + +]> + + + +The &ktuberling; Handbook + + + +Éric +Bischoff + +
&Eric.Bischoff.mail;
+
+ + +Paul +E. +Ahlquist +Jr. + +
&Paul.E.Ahlquist.Jr.mail;
+
+
+ + +Lauri +Watts +Reviewer + + + + +
+ +2006-05-05 +0.05.01 + + +1999200020022006 +Éric Bischoff + + + +2001 +&Paul.E.Ahlquist.Jr; + + +&FDLNotice; + + + +&ktuberling; is a game intended for small children. + + + + +KDE +KTuberling +game +children +tuberling +potato +penguin +aquarium + +
+ + +Introduction + + + + + +&ktuberling; is a game intended for small children. Of course, it may be +suitable for adults who have remained young at heart. + + + +It is a potato editor. That means that you can drag and drop +eyes, mouths, mustache, and other parts of face and goodies onto a potato-like +guy. Similarly, you have a penguin and an aquarium on which you can drop other stuff. + + + +There is no winner for the game. The only purpose is to make the funniest faces +you can. + + + +There is a museum (like a Madame Tusseau gallery) where +you can find many funny examples of decorated potatoes, penguins and +aquariums. Of course, you can send your own creations to the programmer, +Éric Bischoff, who will +include them in the museum if he gets some spare time. + + + +&ktuberling; can also speak. It will spell out the name of +the objects you drag and drop. It will speak in a language +that you can chose. You can even use it to learn a bit of vocabulary +in foreign languages. Currently, &ktuberling; can speak +Danish, German, English, Spanish, French, Italian, Dutch, Portuguese, Romanian, +Serbian, Slovak, Slovenian and Swedish. + + + + +On Screen Fundamentals + + +Mouse Operation + + +There are two areas in the main window: + + + +Playground area, on the left side. + + +Objects area, on the right side, where you select objects to +place on your playground. + + + + +Main Window of &ktuberling; + + + + + + + +Main Window + + + + +Placing an Object + + +To drag an object, move the mouse pointer to the objects +area on the right. Click on the object you want to drag, but do not release +the mouse button until you have moved the mouse pointer to the location in +the playground area where you want to drop the object. + + + + +Moving an Object + +Once dropped in the playground area, an object can be moved. +Just click on the object to select it for further drag and drop. When you +click on it, it goes on top of other objects that were partially hiding it. +This trick is useful for getting the glasses and eyes placed correctly. + + + + +Removing an Object + +To remove an object that has been dropped in the playground +area, drag it back from the playground area to the +objects area. + + + + + +The Tool Bar + + + + + + + + +Toolbar + + + +The toolbar provides buttons for the commonly used functions. + + + +Toolbar Buttons + + + + +Button +Name +Menu Equivalent +Action + + + + + + + + + + + + + + New Game Button + + + + New + + + Game New + + + +Resets the playground area. This cleans all parts off the +playground so a new decoration may be created. + + + + + + + + + + + + + + Load Button + + +Load + + + Game Load... + + + +Opens an existing tuberling file from the museum or from other folders. + + + + + + + + + + + + + Save Button + + +Save + + + Game Save + + + +Saves your creation to your home folder, or to some other folder like +the museum if you wish. The tuberling is saved to a small file where only the +position of objects are saved. + + + + + + + + + + + + + + Print Button + + + +Print + + + + Game Print + + + +Prints your picture (&PostScript; format). + + + + + + + + + + + + + Undo Button + + + + Undo + + + + Edit Undo + + +Undoes last operation. + + + + + + + + + + + + Redo Button + + + +Redo + + + + Edit Redo + + + +Re-does last operation. + + + + + + +
+
+ + +The Menu Items + + + + + + + + +Menu Bar + + + +The <guimenu>Game</guimenu> Menu + + + + + + + +File Menu + + + + + + + +&Ctrl;N + +Game + New + +Clears the playground +area + + + + + +&Ctrl;O + +Game + Load... + + Opens an existing tuberling file from +the museum or from somewhere else if you wish. + + + + + +&Ctrl;S + +Game +Save + +Saves your creation. The tuberling is +saved to a small file where only the position of objects are saved. + + + + + +Game +Save as Picture... + +Creates a graphics file +containing a picture of your tuberling. Available file formats +are XPM, JPEG, PNG and BMP. + + + + + + +&Ctrl;P + +Game +Print... + +Print your tuberling picture using + &PostScript; format. + + + + + + +&Ctrl;Q + +Game +Quit + +Quit &ktuberling;. + + + + + + + + +The <guimenu>Edit</guimenu> Menu + + + + + + + +Edit Menu + + + + + + + +&Ctrl;Z + +Edit +Undo + +Undo the last +object placement. + + + + + + +&Ctrl;Shift +Z + +Edit +Redo + +Re-does the last object +placement. This menu option is active only if you have previously used +Undo. + + + + + + +&Ctrl;C + +Edit +Copy + +Copy the playground area to the +clipboard. + + + + + + + +The <guimenu>Playground</guimenu> Menu + + + + + + + + +Playground Menu + + + + + + +Playground +Potato Guy + +Switches to potato playground. +&ktuberling; remembers the last chosen playground the next +time it starts up. + + + + +Playground +Penguin + +Switches to penguin playground. +&ktuberling; remembers the last chosen playground the next +time it starts up. + + + + +Playground +Aquarium + +Switches to aquarium playground. +&ktuberling; remembers the last chosen playground the next +time it starts up. + + + + + +The <guimenu>Speech</guimenu> Menu + + + + + + + + +Speech Menu + + + +Please note that you need to have kdemultimedia installed +and &artsd; running to be able to hear sounds. + + + + + + +Speech +No Sound + +Toggles sound off. &ktuberling; +remembers of this option the next time it starts up. + + + + +Speech +Danish + +Toggles sound on and speaks Danish. +If Danish sounds are not installed then this option is grayed out. &ktuberling; +remembers of this option the next time it starts up. + + + + +Speech +German + +Toggles sound on and speaks German. +If German sounds are not installed then this option is grayed out. &ktuberling; +remembers of this option the next time it starts up. + + + + +Speech +English + +Toggles sound on and speaks English. +If English sounds are not installed then this option is grayed out. &ktuberling; +remembers of this option the next time it starts up. + + + +etc... +Same for the other languages. + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +Settings Menu + + + + + + +Settings +Show/Hide Toolbar + +Toggle the Toolbar display on and off. + + + + + +Settings +Configure Shortcuts... + +Opens a standard &kde; shortcut configuration dialog, where you can change the +keyboard shortcuts used by &ktuberling;. + + + + + +Settings +Configure Toolbars... + +Display the standard &kde; toolbar configuration dialog. + + + + + + + +The <guimenu>Help</guimenu> Menu + + + + + + + +Help Menu + + +&help.menu.documentation; + + + +
+ +&technical.reference; + + +Credits and License + +&ktuberling; + + + +John Calhoun - Original idea, original pictures and English +sounds + + + +Éric Bischoff &Eric.Bischoff.mail; - &kde; +Programming + + + +François-Xavier Duranceau duranceau@free.fr - Tests, +advice and help + + + +Agnieszka Czajkowska agnieszka@imagegalaxy.de - Penguin graphics + + + +Bas Willems next@euronet.nl - Graphics reworks and aquarium theme + + + +Roger Larsson roger.larsson@norran.net - Sounds tuning + + + +Dolores Almansa dolores.almansa@corazondemaria.org - Educative graphics for COR-EDUX initiative + + + +Peter Silva peter.silva@videotron.ca - Proofreading of +the documentation + + + +Paul Ahlquist &Paul.E.Ahlquist.Jr.mail; - Bettering of +documentation + + + +This game is dedicated to my little daughter Sunniva Bischoff + +Thanks to Apple Computer and to the &LinuxPPC; project for having made +ports of &Linux; to the &Mac;. &ktuberling; would never have existed without +that! + + + +&underFDL; +&underGPL; + + + + + +Installation + + +How to obtain <application>ktuberling</application> + +&install.intro.documentation; + + + + +Requirements + + +In order to successfully compile &ktuberling;, you need &kde; 3.5. +All required libraries as well as &ktuberling; itself can be found on +&kde-ftp;. + + +To be able to hear the sounds, you need to have kdemultimedia installed. + + + + +Compilation and Installation + +&install.compile.documentation; + + + + +
diff --git a/doc/ktuberling/ktuberling.png b/doc/ktuberling/ktuberling.png new file mode 100644 index 00000000..3b44966d Binary files /dev/null and b/doc/ktuberling/ktuberling.png differ diff --git a/doc/ktuberling/menu-edit.png b/doc/ktuberling/menu-edit.png new file mode 100644 index 00000000..d5090713 Binary files /dev/null and b/doc/ktuberling/menu-edit.png differ diff --git a/doc/ktuberling/menu-game.png b/doc/ktuberling/menu-game.png new file mode 100644 index 00000000..93981973 Binary files /dev/null and b/doc/ktuberling/menu-game.png differ diff --git a/doc/ktuberling/menu-help.png b/doc/ktuberling/menu-help.png new file mode 100644 index 00000000..dd02e340 Binary files /dev/null and b/doc/ktuberling/menu-help.png differ diff --git a/doc/ktuberling/menu-playground.png b/doc/ktuberling/menu-playground.png new file mode 100644 index 00000000..c85a7e45 Binary files /dev/null and b/doc/ktuberling/menu-playground.png differ diff --git a/doc/ktuberling/menu-raw.png b/doc/ktuberling/menu-raw.png new file mode 100644 index 00000000..0e0276c6 Binary files /dev/null and b/doc/ktuberling/menu-raw.png differ diff --git a/doc/ktuberling/menu-settings.png b/doc/ktuberling/menu-settings.png new file mode 100644 index 00000000..c581dac7 Binary files /dev/null and b/doc/ktuberling/menu-settings.png differ diff --git a/doc/ktuberling/menu-speech.png b/doc/ktuberling/menu-speech.png new file mode 100644 index 00000000..b8f6e1c5 Binary files /dev/null and b/doc/ktuberling/menu-speech.png differ diff --git a/doc/ktuberling/technical-reference.docbook b/doc/ktuberling/technical-reference.docbook new file mode 100644 index 00000000..94abf945 --- /dev/null +++ b/doc/ktuberling/technical-reference.docbook @@ -0,0 +1,262 @@ + + + + + +Éric +Bischoff + + + + +2006-05-05 +0.05.01 + + +KDE +KTuberling +technical reference + + + +Technical reference + + +&ktuberling; offers a gentle and rewarding introduction to &kde; customization +and programming. The application can be extended. For example, without any +coding, new playgrounds can be added by changing the graphics files. By +adding appropriate sound files, translators can change the sounds to their +native tongue! + + + +If you extend or add to the game please consider sending your additions to the +developer Éric Bischoff for +inclusion in future releases. + + + +For artists + + +The size and shape of the playground and the number of objects can be +changed. New playgrounds can be added. Only two image files need to be +created for each playground: a gameboard and a mask. A maximum of 8 +playgrounds is allowed, out of which only 3 are currently used. + + + +Six images are used in &ktuberling;: potato-game.png, +potato-mask.png, penguin-game.png, +penguin-mask.png, aquarium-game.png +and aquarium-mask.png. The standard location +for these files is the folder $KDEDIR/share/apps/ktuberling/pics/. + + + +The first type of images, *-game.png holds the playground +and the objects that the user selects. This is the graphic that the user sees +when playing the game. + + + +The second type of images, *-mask.png, contains only masks of the +objects. The masks are used to delimit the borders of the objects and, in some +cases, give the object some transparency (for example, the spectacles). It is +mandatory to put the objects at the same position in +the gameboard file as in the mask file. + + + +In the same folder, a file named layout.xml. +($KDEDIR/share/apps/ktuberling/pics/layout.xml) tells +which images to use and links them to menu entries. It also contains the +position parameters of the playground and the objects in the gameboard and +in the masks. It assigns the sounds to objects and places the +objects in groups. It finally declares languages as sets of translated sounds. +It follows standard &XML; syntax (see details +below). + + + +Still in the same folder, a file named layout.i18n +($KDEDIR/share/apps/ktuberling/pics/layout.xml) +recapitulates the strings in layout.xml that can be +translated: + +The menu entries that allow to choose the playground and the language +The names of the categories of objects + + + + +One folder above, a file named ktuberlingui.rc +($KDEDIR/share/apps/ktuberling/ktuberlingui.rc) is a +second &XML; file describing the menus of &ktuberling;. It should contain +one <action> tag per playground and language. +The symbolic name of the action in this file should be identical to +the symbolic name of the action in layout.xml. + + + + + +Translation + + +Besides the usual .po files mechanism for +translating program labels and prompts, the sounds can be localized too. + + + +If the various +translators can record their voice to a .wav +file, they can store that file to the language-specific subfolder of the +sounds folder. The name of the sound is then assigned to a file in the +layout.xml file. For example, if destination language is +Italian, translators can record their voice in .wav files located in +$KDEDIR/share/apps/ktuberling/sounds/it. Then they can +assign the sound named hat to the filename +it/cappello.wav. + + + +In a future release, &ktuberling; will use OGG Vorbis rc3 file format for sounds. +At that moment, it will be possible to convert the WAV files to OGG Vorbis rc3 +through the following command line: + +$ oggenc -q 10 -o sound.ogg sound.wav + + + + +Information on how to work with the translation mechanisms in &kde; is available +in The +&kde; Translation HOWTO. + + + + + + +For programmers +&ktuberling; isn't really difficult to extend for programmers. + + +C++ classes + + + +TopLevel + +Top-level window and basic program management + + + + +PlayGround + +Description of one of the game levels + + + +ToDraw + +Description of one of the graphical objects to be +drawn + + + +SoundFactory + +Description of one of the languages and its sounds + + + +Action + +One of the user's manipulation in the undo/redo stack + + + + + + +<literal role="extension">.tuberling</literal> files structure + +A .tuberling file holds all the +necessary data to redraw a tuberling. It can be edited with an ordinary text +editor. + +The first line holds the number of the playground. + +On all other lines, there is one graphical object per line, in the order +that is used to draw them. Each line contains 5 numbers: the identifier of the object, +and the rectangle where it should be drawn (left, top, right, bottom). The numbers are +separated by whitespaces. + + + + + +Structure of the layout file (<filename>layout.xml</filename>) + + +The top-level tag is unique and is named <ktuberling>. +It contains several <playground> tags, one per +playground, and several <language> tags, one per language. + + + +The <playground> tag has two attributes: gameboard +and masks. These attributes give the name of the files holding the +pictures. The <playground> tag also contains one +<menuitem> tag, one <editablearea> +tag, several <category> tags, and several +<object> tags. + + + +The <menuitem> tag describes the action identifier +of the menu item allowing to select position of the +area where you can drop objects, and the label of this menu item. +This action identifier should be identical to the one in +ktuberlingui.rc. + + + +The <editablearea> tag describes the position of the +area where you can drop objects, and the name of the sound associated with it. + + + +The <category> tag describes the position and +the label of a text describing a group of objects. For example, it +can describe the position and the text of the group of goodies. + + + +The <object> tag describes the position (in the +gameboard and in the masks) of an object, as well as the name of the sound +associated with it. + + + +The <language> tag has one attribute: code +This attribute give the code of the locale for that language. +The <language> tag also contains one +<menuitem> tag and several +<sound> tags. + + + +The lower level tags are not explained here, since their meaning is +quite straightforward. If you modify layout.xml, +don't forget to modify layout.i18n and +ktuberlingui.rc accordingly. + + + + diff --git a/doc/ktuberling/toolbar.png b/doc/ktuberling/toolbar.png new file mode 100644 index 00000000..9ff20d9a Binary files /dev/null and b/doc/ktuberling/toolbar.png differ diff --git a/doc/kwin4/Makefile.am b/doc/kwin4/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/kwin4/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/kwin4/index.docbook b/doc/kwin4/index.docbook new file mode 100644 index 00000000..afef0bfc --- /dev/null +++ b/doc/kwin4/index.docbook @@ -0,0 +1,441 @@ + + + + + +]> + + + +Four Wins + + + +Martin +Heni + +
&Martin.Heni.mail;
+
+
+ + +
+ + +19952002 +&Martin.Heni; + + +&FDLNotice; + +2006-06-17 +1.1.0 + + + +&k4wins; is a four-in-a-row game for &kde;. + + + +KDE +kdegames +game +board +board game +four wins +four +four in a row +win4 +kwin4 +connect four +connect 4 + +
+ + +Overview + + +Four wins is a game for two players. + + + +Each player is represented by a color (yellow and red). The goal of the +game is to get four connected pieces of your color into a row, column or +any diagonal. This is done by placing one of your pieces into any of +the seven columns. + + + +A piece will begin to fill a column from the bottom, &ie; it +will fall down until it reaches the ground level or another stone. +After a move is done it is the turn of the other player. + + + +This is repeated until the game is over, which is when one of the +players has four pieces in a row, column or diagonal or no more moves +are possible because the board is filled. + + + + + +Rules + + +The board is separated into three regions. + + + +The game board is constructed out of 7x6 fields +which will be filled from bottom to top. The fields are marked in the +color of the player who made the current move. On top of each column a +colored arrow shows were the last piece had been put. + + + +The status display shows which player color +starts and which color is played by whom (player, computer, remote +connection). It further shows the level of the computer opponent, the +number of moves done as well as the computer calculated chance of +winning. This chance is calculated only if the computer opponent makes a +move. A positive number means that the player has an advantage, a +negative number means that the computer thinks he is better. + + + +The table display shows the number of won, lost +and drawn games is noted for both player. Also the number of aborted +games (Brk) and the sum of games is shown. + + + + + +Remote connections + + +It is possible to play the game over a network connection with another +computer. +One of the computers will act as game server. This one can determine +who should play which color. You can configure the network options +in the menu GameNetwork Configuration... +There you also find a chat dialog +box which allows you to speak with your friend. + + + + +When a network connection is build you are asked to enter a remote host +and a port. The port can usually just be left untouched, but if you know +what you are doing replace it by another number, which has to be the +same in both player games of course. The hostname should be the name of +the remote host to which you are connecting. Only the client in +the connection has to supply a hostname. It is often wise that the player +behind a firewall chooses to be client as the firewall might not allow +incoming connections. + + + + + +Menus + + +<guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New + + + +Starts a new game. In a network game this option is only available for +the network server. The network client will be automatically started by +the server. + + + + + + + +&Ctrl;O + +Game +Load... + + + +Loads a saved game. + + + + + + + +&Ctrl;S + +Game +Save + + + +Saves the current game. + + + + + + + +&Ctrl;End + +Game +End Game + + +Aborts a running game. + + + + + + +Game +Network Configuration... + + +Pops up a dialog for the network configuration. You can choose to be +server or client. If you are server you can also choose what color the +remote player should take over. If a network game is running you can +also disconnect it in this menu. + + + + + + +Game +Network Chat... + + +Pops up a chat widget which allows you to send messages to the +other party. + + + + + + + +H + +Game +Hint + + + +The computer will calculate the best possible move and mark it with a +small circle on the board. How good the move is depends on the level of +the computer. + + + + + + + +&Ctrl;Q + +Game +Quit + + + +Quits the program. + + + + + + + + +<guimenu>Edit</guimenu> Menu + + + + +&Ctrl;Z + +Edit +Undo + + + +Undo the last move. If the previous player is played by the computer two +moves are taken back so that it is the player's turn again. + + + + + + + +&Ctrl;&Shift;Z + +Edit +Redo + + + +Replay a move which had been undone. + + + + + + + + + +<guimenu>Settings</guimenu> Menu + + + + + +Settings +Show Toolbar + + + +Toggle on and off the display of the toolbar. + + + + + + +Settings +Show Statusbar + + + +Toggle on and off the display of the status bar. + + + + + + +Settings +Configure Shortcuts... + + +Open a dialog which lets you redefine all the keyboard shortcuts. + + + + + +Settings +Configure Toolbars... + +Displays a &kde; standard dialog where you can configure the toolbar icons. + + + + +Settings +Configure &k4wins;... + +Displays the &k4wins; +configuration dialog with these options: +Starting Player Color: Determine which color player has the first move in the next game. +Player Names: Change the names of the players. +Yellow Plays With: +Choose who should play for player 1 (yellow). It can be either +Mouse or Keyboard, +&ie; a local player using the mouse or keyboard +as input device or Computer, &ie; the computer +plays for this player. +Red Plays With: Same as Yellow Playes With but for player 2 +(red). +Computer Difficulty: Select the level of the computer player. + + + + + + + +<guimenuitem>Help</guimenuitem> Menu + +&help.menu.documentation; + + + + + + +Credits and License + + +&k4wins; + + +Program copyright 1995-2002 &Martin.Heni; &Martin.Heni.mail; + + +Documentation copyright 2002 &Martin.Heni; &Martin.Heni.mail; + + + + +&underFDL; +&underGPL; + + + + +Installation + + +How to obtain &k4wins; + +&install.intro.documentation; + + + + +Compilation and Installation + +&install.compile.documentation; + + + + + +&documentation.index; +
+ + diff --git a/doc/lskat/Makefile.am b/doc/lskat/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/lskat/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/lskat/index.docbook b/doc/lskat/index.docbook new file mode 100644 index 00000000..198d3ff6 --- /dev/null +++ b/doc/lskat/index.docbook @@ -0,0 +1,467 @@ + + + + + +]> + + + +Lieutnant Skat + + +Martin +Heni + +
martin@heni-online.de
+
+
+ +MikeMcBride +Reviewer + +
mpmcbride7@yahoo.com
+
+ + + +
+ +2003-09-16 +0.09.01 + + +This documentation describes &lskat; version 0.9.1 + + + +KDE +kdegames +game +playing cards +skat +lieutenant +lskat + +
+ + +Overview + + +Lieutenant skat (from German Offiziersskat) is a card game for two +players. It is roughly played according to the rules of +Skat but with only two players and simplified +rules. + + + +Every player has a set of cards in front of him/her, half of them +covered and half of them open. Both players try to win more than 60 of +the 120 possible points. After 16 moves all cards are played and the +game ends. + + + + + +Rules + + +The game has a randomly chosen trump card. It is shown as a small suit +symbol in the upper right corner of the score field. All four Jacks are +also counted as trumps and do not belong to their +suits. So if heart is trump all seven hearts as +well as all four Jacks count as trumps, making 11 trumps. + + + + +The Jack of clubs does not count as club but as +trump. + + + + +A special trump is the Grand. In this game no +suit is trump and only the four Jacks count as trump. In the game this +is indicated by the symbol of the Jack's head. + + + +Every player owns 16 cards. 8 of them covered 8 of them open. The start +player can play any of his/her open cards. + + + +The second player has following choices: + + + + +The second player has in his/her open cards the same suit. +He/she has to play one of this suit then. The player with +the higher card (see table below) wins both cards. + + +The second player does not have a card of the played suit. +He/she can now play any other card. If it is a trump the +second player will win both cards. Otherwise the +first player will win both cards. + + + + +If there is still a covered card below a played card it will be uncovered +and is from now on an open card. + + + +The player who won the cards can make the next turn. + + + +The sequence of the cards (from the highest to the lowest - +holding for any trump): + +Jack of clubs +Jack of spades +Jack of hearts +Jack of diamonds +Ace +Ten +King +Queen +Nine +Eight +Seven + + + +The player who wins both cards will add their value to his score. The +values of the cards are: + + + + + Card Value + + + Ace 11 + Ten 10 + King 4 + Queen 3 + Jack 2 + Nine 0 + Eight 0 + Seven 0 + + + + + +Scoring + + +The game is won if you have more than 60 points. With both players having +60 points the game ends in a draw. + + + + + +PointsScore + + +601 +61...902 +91...1193 +1204 + + + + + + + + +Remote connections + + +It is possible to play the game over a network connection with another +computer. To do so both players on both computers have to select one +color played by the local player and the other by the remote player. Who +chooses which side does not matter. It even does not matter if both +choose to play the same player as this will automatically be +interchanged by the game. + + + +One of the computers will act as game server. Only this one can start a +new network game. Also all its game data will be transfered to the +client computer. You can force your computer to be server if you do not +supply a hostname in the network dialog. This dialog pops up if you +start a new network game. As a client you enter the hostname of the +machine on which the server is running. The port does not usually need to be +changed, but you can enter any valid port number there. + + + + + +Menus + + +<guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New + + +Starts a new game. In a network game this option is +only available for the network server. The network client will be +automatically started by the server. + + + + + + +Game +End Game + + +Aborts a running game. + + + + + +Game +Clear Statistics + + +Clears the all time game memory. This statistic is +usually saved when the game ended and shows the amount of +played and won games per player. This is cleared with this menu +option. + + + + + + +&Ctrl;M + +Game +Send Message... + + +If connected to another game in a remote session this +option allows you to send a message to the remote +player. + + + + + + +&Ctrl;Q + +Game +Quit + + +Quits the program. + + + + + + + +<guimenu>Settings</guimenu> Menu + + + + +Settings +Statusbar + + +Shows or hides the statusbar. + + + + + +Settings +Starting Player + +Selects whether player 1 (top) or player two (bottom) +begins the next game. + + + + + +Settings +Player 1 Played By + + + +Chooses who should play for player 1 (top). It can be one of the +following: + + + + +A local player using the mouse as input device. For this option, choose +Player. + + + +An artificual computer player. For this option, choose +Computer. + + + +A remote player, who is connected to this computer through a network. +For this option choose Remote. + + + + + + +SettingsPlayer 2 Played By + +Same as the Player 1 Played By option, only this refers to Player 2. + + + + + +Settings +Level + + +Selects the level of the computer player. + + + + + +Settings +Select Card Deck + + +Choose the face and back of the carddeck used for playing. You +can select these in a preview dialog showing all carddecks installed for +&kde;. + + + + + +Settings +Change Names... + + +Change the names of the players. + + + + + + +Settings +Configure Shortcuts... + + +Open a dialog which lets you redefine all the keyboard shortcuts. + + + + + + + +The <guimenuitem>Help</guimenuitem> Menu + + +&help.menu.documentation; + + + + + + +Credits and License + + +&lskat; + + +Program copyright 2000,2001 Martin Heni martin@heni-online.de + + +Documentation copyright 2000,2001 Martin Heni martin@heni-online.de + + +Documentation converted to &kde; 2.0 by Mike McBride +mpmcbride7@yahoo.com + + + + +&underFDL; +&underGPL; + + + + + +Installation + +&install.intro.documentation; + + + +Compilation and Installation + +&install.compile.documentation; + + + + +&documentation.index; +
+ + + + diff --git a/kasteroids/ChangeLog b/kasteroids/ChangeLog new file mode 100644 index 00000000..ccccd0c2 --- /dev/null +++ b/kasteroids/ChangeLog @@ -0,0 +1,35 @@ +Version 2.2 + * [Martin Jones] Show "Game Over" and stats in the game window instead + of displaying many annoying dialogs at the end of the + game. + +Version 2.1 + * [Martin Jones] + Shooting uses energy too. + Added firepower powerup. + Adjusted speed of asteroids to make game playable again. + +Version 1.90 + * [Martin Jones] KDE 2.0 and QCanvas port + Introduce ship's energy supply. Thrusting, shields, + etc. use power, and when you run out you're basically + screwed. Fortunately some asteroids release fuel + when you destroy them. + Make brakes, etc. permanent features once you catch them. + Made ship much cooler. + +Version 0.3.1 + * [Kristof ] + Add brakes, shield, teleport, configureable keys, + highscores. + Also the cool shield graphics. + +Verison 0.2.0 + * [Martin Jones] Use QwSpriteField-1.4 from kdesupport + * [Martin Jones] Preliminary sound support + +Version 0.1.0 + * [Robert Williams] Added version.h and ChangeLog + * [Robert Williams] Renamed Kasteroids.kdelnk to kasteroids.kdelnk + * [Robert Williams] Added -caption "%c" to kasteroids.kdelnk + * [Robert Williams] Added getCaption() diff --git a/kasteroids/Makefile.am b/kasteroids/Makefile.am new file mode 100644 index 00000000..3ac5f46a --- /dev/null +++ b/kasteroids/Makefile.am @@ -0,0 +1,26 @@ +SUBDIRS = . sprites sounds + +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) +METASOURCES = AUTO + +bin_PROGRAMS = kasteroids +kasteroids_SOURCES = main.cpp view.cpp ledmeter.cpp toplevel.cpp settings.kcfgc +kasteroids_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kasteroids_LDADD = $(LIB_KDEGAMES) -lsoundserver_idl -lqtmcop +kasteroids_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +picsdir = $(kde_datadir)/kasteroids/sprites +pics_DATA = bg.png + +xdg_apps_DATA = kasteroids.desktop +kde_kcfg_DATA = kasteroids.kcfg + +EXTRA_DIST = $(pics_DATA) +KDE_ICON = kasteroids + +rcdir = $(kde_datadir)/kasteroids +rc_DATA = kasteroidsui.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kasteroids.pot + diff --git a/kasteroids/bg.png b/kasteroids/bg.png new file mode 100644 index 00000000..e4c5adc0 Binary files /dev/null and b/kasteroids/bg.png differ diff --git a/kasteroids/configure.in.in b/kasteroids/configure.in.in new file mode 100644 index 00000000..17ee6ce2 --- /dev/null +++ b/kasteroids/configure.in.in @@ -0,0 +1,5 @@ +dnl KDE_CHECK_QWSPRITEFIELD(DO_NOT_COMPILE="$DO_NOT_COMPILE kasteroids") + +if test "x$build_arts" = "xno"; then + DO_NOT_COMPILE="$DO_NOT_COMPILE kasteroids" +fi diff --git a/kasteroids/hi128-app-kasteroids.png b/kasteroids/hi128-app-kasteroids.png new file mode 100644 index 00000000..9f5307ff Binary files /dev/null and b/kasteroids/hi128-app-kasteroids.png differ diff --git a/kasteroids/hi16-app-kasteroids.png b/kasteroids/hi16-app-kasteroids.png new file mode 100644 index 00000000..8fca3e8f Binary files /dev/null and b/kasteroids/hi16-app-kasteroids.png differ diff --git a/kasteroids/hi22-app-kasteroids.png b/kasteroids/hi22-app-kasteroids.png new file mode 100644 index 00000000..207327a6 Binary files /dev/null and b/kasteroids/hi22-app-kasteroids.png differ diff --git a/kasteroids/hi32-app-kasteroids.png b/kasteroids/hi32-app-kasteroids.png new file mode 100644 index 00000000..7416e7fb Binary files /dev/null and b/kasteroids/hi32-app-kasteroids.png differ diff --git a/kasteroids/hi48-app-kasteroids.png b/kasteroids/hi48-app-kasteroids.png new file mode 100644 index 00000000..1ce848f8 Binary files /dev/null and b/kasteroids/hi48-app-kasteroids.png differ diff --git a/kasteroids/hi64-app-kasteroids.png b/kasteroids/hi64-app-kasteroids.png new file mode 100644 index 00000000..f879c3ae Binary files /dev/null and b/kasteroids/hi64-app-kasteroids.png differ diff --git a/kasteroids/kasteroids.desktop b/kasteroids/kasteroids.desktop new file mode 100644 index 00000000..caa94e9e --- /dev/null +++ b/kasteroids/kasteroids.desktop @@ -0,0 +1,79 @@ +[Desktop Entry] +Name=KAsteroids +Name[af]=Kasteroids +Name[ar]=لعبة الكويكبات (KAsteroids) +Name[be]=ÐÑÑ‚Ñроіды +Name[bn]=কে-অà§à¦¯à¦¾à¦¸à§à¦Ÿà§‡à¦°à§Ÿà§‡à¦¡ +Name[eo]=Asteroidoj +Name[hi]=के-à¤à¤¸à¥à¤Ÿà¤°à¥‰à¤‡à¤¡à¥à¤¸ +Name[nb]=KAsteroider +Name[ne]=केडीई à¤à¤¸à¥à¤Ÿà¥‡à¤°à¥‹à¤‡à¤¡ +Name[pa]=ਕੇ-ਉਲਕਾ +Name[pl]=Asteroidy +Name[pt_BR]=KAsteróides +Name[sv]=Kasteroids +Name[ta]=கேஅஸà¯à®Ÿà®°à®¾à®¯à¯à®Ÿà¯à®¸à¯ +Name[tg]=KÐÑтероидҳо +Name[th]=ยิงอุà¸à¸²à¸šà¸²à¸• - K +Name[tr]=GöktaÅŸları +Name[uz]=Asteroidlar +Name[uz@cyrillic]=ÐÑтероидлар +Name[wa]=KAsteroyides +Name[zh_TW]=KAsteroids å°è¡Œæ˜Ÿ +MimeType= +Exec=kasteroids %i %m -caption "%c" +GenericName=Space Game +GenericName[be]=КаÑÐ¼Ñ–Ñ‡Ð½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ +GenericName[bg]=КоÑмичеÑка игра +GenericName[bn]=মহাশূনà§à¦¯à§‡à¦° খেলা +GenericName[bs]=Svemirska igra +GenericName[ca]=Joc de l'espai +GenericName[cs]=Vesmírná hra +GenericName[cy]=Gêm Ofod +GenericName[da]=Rumspil +GenericName[de]=Weltraumspiel +GenericName[el]=Διαστημικό παιχνίδι +GenericName[eo]=Kosmoludo +GenericName[es]=Juego espacial +GenericName[et]=Kosmoselahing +GenericName[eu]=Espazio-jokoa +GenericName[fa]=بازی Ùضایی +GenericName[fi]=Avaruuspeli +GenericName[fr]=Jeu dans l'espace +GenericName[he]=משחק חלל +GenericName[hr]=Igra u Svemiru +GenericName[hu]=Å°rhajós +GenericName[is]=Geimleikur +GenericName[it]=Gioco spaziale +GenericName[ja]=宇宙ゲーム +GenericName[km]=ល្បែង​អាវកាស +GenericName[lt]=Kosminis žaidimas +GenericName[lv]=Kosmiska spÄ“le +GenericName[mk]=Ð’ÑеленÑка игра +GenericName[nb]=Romspill +GenericName[nds]=Weltruumspeel +GenericName[ne]=खाली सà¥à¤¥à¤¾à¤¨ खेल +GenericName[nl]=Ruimtespel +GenericName[nn]=Romspel +GenericName[pa]=ਪà©à¨²à¨¾à©œ ਖੇਡ +GenericName[pl]=Gra kosmiczna +GenericName[pt]=Jogo Espacial +GenericName[pt_BR]=Jogo espacial +GenericName[ru]=ÐÑтероиды +GenericName[sk]=Vesmírna hra +GenericName[sl]=Vesoljska igra +GenericName[sr]=СвемирÑка игра +GenericName[sr@Latn]=Svemirska igra +GenericName[sv]=Rymdspel +GenericName[ta]=விணà¯à®µà¯†à®³à®¿ விளையாடà¯à®Ÿà¯ +GenericName[uk]=КоÑмічна гра +GenericName[zh_CN]=ç©ºé—´æ¸¸æˆ +GenericName[zh_TW]=太空éŠæˆ² +Icon=kasteroids +Path= +DocPath=kasteroids/index.html +Type=Application +Terminal=false +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;ArcadeGame; diff --git a/kasteroids/kasteroids.kcfg b/kasteroids/kasteroids.kcfg new file mode 100644 index 00000000..03451e75 --- /dev/null +++ b/kasteroids/kasteroids.kcfg @@ -0,0 +1,35 @@ + + + + + + + true + + + + false + + + + 3 + + + + + + true + + + + Explosion.wav + + + + ploop.wav + + + diff --git a/kasteroids/kasteroidsui.rc b/kasteroids/kasteroidsui.rc new file mode 100644 index 00000000..8305e3d6 --- /dev/null +++ b/kasteroids/kasteroidsui.rc @@ -0,0 +1,6 @@ + + + + + + diff --git a/kasteroids/ledmeter.cpp b/kasteroids/ledmeter.cpp new file mode 100644 index 00000000..3df87b8f --- /dev/null +++ b/kasteroids/ledmeter.cpp @@ -0,0 +1,118 @@ +/* + * KAsteroids - Copyright (c) Martin R. Jones 1997 + * + * Part of the KDE project + */ + +#include +#include "ledmeter.h" +#include "ledmeter.moc" + +KALedMeter::KALedMeter( QWidget *parent ) : QFrame( parent ) +{ + mCRanges.setAutoDelete( true ); + mRange = 100; + mCount = 20; + mCurrentCount = 0; + mValue = 0; + setMinimumWidth( mCount * 2 + frameWidth() ); +} + +void KALedMeter::setRange( int r ) +{ + mRange = r; + if ( mRange < 1 ) + mRange = 1; + setValue( mValue ); + update(); +} + +void KALedMeter::setCount( int c ) +{ + mCount = c; + if ( mCount < 1 ) + mCount = 1; + setMinimumWidth( mCount * 2 + frameWidth() ); + calcColorRanges(); + setValue( mValue ); + update(); +} + +void KALedMeter::setValue( int v ) +{ + mValue = v; + if ( mValue > mRange ) + mValue = mRange; + else if ( mValue < 0 ) + mValue = 0; + int c = ( mValue + mRange / mCount - 1 ) * mCount / mRange; + if ( c != mCurrentCount ) + { + mCurrentCount = c; + update(); + } +} + +void KALedMeter::addColorRange( int pc, const QColor &c ) +{ + ColorRange *cr = new ColorRange; + cr->mPc = pc; + cr->mColor = c; + mCRanges.append( cr ); + calcColorRanges(); +} + +void KALedMeter::resizeEvent( QResizeEvent *e ) +{ + QFrame::resizeEvent( e ); + int w = ( width() - frameWidth() - 2 ) / mCount * mCount; + w += frameWidth() + 2; + setFrameRect( QRect( 0, 0, w, height() ) ); +} + +void KALedMeter::drawContents( QPainter *p ) +{ + QRect b = contentsRect(); + + unsigned cidx = 0; + int ncol = mCount; + QColor col = colorGroup().foreground(); + + if ( !mCRanges.isEmpty() ) + { + col = mCRanges.at( cidx )->mColor; + ncol = mCRanges.at( cidx )->mValue; + } + p->setBrush( col ); + p->setPen( col ); + + int lw = b.width() / mCount; + int lx = b.left() + 1; + for ( int i = 0; i < mCurrentCount; i++, lx += lw ) + { + if ( i > ncol ) + { + if ( ++cidx < mCRanges.count() ) + { + col = mCRanges.at( cidx )->mColor; + ncol = mCRanges.at( cidx )->mValue; + p->setBrush( col ); + p->setPen( col ); + } + } + + p->drawRect( lx, b.top() + 1, lw - 1, b.height() - 2 ); + } +} + +void KALedMeter::calcColorRanges() +{ + int prev = 0; + ColorRange *cr; + for ( cr = mCRanges.first(); cr; cr = mCRanges.next() ) + { + cr->mValue = prev + cr->mPc * mCount / 100; + prev = cr->mValue; + } +} + diff --git a/kasteroids/ledmeter.h b/kasteroids/ledmeter.h new file mode 100644 index 00000000..ea9b7e96 --- /dev/null +++ b/kasteroids/ledmeter.h @@ -0,0 +1,54 @@ +/* + * KAsteroids - Copyright (c) Martin R. Jones 1997 + * + * Part of the KDE project + */ + +#ifndef __LEDMETER_H__ +#define __LEDMETER_H__ + +#include +#include + + +class KALedMeter : public QFrame +{ + Q_OBJECT +public: + KALedMeter( QWidget *parent ); + + int range() const { return mRange; } + void setRange( int r ); + + int count() const { return mCount; } + void setCount( int c ); + + int value () const { return mValue; } + + void addColorRange( int pc, const QColor &c ); + +public slots: + void setValue( int v ); + +protected: + virtual void resizeEvent( QResizeEvent * ); + virtual void drawContents( QPainter * ); + void calcColorRanges(); + +protected: + struct ColorRange + { + int mPc; + int mValue; + QColor mColor; + }; + + int mRange; + int mCount; + int mCurrentCount; + int mValue; + QPtrList mCRanges; +}; + +#endif + diff --git a/kasteroids/main.cpp b/kasteroids/main.cpp new file mode 100644 index 00000000..361c9c77 --- /dev/null +++ b/kasteroids/main.cpp @@ -0,0 +1,51 @@ +/* + * KAsteroids - Copyright (c) Martin R. Jones 1997 + * + * Part of the KDE project + */ +#include + +#include +#include +#include +#include + +#include "version.h" +#include "toplevel.h" + +#ifdef KA_ENABLE_SOUND +#include +#endif + + +static const char description[] = + I18N_NOOP("KDE Space Game"); + +int main( int argc, char *argv[] ) +{ + KAboutData aboutData( "kasteroids", I18N_NOOP("KAsteroids"), + KASTEROIDS_VERSION, description, KAboutData::License_GPL, + "(c) 1997, Martin R. Jones"); + aboutData.addAuthor("Martin R. Jones",0, "mjones@kde.org"); + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication app; + KGlobal::locale()->insertCatalogue("libkdegames"); + KGlobal::dirs()->addResourceType("sprite", KStandardDirs::kde_default("data") + "kasteroids/sprites/"); + KGlobal::dirs()->addResourceType("sounds", KStandardDirs::kde_default("data") + "kasteroids/sounds/"); + +#ifdef KA_ENABLE_SOUND + // setup mcop communication + Arts::Dispatcher dispatcher; +#endif + + if( app.isRestored() ) + RESTORE(KAstTopLevel) + else { + KAstTopLevel *w = new KAstTopLevel; + app.setMainWidget(w); + w->show(); + } + return app.exec(); +} + diff --git a/kasteroids/settings.kcfgc b/kasteroids/settings.kcfgc new file mode 100644 index 00000000..f8d9e21d --- /dev/null +++ b/kasteroids/settings.kcfgc @@ -0,0 +1,5 @@ +# Code generation options for kconfig_compiler +File=kasteroids.kcfg +ClassName=Settings +Singleton=true +Mutators=false diff --git a/kasteroids/sounds/Explosion.wav b/kasteroids/sounds/Explosion.wav new file mode 100644 index 00000000..7b140b1c Binary files /dev/null and b/kasteroids/sounds/Explosion.wav differ diff --git a/kasteroids/sounds/Makefile.am b/kasteroids/sounds/Makefile.am new file mode 100644 index 00000000..9bdf7b99 --- /dev/null +++ b/kasteroids/sounds/Makefile.am @@ -0,0 +1,5 @@ +sound_DATA = Explosion.wav +sounddir = $(kde_datadir)/kasteroids/sounds + +EXTRA_DIST = $(sound_DATA) + diff --git a/kasteroids/sprites.h b/kasteroids/sprites.h new file mode 100644 index 00000000..0b0a718a --- /dev/null +++ b/kasteroids/sprites.h @@ -0,0 +1,129 @@ +/* + * KAsteroids - Copyright (c) Martin R. Jones 1997 + * + * Part of the KDE project + */ + +#ifndef __SPRITES_H__ +#define __SPRITES_H__ + +#include + +#define ID_ROCK_LARGE 1024 +#define ID_ROCK_MEDIUM 1025 +#define ID_ROCK_SMALL 1026 + +#define ID_MISSILE 1030 + +#define ID_BIT 1040 +#define ID_EXHAUST 1041 + +#define ID_ENERGY_POWERUP 1310 +#define ID_TELEPORT_POWERUP 1311 +#define ID_BRAKE_POWERUP 1312 +#define ID_SHIELD_POWERUP 1313 +#define ID_SHOOT_POWERUP 1314 + +#define ID_SHIP 1350 +#define ID_SHIELD 1351 + +#define MAX_SHIELD_AGE 350 +#define MAX_POWERUP_AGE 500 +#define MAX_MISSILE_AGE 40 + +class KMissile : public QCanvasSprite +{ +public: + KMissile( QCanvasPixmapArray *s, QCanvas *c ) : QCanvasSprite( s, c ) + { myAge = 0; } + + virtual int rtti() const { return ID_MISSILE; } + + void growOlder() { myAge++; } + bool expired() { return myAge > MAX_MISSILE_AGE; } + +private: + int myAge; +}; + +class KBit : public QCanvasSprite +{ +public: + KBit( QCanvasPixmapArray *s, QCanvas *c ) : QCanvasSprite( s, c ) + { death = 7; } + + virtual int rtti() const { return ID_BIT; } + + void setDeath( int d ) { death = d; } + void growOlder() { death--; } + bool expired() { return death <= 0; } + +private: + int death; +}; + +class KExhaust : public QCanvasSprite +{ +public: + KExhaust( QCanvasPixmapArray *s, QCanvas *c ) : QCanvasSprite( s, c ) + { death = 1; } + + virtual int rtti() const { return ID_EXHAUST; } + + void setDeath( int d ) { death = d; } + void growOlder() { death--; } + bool expired() { return death <= 0; } + +private: + int death; +}; + +class KPowerup : public QCanvasSprite +{ +public: + KPowerup( QCanvasPixmapArray *s, QCanvas *c, int t ) : QCanvasSprite( s, c ), + myAge( 0 ), type(t) { } + + virtual int rtti() const { return type; } + + void growOlder() { myAge++; } + bool expired() const { return myAge > MAX_POWERUP_AGE; } + +protected: + int myAge; + int type; +}; + +class KRock : public QCanvasSprite +{ +public: + KRock (QCanvasPixmapArray *s, QCanvas *c, int t, int sk, int st) : QCanvasSprite( s, c ) + { type = t; skip = cskip = sk; step = st; } + + void nextFrame() + { + if (cskip-- <= 0) { + setFrame( (frame()+step+frameCount())%frameCount() ); + cskip = QABS(skip); + } + } + + virtual int rtti() const { return type; } + +private: + int type; + int skip; + int cskip; + int step; +}; + +class KShield : public QCanvasSprite +{ +public: + KShield( QCanvasPixmapArray *s, QCanvas *c ) + : QCanvasSprite( s, c ) {} + + virtual int rtti() const { return ID_SHIELD; } +}; + +#endif diff --git a/kasteroids/sprites/.pbm b/kasteroids/sprites/.pbm new file mode 100644 index 00000000..e69de29b diff --git a/kasteroids/sprites/Makefile.am b/kasteroids/sprites/Makefile.am new file mode 100644 index 00000000..f11d78f9 --- /dev/null +++ b/kasteroids/sprites/Makefile.am @@ -0,0 +1,36 @@ + +SUBDIRS = bits missile rock1 rock2 rock3 ship powerups exhaust + +POVRAY=x-povray # Must be Povray 3.0 + +#all: pngtoppmpgm + +#clean: +# rm -f *~ pngtoppmpgm + +#distclean: clean +# rm -f Makefile + +FILES = `cd $(srcdir) && find . \( -name "*.png" \) -print` +spritesdir = $(kde_datadir)/kasteroids/sprites + +install-data-local: + $(mkinstalldirs) $(DESTDIR)$(spritesdir)/rock1 + $(mkinstalldirs) $(DESTDIR)$(spritesdir)/rock2 + $(mkinstalldirs) $(DESTDIR)$(spritesdir)/rock3 + $(mkinstalldirs) $(DESTDIR)$(spritesdir)/ship + $(mkinstalldirs) $(DESTDIR)$(spritesdir)/bits + $(mkinstalldirs) $(DESTDIR)$(spritesdir)/missile + $(mkinstalldirs) $(DESTDIR)$(spritesdir)/powerups + $(mkinstalldirs) $(DESTDIR)$(spritesdir)/shield + $(mkinstalldirs) $(DESTDIR)$(spritesdir)/exhaust + @for file in $(FILES); do \ + echo $(INSTALL_DATA) $(srcdir)/$$file $(DESTDIR)$(spritesdir)/$$file ;\ + $(INSTALL_DATA) $(srcdir)/$$file $(DESTDIR)$(spritesdir)/$$file ;\ + done + +uninstall-local: + for file in $(FILES); do \ + rm -f $(DESTDIR)$(spritesdir)/$$file ;\ + done + diff --git a/kasteroids/sprites/bits/Makefile.am b/kasteroids/sprites/bits/Makefile.am new file mode 100644 index 00000000..21484518 --- /dev/null +++ b/kasteroids/sprites/bits/Makefile.am @@ -0,0 +1,17 @@ +#POVRAY=x-povray # Must be Povray 3.0 + +#bits: bits.pov +# $(POVRAY) +L/usr/lib/povray3/include bits.ini + +#clean: +# rm -f *~ *.pgm + +#realclean: clean +# rm -f *.ppm *.pbm + +EXTRA_DIST = bits0000.png bits0001.png bits0002.png bits0003.png \ + bits0004.png bits0005.png bits0006.png bits0007.png \ + bits0008.png bits0009.png bits0010.png bits0011.png \ + bits0012.png bits0013.png bits0014.png bits0015.png \ + bits.ini bits.pov + diff --git a/kasteroids/sprites/bits/bits.ini b/kasteroids/sprites/bits/bits.ini new file mode 100644 index 00000000..cb2976f8 --- /dev/null +++ b/kasteroids/sprites/bits/bits.ini @@ -0,0 +1,9 @@ +Cyclic_Animation=On +Width=12 +Height=12 +Final_frame=16 ;; NR_ROTS +Antialias=On +Output_Alpha=On +Output_to_File=On +Output_File_Type=n +Input_File_Name=bits.pov diff --git a/kasteroids/sprites/bits/bits.pov b/kasteroids/sprites/bits/bits.pov new file mode 100644 index 00000000..9be7ccb6 --- /dev/null +++ b/kasteroids/sprites/bits/bits.pov @@ -0,0 +1,31 @@ + +#version 3.0 +global_settings { assumed_gamma 2.0 } + +#include "colors.inc" +#include "textures.inc" +#include "metals.inc" + +camera { + location <15, -15, -100> + look_at <0, 0, 0> +} + +light_source { <50, 50, -50> colour White } +light_source { <0, 0, -50> colour White } + +prism { + linear_sweep + linear_spline + 0, + 0.2, + 5, + <2, 0>, <0, 2>, <-1, 1>, <0, -3>, <2, 0> + texture { T_Silver_2A } + + rotate <360*clock, 50, 30> + scale <20, 20, 20> +} + + + diff --git a/kasteroids/sprites/bits/bits0000.png b/kasteroids/sprites/bits/bits0000.png new file mode 100644 index 00000000..04c6482b Binary files /dev/null and b/kasteroids/sprites/bits/bits0000.png differ diff --git a/kasteroids/sprites/bits/bits0001.png b/kasteroids/sprites/bits/bits0001.png new file mode 100644 index 00000000..b4aa04f3 Binary files /dev/null and b/kasteroids/sprites/bits/bits0001.png differ diff --git a/kasteroids/sprites/bits/bits0002.png b/kasteroids/sprites/bits/bits0002.png new file mode 100644 index 00000000..3a654cd8 Binary files /dev/null and b/kasteroids/sprites/bits/bits0002.png differ diff --git a/kasteroids/sprites/bits/bits0003.png b/kasteroids/sprites/bits/bits0003.png new file mode 100644 index 00000000..483ecb78 Binary files /dev/null and b/kasteroids/sprites/bits/bits0003.png differ diff --git a/kasteroids/sprites/bits/bits0004.png b/kasteroids/sprites/bits/bits0004.png new file mode 100644 index 00000000..fc4f681a Binary files /dev/null and b/kasteroids/sprites/bits/bits0004.png differ diff --git a/kasteroids/sprites/bits/bits0005.png b/kasteroids/sprites/bits/bits0005.png new file mode 100644 index 00000000..ffbdcf00 Binary files /dev/null and b/kasteroids/sprites/bits/bits0005.png differ diff --git a/kasteroids/sprites/bits/bits0006.png b/kasteroids/sprites/bits/bits0006.png new file mode 100644 index 00000000..0c6b0752 Binary files /dev/null and b/kasteroids/sprites/bits/bits0006.png differ diff --git a/kasteroids/sprites/bits/bits0007.png b/kasteroids/sprites/bits/bits0007.png new file mode 100644 index 00000000..302a9535 Binary files /dev/null and b/kasteroids/sprites/bits/bits0007.png differ diff --git a/kasteroids/sprites/bits/bits0008.png b/kasteroids/sprites/bits/bits0008.png new file mode 100644 index 00000000..aae11420 Binary files /dev/null and b/kasteroids/sprites/bits/bits0008.png differ diff --git a/kasteroids/sprites/bits/bits0009.png b/kasteroids/sprites/bits/bits0009.png new file mode 100644 index 00000000..5aed2027 Binary files /dev/null and b/kasteroids/sprites/bits/bits0009.png differ diff --git a/kasteroids/sprites/bits/bits0010.png b/kasteroids/sprites/bits/bits0010.png new file mode 100644 index 00000000..13069992 Binary files /dev/null and b/kasteroids/sprites/bits/bits0010.png differ diff --git a/kasteroids/sprites/bits/bits0011.png b/kasteroids/sprites/bits/bits0011.png new file mode 100644 index 00000000..742ae1b6 Binary files /dev/null and b/kasteroids/sprites/bits/bits0011.png differ diff --git a/kasteroids/sprites/bits/bits0012.png b/kasteroids/sprites/bits/bits0012.png new file mode 100644 index 00000000..d10fdba5 Binary files /dev/null and b/kasteroids/sprites/bits/bits0012.png differ diff --git a/kasteroids/sprites/bits/bits0013.png b/kasteroids/sprites/bits/bits0013.png new file mode 100644 index 00000000..6ee0aaac Binary files /dev/null and b/kasteroids/sprites/bits/bits0013.png differ diff --git a/kasteroids/sprites/bits/bits0014.png b/kasteroids/sprites/bits/bits0014.png new file mode 100644 index 00000000..167ebe4a Binary files /dev/null and b/kasteroids/sprites/bits/bits0014.png differ diff --git a/kasteroids/sprites/bits/bits0015.png b/kasteroids/sprites/bits/bits0015.png new file mode 100644 index 00000000..15a287e3 Binary files /dev/null and b/kasteroids/sprites/bits/bits0015.png differ diff --git a/kasteroids/sprites/exhaust/Makefile.am b/kasteroids/sprites/exhaust/Makefile.am new file mode 100644 index 00000000..4493dbd7 --- /dev/null +++ b/kasteroids/sprites/exhaust/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = exhaust.png diff --git a/kasteroids/sprites/exhaust/exhaust.png b/kasteroids/sprites/exhaust/exhaust.png new file mode 100644 index 00000000..78f987e3 Binary files /dev/null and b/kasteroids/sprites/exhaust/exhaust.png differ diff --git a/kasteroids/sprites/missile/Makefile.am b/kasteroids/sprites/missile/Makefile.am new file mode 100644 index 00000000..cc06088f --- /dev/null +++ b/kasteroids/sprites/missile/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = missile.png diff --git a/kasteroids/sprites/missile/missile.png b/kasteroids/sprites/missile/missile.png new file mode 100644 index 00000000..e78a0ac0 Binary files /dev/null and b/kasteroids/sprites/missile/missile.png differ diff --git a/kasteroids/sprites/powerups/Makefile.am b/kasteroids/sprites/powerups/Makefile.am new file mode 100644 index 00000000..4431ae3c --- /dev/null +++ b/kasteroids/sprites/powerups/Makefile.am @@ -0,0 +1,2 @@ + +EXTRA_DIST = brake.png energy.png shield.png shoot.png diff --git a/kasteroids/sprites/powerups/brake.png b/kasteroids/sprites/powerups/brake.png new file mode 100644 index 00000000..fe12dfa8 Binary files /dev/null and b/kasteroids/sprites/powerups/brake.png differ diff --git a/kasteroids/sprites/powerups/energy.png b/kasteroids/sprites/powerups/energy.png new file mode 100644 index 00000000..f8df39f9 Binary files /dev/null and b/kasteroids/sprites/powerups/energy.png differ diff --git a/kasteroids/sprites/powerups/shield.png b/kasteroids/sprites/powerups/shield.png new file mode 100644 index 00000000..f8a6d0c5 Binary files /dev/null and b/kasteroids/sprites/powerups/shield.png differ diff --git a/kasteroids/sprites/powerups/shoot.png b/kasteroids/sprites/powerups/shoot.png new file mode 100644 index 00000000..bebdb5ef Binary files /dev/null and b/kasteroids/sprites/powerups/shoot.png differ diff --git a/kasteroids/sprites/powerups/teleport.png b/kasteroids/sprites/powerups/teleport.png new file mode 100644 index 00000000..4ffde863 Binary files /dev/null and b/kasteroids/sprites/powerups/teleport.png differ diff --git a/kasteroids/sprites/rock1/Makefile.am b/kasteroids/sprites/rock1/Makefile.am new file mode 100644 index 00000000..c8d5c04b --- /dev/null +++ b/kasteroids/sprites/rock1/Makefile.am @@ -0,0 +1,24 @@ +#POVRAY=x-povray # Must be Povray 3.0 + +#rocks: rock1.pov +# gforge -s 0 -m 256 -crat 1.9 1.5 -type mat -na rock.mat +# orb rock.mat rock.inc 0.7 6 +# rm rock.mat +# $(POVRAY) +L/usr/lib/povray3/include rock1.ini + +#clean: +# rm -f *~ *.pgm + +#realclean: clean +# rm -f *.ppm *.pbm + +EXTRA_DIST = rock10000.png rock10001.png rock10002.png rock10003.png \ + rock10004.png rock10005.png rock10006.png rock10007.png \ + rock10008.png rock10009.png rock10010.png rock10011.png \ + rock10012.png rock10013.png rock10014.png rock10015.png \ + rock10016.png rock10017.png rock10018.png rock10019.png \ + rock10020.png rock10021.png rock10022.png rock10023.png \ + rock10024.png rock10025.png rock10026.png rock10027.png \ + rock10028.png rock10029.png rock10030.png rock10031.png \ + rock1.ini rock1.pov + diff --git a/kasteroids/sprites/rock1/rock1.ini b/kasteroids/sprites/rock1/rock1.ini new file mode 100644 index 00000000..e42fc766 --- /dev/null +++ b/kasteroids/sprites/rock1/rock1.ini @@ -0,0 +1,9 @@ +Cyclic_Animation=On +Width=48 +Height=48 +Final_frame=32 ;; NR_ROTS +Antialias=On +Output_Alpha=On +Output_to_File=On +Output_File_Type=n +Input_File_Name=rock1.pov diff --git a/kasteroids/sprites/rock1/rock1.pov b/kasteroids/sprites/rock1/rock1.pov new file mode 100644 index 00000000..58298c05 --- /dev/null +++ b/kasteroids/sprites/rock1/rock1.pov @@ -0,0 +1,26 @@ +#include "colors.inc" +#include "shapes.inc" +#include "textures.inc" +// #include "stones.inc" + +camera { + location <2,2,-6> + up <0, 1, 0> +// right <4/3, 0, 0> + look_at <0,0,0> +} + +object { light_source { <10, 5, -5> color red 1.1 green 1.1 blue 1.0 } } + +#declare Rock = +mesh { + #include "rock.inc" /* collection of triangle or smooth_triangle data */ +} + +object { + Rock + texture { pigment {White} } + scale 1.9 + rotate <60, 45, 360*clock> +} + diff --git a/kasteroids/sprites/rock1/rock10000.png b/kasteroids/sprites/rock1/rock10000.png new file mode 100644 index 00000000..ddcc7c37 Binary files /dev/null and b/kasteroids/sprites/rock1/rock10000.png differ diff --git a/kasteroids/sprites/rock1/rock10001.png b/kasteroids/sprites/rock1/rock10001.png new file mode 100644 index 00000000..41ab94ee Binary files /dev/null and b/kasteroids/sprites/rock1/rock10001.png differ diff --git a/kasteroids/sprites/rock1/rock10002.png b/kasteroids/sprites/rock1/rock10002.png new file mode 100644 index 00000000..f96a3c51 Binary files /dev/null and b/kasteroids/sprites/rock1/rock10002.png differ diff --git a/kasteroids/sprites/rock1/rock10003.png b/kasteroids/sprites/rock1/rock10003.png new file mode 100644 index 00000000..6d6abce3 Binary files /dev/null and b/kasteroids/sprites/rock1/rock10003.png differ diff --git a/kasteroids/sprites/rock1/rock10004.png b/kasteroids/sprites/rock1/rock10004.png new file mode 100644 index 00000000..94d2e34d Binary files /dev/null and b/kasteroids/sprites/rock1/rock10004.png differ diff --git a/kasteroids/sprites/rock1/rock10005.png b/kasteroids/sprites/rock1/rock10005.png new file mode 100644 index 00000000..163c4f0f Binary files /dev/null and b/kasteroids/sprites/rock1/rock10005.png differ diff --git a/kasteroids/sprites/rock1/rock10006.png b/kasteroids/sprites/rock1/rock10006.png new file mode 100644 index 00000000..3d1fb0ed Binary files /dev/null and b/kasteroids/sprites/rock1/rock10006.png differ diff --git a/kasteroids/sprites/rock1/rock10007.png b/kasteroids/sprites/rock1/rock10007.png new file mode 100644 index 00000000..c172d03e Binary files /dev/null and b/kasteroids/sprites/rock1/rock10007.png differ diff --git a/kasteroids/sprites/rock1/rock10008.png b/kasteroids/sprites/rock1/rock10008.png new file mode 100644 index 00000000..3e8e33fa Binary files /dev/null and b/kasteroids/sprites/rock1/rock10008.png differ diff --git a/kasteroids/sprites/rock1/rock10009.png b/kasteroids/sprites/rock1/rock10009.png new file mode 100644 index 00000000..b97c42b0 Binary files /dev/null and b/kasteroids/sprites/rock1/rock10009.png differ diff --git a/kasteroids/sprites/rock1/rock10010.png b/kasteroids/sprites/rock1/rock10010.png new file mode 100644 index 00000000..227c822b Binary files /dev/null and b/kasteroids/sprites/rock1/rock10010.png differ diff --git a/kasteroids/sprites/rock1/rock10011.png b/kasteroids/sprites/rock1/rock10011.png new file mode 100644 index 00000000..7ac34a2e Binary files /dev/null and b/kasteroids/sprites/rock1/rock10011.png differ diff --git a/kasteroids/sprites/rock1/rock10012.png b/kasteroids/sprites/rock1/rock10012.png new file mode 100644 index 00000000..5607651b Binary files /dev/null and b/kasteroids/sprites/rock1/rock10012.png differ diff --git a/kasteroids/sprites/rock1/rock10013.png b/kasteroids/sprites/rock1/rock10013.png new file mode 100644 index 00000000..e5ce1f28 Binary files /dev/null and b/kasteroids/sprites/rock1/rock10013.png differ diff --git a/kasteroids/sprites/rock1/rock10014.png b/kasteroids/sprites/rock1/rock10014.png new file mode 100644 index 00000000..527dff57 Binary files /dev/null and b/kasteroids/sprites/rock1/rock10014.png differ diff --git a/kasteroids/sprites/rock1/rock10015.png b/kasteroids/sprites/rock1/rock10015.png new file mode 100644 index 00000000..6199740f Binary files /dev/null and b/kasteroids/sprites/rock1/rock10015.png differ diff --git a/kasteroids/sprites/rock1/rock10016.png b/kasteroids/sprites/rock1/rock10016.png new file mode 100644 index 00000000..fcef0110 Binary files /dev/null and b/kasteroids/sprites/rock1/rock10016.png differ diff --git a/kasteroids/sprites/rock1/rock10017.png b/kasteroids/sprites/rock1/rock10017.png new file mode 100644 index 00000000..e58d712a Binary files /dev/null and b/kasteroids/sprites/rock1/rock10017.png differ diff --git a/kasteroids/sprites/rock1/rock10018.png b/kasteroids/sprites/rock1/rock10018.png new file mode 100644 index 00000000..607ec96e Binary files /dev/null and b/kasteroids/sprites/rock1/rock10018.png differ diff --git a/kasteroids/sprites/rock1/rock10019.png b/kasteroids/sprites/rock1/rock10019.png new file mode 100644 index 00000000..8694376a Binary files /dev/null and b/kasteroids/sprites/rock1/rock10019.png differ diff --git a/kasteroids/sprites/rock1/rock10020.png b/kasteroids/sprites/rock1/rock10020.png new file mode 100644 index 00000000..44560d5e Binary files /dev/null and b/kasteroids/sprites/rock1/rock10020.png differ diff --git a/kasteroids/sprites/rock1/rock10021.png b/kasteroids/sprites/rock1/rock10021.png new file mode 100644 index 00000000..6746b712 Binary files /dev/null and b/kasteroids/sprites/rock1/rock10021.png differ diff --git a/kasteroids/sprites/rock1/rock10022.png b/kasteroids/sprites/rock1/rock10022.png new file mode 100644 index 00000000..ccb7d8e6 Binary files /dev/null and b/kasteroids/sprites/rock1/rock10022.png differ diff --git a/kasteroids/sprites/rock1/rock10023.png b/kasteroids/sprites/rock1/rock10023.png new file mode 100644 index 00000000..22322356 Binary files /dev/null and b/kasteroids/sprites/rock1/rock10023.png differ diff --git a/kasteroids/sprites/rock1/rock10024.png b/kasteroids/sprites/rock1/rock10024.png new file mode 100644 index 00000000..a8b9e034 Binary files /dev/null and b/kasteroids/sprites/rock1/rock10024.png differ diff --git a/kasteroids/sprites/rock1/rock10025.png b/kasteroids/sprites/rock1/rock10025.png new file mode 100644 index 00000000..ba806f64 Binary files /dev/null and b/kasteroids/sprites/rock1/rock10025.png differ diff --git a/kasteroids/sprites/rock1/rock10026.png b/kasteroids/sprites/rock1/rock10026.png new file mode 100644 index 00000000..6eb57dd8 Binary files /dev/null and b/kasteroids/sprites/rock1/rock10026.png differ diff --git a/kasteroids/sprites/rock1/rock10027.png b/kasteroids/sprites/rock1/rock10027.png new file mode 100644 index 00000000..56d7a090 Binary files /dev/null and b/kasteroids/sprites/rock1/rock10027.png differ diff --git a/kasteroids/sprites/rock1/rock10028.png b/kasteroids/sprites/rock1/rock10028.png new file mode 100644 index 00000000..fc7e8681 Binary files /dev/null and b/kasteroids/sprites/rock1/rock10028.png differ diff --git a/kasteroids/sprites/rock1/rock10029.png b/kasteroids/sprites/rock1/rock10029.png new file mode 100644 index 00000000..18eb8aea Binary files /dev/null and b/kasteroids/sprites/rock1/rock10029.png differ diff --git a/kasteroids/sprites/rock1/rock10030.png b/kasteroids/sprites/rock1/rock10030.png new file mode 100644 index 00000000..955307d9 Binary files /dev/null and b/kasteroids/sprites/rock1/rock10030.png differ diff --git a/kasteroids/sprites/rock1/rock10031.png b/kasteroids/sprites/rock1/rock10031.png new file mode 100644 index 00000000..902b86fc Binary files /dev/null and b/kasteroids/sprites/rock1/rock10031.png differ diff --git a/kasteroids/sprites/rock2/Makefile.am b/kasteroids/sprites/rock2/Makefile.am new file mode 100644 index 00000000..05db3bf6 --- /dev/null +++ b/kasteroids/sprites/rock2/Makefile.am @@ -0,0 +1,24 @@ +#POVRAY=x-povray # Must be Povray 3.0 + +#rocks: rock2.pov +# gforge -s 1 -m 128 -dim 2.6 -type mat -na rock.mat +# orb rock.mat rock.inc 0.7 5 +# rm rock.mat +# $(POVRAY) +L/usr/lib/povray3/include rock2.ini + +#clean: +# rm -f *~ *.pgm + +#realclean: clean +# rm -f *.ppm *.pbm + +EXTRA_DIST = rock20000.png rock20001.png rock20002.png rock20003.png \ + rock20004.png rock20005.png rock20006.png rock20007.png \ + rock20008.png rock20009.png rock20010.png rock20011.png \ + rock20012.png rock20013.png rock20014.png rock20015.png \ + rock20016.png rock20017.png rock20018.png rock20019.png \ + rock20020.png rock20021.png rock20022.png rock20023.png \ + rock20024.png rock20025.png rock20026.png rock20027.png \ + rock20028.png rock20029.png rock20030.png rock20031.png \ + rock2.ini rock2.pov + diff --git a/kasteroids/sprites/rock2/rock2.ini b/kasteroids/sprites/rock2/rock2.ini new file mode 100644 index 00000000..d50e6fad --- /dev/null +++ b/kasteroids/sprites/rock2/rock2.ini @@ -0,0 +1,9 @@ +Cyclic_Animation=On +Width=32 +Height=32 +Final_frame=32 ;; NR_ROTS +Antialias=On +Output_Alpha=On +Output_to_File=On +Output_File_Type=n +Input_File_Name=rock2.pov diff --git a/kasteroids/sprites/rock2/rock2.pov b/kasteroids/sprites/rock2/rock2.pov new file mode 100644 index 00000000..2f37a206 --- /dev/null +++ b/kasteroids/sprites/rock2/rock2.pov @@ -0,0 +1,26 @@ +#include "colors.inc" +#include "shapes.inc" +#include "textures.inc" +// #include "stones.inc" + +camera { + location <2,2,-6> + up <0, 1, 0> +// right <4/3, 0, 0> + look_at <0,0,0> +} + +object { light_source { <10, 5, -5> color red 1.1 green 1.1 blue 1.0 } } + +#declare Rock = +mesh { + #include "rock.inc" /* collection of triangle or smooth_triangle data */ +} + +object { + Rock + texture { pigment {White} } + scale 1.9 + rotate <60, 30, 360*clock> +} + diff --git a/kasteroids/sprites/rock2/rock20000.png b/kasteroids/sprites/rock2/rock20000.png new file mode 100644 index 00000000..4c483116 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20000.png differ diff --git a/kasteroids/sprites/rock2/rock20001.png b/kasteroids/sprites/rock2/rock20001.png new file mode 100644 index 00000000..8041395a Binary files /dev/null and b/kasteroids/sprites/rock2/rock20001.png differ diff --git a/kasteroids/sprites/rock2/rock20002.png b/kasteroids/sprites/rock2/rock20002.png new file mode 100644 index 00000000..83b412f4 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20002.png differ diff --git a/kasteroids/sprites/rock2/rock20003.png b/kasteroids/sprites/rock2/rock20003.png new file mode 100644 index 00000000..31f41b16 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20003.png differ diff --git a/kasteroids/sprites/rock2/rock20004.png b/kasteroids/sprites/rock2/rock20004.png new file mode 100644 index 00000000..47d359fe Binary files /dev/null and b/kasteroids/sprites/rock2/rock20004.png differ diff --git a/kasteroids/sprites/rock2/rock20005.png b/kasteroids/sprites/rock2/rock20005.png new file mode 100644 index 00000000..565dfe39 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20005.png differ diff --git a/kasteroids/sprites/rock2/rock20006.png b/kasteroids/sprites/rock2/rock20006.png new file mode 100644 index 00000000..ebf10c42 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20006.png differ diff --git a/kasteroids/sprites/rock2/rock20007.png b/kasteroids/sprites/rock2/rock20007.png new file mode 100644 index 00000000..44425780 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20007.png differ diff --git a/kasteroids/sprites/rock2/rock20008.png b/kasteroids/sprites/rock2/rock20008.png new file mode 100644 index 00000000..2601a824 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20008.png differ diff --git a/kasteroids/sprites/rock2/rock20009.png b/kasteroids/sprites/rock2/rock20009.png new file mode 100644 index 00000000..8987fdc8 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20009.png differ diff --git a/kasteroids/sprites/rock2/rock20010.png b/kasteroids/sprites/rock2/rock20010.png new file mode 100644 index 00000000..a3d19851 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20010.png differ diff --git a/kasteroids/sprites/rock2/rock20011.png b/kasteroids/sprites/rock2/rock20011.png new file mode 100644 index 00000000..0bea56f4 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20011.png differ diff --git a/kasteroids/sprites/rock2/rock20012.png b/kasteroids/sprites/rock2/rock20012.png new file mode 100644 index 00000000..22712591 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20012.png differ diff --git a/kasteroids/sprites/rock2/rock20013.png b/kasteroids/sprites/rock2/rock20013.png new file mode 100644 index 00000000..de91a877 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20013.png differ diff --git a/kasteroids/sprites/rock2/rock20014.png b/kasteroids/sprites/rock2/rock20014.png new file mode 100644 index 00000000..b76af51d Binary files /dev/null and b/kasteroids/sprites/rock2/rock20014.png differ diff --git a/kasteroids/sprites/rock2/rock20015.png b/kasteroids/sprites/rock2/rock20015.png new file mode 100644 index 00000000..01283e6c Binary files /dev/null and b/kasteroids/sprites/rock2/rock20015.png differ diff --git a/kasteroids/sprites/rock2/rock20016.png b/kasteroids/sprites/rock2/rock20016.png new file mode 100644 index 00000000..f220642d Binary files /dev/null and b/kasteroids/sprites/rock2/rock20016.png differ diff --git a/kasteroids/sprites/rock2/rock20017.png b/kasteroids/sprites/rock2/rock20017.png new file mode 100644 index 00000000..cca6ccfe Binary files /dev/null and b/kasteroids/sprites/rock2/rock20017.png differ diff --git a/kasteroids/sprites/rock2/rock20018.png b/kasteroids/sprites/rock2/rock20018.png new file mode 100644 index 00000000..6adac3fe Binary files /dev/null and b/kasteroids/sprites/rock2/rock20018.png differ diff --git a/kasteroids/sprites/rock2/rock20019.png b/kasteroids/sprites/rock2/rock20019.png new file mode 100644 index 00000000..fbb54c95 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20019.png differ diff --git a/kasteroids/sprites/rock2/rock20020.png b/kasteroids/sprites/rock2/rock20020.png new file mode 100644 index 00000000..e8bfd2d4 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20020.png differ diff --git a/kasteroids/sprites/rock2/rock20021.png b/kasteroids/sprites/rock2/rock20021.png new file mode 100644 index 00000000..33e313d7 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20021.png differ diff --git a/kasteroids/sprites/rock2/rock20022.png b/kasteroids/sprites/rock2/rock20022.png new file mode 100644 index 00000000..cf94837a Binary files /dev/null and b/kasteroids/sprites/rock2/rock20022.png differ diff --git a/kasteroids/sprites/rock2/rock20023.png b/kasteroids/sprites/rock2/rock20023.png new file mode 100644 index 00000000..2ef4c458 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20023.png differ diff --git a/kasteroids/sprites/rock2/rock20024.png b/kasteroids/sprites/rock2/rock20024.png new file mode 100644 index 00000000..1fe628fa Binary files /dev/null and b/kasteroids/sprites/rock2/rock20024.png differ diff --git a/kasteroids/sprites/rock2/rock20025.png b/kasteroids/sprites/rock2/rock20025.png new file mode 100644 index 00000000..3403c2e0 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20025.png differ diff --git a/kasteroids/sprites/rock2/rock20026.png b/kasteroids/sprites/rock2/rock20026.png new file mode 100644 index 00000000..b71629b9 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20026.png differ diff --git a/kasteroids/sprites/rock2/rock20027.png b/kasteroids/sprites/rock2/rock20027.png new file mode 100644 index 00000000..0b1301ae Binary files /dev/null and b/kasteroids/sprites/rock2/rock20027.png differ diff --git a/kasteroids/sprites/rock2/rock20028.png b/kasteroids/sprites/rock2/rock20028.png new file mode 100644 index 00000000..ff9514da Binary files /dev/null and b/kasteroids/sprites/rock2/rock20028.png differ diff --git a/kasteroids/sprites/rock2/rock20029.png b/kasteroids/sprites/rock2/rock20029.png new file mode 100644 index 00000000..996efaca Binary files /dev/null and b/kasteroids/sprites/rock2/rock20029.png differ diff --git a/kasteroids/sprites/rock2/rock20030.png b/kasteroids/sprites/rock2/rock20030.png new file mode 100644 index 00000000..db406978 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20030.png differ diff --git a/kasteroids/sprites/rock2/rock20031.png b/kasteroids/sprites/rock2/rock20031.png new file mode 100644 index 00000000..9079d195 Binary files /dev/null and b/kasteroids/sprites/rock2/rock20031.png differ diff --git a/kasteroids/sprites/rock3/Makefile.am b/kasteroids/sprites/rock3/Makefile.am new file mode 100644 index 00000000..8b63bef8 --- /dev/null +++ b/kasteroids/sprites/rock3/Makefile.am @@ -0,0 +1,24 @@ +#POVRAY=x-povray # Must be Povray 3.0 + +#rocks: rock3.pov +# gforge -s 2 -m 128 -dim 2.6 -type mat -na rock.mat +# orb rock.mat rock.inc 0.7 5 +# rm rock.mat +# $(POVRAY) +L/usr/lib/povray3/include rock3.ini + +#clean: +# rm -f *~ *.pgm + +#realclean: clean +# rm -f *.ppm *.pbm + +EXTRA_DIST = rock30000.png rock30001.png rock30002.png rock30003.png \ + rock30004.png rock30005.png rock30006.png rock30007.png \ + rock30008.png rock30009.png rock30010.png rock30011.png \ + rock30012.png rock30013.png rock30014.png rock30015.png \ + rock30016.png rock30017.png rock30018.png rock30019.png \ + rock30020.png rock30021.png rock30022.png rock30023.png \ + rock30024.png rock30025.png rock30026.png rock30027.png \ + rock30028.png rock30029.png rock30030.png rock30031.png \ + rock3.ini rock3.pov + diff --git a/kasteroids/sprites/rock3/rock3.ini b/kasteroids/sprites/rock3/rock3.ini new file mode 100644 index 00000000..26a3cf96 --- /dev/null +++ b/kasteroids/sprites/rock3/rock3.ini @@ -0,0 +1,9 @@ +Cyclic_Animation=On +Width=20 +Height=20 +Final_frame=32 ;; NR_ROTS +Antialias=On +Output_Alpha=On +Output_to_File=On +Output_File_Type=n +Input_File_Name=rock3.pov diff --git a/kasteroids/sprites/rock3/rock3.pov b/kasteroids/sprites/rock3/rock3.pov new file mode 100644 index 00000000..2f37a206 --- /dev/null +++ b/kasteroids/sprites/rock3/rock3.pov @@ -0,0 +1,26 @@ +#include "colors.inc" +#include "shapes.inc" +#include "textures.inc" +// #include "stones.inc" + +camera { + location <2,2,-6> + up <0, 1, 0> +// right <4/3, 0, 0> + look_at <0,0,0> +} + +object { light_source { <10, 5, -5> color red 1.1 green 1.1 blue 1.0 } } + +#declare Rock = +mesh { + #include "rock.inc" /* collection of triangle or smooth_triangle data */ +} + +object { + Rock + texture { pigment {White} } + scale 1.9 + rotate <60, 30, 360*clock> +} + diff --git a/kasteroids/sprites/rock3/rock30000.png b/kasteroids/sprites/rock3/rock30000.png new file mode 100644 index 00000000..e16c5fa5 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30000.png differ diff --git a/kasteroids/sprites/rock3/rock30001.png b/kasteroids/sprites/rock3/rock30001.png new file mode 100644 index 00000000..46148262 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30001.png differ diff --git a/kasteroids/sprites/rock3/rock30002.png b/kasteroids/sprites/rock3/rock30002.png new file mode 100644 index 00000000..0417648d Binary files /dev/null and b/kasteroids/sprites/rock3/rock30002.png differ diff --git a/kasteroids/sprites/rock3/rock30003.png b/kasteroids/sprites/rock3/rock30003.png new file mode 100644 index 00000000..a8ef0bf7 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30003.png differ diff --git a/kasteroids/sprites/rock3/rock30004.png b/kasteroids/sprites/rock3/rock30004.png new file mode 100644 index 00000000..95c55c73 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30004.png differ diff --git a/kasteroids/sprites/rock3/rock30005.png b/kasteroids/sprites/rock3/rock30005.png new file mode 100644 index 00000000..a061fb7f Binary files /dev/null and b/kasteroids/sprites/rock3/rock30005.png differ diff --git a/kasteroids/sprites/rock3/rock30006.png b/kasteroids/sprites/rock3/rock30006.png new file mode 100644 index 00000000..58b79dff Binary files /dev/null and b/kasteroids/sprites/rock3/rock30006.png differ diff --git a/kasteroids/sprites/rock3/rock30007.png b/kasteroids/sprites/rock3/rock30007.png new file mode 100644 index 00000000..727dd538 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30007.png differ diff --git a/kasteroids/sprites/rock3/rock30008.png b/kasteroids/sprites/rock3/rock30008.png new file mode 100644 index 00000000..fffc1444 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30008.png differ diff --git a/kasteroids/sprites/rock3/rock30009.png b/kasteroids/sprites/rock3/rock30009.png new file mode 100644 index 00000000..c8de18bd Binary files /dev/null and b/kasteroids/sprites/rock3/rock30009.png differ diff --git a/kasteroids/sprites/rock3/rock30010.png b/kasteroids/sprites/rock3/rock30010.png new file mode 100644 index 00000000..23085284 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30010.png differ diff --git a/kasteroids/sprites/rock3/rock30011.png b/kasteroids/sprites/rock3/rock30011.png new file mode 100644 index 00000000..c2eda4cd Binary files /dev/null and b/kasteroids/sprites/rock3/rock30011.png differ diff --git a/kasteroids/sprites/rock3/rock30012.png b/kasteroids/sprites/rock3/rock30012.png new file mode 100644 index 00000000..2bf02230 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30012.png differ diff --git a/kasteroids/sprites/rock3/rock30013.png b/kasteroids/sprites/rock3/rock30013.png new file mode 100644 index 00000000..1e12d554 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30013.png differ diff --git a/kasteroids/sprites/rock3/rock30014.png b/kasteroids/sprites/rock3/rock30014.png new file mode 100644 index 00000000..4e2dd198 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30014.png differ diff --git a/kasteroids/sprites/rock3/rock30015.png b/kasteroids/sprites/rock3/rock30015.png new file mode 100644 index 00000000..ea905e22 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30015.png differ diff --git a/kasteroids/sprites/rock3/rock30016.png b/kasteroids/sprites/rock3/rock30016.png new file mode 100644 index 00000000..650b9af4 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30016.png differ diff --git a/kasteroids/sprites/rock3/rock30017.png b/kasteroids/sprites/rock3/rock30017.png new file mode 100644 index 00000000..1ef30eb2 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30017.png differ diff --git a/kasteroids/sprites/rock3/rock30018.png b/kasteroids/sprites/rock3/rock30018.png new file mode 100644 index 00000000..40bddae3 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30018.png differ diff --git a/kasteroids/sprites/rock3/rock30019.png b/kasteroids/sprites/rock3/rock30019.png new file mode 100644 index 00000000..0d2637c8 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30019.png differ diff --git a/kasteroids/sprites/rock3/rock30020.png b/kasteroids/sprites/rock3/rock30020.png new file mode 100644 index 00000000..8eebba63 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30020.png differ diff --git a/kasteroids/sprites/rock3/rock30021.png b/kasteroids/sprites/rock3/rock30021.png new file mode 100644 index 00000000..2fa50efe Binary files /dev/null and b/kasteroids/sprites/rock3/rock30021.png differ diff --git a/kasteroids/sprites/rock3/rock30022.png b/kasteroids/sprites/rock3/rock30022.png new file mode 100644 index 00000000..a1184f77 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30022.png differ diff --git a/kasteroids/sprites/rock3/rock30023.png b/kasteroids/sprites/rock3/rock30023.png new file mode 100644 index 00000000..92d35979 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30023.png differ diff --git a/kasteroids/sprites/rock3/rock30024.png b/kasteroids/sprites/rock3/rock30024.png new file mode 100644 index 00000000..2749bc94 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30024.png differ diff --git a/kasteroids/sprites/rock3/rock30025.png b/kasteroids/sprites/rock3/rock30025.png new file mode 100644 index 00000000..1afa737f Binary files /dev/null and b/kasteroids/sprites/rock3/rock30025.png differ diff --git a/kasteroids/sprites/rock3/rock30026.png b/kasteroids/sprites/rock3/rock30026.png new file mode 100644 index 00000000..e6af352f Binary files /dev/null and b/kasteroids/sprites/rock3/rock30026.png differ diff --git a/kasteroids/sprites/rock3/rock30027.png b/kasteroids/sprites/rock3/rock30027.png new file mode 100644 index 00000000..1ac52764 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30027.png differ diff --git a/kasteroids/sprites/rock3/rock30028.png b/kasteroids/sprites/rock3/rock30028.png new file mode 100644 index 00000000..7395a37a Binary files /dev/null and b/kasteroids/sprites/rock3/rock30028.png differ diff --git a/kasteroids/sprites/rock3/rock30029.png b/kasteroids/sprites/rock3/rock30029.png new file mode 100644 index 00000000..b0b56200 Binary files /dev/null and b/kasteroids/sprites/rock3/rock30029.png differ diff --git a/kasteroids/sprites/rock3/rock30030.png b/kasteroids/sprites/rock3/rock30030.png new file mode 100644 index 00000000..2bddf8ee Binary files /dev/null and b/kasteroids/sprites/rock3/rock30030.png differ diff --git a/kasteroids/sprites/rock3/rock30031.png b/kasteroids/sprites/rock3/rock30031.png new file mode 100644 index 00000000..f5cce0fc Binary files /dev/null and b/kasteroids/sprites/rock3/rock30031.png differ diff --git a/kasteroids/sprites/shield/Makefile.am b/kasteroids/sprites/shield/Makefile.am new file mode 100644 index 00000000..4f86837c --- /dev/null +++ b/kasteroids/sprites/shield/Makefile.am @@ -0,0 +1,3 @@ + +EXTRA_DIST = shield0000.png shield0001.png shield0002.png shield0003.png \ + shield0004.png shield0005.png shield0006.png diff --git a/kasteroids/sprites/shield/shield0000.png b/kasteroids/sprites/shield/shield0000.png new file mode 100644 index 00000000..90f25718 Binary files /dev/null and b/kasteroids/sprites/shield/shield0000.png differ diff --git a/kasteroids/sprites/shield/shield0001.png b/kasteroids/sprites/shield/shield0001.png new file mode 100644 index 00000000..342d40aa Binary files /dev/null and b/kasteroids/sprites/shield/shield0001.png differ diff --git a/kasteroids/sprites/shield/shield0002.png b/kasteroids/sprites/shield/shield0002.png new file mode 100644 index 00000000..d00f4ab1 Binary files /dev/null and b/kasteroids/sprites/shield/shield0002.png differ diff --git a/kasteroids/sprites/shield/shield0003.png b/kasteroids/sprites/shield/shield0003.png new file mode 100644 index 00000000..2ae53d45 Binary files /dev/null and b/kasteroids/sprites/shield/shield0003.png differ diff --git a/kasteroids/sprites/shield/shield0004.png b/kasteroids/sprites/shield/shield0004.png new file mode 100644 index 00000000..72d0847b Binary files /dev/null and b/kasteroids/sprites/shield/shield0004.png differ diff --git a/kasteroids/sprites/shield/shield0005.png b/kasteroids/sprites/shield/shield0005.png new file mode 100644 index 00000000..7efa4f14 Binary files /dev/null and b/kasteroids/sprites/shield/shield0005.png differ diff --git a/kasteroids/sprites/shield/shield0006.png b/kasteroids/sprites/shield/shield0006.png new file mode 100644 index 00000000..18eff6c6 Binary files /dev/null and b/kasteroids/sprites/shield/shield0006.png differ diff --git a/kasteroids/sprites/ship/Makefile.am b/kasteroids/sprites/ship/Makefile.am new file mode 100644 index 00000000..4679332c --- /dev/null +++ b/kasteroids/sprites/ship/Makefile.am @@ -0,0 +1,22 @@ +#POVRAY=x-povray # Must be Povray 3.0 + +#ship: ship.pov +# $(POVRAY) +L/usr/lib/povray3/include ship.ini + +#clean: +# rm -f *~ *.png *.pgm + +#realclean: clean +# rm -f *.ppm *.pbm + + +EXTRA_DIST = ship0000.png ship0001.png ship0002.png ship0003.png \ + ship0004.png ship0005.png ship0006.png ship0007.png \ + ship0008.png ship0009.png ship0010.png ship0011.png \ + ship0012.png ship0013.png ship0014.png ship0015.png \ + ship0016.png ship0017.png ship0018.png ship0019.png \ + ship0020.png ship0021.png ship0022.png ship0023.png \ + ship0024.png ship0025.png ship0026.png ship0027.png \ + ship0028.png ship0029.png ship0030.png ship0031.png \ + ship.ini ship.pov + diff --git a/kasteroids/sprites/ship/ship.ini b/kasteroids/sprites/ship/ship.ini new file mode 100644 index 00000000..ceca85d8 --- /dev/null +++ b/kasteroids/sprites/ship/ship.ini @@ -0,0 +1,11 @@ +Cyclic_Animation=On +Width=42 +Height=42 +Initial_Frame=0 +Final_frame=63 ;; NR_ROTS +Antialias=On +Output_Alpha=On +Output_to_File=On +Output_File_Type=n +Output_File_Name=ship00 +Input_File_Name=ship.pov diff --git a/kasteroids/sprites/ship/ship.pov b/kasteroids/sprites/ship/ship.pov new file mode 100644 index 00000000..8f185cd7 --- /dev/null +++ b/kasteroids/sprites/ship/ship.pov @@ -0,0 +1,128 @@ + +#version 3.0 +global_settings { assumed_gamma 2.0 } + +#include "colors.inc" +#include "textures.inc" +#include "metals.inc" + +camera { + orthographic + up <0, 130, 0> + right <130, 0, 0> + location <0, 0, -130> + look_at <0, 0, 0> +} + +light_source { <50, 25, -25> colour White } +light_source { <0, 0, -100> colour Gray80 } + +#declare ShipColor = color red 1.0 green 1.0 blue 0.9 + +#declare BaseTexture = +texture { + pigment { ShipColor } +} + +#declare Grubby = +texture { + pigment { + bozo + color_map { + [0.0 color rgbt <1, 1, 1, 1>] + [0.8 color rgbt <0.9, 0.9, 0.9, 0.5>] + [1.0 color rgbt <0.8, 0.8, 0.8, 0.5>] + } + turbulence 2.0 + scale 3 + } +} + +#declare ShipTexture = +texture { BaseTexture } +texture { Grubby } + +union { + cone { + <12, 0, 0>, 0.5 + <11, 0, 0>, 1.0 + texture { ShipTexture } + } + cone { + <11, 0, 0>, 1.0 + <8, 0, 0>, 2.0 + texture { ShipTexture } + } + cone { + <8, 0, 0>, 2.0 + <3.5, 0, 0>, 3.8 + texture { ShipTexture } + } + difference { + cone { + <8, 0, -0.01>, 2.0 + <3.5, 0, -0.01>, 3.8 + pigment { color Gray20 } + } + box { + <9, -4.0, -3.7>, + <2, 4.0, 10> + rotate <0, -18, 0> + } + box { + <6.5, -4.0, -8>, + <10, 4.0, 8> + } + box { + <2, -4.0, -8>, + <4.5, 4.0, 8> + } + } + cone { + <3.5, 0, 0>, 3.8 + <2, 0, 0>, 4.0 + texture { ShipTexture } + } + cylinder { + <2, 0, 0>, + <-9, 0, 0>, + 4.0 + texture { ShipTexture } + } + cone { + <-9, 0, 0>, 4.0 + <-10, 0, 0>, 3.5 + texture { ShipTexture } + } + prism { + linear_sweep + linear_spline + 0, + 0.5, + 4, + <7.5, 0>, <-7.5, 10>, <-7.5, -10>, <7.5, 0> + rotate <90, 0, 0> + texture { T_Silver_2A } + texture { ShipTexture } + } + prism { + linear_sweep + linear_spline + -0.5, + 0.5, + 4, + <4, 0>, <-7.5, 5>, <-7.5, -5>, <4, 0> + pigment { color White } + } + cone { + <-12, 0, 0>, 3.0 + <-10, 0, 0>, 2.0 + texture { T_Silver_2A } + pigment { color Gray60 } + } + + rotate <0, 0,-360*clock> + scale <5, 5, 5> +} + + diff --git a/kasteroids/sprites/ship/ship0000.png b/kasteroids/sprites/ship/ship0000.png new file mode 100644 index 00000000..817fe1b4 Binary files /dev/null and b/kasteroids/sprites/ship/ship0000.png differ diff --git a/kasteroids/sprites/ship/ship0001.png b/kasteroids/sprites/ship/ship0001.png new file mode 100644 index 00000000..50cafa14 Binary files /dev/null and b/kasteroids/sprites/ship/ship0001.png differ diff --git a/kasteroids/sprites/ship/ship0002.png b/kasteroids/sprites/ship/ship0002.png new file mode 100644 index 00000000..161fee51 Binary files /dev/null and b/kasteroids/sprites/ship/ship0002.png differ diff --git a/kasteroids/sprites/ship/ship0003.png b/kasteroids/sprites/ship/ship0003.png new file mode 100644 index 00000000..7eb65c78 Binary files /dev/null and b/kasteroids/sprites/ship/ship0003.png differ diff --git a/kasteroids/sprites/ship/ship0004.png b/kasteroids/sprites/ship/ship0004.png new file mode 100644 index 00000000..fd71f2f2 Binary files /dev/null and b/kasteroids/sprites/ship/ship0004.png differ diff --git a/kasteroids/sprites/ship/ship0005.png b/kasteroids/sprites/ship/ship0005.png new file mode 100644 index 00000000..e6df9605 Binary files /dev/null and b/kasteroids/sprites/ship/ship0005.png differ diff --git a/kasteroids/sprites/ship/ship0006.png b/kasteroids/sprites/ship/ship0006.png new file mode 100644 index 00000000..3d1b0f70 Binary files /dev/null and b/kasteroids/sprites/ship/ship0006.png differ diff --git a/kasteroids/sprites/ship/ship0007.png b/kasteroids/sprites/ship/ship0007.png new file mode 100644 index 00000000..1ec6ce4e Binary files /dev/null and b/kasteroids/sprites/ship/ship0007.png differ diff --git a/kasteroids/sprites/ship/ship0008.png b/kasteroids/sprites/ship/ship0008.png new file mode 100644 index 00000000..f2632745 Binary files /dev/null and b/kasteroids/sprites/ship/ship0008.png differ diff --git a/kasteroids/sprites/ship/ship0009.png b/kasteroids/sprites/ship/ship0009.png new file mode 100644 index 00000000..e7188fd1 Binary files /dev/null and b/kasteroids/sprites/ship/ship0009.png differ diff --git a/kasteroids/sprites/ship/ship0010.png b/kasteroids/sprites/ship/ship0010.png new file mode 100644 index 00000000..20c5aa72 Binary files /dev/null and b/kasteroids/sprites/ship/ship0010.png differ diff --git a/kasteroids/sprites/ship/ship0011.png b/kasteroids/sprites/ship/ship0011.png new file mode 100644 index 00000000..1d21e59c Binary files /dev/null and b/kasteroids/sprites/ship/ship0011.png differ diff --git a/kasteroids/sprites/ship/ship0012.png b/kasteroids/sprites/ship/ship0012.png new file mode 100644 index 00000000..1d5c51c3 Binary files /dev/null and b/kasteroids/sprites/ship/ship0012.png differ diff --git a/kasteroids/sprites/ship/ship0013.png b/kasteroids/sprites/ship/ship0013.png new file mode 100644 index 00000000..37e1a68d Binary files /dev/null and b/kasteroids/sprites/ship/ship0013.png differ diff --git a/kasteroids/sprites/ship/ship0014.png b/kasteroids/sprites/ship/ship0014.png new file mode 100644 index 00000000..49e2d6da Binary files /dev/null and b/kasteroids/sprites/ship/ship0014.png differ diff --git a/kasteroids/sprites/ship/ship0015.png b/kasteroids/sprites/ship/ship0015.png new file mode 100644 index 00000000..e65bdf2b Binary files /dev/null and b/kasteroids/sprites/ship/ship0015.png differ diff --git a/kasteroids/sprites/ship/ship0016.png b/kasteroids/sprites/ship/ship0016.png new file mode 100644 index 00000000..bab602e9 Binary files /dev/null and b/kasteroids/sprites/ship/ship0016.png differ diff --git a/kasteroids/sprites/ship/ship0017.png b/kasteroids/sprites/ship/ship0017.png new file mode 100644 index 00000000..c6bede44 Binary files /dev/null and b/kasteroids/sprites/ship/ship0017.png differ diff --git a/kasteroids/sprites/ship/ship0018.png b/kasteroids/sprites/ship/ship0018.png new file mode 100644 index 00000000..50e74c92 Binary files /dev/null and b/kasteroids/sprites/ship/ship0018.png differ diff --git a/kasteroids/sprites/ship/ship0019.png b/kasteroids/sprites/ship/ship0019.png new file mode 100644 index 00000000..6621046d Binary files /dev/null and b/kasteroids/sprites/ship/ship0019.png differ diff --git a/kasteroids/sprites/ship/ship0020.png b/kasteroids/sprites/ship/ship0020.png new file mode 100644 index 00000000..da5d8839 Binary files /dev/null and b/kasteroids/sprites/ship/ship0020.png differ diff --git a/kasteroids/sprites/ship/ship0021.png b/kasteroids/sprites/ship/ship0021.png new file mode 100644 index 00000000..5f4fee20 Binary files /dev/null and b/kasteroids/sprites/ship/ship0021.png differ diff --git a/kasteroids/sprites/ship/ship0022.png b/kasteroids/sprites/ship/ship0022.png new file mode 100644 index 00000000..6828ccec Binary files /dev/null and b/kasteroids/sprites/ship/ship0022.png differ diff --git a/kasteroids/sprites/ship/ship0023.png b/kasteroids/sprites/ship/ship0023.png new file mode 100644 index 00000000..34d81a05 Binary files /dev/null and b/kasteroids/sprites/ship/ship0023.png differ diff --git a/kasteroids/sprites/ship/ship0024.png b/kasteroids/sprites/ship/ship0024.png new file mode 100644 index 00000000..3ecce1a3 Binary files /dev/null and b/kasteroids/sprites/ship/ship0024.png differ diff --git a/kasteroids/sprites/ship/ship0025.png b/kasteroids/sprites/ship/ship0025.png new file mode 100644 index 00000000..de28312d Binary files /dev/null and b/kasteroids/sprites/ship/ship0025.png differ diff --git a/kasteroids/sprites/ship/ship0026.png b/kasteroids/sprites/ship/ship0026.png new file mode 100644 index 00000000..da7d5e3a Binary files /dev/null and b/kasteroids/sprites/ship/ship0026.png differ diff --git a/kasteroids/sprites/ship/ship0027.png b/kasteroids/sprites/ship/ship0027.png new file mode 100644 index 00000000..d11bb640 Binary files /dev/null and b/kasteroids/sprites/ship/ship0027.png differ diff --git a/kasteroids/sprites/ship/ship0028.png b/kasteroids/sprites/ship/ship0028.png new file mode 100644 index 00000000..e0524641 Binary files /dev/null and b/kasteroids/sprites/ship/ship0028.png differ diff --git a/kasteroids/sprites/ship/ship0029.png b/kasteroids/sprites/ship/ship0029.png new file mode 100644 index 00000000..db5497db Binary files /dev/null and b/kasteroids/sprites/ship/ship0029.png differ diff --git a/kasteroids/sprites/ship/ship0030.png b/kasteroids/sprites/ship/ship0030.png new file mode 100644 index 00000000..ee734fa6 Binary files /dev/null and b/kasteroids/sprites/ship/ship0030.png differ diff --git a/kasteroids/sprites/ship/ship0031.png b/kasteroids/sprites/ship/ship0031.png new file mode 100644 index 00000000..8234d015 Binary files /dev/null and b/kasteroids/sprites/ship/ship0031.png differ diff --git a/kasteroids/sprites/ship/ship0032.png b/kasteroids/sprites/ship/ship0032.png new file mode 100644 index 00000000..f438621c Binary files /dev/null and b/kasteroids/sprites/ship/ship0032.png differ diff --git a/kasteroids/sprites/ship/ship0033.png b/kasteroids/sprites/ship/ship0033.png new file mode 100644 index 00000000..35a7c94a Binary files /dev/null and b/kasteroids/sprites/ship/ship0033.png differ diff --git a/kasteroids/sprites/ship/ship0034.png b/kasteroids/sprites/ship/ship0034.png new file mode 100644 index 00000000..ae436b66 Binary files /dev/null and b/kasteroids/sprites/ship/ship0034.png differ diff --git a/kasteroids/sprites/ship/ship0035.png b/kasteroids/sprites/ship/ship0035.png new file mode 100644 index 00000000..ccae1f4a Binary files /dev/null and b/kasteroids/sprites/ship/ship0035.png differ diff --git a/kasteroids/sprites/ship/ship0036.png b/kasteroids/sprites/ship/ship0036.png new file mode 100644 index 00000000..ba1e0f37 Binary files /dev/null and b/kasteroids/sprites/ship/ship0036.png differ diff --git a/kasteroids/sprites/ship/ship0037.png b/kasteroids/sprites/ship/ship0037.png new file mode 100644 index 00000000..66ab96ca Binary files /dev/null and b/kasteroids/sprites/ship/ship0037.png differ diff --git a/kasteroids/sprites/ship/ship0038.png b/kasteroids/sprites/ship/ship0038.png new file mode 100644 index 00000000..f2efab14 Binary files /dev/null and b/kasteroids/sprites/ship/ship0038.png differ diff --git a/kasteroids/sprites/ship/ship0039.png b/kasteroids/sprites/ship/ship0039.png new file mode 100644 index 00000000..1998e6f5 Binary files /dev/null and b/kasteroids/sprites/ship/ship0039.png differ diff --git a/kasteroids/sprites/ship/ship0040.png b/kasteroids/sprites/ship/ship0040.png new file mode 100644 index 00000000..005dc49d Binary files /dev/null and b/kasteroids/sprites/ship/ship0040.png differ diff --git a/kasteroids/sprites/ship/ship0041.png b/kasteroids/sprites/ship/ship0041.png new file mode 100644 index 00000000..a2cd13e6 Binary files /dev/null and b/kasteroids/sprites/ship/ship0041.png differ diff --git a/kasteroids/sprites/ship/ship0042.png b/kasteroids/sprites/ship/ship0042.png new file mode 100644 index 00000000..788f0abd Binary files /dev/null and b/kasteroids/sprites/ship/ship0042.png differ diff --git a/kasteroids/sprites/ship/ship0043.png b/kasteroids/sprites/ship/ship0043.png new file mode 100644 index 00000000..222c63a4 Binary files /dev/null and b/kasteroids/sprites/ship/ship0043.png differ diff --git a/kasteroids/sprites/ship/ship0044.png b/kasteroids/sprites/ship/ship0044.png new file mode 100644 index 00000000..20606f6c Binary files /dev/null and b/kasteroids/sprites/ship/ship0044.png differ diff --git a/kasteroids/sprites/ship/ship0045.png b/kasteroids/sprites/ship/ship0045.png new file mode 100644 index 00000000..dd5d195a Binary files /dev/null and b/kasteroids/sprites/ship/ship0045.png differ diff --git a/kasteroids/sprites/ship/ship0046.png b/kasteroids/sprites/ship/ship0046.png new file mode 100644 index 00000000..53564007 Binary files /dev/null and b/kasteroids/sprites/ship/ship0046.png differ diff --git a/kasteroids/sprites/ship/ship0047.png b/kasteroids/sprites/ship/ship0047.png new file mode 100644 index 00000000..60b0bac3 Binary files /dev/null and b/kasteroids/sprites/ship/ship0047.png differ diff --git a/kasteroids/sprites/ship/ship0048.png b/kasteroids/sprites/ship/ship0048.png new file mode 100644 index 00000000..c3b7a57d Binary files /dev/null and b/kasteroids/sprites/ship/ship0048.png differ diff --git a/kasteroids/sprites/ship/ship0049.png b/kasteroids/sprites/ship/ship0049.png new file mode 100644 index 00000000..a9a1228f Binary files /dev/null and b/kasteroids/sprites/ship/ship0049.png differ diff --git a/kasteroids/sprites/ship/ship0050.png b/kasteroids/sprites/ship/ship0050.png new file mode 100644 index 00000000..89fd0b25 Binary files /dev/null and b/kasteroids/sprites/ship/ship0050.png differ diff --git a/kasteroids/sprites/ship/ship0051.png b/kasteroids/sprites/ship/ship0051.png new file mode 100644 index 00000000..a1039a08 Binary files /dev/null and b/kasteroids/sprites/ship/ship0051.png differ diff --git a/kasteroids/sprites/ship/ship0052.png b/kasteroids/sprites/ship/ship0052.png new file mode 100644 index 00000000..85eadc8a Binary files /dev/null and b/kasteroids/sprites/ship/ship0052.png differ diff --git a/kasteroids/sprites/ship/ship0053.png b/kasteroids/sprites/ship/ship0053.png new file mode 100644 index 00000000..e6e204e0 Binary files /dev/null and b/kasteroids/sprites/ship/ship0053.png differ diff --git a/kasteroids/sprites/ship/ship0054.png b/kasteroids/sprites/ship/ship0054.png new file mode 100644 index 00000000..950b5021 Binary files /dev/null and b/kasteroids/sprites/ship/ship0054.png differ diff --git a/kasteroids/sprites/ship/ship0055.png b/kasteroids/sprites/ship/ship0055.png new file mode 100644 index 00000000..d26dbbff Binary files /dev/null and b/kasteroids/sprites/ship/ship0055.png differ diff --git a/kasteroids/sprites/ship/ship0056.png b/kasteroids/sprites/ship/ship0056.png new file mode 100644 index 00000000..c26fae98 Binary files /dev/null and b/kasteroids/sprites/ship/ship0056.png differ diff --git a/kasteroids/sprites/ship/ship0057.png b/kasteroids/sprites/ship/ship0057.png new file mode 100644 index 00000000..7d2733a4 Binary files /dev/null and b/kasteroids/sprites/ship/ship0057.png differ diff --git a/kasteroids/sprites/ship/ship0058.png b/kasteroids/sprites/ship/ship0058.png new file mode 100644 index 00000000..86e233ea Binary files /dev/null and b/kasteroids/sprites/ship/ship0058.png differ diff --git a/kasteroids/sprites/ship/ship0059.png b/kasteroids/sprites/ship/ship0059.png new file mode 100644 index 00000000..6745551d Binary files /dev/null and b/kasteroids/sprites/ship/ship0059.png differ diff --git a/kasteroids/sprites/ship/ship0060.png b/kasteroids/sprites/ship/ship0060.png new file mode 100644 index 00000000..b227019c Binary files /dev/null and b/kasteroids/sprites/ship/ship0060.png differ diff --git a/kasteroids/sprites/ship/ship0061.png b/kasteroids/sprites/ship/ship0061.png new file mode 100644 index 00000000..c0302e20 Binary files /dev/null and b/kasteroids/sprites/ship/ship0061.png differ diff --git a/kasteroids/sprites/ship/ship0062.png b/kasteroids/sprites/ship/ship0062.png new file mode 100644 index 00000000..41d98263 Binary files /dev/null and b/kasteroids/sprites/ship/ship0062.png differ diff --git a/kasteroids/sprites/ship/ship0063.png b/kasteroids/sprites/ship/ship0063.png new file mode 100644 index 00000000..3f2ac102 Binary files /dev/null and b/kasteroids/sprites/ship/ship0063.png differ diff --git a/kasteroids/toplevel.cpp b/kasteroids/toplevel.cpp new file mode 100644 index 00000000..a4286d2f --- /dev/null +++ b/kasteroids/toplevel.cpp @@ -0,0 +1,678 @@ +/* + * KAsteroids - Copyright (c) Martin R. Jones 1997 + * + * Part of the KDE project + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "toplevel.h" +#include "ledmeter.h" + +#include "toplevel.moc" + +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +#include +#endif +#include + +#define SB_SCORE 1 +#define SB_LEVEL 2 +#define SB_SHIPS 3 + +struct SLevel +{ + int nrocks; + double rockSpeed; +}; + +#define MAX_LEVELS 16 + +SLevel levels[MAX_LEVELS] = +{ + { 1, 0.4 }, + { 1, 0.6 }, + { 2, 0.5 }, + { 2, 0.7 }, + { 2, 0.8 }, + { 3, 0.6 }, + { 3, 0.7 }, + { 3, 0.8 }, + { 4, 0.6 }, + { 4, 0.7 }, + { 4, 0.8 }, + { 5, 0.7 }, + { 5, 0.8 }, + { 5, 0.9 }, + { 5, 1.0 } +}; + +#ifdef KA_ENABLE_SOUND +#include +#include + +Arts::SimpleSoundServer *soundServer = 0; +#endif + + +KAstTopLevel::KAstTopLevel() +{ + QWidget *mainWin = new QWidget( this ); + mainWin->setFixedSize(640, 480); + + view = new KAsteroidsView( mainWin ); + connect( view, SIGNAL( shipKilled() ), SLOT( slotShipKilled() ) ); + connect( view, SIGNAL( rockHit(int) ), SLOT( slotRockHit(int) ) ); + connect( view, SIGNAL( rocksRemoved() ), SLOT( slotRocksRemoved() ) ); + connect( view, SIGNAL( updateVitals() ), SLOT( slotUpdateVitals() ) ); + + QVBoxLayout *vb = new QVBoxLayout( mainWin ); + QHBoxLayout *hb = new QHBoxLayout; + QHBoxLayout *hbd = new QHBoxLayout; + vb->addLayout( hb ); + + QFont labelFont( KGlobalSettings::generalFont().family(), 24 ); + QColorGroup grp( darkGreen, black, QColor( 128, 128, 128 ), + QColor( 64, 64, 64 ), black, darkGreen, black ); + QPalette pal( grp, grp, grp ); + + mainWin->setPalette( pal ); + + hb->addSpacing( 10 ); + + QLabel *label; + label = new QLabel( i18n("Score"), mainWin ); + label->setFont( labelFont ); + label->setPalette( pal ); + label->setFixedWidth( label->sizeHint().width() ); + hb->addWidget( label ); + + scoreLCD = new QLCDNumber( 6, mainWin ); + scoreLCD->setFrameStyle( QFrame::NoFrame ); + scoreLCD->setSegmentStyle( QLCDNumber::Flat ); + scoreLCD->setFixedWidth( 150 ); + scoreLCD->setPalette( pal ); + hb->addWidget( scoreLCD ); + hb->addStretch( 10 ); + + label = new QLabel( i18n("Level"), mainWin ); + label->setFont( labelFont ); + label->setPalette( pal ); + label->setFixedWidth( label->sizeHint().width() ); + hb->addWidget( label ); + + levelLCD = new QLCDNumber( 2, mainWin ); + levelLCD->setFrameStyle( QFrame::NoFrame ); + levelLCD->setSegmentStyle( QLCDNumber::Flat ); + levelLCD->setFixedWidth( 70 ); + levelLCD->setPalette( pal ); + hb->addWidget( levelLCD ); + hb->addStretch( 10 ); + + label = new QLabel( i18n("Ships"), mainWin ); + label->setFont( labelFont ); + label->setFixedWidth( label->sizeHint().width() ); + label->setPalette( pal ); + hb->addWidget( label ); + + shipsLCD = new QLCDNumber( 1, mainWin ); + shipsLCD->setFrameStyle( QFrame::NoFrame ); + shipsLCD->setSegmentStyle( QLCDNumber::Flat ); + shipsLCD->setFixedWidth( 40 ); + shipsLCD->setPalette( pal ); + hb->addWidget( shipsLCD ); + + hb->addStrut( 30 ); + + QFrame *sep = new QFrame( mainWin ); + sep->setMaximumHeight( 5 ); + sep->setFrameStyle( QFrame::HLine | QFrame::Raised ); + sep->setPalette( pal ); + + vb->addWidget( sep ); + + vb->addWidget( view, 10 ); + +// -- bottom layout: + QFrame *sep2 = new QFrame( mainWin ); + sep2->setMaximumHeight( 1 ); + sep2->setFrameStyle( QFrame::HLine | QFrame::Raised ); + sep2->setPalette( pal ); + vb->addWidget( sep2, 1 ); + + vb->addLayout( hbd ); + + QFont smallFont( KGlobalSettings::generalFont().family(), 14 ); + hbd->addSpacing( 10 ); + + QString sprites_prefix = + KGlobal::dirs()->findResourceDir("sprite", "rock1/rock10000.png"); +/* + label = new QLabel( i18n( "T" ), mainWin ); + label->setFont( smallFont ); + label->setFixedWidth( label->sizeHint().width() ); + label->setPalette( pal ); + hbd->addWidget( label ); + + teleportsLCD = new QLCDNumber( 1, mainWin ); + teleportsLCD->setFrameStyle( QFrame::NoFrame ); + teleportsLCD->setSegmentStyle( QLCDNumber::Flat ); + teleportsLCD->setPalette( pal ); + teleportsLCD->setFixedHeight( 20 ); + hbd->addWidget( teleportsLCD ); + + hbd->addSpacing( 10 ); +*/ + QPixmap pm( sprites_prefix + "powerups/brake.png" ); + label = new QLabel( mainWin ); + label->setPixmap( pm ); + label->setFixedWidth( label->sizeHint().width() ); + label->setPalette( pal ); + hbd->addWidget( label ); + + brakesLCD = new QLCDNumber( 1, mainWin ); + brakesLCD->setFrameStyle( QFrame::NoFrame ); + brakesLCD->setSegmentStyle( QLCDNumber::Flat ); + brakesLCD->setPalette( pal ); + brakesLCD->setFixedHeight( 20 ); + hbd->addWidget( brakesLCD ); + + hbd->addSpacing( 10 ); + + pm.load( sprites_prefix + "powerups/shield.png" ); + label = new QLabel( mainWin ); + label->setPixmap( pm ); + label->setFixedWidth( label->sizeHint().width() ); + label->setPalette( pal ); + hbd->addWidget( label ); + + shieldLCD = new QLCDNumber( 1, mainWin ); + shieldLCD->setFrameStyle( QFrame::NoFrame ); + shieldLCD->setSegmentStyle( QLCDNumber::Flat ); + shieldLCD->setPalette( pal ); + shieldLCD->setFixedHeight( 20 ); + hbd->addWidget( shieldLCD ); + + hbd->addSpacing( 10 ); + + pm.load( sprites_prefix + "powerups/shoot.png" ); + label = new QLabel( mainWin ); + label->setPixmap( pm ); + label->setFixedWidth( label->sizeHint().width() ); + label->setPalette( pal ); + hbd->addWidget( label ); + + shootLCD = new QLCDNumber( 1, mainWin ); + shootLCD->setFrameStyle( QFrame::NoFrame ); + shootLCD->setSegmentStyle( QLCDNumber::Flat ); + shootLCD->setPalette( pal ); + shootLCD->setFixedHeight( 20 ); + hbd->addWidget( shootLCD ); + + hbd->addStretch( 1 ); + + label = new QLabel( i18n( "Fuel" ), mainWin ); + label->setFont( smallFont ); + label->setFixedWidth( label->sizeHint().width() + 10 ); + label->setPalette( pal ); + hbd->addWidget( label ); + + powerMeter = new KALedMeter( mainWin ); + powerMeter->setFrameStyle( QFrame::Box | QFrame::Plain ); + powerMeter->setRange( MAX_POWER_LEVEL ); + powerMeter->addColorRange( 10, darkRed ); + powerMeter->addColorRange( 20, QColor(160, 96, 0) ); + powerMeter->addColorRange( 70, darkGreen ); + powerMeter->setCount( 40 ); + powerMeter->setPalette( pal ); + powerMeter->setFixedSize( 200, 12 ); + hbd->addWidget( powerMeter ); + + initKAction(); + + setCentralWidget( mainWin ); + + setupGUI( KMainWindow::Save | Create ); + + setFocusPolicy( StrongFocus ); + setFocus(); + +#ifdef KA_ENABLE_SOUND + soundServer = new Arts::SimpleSoundServer( + Arts::Reference("global:Arts_SimpleSoundServer") ); +#endif + + loadSettings(); + slotNewGame(); + installEventFilter(this); +} + +KAstTopLevel::~KAstTopLevel() +{ +#if defined Q_WS_X11 && ! defined K_WS_QTONLY + XAutoRepeatOn( qt_xdisplay() ); +#endif + soundDict.setAutoDelete(true); + soundDict.clear(); +#ifdef KA_ENABLE_SOUND + delete soundServer; +#endif +} + +void KAstTopLevel::initKAction() +{ +// game + KStdGameAction::gameNew( this, SLOT( slotNewGame() ), actionCollection() ); + KStdGameAction::highscores( this, SLOT( slotShowHighscores() ), actionCollection() ); + KStdGameAction::pause( this, SLOT( slotPause() ), actionCollection() ); + KStdGameAction::quit(this, SLOT( close() ), actionCollection()); + +// settings + KStdAction::keyBindings(this, SLOT( slotKeyConfig() ), actionCollection()); + KStdAction::preferences(this, SLOT( slotPref() ), actionCollection()); + +// keyboard-only actions + keycodes.insert(Thrust, new KAction(i18n("Thrust"), Qt::Key_Up, 0, 0, actionCollection(), "Thrust")); + keycodes.insert(RotateLeft, new KAction(i18n("Rotate Left"), Qt::Key_Left, 0, 0, actionCollection(), "RotateLeft")); + keycodes.insert(RotateRight, new KAction(i18n("Rotate Right"), Qt::Key_Right, 0, 0, actionCollection(), "RotateRight")); + keycodes.insert(Shoot, new KAction(i18n("Shoot"), Qt::Key_Space, 0, 0, actionCollection(), "Shoot")); +// keycodes.insert(Teleport, new KAction(i18n("Teleport"), Qt::Key_Z, 0, 0, actionCollection(), "Teleport")); + keycodes.insert(Brake, new KAction(i18n("Brake"), Qt::Key_X, 0, 0, actionCollection(), "Brake")); + keycodes.insert(Shield, new KAction(i18n("Shield"), Qt::Key_S, 0, 0, actionCollection(), "Shield")); + launchAction = new KAction(i18n("Launch"), Qt::Key_L, this, SLOT(slotLaunch()), actionCollection(), "Launch"); +} + + +void KAstTopLevel::loadSettings() +{ + soundDict.insert("ShipDestroyed", + new QString( locate("sounds", Settings::soundShipDestroyed())) ); + soundDict.insert("RockDestroyed", + new QString( locate("sounds", Settings::soundRockDestroyed())) ); + + shipsRemain = Settings::numShips(); +} + +void KAstTopLevel::playSound( const char *snd ) +{ +// KAudioPlayer sometimes seem to be a little bit...slow +// it takes a moment until the sound is played - maybe an arts problem +// but it's a good temporary solution + if (!Settings::playSounds()) + return; + + QString *filename = soundDict[ snd ]; + if (filename) { + KAudioPlayer::play(*filename); + } + +return; // remove this and the above when the sound below is working correctly +#ifdef KA_ENABLE_SOUND + QString *filename = soundDict[ snd ]; + if (filename) { + kdDebug(12012)<< "playing " << *filename << endl; + if(!soundServer->isNull()) soundServer->play(filename->latin1()); + } +#endif +} + +bool KAstTopLevel::eventFilter( QObject* /* object */, QEvent *event ) +{ + QKeyEvent *e = static_cast(event); + if (event->type() == QEvent::AccelOverride) + { + if (processKeyPress(e)) return true; + else return false; + } + else if (event->type() == QEvent::KeyRelease) + { + if (processKeyRelease(e)) return true; + else return false; + } + return false; +} + +bool KAstTopLevel::processKeyPress( QKeyEvent *event ) +{ + KKey key(event); + Action a = Invalid; + QMap::Iterator it = keycodes.begin(); + for (; it != keycodes.end(); ++it) + { + if ( (*it)->shortcut().contains(key) ) + { + a = it.key(); + break; + } + } + switch ( a ) + { + case RotateLeft: + view->rotateLeft( true ); + break; + + case RotateRight: + view->rotateRight( true ); + break; + + case Thrust: + view->thrust( true ); + break; + + case Shoot: + if (gameOver) + slotNewGame(); + else + view->shoot( true ); + break; + + case Shield: + view->setShield( true ); + break; + +/* case Teleport: + view->teleport( true ); + break;*/ + + case Brake: + view->brake( true ); + break; + + case Invalid: + default: + return false; + } + return true; +} + +bool KAstTopLevel::processKeyRelease( QKeyEvent *event ) +{ + KKey key(event); + Action a = Invalid; + QMap::Iterator it = keycodes.begin(); + for (; it != keycodes.end(); ++it) + { + if ( (*it)->shortcut().contains(key) ) + { + a = it.key(); + break; + } + } + switch ( a ) + { + case RotateLeft: + view->rotateLeft( false ); + break; + + case RotateRight: + view->rotateRight( false ); + break; + + case Thrust: + view->thrust( false ); + break; + + case Shoot: + view->shoot( false ); + break; + + case Brake: + view->brake( false ); + break; + + case Shield: + view->setShield( false ); + break; + +/* case Teleport: + view->teleport( false ); + break;*/ + + case Invalid: + default: + return false; + } + + return true; +} + +void KAstTopLevel::focusInEvent( QFocusEvent *e ) +{ + view->pause( false ); +#if defined Q_WS_X11 && ! defined K_WS_QTONLY + XAutoRepeatOff( qt_xdisplay() ); +#endif + KMainWindow::focusInEvent(e); +} + +void KAstTopLevel::focusOutEvent( QFocusEvent *e ) +{ + view->pause( true ); +#if defined Q_WS_X11 && ! defined K_WS_QTONLY + XAutoRepeatOn( qt_xdisplay() ); +#endif + KMainWindow::focusOutEvent(e); +} + +void KAstTopLevel::slotNewGame() +{ + loadSettings(); + score = 0; + scoreLCD->display( 0 ); + level = 0; + levelLCD->display( level ); + shipsLCD->display( shipsRemain-1 ); + view->newGame(); + view->setRockSpeed( levels[0].rockSpeed ); + view->addRocks( levels[0].nrocks ); + view->showText( i18n( "Press %1 to launch." ) + .arg(launchAction->shortcut().seq(0).toString()), + yellow ); + waitShip = true; + gameOver = false; +} + +bool KAstTopLevel::queryExit() +{ +#if defined Q_WS_X11 && ! defined K_WS_QTONLY + XAutoRepeatOn( qt_xdisplay() ); +#endif + return true; +} + +void KAstTopLevel::slotShipKilled() +{ + shipsRemain--; + shipsLCD->display( shipsRemain-1 ); + + playSound( "ShipDestroyed" ); + + if ( shipsRemain ) + { + waitShip = true; + view->showText( i18n( "Ship Destroyed. Press %1 to launch.") + .arg(launchAction->shortcut().seq(0).toString()), + yellow ); + } + else + { + view->showText( i18n("Game Over!"), red ); + view->endGame(); + doStats(); + KScoreDialog d(KScoreDialog::Name | KScoreDialog::Level | KScoreDialog::Score, this); + KScoreDialog::FieldInfo scoreInfo; + scoreInfo[KScoreDialog::Level].setNum(level); + + if (d.addScore(score, scoreInfo) || Settings::showHiscores()) + { + // Show highscore and ask for name. + d.exec(); + slotGameOver(); + } + else + { + QTimer::singleShot(1000, this, SLOT(slotGameOver())); + } + } +} + +void KAstTopLevel::slotGameOver() +{ + gameOver = true; +} + +void KAstTopLevel::slotRockHit( int size ) +{ + switch ( size ) + { + case 0: + score += 10; + break; + + case 1: + score += 20; + break; + + default: + score += 40; + } + + playSound( "RockDestroyed" ); + + scoreLCD->display( score ); +} + +void KAstTopLevel::slotRocksRemoved() +{ + level++; + + if ( level >= MAX_LEVELS ) + level = MAX_LEVELS - 1; + + view->setRockSpeed( levels[level-1].rockSpeed ); + view->addRocks( levels[level-1].nrocks ); + + levelLCD->display( level ); +} + +void KAstTopLevel::slotKeyConfig() +{ + KKeyDialog::configure( actionCollection(), this ); + if ( waitShip ) view->showText( i18n( "Press %1 to launch." ) + .arg(launchAction->shortcut().seq(0).toString()), + yellow, false ); +} + +void KAstTopLevel::slotPref() +{ + if(KConfigDialog::showDialog("settings")) + return; + + KConfigDialog *dialog = new KConfigDialog(this, "settings", Settings::self(), KDialogBase::Swallow); + + /* Make widget */ + QWidget *w = new QWidget(0, "Settings"); + QVBoxLayout *page = new QVBoxLayout( w, 11 ); + + QHBoxLayout *hb = new QHBoxLayout( page, 11 ); + QLabel *label = new QLabel( i18n("Start new game with"), w ); + QSpinBox* sb = new QSpinBox( 1, 5, 1, w, "kcfg_numShips" ); + sb->setValue(3); + QLabel *lb = new QLabel( i18n(" ships."), w ); + QSpacerItem* hspacer = new QSpacerItem( 16, 20, QSizePolicy::Minimum, QSizePolicy::Expanding ); + hb->addWidget(label); + hb->addWidget(sb); + hb->addWidget(lb); + hb->addItem(hspacer); + + QCheckBox *f1 = new QCheckBox(i18n("Show highscores on Game Over"), w, "kcfg_showHiscores"); + QCheckBox *f2 = new QCheckBox(i18n("Player can destroy Powerups"), w, "kcfg_canDestroyPowerups"); + f2->setChecked(true); + page->addWidget(f1); + page->addWidget(f2); + QSpacerItem* spacer = new QSpacerItem( 20, 16, QSizePolicy::Minimum, QSizePolicy::Expanding ); + page->addItem( spacer ); + /* Done */ + + dialog->addPage(w, i18n("General"), "package_settings"); + connect(dialog, SIGNAL(settingsChanged()), this, SLOT(loadSettings())); + dialog->show(); +} + +void KAstTopLevel::slotShowHighscores() +{ + KScoreDialog d(KScoreDialog::Name | KScoreDialog::Level | KScoreDialog::Score, this); + d.exec(); +} + +void KAstTopLevel::doStats() +{ + QString r; + if ( view->shots() ) + r = KGlobal::locale()->formatNumber(( (float)view->hits() / + (float)view->shots() ) * 100, 2 ); + else + r = KGlobal::locale()->formatNumber( 0.0, 2 ); + + QString s = i18n( "Game Over\n\n" + "Shots fired:\t%1\n" + " Hit:\t%2\n" + " Missed:\t%3\n" + "Hit ratio:\t%4 %\t\t") + .arg(view->shots()).arg(view->hits()) + .arg(view->shots() - view->hits()) + .arg(r); + + view->showText( s, green, FALSE ); +} + +void KAstTopLevel::slotUpdateVitals() +{ + brakesLCD->display( view->brakeCount() ); + shieldLCD->display( view->shieldCount() ); + shootLCD->display( view->shootCount() ); +// teleportsLCD->display( view->teleportCount() ); + powerMeter->setValue( view->power() ); +} + +void KAstTopLevel::slotPause() +{ + KMessageBox::information( this, + i18n("KAsteroids is paused."), + i18n("Paused") ); + //Hack (montel@kde.org) + //To create "pause" action we display a messagebox + //and we "play" when we close this messagebox + //so before this action was 'checked/uncheched' + //so I force to unchecked it when we close messagebox. + KToggleAction * act = (KToggleAction *)(sender()); + act->setChecked(false); +} + +void KAstTopLevel::slotLaunch() +{ + if ( waitShip ) + { + view->newShip(); + waitShip = false; + view->hideText(); + } +} diff --git a/kasteroids/toplevel.h b/kasteroids/toplevel.h new file mode 100644 index 00000000..70545f71 --- /dev/null +++ b/kasteroids/toplevel.h @@ -0,0 +1,94 @@ +/* + * KAsteroids - Copyright (c) Martin R. Jones 1997 + * + * Part of the KDE project + */ + +#ifndef __KAST_TOPLEVEL_H__ +#define __KAST_TOPLEVEL_H__ + +#include +#include +#include +#include + +#include "view.h" + +//#define KA_ENABLE_SOUND + +class KALedMeter; +class QLCDNumber; +class KDialogBase; + +class KAstTopLevel : public KMainWindow +{ + Q_OBJECT +public: + KAstTopLevel(); + virtual ~KAstTopLevel(); + +private: + void initKAction(); + void playSound( const char *snd ); + void readSoundMapping(); + void doStats(); + bool queryExit(); + bool processKeyPress( QKeyEvent *event ); + bool processKeyRelease( QKeyEvent *event ); + +protected: + virtual bool eventFilter( QObject *object, QEvent *event ); + virtual void focusInEvent( QFocusEvent *event ); + virtual void focusOutEvent( QFocusEvent *event ); + +private slots: + void loadSettings(); + void slotNewGame(); + void slotGameOver(); + + void slotShipKilled(); + void slotRockHit( int size ); + void slotRocksRemoved(); + + void slotUpdateVitals(); + + void slotKeyConfig(); + void slotPref(); + void slotShowHighscores(); + + void slotPause(); + void slotLaunch(); + +private: + KAsteroidsView *view; + QLCDNumber *scoreLCD; + QLCDNumber *levelLCD; + QLCDNumber *shipsLCD; + + QLCDNumber *teleportsLCD; +// QLCDNumber *bombsLCD; + QLCDNumber *brakesLCD; + QLCDNumber *shieldLCD; + QLCDNumber *shootLCD; + KALedMeter *powerMeter; + + QDict soundDict; + + // waiting for user to press Enter to launch a ship + bool waitShip; + bool gameOver; + + int shipsRemain; + int score; + int level; + + enum Action { Invalid, Launch, Thrust, RotateLeft, RotateRight, Shoot, + Teleport, Brake, Shield }; + + QMap keycodes; + + KAction *launchAction; +}; + +#endif + diff --git a/kasteroids/version.h b/kasteroids/version.h new file mode 100644 index 00000000..5ab917a4 --- /dev/null +++ b/kasteroids/version.h @@ -0,0 +1 @@ +#define KASTEROIDS_VERSION "2.3" diff --git a/kasteroids/view.cpp b/kasteroids/view.cpp new file mode 100644 index 00000000..8ca358fd --- /dev/null +++ b/kasteroids/view.cpp @@ -0,0 +1,857 @@ +/* + * KAsteroids - Copyright (c) Martin R. Jones 1997 + * + * Part of the KDE project + */ + +#include +#include +#include +#include +#include + +#include "settings.h" +#include "view.h" +#include "view.moc" + +#define IMG_BACKGROUND "bg.png" +#define SPRITES_PREFIX kapp->kde_datadir() + "/kasteroids/" + +#define REFRESH_DELAY 33 +#define SHIP_SPEED 0.3 +#define MISSILE_SPEED 10.0 +#define SHIP_STEPS 64 +#define ROTATE_RATE 2 +#define SHIELD_ON_COST 1 +#define SHIELD_HIT_COST 30 +#define BRAKE_ON_COST 4 + +#define MAX_ROCK_SPEED 2.5 +#define MAX_POWERUP_SPEED 1.5 +#define MAX_SHIP_SPEED 12 +#define MAX_BRAKES 5 +#define MAX_SHIELDS 5 +#define MAX_FIREPOWER 5 + +#define TEXT_SPEED 4 + +#define PI_X_2 6.283185307 + +static struct +{ + int id; + const char *path; + int frames; +} +kas_animations [] = +{ + { ID_ROCK_LARGE, "rock1/rock1%1.png", 32 }, + { ID_ROCK_MEDIUM, "rock2/rock2%1.png", 32 }, + { ID_ROCK_SMALL, "rock3/rock3%1.png", 32 }, + { ID_SHIP, "ship/ship%1.png", 64 }, + { ID_MISSILE, "missile/missile.png", 1 }, + { ID_BIT, "bits/bits%1.png", 16 }, + { ID_EXHAUST, "exhaust/exhaust.png", 1 }, + { ID_ENERGY_POWERUP, "powerups/energy.png", 1 }, +// { ID_TELEPORT_POWERUP, "powerups/teleport%1.png", 12 }, + { ID_BRAKE_POWERUP, "powerups/brake.png", 1 }, + { ID_SHIELD_POWERUP, "powerups/shield.png", 1 }, + { ID_SHOOT_POWERUP, "powerups/shoot.png", 1 }, + { ID_SHIELD, "shield/shield%1.png", 6 }, + { 0, 0, 0 } +}; + + + +KAsteroidsView::KAsteroidsView( QWidget *parent, const char *name ) + : QWidget( parent, name ), + field(640, 440), + view(&field,this) +{ + view.setVScrollBarMode( QScrollView::AlwaysOff ); + view.setHScrollBarMode( QScrollView::AlwaysOff ); + rocks.setAutoDelete( true ); + missiles.setAutoDelete( true ); + bits.setAutoDelete( true ); + powerups.setAutoDelete( true ); + exhaust.setAutoDelete( true ); + + field.setBackgroundColor(black); + QPixmap pm( locate("sprite", IMG_BACKGROUND) ); + field.setBackgroundPixmap( pm ); + + textSprite = new QCanvasText( &field ); + QFont font( KGlobalSettings::generalFont().family(), 18 ); + textSprite->setFont( font ); + + shield = 0; + shieldOn = false; + refreshRate = REFRESH_DELAY; + + readSprites(); + + shieldTimer = new QTimer( this ); + connect( shieldTimer, SIGNAL(timeout()), this, SLOT(hideShield()) ); + mTimerId = -1; + + shipPower = MAX_POWER_LEVEL; + vitalsChanged = true; + mBrakeCount = 0; + mShootCount = 0; + mShieldCount = 0; +} + +// - - - + +KAsteroidsView::~KAsteroidsView() +{ +} + +// - - - + +void KAsteroidsView::reset() +{ + rocks.clear(); + missiles.clear(); + bits.clear(); + powerups.clear(); + exhaust.clear(); + + shotsFired = 0; + shotsHit = 0; + + rockSpeed = 1.0; + powerupSpeed = 1.0; + mFrameNum = 0; + mPaused = false; + + ship->hide(); + shield->hide(); + + if ( mTimerId >= 0 ) { + killTimer( mTimerId ); + mTimerId = -1; + } +} + +// - -- + +void KAsteroidsView::newGame() +{ + if ( shieldOn ) + { + shield->hide(); + shieldOn = false; + } + reset(); + if ( mTimerId < 0 ) + mTimerId = startTimer( REFRESH_DELAY ); + emit updateVitals(); +} + +// - - - + +void KAsteroidsView::endGame() +{ +} + +void KAsteroidsView::pause( bool p ) +{ + if ( !mPaused && p ) { + if ( mTimerId >= 0 ) { + killTimer( mTimerId ); + mTimerId = -1; + } + } else if ( mPaused && !p ) + mTimerId = startTimer( REFRESH_DELAY ); + mPaused = p; +} + +// - - - + +void KAsteroidsView::newShip() +{ + ship->move( field.width()/2, field.height()/2, 0 ); + shield->move( field.width()/2, field.height()/2, 0 ); + ship->setVelocity( 0.0, 0.0 ); + shipDx = 0; + shipDy = 0; + shipAngle = 0; + rotateL = false; + rotateR = false; + thrustShip = false; + shootShip = false; + brakeShip = false; + teleportShip = false; + shieldOn = true; + shootDelay = 0; + shipPower = MAX_POWER_LEVEL; + rotateRate = ROTATE_RATE; + rotateSlow = 0; + + mBrakeCount = 0; + mTeleportCount = 0; + mShootCount = 0; + + ship->show(); + shield->show(); + mShieldCount = 1; // just in case the ship appears on a rock. + shieldTimer->start( 1000, true ); +} + +void KAsteroidsView::setShield( bool s ) +{ + if ( shieldTimer->isActive() && !s ) { + shieldTimer->stop(); + hideShield(); + } else { + shieldOn = s && mShieldCount; + } +} + +void KAsteroidsView::brake( bool b ) +{ + if ( mBrakeCount ) + { + if ( brakeShip && !b ) + { + rotateL = false; + rotateR = false; + thrustShip = false; + rotateRate = ROTATE_RATE; + } + + brakeShip = b; + } +} + +// - - - + +void KAsteroidsView::readSprites() +{ + QString sprites_prefix = + KGlobal::dirs()->findResourceDir("sprite", "rock1/rock10000.png"); + + int i = 0; + while ( kas_animations[i].id ) + { + animation.insert( kas_animations[i].id, + new QCanvasPixmapArray( sprites_prefix + kas_animations[i].path, + kas_animations[i].frames ) ); + i++; + } + + ship = new QCanvasSprite( animation[ID_SHIP], &field ); + ship->hide(); + + shield = new KShield( animation[ID_SHIELD], &field ); + shield->hide(); +} + +// - - - + +void KAsteroidsView::addRocks( int num ) +{ + for ( int i = 0; i < num; i++ ) + { + KRock *rock = new KRock( animation[ID_ROCK_LARGE], &field, + ID_ROCK_LARGE, krandom.getLong(2), + krandom.getLong(2) ? -1 : 1 ); + double dx = (2.0 - krandom.getDouble()*4.0) * rockSpeed; + double dy = (2.0 - krandom.getDouble()*4.0) * rockSpeed; + rock->setVelocity( dx, dy ); + rock->setFrame( krandom.getLong( rock->frameCount() ) ); + if ( dx > 0 ) + { + if ( dy > 0 ) + rock->move( 5, 5, 0 ); + else + rock->move( 5, field.height() - 25, 0 ); + } + else + { + if ( dy > 0 ) + rock->move( field.width() - 25, 5, 0 ); + else + rock->move( field.width() - 25, field.height() - 25, 0 ); + } + rock->show( ); + rocks.append( rock ); + } +} + +// - - - + +void KAsteroidsView::showText( const QString &text, const QColor &color, bool scroll ) +{ +// textSprite->setTextFlags( AlignHCenter | AlignVCenter ); + textSprite->setText( text ); + textSprite->setColor( color ); + + if ( scroll ) { + textSprite->move( (field.width()-textSprite->boundingRect().width()) / 2, + -textSprite->boundingRect().height() ); + textDy = TEXT_SPEED; + } else { + textSprite->move( (field.width()-textSprite->boundingRect().width()) / 2, + (field.height()-textSprite->boundingRect().height()) / 2 ); + textDy = 0; + } + textSprite->show(); +} + +// - - - + +void KAsteroidsView::hideText() +{ + textDy = -TEXT_SPEED; +} + +// - - - + +void KAsteroidsView::resizeEvent(QResizeEvent* event) +{ + QWidget::resizeEvent(event); + field.resize(width()-4, height()-4); + view.resize(width(),height()); +} + +// - - - + +void KAsteroidsView::timerEvent( QTimerEvent * ) +{ + field.advance(); + + QCanvasSprite *rock; + + // move rocks forward + for ( rock = rocks.first(); rock; rock = rocks.next() ) { + ((KRock *)rock)->nextFrame(); + wrapSprite( rock ); + } + + wrapSprite( ship ); + + // check for missile collision with rocks. + processMissiles(); + + // these are generated when a ship explodes + for ( KBit *bit = bits.first(); bit; bit = bits.next() ) + { + if ( bit->expired() ) + { + bits.removeRef( bit ); + } + else + { + bit->growOlder(); + bit->setFrame( ( bit->frame()+1 ) % bit->frameCount() ); + } + } + + for ( KExhaust *e = exhaust.first(); e; e = exhaust.next() ) + exhaust.removeRef( e ); + + // move / rotate ship. + // check for collision with a rock. + processShip(); + + // move powerups and check for collision with player and missiles + processPowerups(); + + if ( textSprite->isVisible() ) + { + if ( textDy < 0 && + textSprite->boundingRect().y() <= -textSprite->boundingRect().height() ) + textSprite->hide(); + else + { + textSprite->moveBy( 0, textDy ); + } + if ( textSprite->boundingRect().y() > (field.height()-textSprite->boundingRect().height())/2 ) + textDy = 0; + } + + if ( vitalsChanged && !(mFrameNum % 10) ) { + emit updateVitals(); + vitalsChanged = false; + } + + mFrameNum++; +} + +void KAsteroidsView::wrapSprite( QCanvasItem *s ) +{ + int x = int(s->x() + s->boundingRect().width() / 2); + int y = int(s->y() + s->boundingRect().height() / 2); + + if ( x > field.width() ) + s->move( s->x() - field.width(), s->y() ); + else if ( x < 0 ) + s->move( field.width() + s->x(), s->y() ); + + if ( y > field.height() ) + s->move( s->x(), s->y() - field.height() ); + else if ( y < 0 ) + s->move( s->x(), field.height() + s->y() ); +} + +// - - - + +void KAsteroidsView::rockHit( QCanvasItem *hit ) +{ + KPowerup *nPup = 0; + int rnd = static_cast(krandom.getDouble()*30.0) % 30; + switch( rnd ) + { + case 4: + case 5: + nPup = new KPowerup( animation[ID_ENERGY_POWERUP], &field, + ID_ENERGY_POWERUP ); + break; + case 10: +// nPup = new KPowerup( animation[ID_TELEPORT_POWERUP], &field, +// ID_TELEPORT_POWERUP ); + break; + case 15: + nPup = new KPowerup( animation[ID_BRAKE_POWERUP], &field, + ID_BRAKE_POWERUP ); + break; + case 20: + nPup = new KPowerup( animation[ID_SHIELD_POWERUP], &field, + ID_SHIELD_POWERUP ); + break; + case 24: + case 25: + nPup = new KPowerup( animation[ID_SHOOT_POWERUP], &field, + ID_SHOOT_POWERUP ); + break; + } + if ( nPup ) + { + double r = 0.5 - krandom.getDouble(); + nPup->move( hit->x(), hit->y(), 0 ); + nPup->setVelocity( hit->xVelocity() + r, hit->yVelocity() + r ); + nPup->show( ); + powerups.append( nPup ); + } + + if ( hit->rtti() == ID_ROCK_LARGE || hit->rtti() == ID_ROCK_MEDIUM ) + { + // break into smaller rocks + double addx[4] = { 1.0, 1.0, -1.0, -1.0 }; + double addy[4] = { -1.0, 1.0, -1.0, 1.0 }; + + double dx = hit->xVelocity(); + double dy = hit->yVelocity(); + + double maxRockSpeed = MAX_ROCK_SPEED * rockSpeed; + if ( dx > maxRockSpeed ) + dx = maxRockSpeed; + else if ( dx < -maxRockSpeed ) + dx = -maxRockSpeed; + if ( dy > maxRockSpeed ) + dy = maxRockSpeed; + else if ( dy < -maxRockSpeed ) + dy = -maxRockSpeed; + + QCanvasSprite *nrock; + + for ( int i = 0; i < 4; i++ ) + { + double r = rockSpeed/2 - krandom.getDouble()*rockSpeed; + if ( hit->rtti() == ID_ROCK_LARGE ) + { + nrock = new KRock( animation[ID_ROCK_MEDIUM], &field, + ID_ROCK_MEDIUM, krandom.getLong(2), + krandom.getLong(2) ? -1 : 1 ); + emit rockHit( 0 ); + } + else + { + nrock = new KRock( animation[ID_ROCK_SMALL], &field, + ID_ROCK_SMALL, krandom.getLong(2), + krandom.getLong(2) ? -1 : 1 ); + emit rockHit( 1 ); + } + + nrock->move( hit->x(), hit->y(), 0 ); + nrock->setVelocity( dx+addx[i]*rockSpeed+r, dy+addy[i]*rockSpeed+r ); + nrock->setFrame( krandom.getLong( nrock->frameCount() ) ); + nrock->show( ); + rocks.append( nrock ); + } + } + else if ( hit->rtti() == ID_ROCK_SMALL ) + emit rockHit( 2 ); + rocks.removeRef( (QCanvasSprite *)hit ); + if ( rocks.count() == 0 ) + emit rocksRemoved(); +} + +void KAsteroidsView::reducePower( int val ) +{ + shipPower -= val; + if ( shipPower <= 0 ) + { + shipPower = 0; + thrustShip = false; + if ( shieldOn ) + { + shieldOn = false; + shield->hide(); + } + } + vitalsChanged = true; +} + +void KAsteroidsView::addExhaust( double x, double y, double dx, + double dy, int count ) +{ + for ( int i = 0; i < count; i++ ) + { + KExhaust *e = new KExhaust( animation[ID_EXHAUST], &field ); + e->move( x + 2 - krandom.getDouble()*4, y + 2 - krandom.getDouble()*4 ); + e->setVelocity( dx, dy ); + e->show( ); + exhaust.append( e ); + } +} + +void KAsteroidsView::processMissiles() +{ + KMissile *missile; + + // if a missile has hit a rock, remove missile and break rock into smaller + // rocks or remove completely. + QPtrListIterator it(missiles); + + for ( ; it.current(); ++it ) + { + missile = it.current(); + missile->growOlder(); + + if ( missile->expired() ) + { + missiles.removeRef( missile ); + continue; + } + + wrapSprite( missile ); + + QCanvasItemList hits = missile->collisions( true ); + QCanvasItemList::Iterator hit; + for ( hit = hits.begin(); hit != hits.end(); ++hit ) + { + if ( (*hit)->rtti() >= ID_ROCK_LARGE && + (*hit)->rtti() <= ID_ROCK_SMALL ) + { + shotsHit++; + rockHit( *hit ); + missiles.removeRef( missile ); + break; + } + } + } +} + +// - - - + +void KAsteroidsView::processShip() +{ + if ( ship->isVisible() ) + { + if ( shieldOn ) + { + shield->show(); + reducePower( SHIELD_ON_COST ); + static int sf = 0; + sf++; + + if ( sf % 2 ) + shield->setFrame( (shield->frame()+1) % shield->frameCount() ); + shield->move( ship->x() - 9, ship->y() - 9 ); + + QCanvasItemList hits = shield->collisions( true ); + QCanvasItemList::Iterator it; + for ( it = hits.begin(); it != hits.end(); ++it ) + { + if ( (*it)->rtti() >= ID_ROCK_LARGE && + (*it)->rtti() <= ID_ROCK_SMALL ) + { + int factor; + switch ( (*it)->rtti() ) + { + case ID_ROCK_LARGE: + factor = 3; + break; + + case ID_ROCK_MEDIUM: + factor = 2; + break; + + default: + factor = 1; + } + + if ( factor > mShieldCount ) + { + // shield not strong enough + shieldOn = false; + break; + } + rockHit( *it ); + // the more shields we have the less costly + reducePower( factor * (SHIELD_HIT_COST - mShieldCount*2) ); + } + } + } + + if ( !shieldOn ) + { + shield->hide(); + QCanvasItemList hits = ship->collisions( true ); + QCanvasItemList::Iterator it; + for ( it = hits.begin(); it != hits.end(); ++it ) + { + if ( (*it)->rtti() >= ID_ROCK_LARGE && + (*it)->rtti() <= ID_ROCK_SMALL ) + { + KBit *bit; + for ( int i = 0; i < 12; i++ ) + { + bit = new KBit( animation[ID_BIT], &field ); + bit->move( ship->x() + 5 - krandom.getDouble() * 10, + ship->y() + 5 - krandom.getDouble() * 10, + krandom.getLong(bit->frameCount()) ); + bit->setVelocity( 1-krandom.getDouble()*2, + 1-krandom.getDouble()*2 ); + bit->setDeath( 60 + krandom.getLong(60) ); + bit->show( ); + bits.append( bit ); + } + ship->hide(); + shield->hide(); + emit shipKilled(); + break; + } + } + } + + + if ( rotateSlow ) + rotateSlow--; + + if ( rotateL ) + { + shipAngle -= rotateSlow ? 1 : rotateRate; + if ( shipAngle < 0 ) + shipAngle += SHIP_STEPS; + } + + if ( rotateR ) + { + shipAngle += rotateSlow ? 1 : rotateRate; + if ( shipAngle >= SHIP_STEPS ) + shipAngle -= SHIP_STEPS; + } + + double angle = shipAngle * PI_X_2 / SHIP_STEPS; + double cosangle = cos( angle ); + double sinangle = sin( angle ); + + if ( brakeShip ) + { + thrustShip = false; + rotateL = false; + rotateR = false; + rotateRate = ROTATE_RATE; + if ( fabs(shipDx) < 2.5 && fabs(shipDy) < 2.5 ) + { + shipDx = 0.0; + shipDy = 0.0; + ship->setVelocity( shipDx, shipDy ); + brakeShip = false; + } + else + { + double motionAngle = atan2( -shipDy, -shipDx ); + if ( angle > M_PI ) + angle -= PI_X_2; + double angleDiff = angle - motionAngle; + if ( angleDiff > M_PI ) + angleDiff = PI_X_2 - angleDiff; + else if ( angleDiff < -M_PI ) + angleDiff = PI_X_2 + angleDiff; + double fdiff = fabs( angleDiff ); + if ( fdiff > 0.08 ) + { + if ( angleDiff > 0 ) + rotateL = true; + else if ( angleDiff < 0 ) + rotateR = true; + if ( fdiff > 0.6 ) + rotateRate = mBrakeCount + 1; + else if ( fdiff > 0.4 ) + rotateRate = 2; + else + rotateRate = 1; + + if ( rotateRate > 5 ) + rotateRate = 5; + } + else if ( fabs(shipDx) > 1 || fabs(shipDy) > 1 ) + { + thrustShip = true; + // we'll make braking a bit faster + shipDx += cosangle/6 * (mBrakeCount - 1); + shipDy += sinangle/6 * (mBrakeCount - 1); + reducePower( BRAKE_ON_COST ); + addExhaust( ship->x() + 20 - cosangle*22, + ship->y() + 20 - sinangle*22, + shipDx-cosangle, shipDy-sinangle, + mBrakeCount+1 ); + } + } + } + + if ( thrustShip ) + { + // The ship has a terminal velocity, but trying to go faster + // still uses fuel (can go faster diagonally - don't care). + double thrustx = cosangle/4; + double thrusty = sinangle/4; + if ( fabs(shipDx + thrustx) < MAX_SHIP_SPEED ) + shipDx += thrustx; + if ( fabs(shipDy + thrusty) < MAX_SHIP_SPEED ) + shipDy += thrusty; + ship->setVelocity( shipDx, shipDy ); + reducePower( 1 ); + addExhaust( ship->x() + 20 - cosangle*20, + ship->y() + 20 - sinangle*20, + shipDx-cosangle, shipDy-sinangle, 3 ); + } + + ship->setFrame( shipAngle ); + + if ( shootShip ) + { + if ( !shootDelay && (int)missiles.count() < mShootCount + 2 ) + { + KMissile *missile = new KMissile( animation[ID_MISSILE], &field ); + missile->move( 21+ship->x()+cosangle*10, + 21+ship->y()+sinangle*10, 0 ); + missile->setVelocity( shipDx + cosangle*MISSILE_SPEED, + shipDy + sinangle*MISSILE_SPEED ); + missile->show( ); + missiles.append( missile ); + shotsFired++; + reducePower( 1 ); + + shootDelay = 5; + } + + if ( shootDelay ) + shootDelay--; + } + + if ( teleportShip ) + { + int ra = rand() % 10; + if( ra == 0 ) + ra += rand() % 20; + int xra = ra * 60 + ( (rand() % 20) * (rand() % 20) ); + int yra = ra * 50 - ( (rand() % 20) * (rand() % 20) ); + ship->move( xra, yra ); + } + + vitalsChanged = true; + } +} + +// - - - + +void KAsteroidsView::processPowerups() +{ + if ( !powerups.isEmpty() ) + { + // if player gets the powerup remove it from the screen, if option + // "Can destroy powerups" is enabled and a missile hits the powerup + // destroy it + + KPowerup *pup; + QPtrListIterator it( powerups ); + + for( ; (pup = it.current()); ) + { + ++it; // We have to increase here, because pup may get deleted. + pup->growOlder(); + + if( pup->expired() ) + { + powerups.removeRef( pup ); + continue; + } + + wrapSprite( pup ); + + QCanvasItemList hits = pup->collisions( true ); + QCanvasItemList::Iterator it; + for ( it = hits.begin(); it != hits.end(); ++it ) + { + if ( (*it) == ship || (*it) == shield ) + { + switch( pup->rtti() ) + { + case ID_ENERGY_POWERUP: + shipPower += 150; + if ( shipPower > MAX_POWER_LEVEL ) + shipPower = MAX_POWER_LEVEL; + break; + case ID_TELEPORT_POWERUP: + mTeleportCount++; + break; + case ID_BRAKE_POWERUP: + if ( mBrakeCount < MAX_BRAKES ) + mBrakeCount++; + break; + case ID_SHIELD_POWERUP: + if ( mShieldCount < MAX_SHIELDS ) + mShieldCount++; + break; + case ID_SHOOT_POWERUP: + if ( mShootCount < MAX_FIREPOWER ) + mShootCount++; + break; + } + + powerups.removeRef( pup ); + vitalsChanged = true; + break; + } + + if ( (*it)->rtti() == ID_MISSILE ) + { + if ( Settings::canDestroyPowerups() ) + { + powerups.removeRef( pup ); + break; + } + } + } + } + } // -- if( powerups.isEmpty() ) +} + +// - - - + +void KAsteroidsView::hideShield() +{ + shield->hide(); + mShieldCount = 0; + shieldOn = false; +} + + +// - - - + diff --git a/kasteroids/view.h b/kasteroids/view.h new file mode 100644 index 00000000..6807037a --- /dev/null +++ b/kasteroids/view.h @@ -0,0 +1,133 @@ +/* + * KAsteroids - Copyright (c) Martin R. Jones 1997 + * + * Part of the KDE project + */ + +#ifndef __AST_VIEW_H__ +#define __AST_VIEW_H__ + +#include +#include +#include +#include +#include +#include "sprites.h" +#include + +#define MAX_POWER_LEVEL 1000 + +class KAsteroidsView : public QWidget +{ + Q_OBJECT +public: + KAsteroidsView( QWidget *parent = 0, const char *name = 0 ); + virtual ~KAsteroidsView(); + + int refreshRate; + + void reset(); + void setRockSpeed( double rs ) { rockSpeed = rs; } + void addRocks( int num ); + void newGame(); + void endGame(); + void newShip(); + + void rotateLeft( bool r ) { rotateL = r; rotateSlow = 5; } + void rotateRight( bool r ) { rotateR = r; rotateSlow = 5; } + void thrust( bool t ) { thrustShip = t && shipPower > 0; } + void shoot( bool s ) { shootShip = s; shootDelay = 0; } + void setShield( bool s ); + void teleport( bool te) { teleportShip = te && mTeleportCount; } + void brake( bool b ); + void pause( bool p); + + void showText( const QString &text, const QColor &color, bool scroll=TRUE ); + void hideText(); + + int shots() const { return shotsFired; } + int hits() const { return shotsHit; } + int power() const { return shipPower; } + + int teleportCount() const { return mTeleportCount; } + int brakeCount() const { return mBrakeCount; } + int shieldCount() const { return mShieldCount; } + int shootCount() const { return mShootCount; } + +signals: + void shipKilled(); + void rockHit( int size ); + void rocksRemoved(); + void updateVitals(); + +private slots: + void hideShield(); + +protected: + void readSprites(); + void wrapSprite( QCanvasItem * ); + void rockHit( QCanvasItem * ); + void reducePower( int val ); + void addExhaust( double x, double y, double dx, double dy, int count ); + void processMissiles(); + void processShip(); + void processPowerups(); + void processShield(); + + virtual void resizeEvent( QResizeEvent *event ); + virtual void timerEvent( QTimerEvent * ); + +private: + QCanvas field; + QCanvasView view; + QIntDict animation; + QPtrList rocks; + QPtrList missiles; + QPtrList bits; + QPtrList exhaust; + QPtrList powerups; + KShield *shield; + QCanvasSprite *ship; + QCanvasText *textSprite; + + bool rotateL; + bool rotateR; + bool thrustShip; + bool shootShip; + bool teleportShip; + bool brakeShip; + bool pauseShip; + bool shieldOn; + + bool vitalsChanged; + + int shipAngle; + int rotateSlow; + int rotateRate; + int shipPower; + + int shotsFired; + int shotsHit; + int shootDelay; + + int mBrakeCount; + int mShieldCount; + int mTeleportCount; + int mShootCount; + + double shipDx; + double shipDy; + + int textDy; + int mFrameNum; + bool mPaused; + int mTimerId; + + double rockSpeed; + double powerupSpeed; + + KRandomSequence krandom; + QTimer *shieldTimer; +}; + +#endif diff --git a/katomic/Makefile.am b/katomic/Makefile.am new file mode 100644 index 00000000..4d554b33 --- /dev/null +++ b/katomic/Makefile.am @@ -0,0 +1,35 @@ + +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) +katomic_LDFLAGS = $(all_libraries) $(KDE_RPATH) +katomic_LDADD = $(LIB_KDEGAMES) +katomic_DEPENDENCIES = $(LIB_KDEGAMES_DEP) +METASOURCES = AUTO + +bin_PROGRAMS = katomic +katomic_SOURCES = feld.cpp molek.cpp main.cpp toplevel.cpp configbox.cpp\ + gamewidget.cpp + +pics_DATA = abilder.png molek.png +picsdir = $(appdir)/pics + +xdg_apps_DATA = katomic.desktop + +EXTRA_DIST = $(pics_DATA) +KDE_ICON = katomic + +appdir = $(kde_datadir)/katomic +app_DATA = katomicui.rc + +messages: rc.cpp + : > levelnames.cpp ;\ + for i in levels/level_*; do \ + egrep '^Name=' $$i | sed -e 's#^Name=\(.*\)$$#i18n(\"\1\")#' >> levelnames.cpp ;\ + done + $(XGETTEXT) *.cpp -o $(podir)/katomic.pot + rm -f levelnames.cpp + +install-data-local: + $(mkinstalldirs) $(DESTDIR)$(kde_datadir)/katomic + -rm -rf $(DESTDIR)$(kde_datadir)/katomic/levels + mkdir $(DESTDIR)$(kde_datadir)/katomic/levels + cp $(srcdir)/levels/level_* $(DESTDIR)$(kde_datadir)/katomic/levels/ diff --git a/katomic/abilder.png b/katomic/abilder.png new file mode 100644 index 00000000..99c60a0c Binary files /dev/null and b/katomic/abilder.png differ diff --git a/katomic/abilder.svgz b/katomic/abilder.svgz new file mode 100644 index 00000000..8a412b3f Binary files /dev/null and b/katomic/abilder.svgz differ diff --git a/katomic/atom.h b/katomic/atom.h new file mode 100644 index 00000000..19d9b087 --- /dev/null +++ b/katomic/atom.h @@ -0,0 +1,35 @@ +#ifndef ATOM_H +#define ATOM_H + +#define MAX_CONNS_PER_ATOM 8 + +class atom { + public: + char obj; + char conn[MAX_CONNS_PER_ATOM + 1]; + + bool operator==(const atom& rhs) const { return (rhs.obj == obj && !strcmp(rhs.conn,conn)); } + bool isEmpty() const { return (obj == 0 || obj == '.'); } +}; + +inline char int2atom(int i) { + if (!i) + return '.'; + if (i == 254) + return '#'; + if (i <= 9) + return i + '0'; + return i + 'a' - 10; +} + +inline int atom2int(char ch) { + if (ch == '.' || ch == 0) + return 0; + if (ch == '#') + return 254; + if (ch >= '0' && ch <= '9') + return ch - '0'; + return ch - 'a' + 10; +} + +#endif diff --git a/katomic/configbox.cpp b/katomic/configbox.cpp new file mode 100644 index 00000000..ea546d89 --- /dev/null +++ b/katomic/configbox.cpp @@ -0,0 +1,59 @@ +/* configbox.cpp + * + * 1999 (c) Cristian Tibirna (tibirna@kde.org) + * + */ + +#include "configbox.h" +#include +#include +#include +#include +#include +#include + +#include "settings.h" + +extern Options settings; + +ConfigBox::ConfigBox ( QWidget *parent, const char *name) + : KDialogBase ( parent, name, true, i18n("Configure"), Ok | Cancel, Ok, true ) +{ + QWidget *page = makeMainWidget(); + + QGridLayout *glay = new QGridLayout (page, 4, 5, 0, spacingHint()); + glay->setRowStretch(0, 1); + glay->setRowStretch(3, 1); + glay->setColStretch(0, 1); + glay->setColStretch(4, 1); + + glay->addWidget(new QLabel(i18n("Animation speed:"),page), 2, 1); + + disp = new QLCDNumber(page); + glay->addWidget(disp, 1, 2); + disp->display(1); + + speed = new QSlider(1, 10, 1, 1, QSlider::Horizontal, page); + glay->addMultiCellWidget(speed, 2, 2, 2, 3); + + connect(speed, SIGNAL(valueChanged(int)), disp, SLOT(display(int))); + + speed->setValue(settings.anim_speed); + incInitialSize(QSize(20,20), true); +} + +void ConfigBox::slotOk() +{ + settings.anim_speed = speed->value(); + settings.changed = true; + + emit speedChanged(); + + accept(); +} + +ConfigBox::~ConfigBox() +{ +} + +#include "configbox.moc" diff --git a/katomic/configbox.h b/katomic/configbox.h new file mode 100644 index 00000000..0acdfd0d --- /dev/null +++ b/katomic/configbox.h @@ -0,0 +1,35 @@ +/* configbox.h + * + * 1999 (c) Cristian Tibirna (tibirna@kde.org) + * + */ + +#ifndef CONFIGBOX_H +#define CONFIGBOX_H + +#include +#include +#include + +#include + +class ConfigBox : public KDialogBase +{ + Q_OBJECT + +public: + ConfigBox ( QWidget *, const char* name ); + ~ConfigBox(); + +protected slots: + void slotOk(); + +signals: + void speedChanged(); + +private: + QSlider *speed; + QLCDNumber *disp; +}; + +#endif diff --git a/katomic/feld.cpp b/katomic/feld.cpp new file mode 100644 index 00000000..47af46d5 --- /dev/null +++ b/katomic/feld.cpp @@ -0,0 +1,644 @@ +/**************************************************************** +** +** Implementation Feld class, derieved from Qt tutorial 8 +** +****************************************************************/ + +// bemerkungen : wenn paintEvent aufgerufen wird, wird das komplette +// widget gelöscht und nur die sachen gezeichnet, die in +// paintEvent stehen ! sollen dinge z.b nur bei maustasten- +// druck gezeichnet werden, so muß dies in mousePressEvent +// stehen ! +// paintEvent wird aufgerufen, falls fenster überdeckt wird, +// oder auch einfach bewegt wird + +#include +#include +#include +#include +#include "molek.h" +#include "feld.h" +#include "settings.h" + +#if FIELD_SIZE < MOLEK_SIZE +#error Molecule size (MOLEK_SIZE) must be <= field size (FIELD_SIZE) +#endif + +extern Options settings; + +Feld::Feld( QWidget *parent, const char *name ) : + QWidget( parent, name ), + data(locate("appdata", "pics/abilder.png")), + undoBegin (0), undoSize (0), redoSize (0) +{ + anim = false; + dir = None; + sprite = QPixmap (30, 30); + + cx = -1; + cy = -1; + + point = new QPoint [1]; + + moving = false; + chosen = false; + + setMouseTracking(true); + + setFocusPolicy(QWidget::StrongFocus); + setBackgroundColor( QColor( 0, 0, 0) ); + + setFixedSize(15 * 30, 15 * 30); +} + +Feld::~Feld () +{ + delete [] point; +} + +void Feld::resetValidDirs() +{ + for (int j = 0; j < FIELD_SIZE; j++) + for (int i = 0; i < FIELD_SIZE; i++) + if (feld[i][j] >= 150 && feld[i][j] <= 153) + { + feld[i][j] = 0; + putNonAtom(i,j, Feld::None); + } +} + +void Feld::load (const KSimpleConfig& config) +{ + if(moving) + killTimers(); + + mol->load(config); + + QString key; + + for (int j = 0; j < FIELD_SIZE; j++) { + + key.sprintf("feld_%02d", j); + QString line = config.readEntry(key); + + for (int i = 0; i < FIELD_SIZE; i++) + feld[i][j] = atom2int(line[i].latin1()); + + } + + moves = 0; + chosen = false; + moving = false; + + undoSize = redoSize = undoBegin = 0; + emit enableUndo(false); + emit enableRedo(false); + + xpos = ypos = 0; + nextAtom(); +} + +void Feld::mousePressEvent (QMouseEvent *e) +{ + if (moving) + return; + + int x = e->pos ().x () / 30; + int y = e->pos ().y () / 30; + + if ( feld [x] [y] == 150) + startAnimation (Feld::MoveUp); + else if ( feld [x] [y] == 151) + startAnimation (Feld::MoveLeft); + else if ( feld [x] [y] == 152) + startAnimation (Feld::MoveDown); + else if ( feld [x] [y] == 153) + startAnimation (Feld::MoveRight); + else if (feld [x] [y] != 254 && feld [x] [y] != 0) { + chosen = true; + xpos = x; + ypos = y; + dir = None; + resetValidDirs(); + } else { + resetValidDirs(); + chosen = false; + } + emitStatus(); +} + +const atom& Feld::getAtom(uint index) const +{ + return mol->getAtom(index); +} + + +void Feld::nextAtom() +{ + int x = xpos, y; + + // make sure we don't check the current atom :-) + if (ypos++ >= 15) ypos = 0; + + while(1) + { + for (y = ypos; y < FIELD_SIZE; y++) + { + if ( feld [x] [y] != 0 && + feld [x] [y] != 254 && + feld [x] [y] != 150 && + feld [x] [y] != 151 && + feld [x] [y] != 152 && + feld [x] [y] != 153 ) + { + xpos = x; ypos = y; + chosen = true; + resetValidDirs(); + emitStatus(); + return; + } + } + ypos = 0; + x++; + if (x >= FIELD_SIZE) x = 0; + } + +} + + +void Feld::previousAtom() +{ + int x = xpos, y; + + // make sure we don't check the current atom :-) + if (ypos-- <= 0) ypos = FIELD_SIZE-1; + + while(1) + { + for (y = ypos; y >= 0; y--) + { + if ( feld [x] [y] != 0 && + feld [x] [y] != 254 && + feld [x] [y] != 150 && + feld [x] [y] != 151 && + feld [x] [y] != 152 && + feld [x] [y] != 153 ) + { + xpos = x; ypos = y; + chosen = true; + resetValidDirs(); + emitStatus(); + return; + } + } + ypos = FIELD_SIZE-1; + x--; + if (x <= 0) x = FIELD_SIZE-1; + } +} + + +void Feld::emitStatus() +{ + if (!chosen || moving) {} + else { + + if (ypos > 0 && feld[xpos][ypos-1] == 0) { + feld [xpos][ypos-1] = 150; + putNonAtom(xpos, ypos-1, Feld::MoveUp); + } + + if (ypos < FIELD_SIZE-1 && feld[xpos][ypos+1] == 0) { + feld [xpos][ypos+1] = 152; + putNonAtom(xpos, ypos+1, Feld::MoveDown); + } + + if (xpos > 0 && feld[xpos-1][ypos] == 0) { + feld [xpos-1][ypos] = 151; + putNonAtom(xpos-1, ypos, Feld::MoveLeft); + } + + if (xpos < FIELD_SIZE-1 && feld[xpos+1][ypos] == 0) { + feld [xpos+1][ypos] = 153; + putNonAtom(xpos+1, ypos, Feld::MoveRight); + } + + } +} + +void Feld::done () +{ + if (moving) + return; + + emitStatus(); + + if (checkDone()) + emit gameOver(moves); + +} + +void Feld::startAnimation (Direction d) +{ + // if animation is already started, return + if (moving || !chosen) + return; + + switch (d) { + case MoveUp: + if (ypos == 0 || feld [xpos] [ypos-1] != 150) + return; + break; + case MoveDown: + if (ypos == FIELD_SIZE-1 || feld [xpos] [ypos+1] != 152) + return; + break; + case MoveLeft: + if (xpos == 0 || feld [xpos-1] [ypos] != 151) + return; + break; + case MoveRight: + if (xpos == FIELD_SIZE-1 || feld [xpos+1] [ypos] != 153) + return; + break; + default: + break; + } + + // reset validDirs now so that arrows don't get drawn + resetValidDirs(); + + int x = 0, y = 0; + + moves++; + emit sendMoves(moves); + dir = d; + + switch (dir) { + case MoveUp : + for (x = xpos, y = ypos-1, anz = 0; y >= 0 && feld [x] [y] == 0; anz++, y--); + if (anz != 0) + { + feld [x] [++y] = feld [xpos] [ypos]; + } + break; + case MoveDown : + for (x = xpos, y = ypos+1, anz = 0; y <= FIELD_SIZE-1 && feld [x] [y] == 0; anz++, y++); + if (anz != 0) + { + feld [x] [--y] = feld [xpos] [ypos]; + } + break; + case MoveRight : + for (x = xpos+1, y = ypos, anz = 0; x <= FIELD_SIZE-1 && feld [x] [y] == 0; anz++, x++); + if (anz != 0) + { + feld [--x] [y] = feld [xpos] [ypos]; + } + break; + case MoveLeft : + for (x = xpos-1, y = ypos, anz = 0; x >= 0 && feld [x] [y] == 0; anz++, x--); + if (anz != 0) + { + feld [++x] [y] = feld [xpos] [ypos]; + } + break; + default: + return; + } + + if (anz != 0) { + moving = true; + + // BEGIN: Insert undo informations + uint undoChunk = (undoBegin + undoSize) % MAX_UNDO; + undo[undoChunk].atom = feld[xpos][ypos]; + undo[undoChunk].oldxpos = xpos; + undo[undoChunk].oldypos = ypos; + undo[undoChunk].xpos = x; + undo[undoChunk].ypos = y; + undo[undoChunk].dir = dir; + if (undoSize == MAX_UNDO) + undoBegin = (undoBegin + 1) % MAX_UNDO; + else + ++undoSize; + redoSize = undoSize; + emit enableUndo(true); + emit enableRedo(false); + // END: Insert undo informations + + feld [xpos] [ypos] = 0; + + // absolutkoordinaten des zu verschiebenden bildes + cx = xpos * 30; + cy = ypos * 30; + xpos = x; + ypos = y; + // 30 animationsstufen + framesbak = frames = anz * 30; + + // 10 mal pro sek + startTimer (10); + + bitBlt (&sprite, 0, 0, this, cx, cy, 30, 30, CopyROP); + } + +} + +void Feld::doUndo () +{ + if (moving || !chosen || undoSize == 0) + return; + + UndoInfo &undo_info = undo[(undoBegin + --undoSize) % MAX_UNDO]; + emit enableUndo(undoSize != 0); + emit enableRedo(true); + + --moves; + emit sendMoves(moves); + + moving = true; + resetValidDirs (); + + cx = undo_info.xpos; + cy = undo_info.ypos; + xpos = undo_info.oldxpos; + ypos = undo_info.oldypos; + feld[cx][cy] = 0; + feld[xpos][ypos] = undo_info.atom; + cx *= 30; cy *= 30; + framesbak = frames = + 30 * (abs (undo_info.xpos - undo_info.oldxpos) + + abs (undo_info.ypos - undo_info.oldypos) ); + startTimer (10); + dir = (Direction) -((int) undo_info.dir); + bitBlt (&sprite, 0, 0, this, cx, cy, 30, 30, CopyROP); +} + +void Feld::doRedo () +{ + if (moving || !chosen || undoSize == redoSize) + return; + + UndoInfo &undo_info = undo[(undoBegin + undoSize++) % MAX_UNDO]; + + emit enableUndo(true); + emit enableRedo(undoSize != redoSize); + + ++moves; + emit sendMoves(moves); + + moving = true; + resetValidDirs (); + + cx = undo_info.oldxpos; + cy = undo_info.oldypos; + xpos = undo_info.xpos; + ypos = undo_info.ypos; + feld[cx][cy] = 0; + feld[xpos][ypos] = undo_info.atom; + cx *= 30; cy *= 30; + framesbak = frames = + 30 * (abs (undo_info.xpos - undo_info.oldxpos) + + abs (undo_info.ypos - undo_info.oldypos) ); + startTimer (10); + dir = undo_info.dir; + bitBlt (&sprite, 0, 0, this, cx, cy, 30, 30, CopyROP); +} + +void Feld::mouseMoveEvent (QMouseEvent *e) +{ + // warning: mouseMoveEvents can report positions upto 1 pixel outside + // of the field widget, so we must be sure handle this case + + if( e->pos().x() < 0 || e->pos().x() >= 450 || + e->pos().y() < 0 || e->pos().y() >= 450 ) + { + setCursor(arrowCursor); + } + else + { + int x = e->pos ().x () / 30; + int y = e->pos ().y () / 30; + + // verschiedene cursor je nach pos + if (feld[x][y] != 254 && feld [x] [y] != 0) + setCursor (crossCursor); + else + setCursor (arrowCursor); + } +} + + +bool Feld::checkDone () +{ + int molecWidth = mol->molecSize().width(); + int molecHeight = mol->molecSize().height(); + int i = 0; + int j = 0; + + // find first atom in molecule + uint firstAtom = 0; + for(j = 0; j < molecHeight && !firstAtom; ++j) + firstAtom = mol->getAtom(0, j); + + // wot no atom? + if(!firstAtom) + return true; // true skips to next level + + // position of first atom (in molecule coordinates) + int mx = 0; + int my = j - 1; + + QRect extent(0, 0, FIELD_SIZE - molecWidth + 1, FIELD_SIZE - molecHeight + 1); + extent.moveBy(0, my); + + // find first atom in playing field + for(i = extent.left(); i <= extent.right(); ++i) + { + for(j = extent.top(); j <= extent.bottom(); ++j) + { + if(feld[i][j] == firstAtom) + { + // attempt to match playing field to molecule + int ox = i - mx; // molecule origin (in field coordinates) + int oy = j - my; // molecule origin (in field coordinates) + ++my; // no need to test first atom again + while(mx < molecWidth) + { + while(my < molecHeight) + { + uint nextAtom = mol->getAtom(mx, my); + if(nextAtom != 0 && feld[ox + mx][oy + my] != nextAtom) + return false; + ++my; + } + my = 0; + ++mx; + } + return true; + } + } + } + // if we got here, then the first atom is too low or too far right + // for the molecule to be assembled correctly + return false; +} + + +void Feld::timerEvent (QTimerEvent *) +{ + // animation beenden + if (frames <= 0) + { + moving = false; + killTimers (); + done(); + dir = None; + } + else + { + frames -= settings.anim_speed; + if (frames < 0) + frames = 0; + + paintMovingAtom(); + } +} + +void Feld::paintMovingAtom() +{ + int a = settings.anim_speed; + + QPainter paint(this); + + switch(dir) + { + case MoveUp: + bitBlt(this, cx, cy - framesbak + frames, &sprite, CopyROP); + if(framesbak - frames > 0) + paint.eraseRect(cx, cy - framesbak + frames + 30, 30, a); + break; + case MoveDown: + bitBlt(this, cx, cy + framesbak - frames, &sprite, CopyROP); + if(framesbak - frames > 0) + paint.eraseRect(cx, cy + framesbak - frames - a, 30, a); + break; + case MoveRight: + bitBlt(this, cx + framesbak - frames, cy, &sprite, CopyROP); + if(framesbak - frames > 0) + paint.eraseRect(cx + framesbak - frames - a, cy, a, 30); + break; + case MoveLeft: + bitBlt(this, cx - framesbak + frames, cy, &sprite, CopyROP); + if(framesbak - frames > 0) + paint.eraseRect(cx - framesbak + frames + 30, cy, a, 30); + break; + case None: + break; + } +} + +void Feld::putNonAtom (int x, int y, Direction which, bool brick) +{ + int xarr=0, yarr=0; + switch (which) + { + case None : xarr = 279, yarr = 31 * (brick?1:2); break; + case MoveUp : xarr = 248; yarr = 62; break; + case MoveLeft : xarr = 217; yarr = 93; break; + case MoveDown : xarr = 248; yarr = 93; break; + case MoveRight : xarr = 279; yarr = 93; break; + } + + bitBlt(this, x * 30, y * 30, &data, xarr, yarr, 30, 30, CopyROP); +} + +void Feld::paintEvent( QPaintEvent * ) +{ + int i, j, x, y; + + QPainter paint ( this ); + + paint.setPen (black); + + // spielfeld gleich zeichnen + + for (i = 0; i < FIELD_SIZE; i++) + { + for (j = 0; j < FIELD_SIZE; j++) + { + if(moving && i == xpos && j == ypos) + continue; + + x = i * 30; + y = j * 30; + + // zeichnet Randstücke + if (feld [i] [j] == 254) { + putNonAtom(i, j, Feld::None, true); continue; + } + + if (feld[i][j] == 150) { + putNonAtom(i, j, Feld::MoveUp); continue; + } + + if (feld[i][j] == 151) { + putNonAtom(i, j, Feld::MoveLeft); continue; + } + if (feld[i][j] == 152) { + putNonAtom(i, j, Feld::MoveDown); continue; + } + + if (feld[i][j] == 153) { + putNonAtom(i, j, Feld::MoveRight); continue; + } + + // zeichnet Atome + if (getAtom(feld [i] [j]).obj <= '9' && getAtom(feld [i] [j]).obj > '0') + { + bitBlt (this, x, y, &data, (getAtom(feld [i] [j]).obj - '1') * 31, 0, 30, + 30, CopyROP); + } + + // zeichnet Kristalle + if (getAtom(feld [i] [j]).obj == 'o') + { + bitBlt (this, x, y, &data, 31, 93, 30, 30, CopyROP); + } + + + + // verbindungen zeichnen + if (getAtom(feld [i] [j]).obj <= '9' || + getAtom(feld [i] [j]).obj == 'o') + for (int c = 0; c < MAX_CONNS_PER_ATOM; c++) { + char conn = getAtom(feld [i] [j]).conn[c]; + if (!conn) + break; + + if (conn >= 'a' && conn <= 'a' + 8) + bitBlt (this, x, y, + &data, (conn - 'a') * 31, 31, 30, 30, + XorROP); + else + bitBlt (this, x, y, + &data, (conn - 'A') * 31, 62, 30, 30, + XorROP); + + } + + // zeichnet Verbindungsstäbe + if (getAtom(feld [i] [j]).obj >= 'A' && + getAtom(feld [i] [j]).obj <= 'F') + bitBlt (this, x, y, + &data, + (getAtom(feld [i] [j]).obj - 'A' + 2) * 31 , + 93, 30, 30, + CopyROP); + } + } +} + + +#include "feld.moc" diff --git a/katomic/feld.h b/katomic/feld.h new file mode 100644 index 00000000..6a1cf761 --- /dev/null +++ b/katomic/feld.h @@ -0,0 +1,116 @@ +/**************************************************************** +** +** Definition of Feld class, +** +****************************************************************/ + +#ifndef FELD_H +#define FELD_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "atom.h" + +#define MAX_UNDO (100) + +class KSimpleConfig; +class Molek; + +#define FIELD_SIZE 15 + +class Feld : public QWidget +{ + Q_OBJECT + +public: + Feld (QWidget *parent=0, const char *name=0); + ~Feld (); + + enum Direction { None = 0, + MoveUp = 1, + MoveDown = (-1), + MoveLeft = 2, + MoveRight = (-2) }; + + void startAnimation (Direction dir); + void done (); + + void load (const KSimpleConfig& config); + + void setMolek(Molek *_mol) { mol = _mol; } + + void doUndo (); + void doRedo (); + +signals: + void gameOver(int moves); + void sendMoves(int moves); + void enableRedo(bool enable); + void enableUndo(bool enable); + +protected: + bool checkDone(); + void timerEvent (QTimerEvent *); + void paintEvent( QPaintEvent * ); + void paintMovingAtom(); + void mousePressEvent (QMouseEvent *); + void mouseMoveEvent (QMouseEvent *); + void emitStatus(); + +protected: + struct UndoInfo { + uint atom; + int oldxpos, oldypos; + int xpos, ypos; + Direction dir; + }; + +public slots: + void nextAtom(); + void previousAtom(); + +private: + + const atom& getAtom(uint index) const; + + void putNonAtom(int, int, Direction, bool brick = false); + + QPoint *point; + QPixmap data; + QPixmap sprite; + + Molek *mol; + + uint feld[FIELD_SIZE][FIELD_SIZE]; + + // number of movements + int moves; + + Direction dir; + int cx, cy; + int xpos, ypos; + int anz; + int frames, framesbak; + + bool anim; + bool chosen, moving; + + uint undoBegin; + uint undoSize; + uint redoSize; + UndoInfo undo[MAX_UNDO]; + + void resetValidDirs(); + +}; + +#endif // FELD_H diff --git a/katomic/gamewidget.cpp b/katomic/gamewidget.cpp new file mode 100644 index 00000000..fc6838a8 --- /dev/null +++ b/katomic/gamewidget.cpp @@ -0,0 +1,230 @@ +/* toplevel.cpp + + Copyright (C) 1998 Andreas Wüst (AndreasWuest@gmx.de) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + */ + +#include "settings.h" +#include "gamewidget.h" +#include "feld.h" +#include "molek.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +Options settings; + +#define MPOSX 480 +#define MPOSY 90 + +// ########################## +// # class GameWidget # +// ########################## + +int level; + +void GameWidget::moveUp() +{ + feld->startAnimation (Feld::MoveUp); +} + +void GameWidget::moveDown() +{ + feld->startAnimation (Feld::MoveDown); +} + +void GameWidget::moveLeft() +{ + feld->startAnimation (Feld::MoveLeft); +} + +void GameWidget::moveRight() +{ + feld->startAnimation (Feld::MoveRight); +} + +void GameWidget::nextAtom() +{ + feld->nextAtom(); +} + +void GameWidget::previousAtom() +{ + feld->previousAtom(); +} + +void GameWidget::getButton (int button) +{ + feld->startAnimation ((Feld::Direction)button); +} + +void GameWidget::doUndo () +{ + feld->doUndo (); +} + +void GameWidget::doRedo () +{ + feld->doRedo (); +} + +void GameWidget::gameOver(int moves) { + KMessageBox::information(this, i18n("You solved level %1 with %2 moves!").arg(level).arg(moves), i18n("Congratulations")); + + KScoreDialog high(KScoreDialog::Name | KScoreDialog::Score, this); + high.setCaption(i18n("Level %1 Highscores").arg(level)); + high.setConfigGroup(QString("Highscores Level %1").arg(level)); + + KScoreDialog::FieldInfo scoreInfo; + + if (high.addScore(moves, scoreInfo, true, true)) + { + high.exec(); + } + updateLevel(level+1); +} + +void GameWidget::getMoves(int moves) +{ + current.setNum(moves); + ys->setText(current); +} + +void GameWidget::updateLevel (int l) +{ + level=l; + QString levelFile = locate("appdata", QString("levels/level_%1").arg(l)); + if (levelFile.isNull()) { + return updateLevel(1); + } + + KSimpleConfig cfg(levelFile, true); + cfg.setGroup("Level"); + feld->load(cfg); + + highScore->setConfigGroup(QString("High Scores Level %1").arg(level)); + highest.setNum(highScore->highScore()); + + if (highest != "0" ) hs->setText(highest); + else hs->setText("-"); + ys->setText("0"); + scrl->setValue(level); + + feld->repaint(); +} + +void GameWidget::restartLevel() +{ + updateLevel(level); +} + +GameWidget::GameWidget ( QWidget *parent, const char* name ) + : QWidget( parent, name ) +{ + level = 1; + nlevels = KGlobal::dirs()->findAllResources("appdata", "levels/level_*", + false, true).count(); + + QHBoxLayout *top = new QHBoxLayout(this, 10); + + // spielfeld + feld = new Feld (this, "feld"); + feld->setFocus(); + + top->addWidget(feld); + + QVBox *vb = new QVBox(this); + vb->setSpacing(20); + top->addWidget(vb); + + // scrollbar + scrl = new QScrollBar(1, nlevels, 1, + 5, 1, QScrollBar::Horizontal, vb, "scrl" ); + connect (scrl, SIGNAL (valueChanged (int)), SLOT (updateLevel (int))); + + // molekül + molek = new Molek (vb, "molek"); + feld->setMolek(molek); + + connect (feld, SIGNAL (gameOver(int)), SLOT(gameOver(int))); + connect (feld, SIGNAL (sendMoves(int)), SLOT(getMoves(int))); + connect (feld, SIGNAL (enableRedo(bool)), SIGNAL(enableRedo(bool))); + connect (feld, SIGNAL (enableUndo(bool)), SIGNAL(enableUndo(bool))); + + highScore = new KScoreDialog(KScoreDialog::Name | KScoreDialog::Score, this); + + // the score group + QGroupBox *bg = new QGroupBox (i18n("Score"), vb, "bg"); + QBoxLayout *slay = new QVBoxLayout (bg, 10); + + slay->addSpacing(10); + + slay->addWidget(new QLabel(i18n("Highscore:"), bg)); + + QFont headerFont = KGlobalSettings::generalFont(); + headerFont.setBold(true); + + hs = new QLabel (highest, bg); + hs->setAlignment(Qt::AlignRight); + hs->setFont(headerFont); + slay->addWidget(hs); + + slay->addSpacing(10); + + slay->addWidget(new QLabel(i18n("Your score so far:"), bg)); + + ys = new QLabel (current, bg); + ys->setAlignment(Qt::AlignRight); + ys->setFont(headerFont); + slay->addWidget(ys); + + updateLevel(1); + + KConfig *config = KGlobal::config(); + config->setGroup("Options"); + settings.anim_speed = config->readNumEntry("Animation Speed", 1); + if (settings.anim_speed < 1 || settings.anim_speed > MAX_SPEED) + settings.anim_speed = 1; + + settings.changed = false; +} + +GameWidget::~GameWidget() +{ +} + +void GameWidget::showHighscores () +{ + KScoreDialog high(KScoreDialog::Name | KScoreDialog::Score, this); + high.setCaption(i18n("Level %1 Highscores").arg(level)); + high.setConfigGroup(QString("Highscores Level %1").arg(level)); + high.exec(); +} + +#include "gamewidget.moc" diff --git a/katomic/gamewidget.h b/katomic/gamewidget.h new file mode 100644 index 00000000..6259e6b7 --- /dev/null +++ b/katomic/gamewidget.h @@ -0,0 +1,74 @@ + +#ifndef GAMEWIDGET_H +#define GAMEWIDGET_H + +class Feld; +class Molek; +class QScrollBar; +class QLabel; +class KScoreDialog; + +#include + +class GameWidget : public QWidget +{ + Q_OBJECT + + public: + + GameWidget ( QWidget *parent, const char *name=0 ); + + ~GameWidget(); + + signals: + void enableRedo(bool enable); + void enableUndo(bool enable); + + public slots: + // bringt level auf neuesten stand + void updateLevel (int); + + // restart current level + void restartLevel(); + + // getbutton erhält button der gedrückt wurde + void getButton (int); + + void gameOver(int moves); + + // use this slot to update the moves continually + void getMoves(int moves); + + // Menupunkt Highscores im Pop-up Menu, der Highscore anzeigt + void showHighscores (); + + void moveUp(); + void moveDown(); + void moveLeft(); + void moveRight(); + void nextAtom(); + void previousAtom(); + void doUndo (); + void doRedo (); + + protected: + + // stellt das spielfeld dar ! + Feld *feld; + + // stellt molekül dar + Molek *molek; + + // scorllbar zur levelwahl + QScrollBar *scrl; + + // important labels : highest and current scores + QLabel *hs, *ys; + QString highest, current; + + int nlevels; + + KScoreDialog *highScore; +}; + +#endif diff --git a/katomic/hi128-app-katomic.png b/katomic/hi128-app-katomic.png new file mode 100644 index 00000000..94f1cbd0 Binary files /dev/null and b/katomic/hi128-app-katomic.png differ diff --git a/katomic/hi16-app-katomic.png b/katomic/hi16-app-katomic.png new file mode 100644 index 00000000..fde5a50a Binary files /dev/null and b/katomic/hi16-app-katomic.png differ diff --git a/katomic/hi22-app-katomic.png b/katomic/hi22-app-katomic.png new file mode 100644 index 00000000..d4a26a6a Binary files /dev/null and b/katomic/hi22-app-katomic.png differ diff --git a/katomic/hi32-app-katomic.png b/katomic/hi32-app-katomic.png new file mode 100644 index 00000000..afbe144d Binary files /dev/null and b/katomic/hi32-app-katomic.png differ diff --git a/katomic/hi48-app-katomic.png b/katomic/hi48-app-katomic.png new file mode 100644 index 00000000..8f18153e Binary files /dev/null and b/katomic/hi48-app-katomic.png differ diff --git a/katomic/hi64-app-katomic.png b/katomic/hi64-app-katomic.png new file mode 100644 index 00000000..26d07e0a Binary files /dev/null and b/katomic/hi64-app-katomic.png differ diff --git a/katomic/katomic.desktop b/katomic/katomic.desktop new file mode 100644 index 00000000..a614a171 --- /dev/null +++ b/katomic/katomic.desktop @@ -0,0 +1,73 @@ +[Desktop Entry] +Name=KAtomic +Name[af]=Katomic +Name[ar]=لعبة التركيب الجزيئي الكيميائي (KAtomic) +Name[be]=Ðтамы +Name[bn]=কে-অà§à¦¯à¦¾à¦Ÿà¦®à¦¿à¦• +Name[eo]=Atomoj +Name[hi]=के-à¤à¤Ÿà¥‰à¤®à¤¿à¤• +Name[ne]=केडीई à¤à¤Ÿà¥‹à¤®à¤¿à¤• +Name[pa]=ਕੇ-ਪਰਮਾਣੂ +Name[pl]=Atomy +Name[pt_BR]=KAtômico +Name[sv]=Katomic +Name[ta]=கேஅடாமிக௠+Name[tg]=KÐтомҳо +Name[th]=อะตอมมิภ- K +Name[tr]=Atomlar +Name[zh_TW]=KAtomic åŽŸå­ +Icon=katomic +Type=Application +Exec=katomic %i %m -caption "%c" +DocPath=katomic/index.html +GenericName=Sokoban-like Logic Game +GenericName[be]=Ð›Ð°Ð³Ñ–Ñ‡Ð½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ Ñ‚Ñ‹Ð¿Ñƒ Sokoban +GenericName[bg]=ЛогичеÑка игра Ñ Ð°Ñ‚Ð¾Ð¼Ð¸ +GenericName[bn]=সোকোবান-à¦à¦° মত যà§à¦•à§à¦¤à¦¿à¦° খেলা +GenericName[bs]=LogiÄka igra nalik na Sokoban +GenericName[ca]=Joc de lògica similar al Sokoban +GenericName[cs]=Logická hra jako Sokoban +GenericName[cy]=Gêm Resymeg sy'n debyg i Sokoban +GenericName[da]=Sokoban-lignende logisk spil +GenericName[de]=Logikspiel (ähnlich Sokoban) +GenericName[el]=Παιχνίδι λογικής παÏόμοιο με το Sokoban +GenericName[eo]=Logikludo simila al Sokoban +GenericName[es]=Juego de lógica similar al Sokoban +GenericName[et]=Sokobani moodi loogikamäng +GenericName[eu]=Sokoban-en antzeko joko logikoa +GenericName[fi]=Sokoban-tyylinen Logiikkapeli +GenericName[fr]=Jeu de logique dans le style de Sokoban +GenericName[ga]=Cluiche Loighce Mar Sokoban +GenericName[he]=חיקוי Sokoban, משחק לוגי +GenericName[hr]=LogiÄka igra poput Sokobana +GenericName[hu]=Sokoban-szerű logikai +GenericName[is]=Leikur sem líkist Sokoban +GenericName[it]=Gioco di logica simile a Sokoban +GenericName[ja]=倉庫番ã®ã‚ˆã†ãªè«–ç†ã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​ážáž€áŸ’ក​ដូច Sokoban +GenericName[ko]=소코반 ê°™ì€ ì „ëžµ 게임 +GenericName[lt]=Sokoban tipo loginis žaidimas +GenericName[lv]=Sokoban lÄ«dzÄ«ga loÄ£iskÄ spÄ“le +GenericName[mk]=Логичка игра Ñлична на Сокобан +GenericName[nb]=Sokoban-lignende logikkspill +GenericName[nds]=Sokoban-liek Logikspeel +GenericName[ne]=सोकोबान जसà¥à¤¤à¥ˆ यà¥à¤•à¥à¤¤à¤¿à¤¸à¤‚गत खेल +GenericName[nl]=Sokoban-achtig logisch spel +GenericName[nn]=Sokoban-liknande logikkspel +GenericName[pl]=Gra logiczna typu Sokoban +GenericName[pt]=Jogo de Lógica tipo Sokoban +GenericName[pt_BR]=Jogo Lógico parecido com Sokoban +GenericName[ru]=Забавные атомы +GenericName[se]=Sokoban-lágan logihkkaspeallu +GenericName[sk]=Logická hra ako Sokoban +GenericName[sl]=LogiÄna igra podobna Sokobanu +GenericName[sr]=Логичка игра налик на Sokoban +GenericName[sr@Latn]=LogiÄka igra nalik na Sokoban +GenericName[sv]=Sokoban-liknande logiskt spel +GenericName[ta]=சோகோபான௠போனà¯à®± லாஜிக௠விளையாடà¯à®Ÿà¯ +GenericName[uk]=Гра на логіку подібна до Сокобану +GenericName[wa]=Djeu di lodjike rishonnant a sokoban +GenericName[zh_TW]=倉庫番(sokoban)å¼çš„智力éŠæˆ² +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;StrategyGame; diff --git a/katomic/katomicui.rc b/katomic/katomicui.rc new file mode 100644 index 00000000..da25f243 --- /dev/null +++ b/katomic/katomicui.rc @@ -0,0 +1,6 @@ + + + + + + diff --git a/katomic/levels/level_1 b/katomic/levels/level_1 new file mode 100644 index 00000000..dc63e883 --- /dev/null +++ b/katomic/levels/level_1 @@ -0,0 +1,21 @@ +[Level] +Name=Water +atom_1=1-c +atom_2=3-cg +atom_3=1-g +feld_00=............... +feld_01=............... +feld_02=............... +feld_03=..###########.. +feld_04=..#..#......#.. +feld_05=..#.3#......#.. +feld_06=..#.##......#.. +feld_07=..#.#..#.####.. +feld_08=..#....#.2..#.. +feld_09=..###.#.....#.. +feld_10=..#1....#...#.. +feld_11=..###########.. +feld_12=............... +feld_13=............... +feld_14=............... +mole_0=123 diff --git a/katomic/levels/level_10 b/katomic/levels/level_10 new file mode 100644 index 00000000..28bce761 --- /dev/null +++ b/katomic/levels/level_10 @@ -0,0 +1,24 @@ +[Level] +Name=Formic Acid +atom_1=1-c +atom_2=3-C +atom_3=2-cgA +atom_4=3-cg +atom_5=1-g +feld_00=............... +feld_01=.#############. +feld_02=.#...........#. +feld_03=.###..###..###. +feld_04=.#.....#.....#. +feld_05=.#.###...###.#. +feld_06=.#.#2..#...#.#. +feld_07=.#1435###....#. +feld_08=.#.#...#...#.#. +feld_09=.#.###...###.#. +feld_10=.#.....#.....#. +feld_11=.###..###..###. +feld_12=.#...........#. +feld_13=.#############. +feld_14=............... +mole_0=.2.. +mole_1=1345 diff --git a/katomic/levels/level_11 b/katomic/levels/level_11 new file mode 100644 index 00000000..4b423171 --- /dev/null +++ b/katomic/levels/level_11 @@ -0,0 +1,28 @@ +[Level] +Name=Acetic Acid +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-C +atom_6=2-cgA +atom_7=3-cg +atom_8=1-g +feld_00=............... +feld_01=............... +feld_02=..###########.. +feld_03=..#...#...#.#.. +feld_04=..#.7.......#.. +feld_05=..#.#..8#.1.#.. +feld_06=..#4.3......#.. +feld_07=..#...#...#.#.. +feld_08=..#.........#.. +feld_09=..#.#.6.#...#.. +feld_10=..#5.....2..#.. +feld_11=..#...#...#.#.. +feld_12=..###########.. +feld_13=............... +feld_14=............... +mole_0=.25.. +mole_1=13678 +mole_2=.4... diff --git a/katomic/levels/level_12 b/katomic/levels/level_12 new file mode 100644 index 00000000..bb51be76 --- /dev/null +++ b/katomic/levels/level_12 @@ -0,0 +1,29 @@ +[Level] +Name=trans-Butene +atom_1=1-d +atom_2=1-b +atom_3=2-bdfh +atom_4=2-fhB +atom_5=1-h +atom_6=2-bdD +atom_7=1-f +feld_00=............... +feld_01=.#############. +feld_02=.#...........#. +feld_03=.#...#..6#...#. +feld_04=.#4.#.7...#..#. +feld_05=.#.#.##.##.#.#. +feld_06=.#..2........#. +feld_07=.###5##.##.###. +feld_08=.#...........#. +feld_09=.#.#.##1##.#.#. +feld_10=.#..#31...#1.#. +feld_11=.#...#..5#...#. +feld_12=.#3..5.......#. +feld_13=.#############. +feld_14=............... +mole_0=...1.7 +mole_1=.1..3. +mole_2=1.46.5 +mole_3=.3..5. +mole_4=2.5... diff --git a/katomic/levels/level_13 b/katomic/levels/level_13 new file mode 100644 index 00000000..29433c5e --- /dev/null +++ b/katomic/levels/level_13 @@ -0,0 +1,28 @@ +[Level] +Name=cis-Butene +atom_1=1-d +atom_2=1-b +atom_3=2-bdfh +atom_4=2-fhB +atom_5=1-h +atom_6=2-bdD +atom_7=1-f +feld_00=............... +feld_01=.#############. +feld_02=.#.#..##...#.#. +feld_03=.##..#.....#.#. +feld_04=.##....#..#..#. +feld_05=.#.5..1#.2..7#. +feld_06=.###....#.#..#. +feld_07=.#..#......###. +feld_08=.#.4..3.#6..3#. +feld_09=.#.##.##.....#. +feld_10=.##......#.#.#. +feld_11=.#.7##5.#2.#1#. +feld_12=.#.#....#.#..#. +feld_13=.#############. +feld_14=............... +mole_0=.1..7. +mole_1=1.46.7 +mole_2=.3..3. +mole_3=2.52.5 diff --git a/katomic/levels/level_14 b/katomic/levels/level_14 new file mode 100644 index 00000000..1dd41fa7 --- /dev/null +++ b/katomic/levels/level_14 @@ -0,0 +1,26 @@ +[Level] +Name=Dimethyl ether +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-cg +atom_6=1-g +feld_00=............... +feld_01=.#############. +feld_02=.#...3.#5...1#. +feld_03=.#.###.#.###.#. +feld_04=.#.#..4#...#.#. +feld_05=.#.#...#...#.#. +feld_06=.#.....#.....#. +feld_07=.####2....####. +feld_08=.#.....#.....#. +feld_09=.#.#...#...#.#. +feld_10=.#.#..6#..4#.#. +feld_11=.#.###.#.###2#. +feld_12=.#....3#.....#. +feld_13=.#############. +feld_14=............... +mole_0=.2.2. +mole_1=13536 +mole_2=.4.4. diff --git a/katomic/levels/level_15 b/katomic/levels/level_15 new file mode 100644 index 00000000..5de947f1 --- /dev/null +++ b/katomic/levels/level_15 @@ -0,0 +1,27 @@ +[Level] +Name=Butanol +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-ae +atom_6=1-g +feld_00=............... +feld_01=.#############. +feld_02=.#..42.....3.#. +feld_03=.#.#..####.###. +feld_04=.###2...#....#. +feld_05=.#.6..#.....4#. +feld_06=.#.##.#.##.4.#. +feld_07=.#.#..#..#.###. +feld_08=.###.3#.5.3#.#. +feld_09=.#1..........#. +feld_10=.#4...#..2...#. +feld_11=.#..###2##.#.#. +feld_12=.#....#.3..#.#. +feld_13=.#############. +feld_14=............... +mole_0=.2222. +mole_1=133336 +mole_2=.4445. +mole_3=....4. diff --git a/katomic/levels/level_16 b/katomic/levels/level_16 new file mode 100644 index 00000000..0e466be9 --- /dev/null +++ b/katomic/levels/level_16 @@ -0,0 +1,31 @@ +[Level] +Name=2-Methyl-2-Propanol +atom_1=1-c +atom_2=1-d +atom_3=1-e +atom_4=2-aceg +atom_5=1-a +atom_6=2-abeh +atom_7=3-ae +atom_8=1-f +atom_9=1-g +feld_00=............... +feld_01=.#####...#####. +feld_02=.#9.6#...#5.2#. +feld_03=.#8#.#####.#.#. +feld_04=.#5....#.....#. +feld_05=.###.#..3#.###. +feld_06=...#.....54#... +feld_07=...##.....##... +feld_08=...#4.....3#... +feld_09=.###.#...#.###. +feld_10=.#.....#.3..1#. +feld_11=.#.#7#####.#.#. +feld_12=.#4..#...#...#. +feld_13=.#####...#####. +feld_14=............... +mole_0=.238. +mole_1=.363. +mole_2=14449 +mole_3=.575. +mole_4=..5.. diff --git a/katomic/levels/level_17 b/katomic/levels/level_17 new file mode 100644 index 00000000..dc9efe1b --- /dev/null +++ b/katomic/levels/level_17 @@ -0,0 +1,28 @@ +[Level] +Name=Glycerin +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-cg +atom_6=1-g +feld_00=............... +feld_01=..###########.. +feld_02=..#.#.4#3.#.#.. +feld_03=..#.5#.#.#..#.. +feld_04=..##..3....##.. +feld_05=..#.#.....#.#.. +feld_06=..#.6....1..#.. +feld_07=..####5..####.. +feld_08=..#....15.1.#.. +feld_09=..#.#....3#.#.. +feld_10=..##.....6.##.. +feld_11=..#..#.#.#.2#.. +feld_12=..#.#.6#..#.#.. +feld_13=..###########.. +feld_14=............... +mole_0=.2.. +mole_1=1356 +mole_2=1356 +mole_3=1356 +mole_4=.4.. diff --git a/katomic/levels/level_18 b/katomic/levels/level_18 new file mode 100644 index 00000000..3fcd823d --- /dev/null +++ b/katomic/levels/level_18 @@ -0,0 +1,23 @@ +[Level] +Name=Poly-Tetra-Fluoro-Ethene +atom_1=6-e +atom_2=6-a +atom_3=2-aceg +feld_00=............... +feld_01=.#############. +feld_02=.#...........#. +feld_03=.#....#2#1...#. +feld_04=.#3...#.#....#. +feld_05=.#.####3####.#. +feld_06=.#2..........#. +feld_07=.####..#.2####. +feld_08=.#1.........1#. +feld_09=.#.####.####.#. +feld_10=.#..2.#.#....#. +feld_11=.#1...#.#...3#. +feld_12=.#..3........#. +feld_13=.#############. +feld_14=............... +mole_0=1111 +mole_1=3333 +mole_2=2222 diff --git a/katomic/levels/level_19 b/katomic/levels/level_19 new file mode 100644 index 00000000..70871b37 --- /dev/null +++ b/katomic/levels/level_19 @@ -0,0 +1,26 @@ +[Level] +Name=Oxalic Acid +atom_1=3-D +atom_2=2-ceD +atom_3=3-ae +atom_4=1-a +atom_5=2-egB +atom_6=3-B +feld_00=............... +feld_01=############### +feld_02=###3...#....### +feld_03=##.....#..6.5## +feld_04=#...#.....#...# +feld_05=#...##.2.##...# +feld_06=#....##.##....# +feld_07=###1...3....### +feld_08=#....##.##....# +feld_09=#...##.4.##...# +feld_10=#.4.#.....#...# +feld_11=##.....#.....## +feld_12=###....#....### +feld_13=############### +feld_14=............... +mole_0=6251 +mole_1=.33 +mole_2=.44 diff --git a/katomic/levels/level_2 b/katomic/levels/level_2 new file mode 100644 index 00000000..bc211909 --- /dev/null +++ b/katomic/levels/level_2 @@ -0,0 +1,25 @@ +[Level] +Name=Methane +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=1-g +feld_00=............... +feld_01=............... +feld_02=.#############. +feld_03=.#...#..#....#. +feld_04=.#.#..#5#....#. +feld_05=.#..#...#3.#.#. +feld_06=.#......####.#. +feld_07=.#.......#1#.#. +feld_08=.#.2..#..#...#. +feld_09=.#.#####....##. +feld_10=.#.4.#..#...#.. +feld_11=.#####..#...#.. +feld_12=........#####.. +feld_13=............... +feld_14=............... +mole_0=.2. +mole_1=135 +mole_2=.4. diff --git a/katomic/levels/level_20 b/katomic/levels/level_20 new file mode 100644 index 00000000..8ae7aaf9 --- /dev/null +++ b/katomic/levels/level_20 @@ -0,0 +1,24 @@ +[Level] +Name=Formaldehyde +atom_1=1-e +atom_2=2-aeB +atom_3=1-a +atom_4=3-D +feld_00=............... +feld_01=.#############. +feld_02=.#4....#....2#. +feld_03=.#..##.#.##..#. +feld_04=.##.#.....#.##. +feld_05=.#..#.....#..#. +feld_06=.#.##..#..####. +feld_07=.#....###....#. +feld_08=.#.##..#..##.#. +feld_09=.#..#.....#..#. +feld_10=.##.#.....#.##. +feld_11=.#..##.#.##..#. +feld_12=.#3....#....1#. +feld_13=.#############. +feld_14=............... +mole_0=1 +mole_1=24 +mole_2=3 diff --git a/katomic/levels/level_21 b/katomic/levels/level_21 new file mode 100644 index 00000000..a15a81c6 --- /dev/null +++ b/katomic/levels/level_21 @@ -0,0 +1,30 @@ +[Level] +Name=Crystal 1 +atom_1=o-ce +atom_2=o-ace +atom_3=o-ac +atom_4=o-ceg +atom_5=o-aceg +atom_6=o-acg +atom_7=o-eg +atom_8=o-aeg +atom_9=o-ag +feld_00=............... +feld_01=......##....... +feld_02=.....####...... +feld_03=.....#55#...... +feld_04=....##56##..... +feld_05=..###89..###... +feld_06=.##2....72.##.. +feld_07=.##.41...5.##.. +feld_08=..###34.6###... +feld_09=....##.8##..... +feld_10=.....#..#...... +feld_11=.....####...... +feld_12=......##....... +feld_13=............... +feld_14=............... +mole_0=1447 +mole_1=2558 +mole_2=2558 +mole_3=3669 diff --git a/katomic/levels/level_22 b/katomic/levels/level_22 new file mode 100644 index 00000000..50568c5a --- /dev/null +++ b/katomic/levels/level_22 @@ -0,0 +1,28 @@ +[Level] +Name=Acetic acid ethyl ester +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-C +atom_6=2-cgA +atom_7=3-cg +atom_8=1-g +feld_00=............... +feld_01=.#############. +feld_02=.#..##.#.##..#. +feld_03=.#...#.4.#...#. +feld_04=.#....3...3.3#. +feld_05=.##..#...#..##. +feld_06=.#7.##6..##..#. +feld_07=.#8....#.....#. +feld_08=.#..##...##..#. +feld_09=.##..#22.#4.##. +feld_10=.#...2.......#. +feld_11=.#..5#..1#...#. +feld_12=.#.4##.#.##..#. +feld_13=.#############. +feld_14=............... +mole_0=.25.22. +mole_1=1367338 +mole_2=.4..44. diff --git a/katomic/levels/level_23 b/katomic/levels/level_23 new file mode 100644 index 00000000..c44492ff --- /dev/null +++ b/katomic/levels/level_23 @@ -0,0 +1,24 @@ +[Level] +Name=Ammonia +atom_1=1-c +atom_2=4-bdg +atom_3=1-f +atom_4=1-h +feld_00=............... +feld_01=.#############. +feld_02=.##..3#..#..##. +feld_03=.#...#..#....#. +feld_04=.#........#..#. +feld_05=.#1#.#...#..##. +feld_06=.##...#....#.#. +feld_07=.#....2......#. +feld_08=.#.#....#..4##. +feld_09=.##..#...#.#.#. +feld_10=.#..#........#. +feld_11=.#....#..#...#. +feld_12=.##..#..#...##. +feld_13=.#############. +feld_14=............... +mole_0=..3 +mole_1=12. +mole_2=..4 diff --git a/katomic/levels/level_24 b/katomic/levels/level_24 new file mode 100644 index 00000000..0d47d71d --- /dev/null +++ b/katomic/levels/level_24 @@ -0,0 +1,29 @@ +[Level] +Name=3-Methyl-Pentane +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=1-d +atom_6=2-abeh +atom_7=1-f +atom_8=1-g +feld_00=............... +feld_01=############### +feld_02=#.2.5..#..3..4# +feld_03=#2.#...#...#..# +feld_04=#.###.12..###.# +feld_05=#.3#....4..#..# +feld_06=#.......3.....# +feld_07=###.2..4....### +feld_08=#....3.......7# +feld_09=#8.#.......#.6# +feld_10=#.###....2###.# +feld_11=#..#4..#4..#..# +feld_12=#.....3#......# +feld_13=############### +feld_14=............... +mole_0=..527.. +mole_1=.22622. +mole_2=1333338 +mole_3=.44444. diff --git a/katomic/levels/level_25 b/katomic/levels/level_25 new file mode 100644 index 00000000..32b93dca --- /dev/null +++ b/katomic/levels/level_25 @@ -0,0 +1,27 @@ +[Level] +Name=Propanal +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=2-cgC +atom_6=3-A +atom_7=1-g +feld_00=............... +feld_01=..###########.. +feld_02=..#.2#.5....#.. +feld_03=..#.##4..##.#.. +feld_04=..#.....3.###.. +feld_05=..#..###....#.. +feld_06=..#..#....###.. +feld_07=..#....#....#.. +feld_08=..####..#...#.. +feld_09=..#72.....#.#.. +feld_10=..#..#.31.#.#.. +feld_11=..#6##..#.#4#.. +feld_12=..#.....#...#.. +feld_13=..###########.. +feld_14=............... +mole_0=.22.. +mole_1=13357 +mole_2=.446. diff --git a/katomic/levels/level_26 b/katomic/levels/level_26 new file mode 100644 index 00000000..1548b710 --- /dev/null +++ b/katomic/levels/level_26 @@ -0,0 +1,27 @@ +[Level] +Name=Propyne +atom_1=1-c +atom_2=2-gF +atom_3=1-e +atom_4=2-cH +atom_5=2-aceg +atom_6=1-a +atom_7=1-g +feld_00=............... +feld_01=.#############. +feld_02=.#...#####...#. +feld_03=.#.#.........#. +feld_04=.#.#.2#...#..#. +feld_05=.####.###6#..#. +feld_06=.#......#.##.#. +feld_07=.#3#.......#.#. +feld_08=.####.####...#. +feld_09=.#.#....#....#. +feld_10=.#...#.....#.#. +feld_11=.#..###.1.##5#. +feld_12=.#.7#....##4.#. +feld_13=.#############. +feld_14=............... +mole_0=...3. +mole_1=12457 +mole_2=...6. diff --git a/katomic/levels/level_27 b/katomic/levels/level_27 new file mode 100644 index 00000000..06a349f7 --- /dev/null +++ b/katomic/levels/level_27 @@ -0,0 +1,33 @@ +[Level] +Name=Furanal +atom_1=1-d +atom_2=1-b +atom_3=2-chC +atom_4=2-dfA +atom_5=A- +atom_6=3-bh +atom_7=2-bgC +atom_8=1-f +atom_9=2-ehB +atom_a=1-a +atom_b=3-D +feld_00=............... +feld_01=...########.... +feld_02=...#..#...#.... +feld_03=.###..5...####. +feld_04=.#..8###..9..#. +feld_05=.#..a...3#...#. +feld_06=.####.#..#6..#. +feld_07=.#b...#.###..#. +feld_08=.#..........4#. +feld_09=.##.####....##. +feld_10=.#4.#.7...#..#. +feld_11=.#.....#..#..#. +feld_12=.####.1#.2#.##. +feld_13=....##########. +feld_14=............... +mole_0=1...8 +mole_1=.357. +mole_2=.4.4. +mole_3=2.6.9b +mole_4=....a diff --git a/katomic/levels/level_28 b/katomic/levels/level_28 new file mode 100644 index 00000000..a614ec0a --- /dev/null +++ b/katomic/levels/level_28 @@ -0,0 +1,32 @@ +[Level] +Name=Pyran +atom_1=3-bd +atom_2=1-e +atom_3=2-afB +atom_4=2-ehB +atom_5=1-a +atom_6=2-adD +atom_7=2-beD +atom_8=2-bdfh +atom_9=1-f +atom_a=1-h +feld_00=............... +feld_01=..#######...... +feld_02=..#.....#...... +feld_03=..#..6..######. +feld_04=..#.##..#9...#. +feld_05=..#.12.7####.#. +feld_06=..###...#....#. +feld_07=.##.##..#.4..#. +feld_08=.#.....#2.#..#. +feld_09=.#........#5.#. +feld_10=.#.3a#5.#.##.#. +feld_11=.#####..#....#. +feld_12=....#...#8####. +feld_13=....#######.... +feld_14=............... +mole_0=.22.. +mole_1=.36.9 +mole_2=1..8. +mole_3=.47.a +mole_4=.55.. diff --git a/katomic/levels/level_29 b/katomic/levels/level_29 new file mode 100644 index 00000000..e5417b46 --- /dev/null +++ b/katomic/levels/level_29 @@ -0,0 +1,31 @@ +[Level] +Name=Cyclo-Pentane +atom_1=1-d +atom_2=1-c +atom_3=2-begh +atom_4=2-aceg +atom_5=1-a +atom_6=2-bdfh +atom_7=2-bdeg +atom_8=1-f +atom_9=1-h +feld_00=............... +feld_01=..########..... +feld_02=..#5.....#####. +feld_03=..##..2#..9..#. +feld_04=..#..6#1###7.#. +feld_05=..#.......####. +feld_06=..#........#... +feld_07=..##2#.8#..#... +feld_08=...###.##..#... +feld_09=.###453....###. +feld_10=.#1..##..#9..#. +feld_11=.#....6..#####. +feld_12=.#.#....8#..... +feld_13=.#########..... +feld_14=............... +mole_0=.1.8. +mole_1=1.6.8 +mole_2=23.6. +mole_3=247.9 +mole_4=.559. diff --git a/katomic/levels/level_3 b/katomic/levels/level_3 new file mode 100644 index 00000000..0df76525 --- /dev/null +++ b/katomic/levels/level_3 @@ -0,0 +1,26 @@ +[Level] +Name=Methanol +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-cg +atom_6=1-g +feld_00=............... +feld_01=............... +feld_02=..........####. +feld_03=.####.....#..#. +feld_04=.#..#.....#..#. +feld_05=.#..##...##.5#. +feld_06=.#..4#####...#. +feld_07=.#.#.#.3#....#. +feld_08=.#......#.6..#. +feld_09=.#..#.#...#..#. +feld_10=.#2..........#. +feld_11=.#...#.#...#.#. +feld_12=.######1...#.#. +feld_13=......########. +feld_14=............... +mole_0=.2.. +mole_1=1356 +mole_2=.4.. diff --git a/katomic/levels/level_30 b/katomic/levels/level_30 new file mode 100644 index 00000000..8b78c3c8 --- /dev/null +++ b/katomic/levels/level_30 @@ -0,0 +1,40 @@ +[Level] +Name=Nitro-Glycerin +atom_1=3-cd +atom_2=1-c +atom_3=4-beg +atom_4=3-ah +atom_5=1-e +atom_6=2-aceg +atom_7=3-af +atom_8=A- +atom_9=4-beh +atom_a=3-ae +atom_b=1-a +atom_c=3-fg +atom_d=3-ad +atom_e=1-g +atom_f=4-ceh +atom_g=3-ba +feld_00=............... +feld_01=...#########... +feld_02=...#.......#... +feld_03=...#.1#f..####. +feld_04=...#6#9#.c...#. +feld_05=...#1#...e..4#. +feld_06=.###.###...###. +feld_07=.#.....3..a.c#. +feld_08=.#.5#2.6.##..#. +feld_09=.#.###8#..####. +feld_10=.#g##..#b..d7#. +feld_11=.#6....##..###. +feld_12=.#######5..#... +feld_13=.......#####... +feld_14=............... +mole_0=..18c.. +mole_1=...9... +mole_2=..5a5.. +mole_3=.2666e. +mole_4=..7bd.. +mole_5=13...fc +mole_6=.4...g. diff --git a/katomic/levels/level_31 b/katomic/levels/level_31 new file mode 100644 index 00000000..c23f8a56 --- /dev/null +++ b/katomic/levels/level_31 @@ -0,0 +1,29 @@ +[Level] +Name=Ethane +atom_1=1-d +atom_2=1-b +atom_3=1-e +atom_4=2-abeh +atom_5=2-adef +atom_6=1-a +atom_7=1-f +atom_8=1-h +feld_00=............... +feld_01=............... +feld_02=....#########.. +feld_03=....#....2..#.. +feld_04=....#..#.#..#.. +feld_05=....#3.5.####.. +feld_06=..#####4.#1#... +feld_07=..#...#....#... +feld_08=..#........#... +feld_09=..#.6.###8.##.. +feld_10=..###7...#..#.. +feld_11=..#.........#.. +feld_12=..###########.. +feld_13=............... +feld_14=............... +mole_0=137 +mole_1=.4. +mole_2=.5. +mole_3=268 diff --git a/katomic/levels/level_32 b/katomic/levels/level_32 new file mode 100644 index 00000000..73d96b51 --- /dev/null +++ b/katomic/levels/level_32 @@ -0,0 +1,31 @@ +[Level] +Name=Crystal 2 +atom_1=o-bd +atom_2=o-bdf +atom_3=o-bdh +atom_4=o-df +atom_5=o-bdfh +atom_6=o-bh +atom_7=o-dfh +atom_8=o-bfh +atom_9=o-fh +feld_00=............... +feld_01=............... +feld_02=............... +feld_03=......###...... +feld_04=.....##8##..... +feld_05=....##..2##.... +feld_06=...##7.5..##... +feld_07=...#...6..4#... +feld_08=...##3..9.##... +feld_09=....##..1##.... +feld_10=.....##.##..... +feld_11=......###...... +feld_12=............... +feld_13=............... +feld_14=............... +mole_0=..4.. +mole_1=.2.7. +mole_2=1.5.9 +mole_3=.3.8. +mole_4=..6.. diff --git a/katomic/levels/level_33 b/katomic/levels/level_33 new file mode 100644 index 00000000..929c5fea --- /dev/null +++ b/katomic/levels/level_33 @@ -0,0 +1,27 @@ +[Level] +Name=Ethylene-Glycol +atom_1=1-b +atom_2=1-d +atom_3=3-bf +atom_4=2-bdfh +atom_5=1-f +atom_6=1-h +feld_00=............... +feld_01=....##########. +feld_02=....#....3#..#. +feld_03=....#.#...#..#. +feld_04=....#..#.....#. +feld_05=.####...#...##. +feld_06=.#..4#..4#...#. +feld_07=.#...2#1..#.##. +feld_08=.#..#..#.5#.#.. +feld_09=.##1##..#.###.. +feld_10=..#....3...#... +feld_11=..#....#.6.#... +feld_12=..######.5.#... +feld_13=.......#####... +feld_14=............... +mole_0=.2.5.5 +mole_1=..4.3. +mole_2=.3.4.. +mole_3=1.1.6. diff --git a/katomic/levels/level_34 b/katomic/levels/level_34 new file mode 100644 index 00000000..5bee796c --- /dev/null +++ b/katomic/levels/level_34 @@ -0,0 +1,33 @@ +[Level] +Name=L-Alanine +atom_1=1-d +atom_2=1-c +atom_3=1-b +atom_4=2-cfgh +atom_5=1-e +atom_6=2-aceg +atom_7=4-adf +atom_8=3-C +atom_9=2-cgA +atom_a=1-h +atom_b=3-cg +atom_c=1-g +feld_00=............... +feld_01=.#############. +feld_02=.#.b.4...5...#. +feld_03=.#.####..##..#. +feld_04=.#..3......3.#. +feld_05=.##.....7..#.#. +feld_06=.#..#.#.#2.#.#. +feld_07=.#.1..#.#..#.#. +feld_08=.#c#......9..#. +feld_09=.#.#....8....#. +feld_10=.#....#..#a.##. +feld_11=.#..#.#..#...#. +feld_12=.#....#6.#...#. +feld_13=.#############. +feld_14=............... +mole_0=1.58.. +mole_1=2469bc +mole_2=3.7... +mole_3=.3.a.. diff --git a/katomic/levels/level_35 b/katomic/levels/level_35 new file mode 100644 index 00000000..948dcfa3 --- /dev/null +++ b/katomic/levels/level_35 @@ -0,0 +1,31 @@ +[Level] +Name=Cyanoguanidine +atom_1=1-d +atom_2=1-b +atom_3=4-cfh +atom_4=2-cgC +atom_5=4-eA +atom_6=1-a +atom_7=1-e +atom_8=4-acg +atom_9=2-gG +atom_a=4-E +feld_00=............... +feld_01=............... +feld_02=............... +feld_03=..###########.. +feld_04=..#.#.......#.. +feld_05=..#9###53##.#.. +feld_06=..#..2.#..a.#.. +feld_07=..####....###.. +feld_08=..#.....8...#.. +feld_09=..#7..#6###1#.. +feld_10=..#......4..#.. +feld_11=..###########.. +feld_12=............... +feld_13=............... +feld_14=............... +mole_0=1..7. +mole_1=.3489 +mole_2=2.5.a +mole_3=..6.. diff --git a/katomic/levels/level_36 b/katomic/levels/level_36 new file mode 100644 index 00000000..b455234e --- /dev/null +++ b/katomic/levels/level_36 @@ -0,0 +1,23 @@ +[Level] +Name=Prussic Acid (Cyanic Acid) +atom_1=1-e +atom_2=2-aG +atom_3=4-E +feld_00=............... +feld_01=............... +feld_02=...####........ +feld_03=...#..####..... +feld_04=...##....####.. +feld_05=...#.3.#.#..#.. +feld_06=...###.#....#.. +feld_07=...#...1...##.. +feld_08=...##2......##. +feld_09=.###.....#...#. +feld_10=.#.###...#####. +feld_11=.#.........#... +feld_12=.###########... +feld_13=............... +feld_14=............... +mole_0=1 +mole_1=2 +mole_2=3 \ No newline at end of file diff --git a/katomic/levels/level_37 b/katomic/levels/level_37 new file mode 100644 index 00000000..4571d0ba --- /dev/null +++ b/katomic/levels/level_37 @@ -0,0 +1,41 @@ +[Level] +Name=Anthracene +atom_1=1-d +atom_2=1-c +atom_3=1-b +atom_4=2-ehB +atom_5=2-agC +atom_6=2-cfA +atom_7=2-beD +atom_8=2-acC +atom_9=2-egA +atom_a=2-faB +atom_b=1-f +atom_c=2-bgC +atom_d=2-ceA +atom_e=2-aeD +atom_f=2-adD +atom_g=1-g +atom_h=1-h +feld_00=############### +feld_01=#..4##.....#..# +feld_02=#..h##...7###.# +feld_03=#..d......1...# +feld_04=#.......b....## +feld_05=####..c.###d.## +feld_06=#....ce.g...3.# +feld_07=#..........##f# +feld_08=##....#....a#.# +feld_09=#9.#2.#b5.....# +feld_10=#.###.####....# +feld_11=#..#8.b..3....# +feld_12=#.....#....#..# +feld_13=#.a..#.....#63# +feld_14=############### +mole_0=1..b.. +mole_1=.47.b. +mole_2=258c.b +mole_3=.69dc. +mole_4=3.aedg +mole_5=.3.af. +mole_6=..3..h diff --git a/katomic/levels/level_38 b/katomic/levels/level_38 new file mode 100644 index 00000000..ffe619b3 --- /dev/null +++ b/katomic/levels/level_38 @@ -0,0 +1,30 @@ +[Level] +Name=Thiazole +atom_1=1-d +atom_2=2-ehB +atom_3=C- +atom_4=4-aB +atom_5=2-bdD +atom_6=1-f +atom_7=5-fh +atom_8=1-h +feld_00=............... +feld_01=..###########.. +feld_02=.##.........##. +feld_03=.#..#..###.1.#. +feld_04=.#.##..#8..#.#. +feld_05=.#3#...#..##.#. +feld_06=.#.........5.#. +feld_07=.#...###.5####. +feld_08=.#.#2#7..#4..#. +feld_09=.#6#.#.......#. +feld_10=.#.#.....#...#. +feld_11=.#......##...#. +feld_12=.##.....#...##. +feld_13=..###########.. +feld_14=............... +mole_0=1..6 +mole_1=.25. +mole_2=.3.7 +mole_3=.45. +mole_4=...8 diff --git a/katomic/levels/level_39 b/katomic/levels/level_39 new file mode 100644 index 00000000..cabcb547 --- /dev/null +++ b/katomic/levels/level_39 @@ -0,0 +1,39 @@ +[Level] +Name=Saccharin +atom_1=1-c +atom_2=4-bdg +atom_3=3-C +atom_4=2-dfA +atom_5=5-chCD +atom_6=2-ehB +atom_7=2-agB +atom_8=1-e +atom_9=2-acD +atom_a=2-ceD +atom_b=1-a +atom_c=2-bgC +atom_d=2-dgA +atom_e=1-f +atom_f=1-h +atom_g=3-A +atom_h=3-B +feld_00=............... +feld_01=.......#####... +feld_02=..######d#h###. +feld_03=..#4..3#.#.9e#. +feld_04=..#......b...#. +feld_05=..#c#5.......#. +feld_06=.##....#.#...#. +feld_07=.#.....#8#1..#. +feld_08=.#..f###.###.#. +feld_09=.#.....2.....#. +feld_10=.####...#g.###. +feld_11=....#a...7.#... +feld_12=....###..6.#... +feld_13=......######... +feld_14=............... +mole_0=..3.... +mole_1=..4.8.e +mole_2=12.69c. +mole_3=.h57ad. +mole_4=..g.b.f diff --git a/katomic/levels/level_4 b/katomic/levels/level_4 new file mode 100644 index 00000000..20558070 --- /dev/null +++ b/katomic/levels/level_4 @@ -0,0 +1,26 @@ +[Level] +Name=Ethylene +atom_1=1-d +atom_2=1-b +atom_3=2-fhB +atom_4=2-bdD +atom_5=1-f +atom_6=1-h +feld_00=............... +feld_01=............... +feld_02=..###.....###.. +feld_03=..#.#.....#.#.. +feld_04=..#2#######.#.. +feld_05=..#......#..#.. +feld_06=..##..4.#...#.. +feld_07=..#.#....5#3#.. +feld_08=..#...#....##.. +feld_09=..#..#....61#.. +feld_10=..#.#######.#.. +feld_11=..#.#.....#.#.. +feld_12=..###.....###.. +feld_13=............... +feld_14=............... +mole_0=1..5 +mole_1=.34. +mole_2=2..6 diff --git a/katomic/levels/level_40 b/katomic/levels/level_40 new file mode 100644 index 00000000..7a9bef2f --- /dev/null +++ b/katomic/levels/level_40 @@ -0,0 +1,37 @@ +[Level] +Name=Styrene +atom_1=1-d +atom_2=1-b +atom_3=2-fhB +atom_4=2-bdD +atom_5=1-c +atom_6=1-f +atom_7=2-chC +atom_8=2-egA +atom_9=2-afB +atom_a=2-bgC +atom_b=2-ceA +atom_c=2-adD +atom_d=1-g +atom_e=1-h +feld_00=............... +feld_01=............... +feld_02=..###########.. +feld_03=..#.......#.#.. +feld_04=..#..b###.8.#.. +feld_05=..#...16#.#.#.. +feld_06=..#...2e.6#5#.. +feld_07=..#..c.3.##.#.. +feld_08=..#.##2##.7##.. +feld_09=..#..#4#....#.. +feld_10=..##9#.#.d..#.. +feld_11=..#......#.a#.. +feld_12=..###########.. +feld_13=............... +feld_14=............... +mole_0=1..6.. +mole_1=.34..6 +mole_2=2..7a. +mole_3=..58bd +mole_4=...9c. +mole_5=..2..e diff --git a/katomic/levels/level_41 b/katomic/levels/level_41 new file mode 100644 index 00000000..733a333d --- /dev/null +++ b/katomic/levels/level_41 @@ -0,0 +1,34 @@ +[Level] +Name=Melamine +atom_1=1-c +atom_2=1-e +atom_3=4-acg +atom_4=2-cgC +atom_5=4-cA +atom_6=1-b +atom_7=4-gec +atom_8=2-aceg +atom_9=4-adf +atom_a=4-gA +atom_b=1-h +atom_c=1-g +feld_00=............... +feld_01=.####.....####. +feld_02=.#..#######3.#. +feld_03=.#9....#....6#. +feld_04=.##.#.4#..#.##. +feld_05=..#.........#.. +feld_06=..#....2..2.#.. +feld_07=..###.13c.###.. +feld_08=..#7........#.. +feld_09=..#....4...a#.. +feld_10=.##.#..#5.#.##. +feld_11=.#.....#8....#. +feld_12=.#b.#######..#. +feld_13=.####.....####. +feld_14=............... +mole_0=.2...2. +mole_1=134743c +mole_2=..58a.. +mole_3=...9... +mole_4=..6.b.. diff --git a/katomic/levels/level_42 b/katomic/levels/level_42 new file mode 100644 index 00000000..50cc2ce5 --- /dev/null +++ b/katomic/levels/level_42 @@ -0,0 +1,26 @@ +[Level] +Name=Cyclobutane +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=1-g +feld_00=............... +feld_01=............... +feld_02=..########..... +feld_03=..#...1..##.... +feld_04=..##5.##.4###.. +feld_05=...##.3.....#.. +feld_06=....#.#.....#.. +feld_07=...##1##..#3#.. +feld_08=...#...5#2###.. +feld_09=...#......#.#.. +feld_10=...#3...3..2#.. +feld_11=...#4.#.###.#.. +feld_12=...######.###.. +feld_13=............... +feld_14=............... +mole_0=.22. +mole_1=1335 +mole_2=1335 +mole_3=.44. diff --git a/katomic/levels/level_43 b/katomic/levels/level_43 new file mode 100644 index 00000000..e2aabf80 --- /dev/null +++ b/katomic/levels/level_43 @@ -0,0 +1,42 @@ +[Level] +Name=Nicotine +atom_1=1-d +atom_2=1-c +atom_3=2-ehB +atom_4=2-agC +atom_5=4-cA +atom_6=1-e +atom_7=2-aeD +atom_8=2-acC +atom_9=2-egA +atom_a=1-a +atom_b=1-b +atom_c=2-abeg +atom_d=4-ace +atom_e=2-adef +atom_f=2-abdf +atom_g=2-bdeg +atom_h=1-h +atom_i=1-f +atom_j=2-bdfh +feld_00=............... +feld_01=...###########. +feld_02=..##.h..#a..i#. +feld_03=..#3...6#...##. +feld_04=.##..#..6...#.. +feld_05=.#...2..j#d.##. +feld_06=.#.a#....a#.e#. +feld_07=.#.#g..c...#.#. +feld_08=.##h..8.9..h##. +feld_09=.#......1....#. +feld_10=.##7f#5.#.6.##. +feld_11=..#..b#.#4###.. +feld_12=..##..i####.... +feld_13=...#####....... +feld_14=............... +mole_0=1.6.6i. +mole_1=.376f.i +mole_2=248c.j. +mole_3=.59dg.h +mole_4=..aeah. +mole_5=..bah.. diff --git a/katomic/levels/level_44 b/katomic/levels/level_44 new file mode 100644 index 00000000..3d432f09 --- /dev/null +++ b/katomic/levels/level_44 @@ -0,0 +1,43 @@ +[Level] +Name=Acetyle salicylic acid +atom_1=1-c +atom_2=3-B +atom_3=1-b +atom_4=1-e +atom_5=2-aceg +atom_6=2-aeD +atom_7=3-ae +atom_8=2-acC +atom_9=2-egA +atom_a=2-afB +atom_b=1-g +atom_c=2-bgC +atom_d=2-ceA +atom_e=2-adD +atom_f=2-cfA +atom_g=1-h +atom_h=3-cg +atom_i=3-C +feld_00=............... +feld_01=.........#####. +feld_02=.#########9..#. +feld_03=.#.#..f..#..##. +feld_04=.#..#2#.5.#1.#. +feld_05=.#7...8..b...#. +feld_06=.###6#.#.#bh.#. +feld_07=...#.e#.1.#d.#. +feld_08=...##c..g.b###. +feld_09=...#.#.#.4i#... +feld_10=..##...3#..#... +feld_11=..#..a#..###... +feld_12=..########..... +feld_13=............... +feld_14=............... +mole_0=.4.... +mole_1=15b... +mole_2=26.i.. +mole_3=.7.fhb +mole_4=.8c... +mole_5=19db.. +mole_6=.ae... +mole_7=3..g.. diff --git a/katomic/levels/level_45 b/katomic/levels/level_45 new file mode 100644 index 00000000..376fc944 --- /dev/null +++ b/katomic/levels/level_45 @@ -0,0 +1,36 @@ +[Level] +Name=Meta-Di-Nitro-Benzene +atom_1=3-B +atom_2=1-d +atom_3=4-bD +atom_4=3-A +atom_5=2-ehB +atom_6=2-afB +atom_7=1-e +atom_8=2-acD +atom_9=2-ceD +atom_a=1-a +atom_b=2-bgC +atom_c=2-dgA +atom_d=1-f +atom_e=4-hB +atom_f=3-D +feld_00=............... +feld_01=...####........ +feld_02=...#..####..... +feld_03=...#e2..f#..... +feld_04=...#.#..8#..... +feld_05=..##b...##..... +feld_06=..#.#....####.. +feld_07=..#....c....#.. +feld_08=..#7..##.6d.##. +feld_09=..#5#..9.#...#. +feld_10=..#.#1...#...#. +feld_11=..#....3.a.#.#. +feld_12=..####.#.#####. +feld_13=.....#####..... +feld_14=............... +mole_0=.2.7.d. +mole_1=..58b.. +mole_2=..69c.. +mole_3=13.a.ef diff --git a/katomic/levels/level_46 b/katomic/levels/level_46 new file mode 100644 index 00000000..9f2317f4 --- /dev/null +++ b/katomic/levels/level_46 @@ -0,0 +1,29 @@ +[Level] +Name=Propyne +atom_1=1-d +atom_2=1-e +atom_3=2-abeh +atom_4=2-aG +atom_5=2-eE +atom_6=1-a +atom_7=1-f +feld_00=............... +feld_01=...##########.. +feld_02=...#........#.. +feld_03=...#.1#.....#.. +feld_04=..##.##.6#.##.. +feld_05=..#.....##7#... +feld_06=..##.....####.. +feld_07=..#4.#......#.. +feld_08=..###5.#.3#.#.. +feld_09=....#..####.#.. +feld_10=....#..#..#.#.. +feld_11=....#..#..#.#.. +feld_12=....####..#2#.. +feld_13=..........###.. +feld_14=............... +mole_0=127 +mole_1=.3. +mole_2=.4. +mole_3=.5. +mole_4=.6. diff --git a/katomic/levels/level_47 b/katomic/levels/level_47 new file mode 100644 index 00000000..d410afa5 --- /dev/null +++ b/katomic/levels/level_47 @@ -0,0 +1,31 @@ +[Level] +Name=Malonic Acid +atom_1=1-c +atom_2=3-dg +atom_3=2-chC +atom_4=3-C +atom_5=1-e +atom_6=2-aceg +atom_7=1-a +atom_8=2-dgA +atom_9=3-ch +atom_a=1-g +atom_b=3-A +feld_00=............... +feld_01=............... +feld_02=..#########.... +feld_03=..#.#2....#.... +feld_04=..#.#...6.#.... +feld_05=..#..4.##5#.... +feld_06=..#..b..#7#.... +feld_07=..#..#.1a.###.. +feld_08=..#..##.....#.. +feld_09=..#.39.8....#.. +feld_10=..#.........#.. +feld_11=..###..#....#.. +feld_12=....#########.. +feld_13=............... +feld_14=............... +mole_0=12.54.. +mole_1=..368.. +mole_2=..b7.9a diff --git a/katomic/levels/level_48 b/katomic/levels/level_48 new file mode 100644 index 00000000..a78c8fc0 --- /dev/null +++ b/katomic/levels/level_48 @@ -0,0 +1,35 @@ +[Level] +Name=2,2-Dimethylpropane +atom_1=1-d +atom_2=1-c +atom_3=1-b +atom_4=2-cfgh +atom_5=1-e +atom_6=2-abeh +atom_7=2-aceg +atom_8=2-adfe +atom_9=1-a +atom_a=1-f +atom_b=2-bcdg +atom_c=1-h +atom_d=1-g +feld_00=............... +feld_01=......######... +feld_02=.####.#..1a#... +feld_03=.#5.#.##.3c#... +feld_04=.#9.###6#..#... +feld_05=.##...47b..###. +feld_06=.#.#..#8.....#. +feld_07=.#...#..#..#.#. +feld_08=.#.#...#.....#. +feld_09=.#..#....#.1a#. +feld_10=.#.2d......3c#. +feld_11=.#....#..#####. +feld_12=.##....#.#..... +feld_13=..########..... +feld_14=............... +mole_0=.15a. +mole_1=1.6.a +mole_2=247bd +mole_3=3.8.c +mole_4=.39c. diff --git a/katomic/levels/level_49 b/katomic/levels/level_49 new file mode 100644 index 00000000..446cba87 --- /dev/null +++ b/katomic/levels/level_49 @@ -0,0 +1,36 @@ +[Level] +Name=Ethyl-Benzene +atom_1=1-d +atom_2=1-b +atom_3=2-ehB +atom_4=2-afB +atom_5=1-e +atom_6=2-abeh +atom_7=2-acD +atom_8=2-ceD +atom_9=1-a +atom_a=1-f +atom_b=2-bgC +atom_c=2-dgA +atom_d=1-h +feld_00=............... +feld_01=..#####........ +feld_02=..#15a#######.. +feld_03=..#.6......8#.. +feld_04=..##9.c#...##.. +feld_05=..#.#.#7...3#.. +feld_06=..#........4#.. +feld_07=..#..1##...##.. +feld_08=..#..2.#.a..#.. +feld_09=..###..b#d..#.. +feld_10=....#...##..#.. +feld_11=....#.#.....#.. +feld_12=....#.#.16a.#.. +feld_13=....#########.. +feld_14=............... +mole_0=.15a. +mole_1=.16a. +mole_2=1.6.a +mole_3=.37b. +mole_4=.48c. +mole_5=2.9.d diff --git a/katomic/levels/level_5 b/katomic/levels/level_5 new file mode 100644 index 00000000..30adf5cc --- /dev/null +++ b/katomic/levels/level_5 @@ -0,0 +1,28 @@ +[Level] +Name=Propene +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=2-agB +atom_6=2-bdD +atom_7=1-f +atom_8=1-h +feld_00=............... +feld_01=............... +feld_02=..#########.... +feld_03=..#7#.#.#.#.... +feld_04=..#.#.#.#.###.. +feld_05=.##4.6#2#.#.#.. +feld_06=.#2.1...#5#.#.. +feld_07=.#.#......#.#.. +feld_08=.#.#3#......#.. +feld_09=.#.#.#.#...##.. +feld_10=.###.#8#.#.#... +feld_11=...#.#.#.#.#... +feld_12=...#########... +feld_13=............... +feld_14=............... +mole_0=.22.7 +mole_1=1356. +mole_2=.4..8 diff --git a/katomic/levels/level_50 b/katomic/levels/level_50 new file mode 100644 index 00000000..3c79af00 --- /dev/null +++ b/katomic/levels/level_50 @@ -0,0 +1,38 @@ +[Level] +Name=L-Asparagine +atom_1=3-B +atom_2=1-d +atom_3=1-b +atom_4=1-e +atom_5=3-ae +atom_6=2-acD +atom_7=1-c +atom_8=4-cfh +atom_9=2-aceg +atom_a=2-agC +atom_b=4-bdg +atom_c=1-g +atom_d=1-f +atom_e=1-h +atom_f=3-A +feld_00=............... +feld_01=............... +feld_02=...#########... +feld_03=...#44#.1f5#... +feld_04=..##c.#..####.. +feld_05=..#7......#b#.. +feld_06=..#2........#.. +feld_07=..#3........#.. +feld_08=..#d#.#.....#.. +feld_09=..#e..##..#8#.. +feld_10=..##..#...###.. +feld_11=...#...699a#... +feld_12=...#########... +feld_13=............... +feld_14=............... +mole_0=.4... +mole_1=.54.d +mole_2=169b. +mole_3=279ce +mole_4=.8a.. +mole_5=3.f.. diff --git a/katomic/levels/level_51 b/katomic/levels/level_51 new file mode 100644 index 00000000..944cbd64 --- /dev/null +++ b/katomic/levels/level_51 @@ -0,0 +1,31 @@ +[Level] +Name=1,3,5,7-Cyclooctatetraene +atom_1=1-d +atom_2=1-b +atom_3=2-bhC +atom_4=2-dfA +atom_5=2-fhB +atom_6=2-bdD +atom_7=1-f +atom_8=1-h +feld_00=............... +feld_01=.########..###. +feld_02=.#1....1####5#. +feld_03=.#..#5.#4....#. +feld_04=.#..##.##...4#. +feld_05=.#..........##. +feld_06=.#.#3..#3.#7#.. +feld_07=.#.##..##.###.. +feld_08=.#7.........##. +feld_09=.##.#8.#8...6#. +feld_10=..#.##.##6#2.#. +feld_11=..#.......##.#. +feld_12=..#2.###.....#. +feld_13=..####.#######. +feld_14=............... +mole_0=.1..7. +mole_1=1.56.7 +mole_2=.3..3. +mole_3=.4..4. +mole_4=2.56.8 +mole_5=.2..8. diff --git a/katomic/levels/level_52 b/katomic/levels/level_52 new file mode 100644 index 00000000..f0480c84 --- /dev/null +++ b/katomic/levels/level_52 @@ -0,0 +1,41 @@ +[Level] +Name=Vanillin +atom_1=1-c +atom_2=3-C +atom_3=2-dgA +atom_4=1-b +atom_5=2-ehB +atom_6=2-afB +atom_7=1-e +atom_8=2-acD +atom_9=2-ceD +atom_a=1-a +atom_b=2-bgC +atom_c=1-d +atom_d=3-bf +atom_e=3-dh +atom_f=2-bdfh +atom_g=1-h +atom_h=1-f +feld_00=............... +feld_01=........######. +feld_02=.#########.c.#. +feld_03=.#d.....f##h.#. +feld_04=.#.....b.4..g#. +feld_05=.#.#.........#. +feld_06=.##...#..87.##. +feld_07=.#5...3#2#1.6#. +feld_08=.##.#...#9..3#. +feld_09=.#..#..#e#..#.. +feld_10=.#.a#.#g..#.#.. +feld_11=.####...#...#.. +feld_12=....#...#...#.. +feld_13=....#########.. +feld_14=............... +mole_0=.....c.h +mole_1=.2....f. +mole_2=13.7.d.g +mole_3=..58b... +mole_4=..693... +mole_5=.4.a.e.. +mole_6=......g. diff --git a/katomic/levels/level_53 b/katomic/levels/level_53 new file mode 100644 index 00000000..85da6ee2 --- /dev/null +++ b/katomic/levels/level_53 @@ -0,0 +1,30 @@ +[Level] +Name=Crystal 3 +atom_1=o-bc +atom_2=o-bcf +atom_3=o-bcg +atom_4=o-bcfg +atom_5=o-cf +atom_6=o-bg +atom_7=o-cfg +atom_8=o-bfg +atom_9=o-fg +feld_00=............... +feld_01=............... +feld_02=............... +feld_03=............... +feld_04=...#########... +feld_05=...#8.1.4..#... +feld_06=...#.6.4.4.#... +feld_07=...#8.3.4.9#... +feld_08=...#.3.2.7.#... +feld_09=...#..2.7.5#... +feld_10=...#########... +feld_11=............... +feld_12=............... +feld_13=............... +feld_14=............... +mole_0=...5779 +mole_1=..2448. +mole_2=.2448.. +mole_3=1336... diff --git a/katomic/levels/level_54 b/katomic/levels/level_54 new file mode 100644 index 00000000..07d710b0 --- /dev/null +++ b/katomic/levels/level_54 @@ -0,0 +1,39 @@ +[Level] +Name=Uric Acid +atom_1=1-d +atom_2=3-B +atom_3=1-b +atom_4=4-ceh +atom_5=2-aeD +atom_6=4-acf +atom_7=2-egA +atom_8=2-acC +atom_9=2-dgA +atom_a=4-bdg +atom_b=4-beh +atom_c=1-a +atom_d=1-f +atom_e=2-fhB +atom_f=3-D +atom_g=3-C +feld_00=............... +feld_01=.###########... +feld_02=.#.#.#.#...#... +feld_03=.#...#.#...#... +feld_04=.#.47.8#..g#... +feld_05=.###...#.#####. +feld_06=.#.5....b#9..#. +feld_07=.#.....cf....#. +feld_08=.#..2#.d....6#. +feld_09=.#####1#...###. +feld_10=...#...#.....#. +feld_11=...#...#.#...#. +feld_12=...#..3#e#a#.#. +feld_13=...###########. +feld_14=............... +mole_0=1.g.. +mole_1=.47.d +mole_2=258a. +mole_3=.69.ef +mole_4=3..b. +mole_5=...c. diff --git a/katomic/levels/level_55 b/katomic/levels/level_55 new file mode 100644 index 00000000..36fb3d64 --- /dev/null +++ b/katomic/levels/level_55 @@ -0,0 +1,36 @@ +[Level] +Name=Thymine +atom_1=1-c +atom_2=4-bdg +atom_3=1-b +atom_4=3-C +atom_5=2-cfA +atom_6=2-fhB +atom_7=2-bdD +atom_8=1-f +atom_9=2-cdeh +atom_a=1-a +atom_b=1-g +atom_c=1-h +atom_d=3-D +feld_00=............... +feld_01=.####....#####. +feld_02=.#31#....#.7.#. +feld_03=.#..######.#.#. +feld_04=.#..##28##.#.#. +feld_05=.#..........b#. +feld_06=.#.##....##a.#. +feld_07=.#..6.##..5#.#. +feld_08=.#4##....##..#. +feld_09=.#....##...#.#. +feld_10=.#.#.c.....#.#. +feld_11=.#.6##..##...#. +feld_12=.#....d2....9#. +feld_13=.#############. +feld_14=............... +mole_0=..4.8. +mole_1=..52.. +mole_2=12..6d +mole_3=..67.. +mole_4=.3..9b +mole_5=....ac diff --git a/katomic/levels/level_56 b/katomic/levels/level_56 new file mode 100644 index 00000000..6badd2f6 --- /dev/null +++ b/katomic/levels/level_56 @@ -0,0 +1,37 @@ +[Level] +Name=Aniline +atom_1=1-c +atom_2=1-d +atom_3=4-beg +atom_4=1-a +atom_5=2-ehB +atom_6=2-agC +atom_7=2-cfA +atom_8=1-e +atom_9=2-adD +atom_a=A- +atom_b=2-chC +atom_c=2-dgA +atom_d=1-g +atom_e=1-h +feld_00=............... +feld_01=.......#######. +feld_02=.#######c#...#. +feld_03=.#..34#......#. +feld_04=.#.......##7##. +feld_05=.#...##d1a###.. +feld_06=.##6..##....#.. +feld_07=.###...e.#..#.. +feld_08=.#1.........#.. +feld_09=.#..##...##.#.. +feld_10=.#..b##...####. +feld_11=.#..8......5.#. +feld_12=.#######9#..2#. +feld_13=.......#######. +feld_14=............... +mole_0=.2.8.. +mole_1=..59.. +mole_2=.16.bd +mole_3=..7ac. +mole_4=13...e +mole_5=.4.... diff --git a/katomic/levels/level_57 b/katomic/levels/level_57 new file mode 100644 index 00000000..6be47483 --- /dev/null +++ b/katomic/levels/level_57 @@ -0,0 +1,25 @@ +[Level] +Name=Chloroform +atom_1=7-d +atom_2=7-b +atom_3=2-bdfh +atom_4=1-f +atom_5=7-h +feld_00=............... +feld_01=......#######.. +feld_02=......#...#.#.. +feld_03=..#######.#.#.. +feld_04=..#...##....#.. +feld_05=..###1#.....#.. +feld_06=..#......4#.#.. +feld_07=..#..#...#3.#.. +feld_08=..#.2########.. +feld_09=..#..5.#....... +feld_10=..####.#....... +feld_11=..#....#....... +feld_12=..######....... +feld_13=............... +feld_14=............... +mole_0=1.4 +mole_1=.3. +mole_2=2.5 diff --git a/katomic/levels/level_58 b/katomic/levels/level_58 new file mode 100644 index 00000000..742d6eb0 --- /dev/null +++ b/katomic/levels/level_58 @@ -0,0 +1,28 @@ +[Level] +Name=Carbonic acid +atom_1=1-e +atom_2=3-ad +atom_3=3-be +atom_4=1-a +atom_5=2-fhB +atom_6=3-D +feld_00=............... +feld_01=............... +feld_02=..##########... +feld_03=..#...#.1..##.. +feld_04=..#...#.#...#.. +feld_05=..#2.##.##..#.. +feld_06=..##.6......#.. +feld_07=..##........#.. +feld_08=..#...##.##.#.. +feld_09=..#....#5#..#.. +feld_10=..###...3#..#.. +feld_11=....#....4..#.. +feld_12=....#########.. +feld_13=............... +feld_14=............... +mole_0=1. +mole_1=2. +mole_2=.56 +mole_3=3. +mole_4=4. diff --git a/katomic/levels/level_59 b/katomic/levels/level_59 new file mode 100644 index 00000000..1684a006 --- /dev/null +++ b/katomic/levels/level_59 @@ -0,0 +1,39 @@ +[Level] +Name=Crystal 4 +atom_1=o-be +atom_2=o-ad +atom_3=o-bdf +atom_4=o-bdh +atom_5=o-cf +atom_6=o-ceh +atom_7=o-acf +atom_8=o-ch +atom_9=o-dg +atom_a=o-beg +atom_b=o-adg +atom_c=o-bg +atom_d=o-dfh +atom_e=o-bfh +atom_f=o-eh +atom_g=o-af +feld_00=............... +feld_01=............... +feld_02=..##.......##.. +feld_03=..###########.. +feld_04=...#.56..d.#... +feld_05=...#...ba..#... +feld_06=...#97...f.#... +feld_07=...#.......#... +feld_08=...#2...e.3#... +feld_09=...#..84...#... +feld_10=...#c...g.1#... +feld_11=..###########.. +feld_12=..##.......##.. +feld_13=............... +feld_14=............... +mole_0=..59.. +mole_1=.3..d. +mole_2=1.6a.f +mole_3=2.7b.g +mole_4=.4..e. +mole_5=..8c.. diff --git a/katomic/levels/level_6 b/katomic/levels/level_6 new file mode 100644 index 00000000..f8de8bf9 --- /dev/null +++ b/katomic/levels/level_6 @@ -0,0 +1,26 @@ +[Level] +Name=Ethanol +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-cg +atom_6=1-g +feld_00=............... +feld_01=.......#####... +feld_02=.......#43.#... +feld_03=...#####.#.#... +feld_04=...#......4#... +feld_05=...###2...##... +feld_06=.....#.#1#.###. +feld_07=.....##2...#.#. +feld_08=..####.3.....#. +feld_09=..#.5....#...#. +feld_10=..#####.#..#.#. +feld_11=.....#....#.6#. +feld_12=.....#########. +feld_13=............... +feld_14=............... +mole_0=.22.. +mole_1=13356 +mole_2=.44.. diff --git a/katomic/levels/level_60 b/katomic/levels/level_60 new file mode 100644 index 00000000..479d51d2 --- /dev/null +++ b/katomic/levels/level_60 @@ -0,0 +1,28 @@ +[Level] +Name=Acrylo-Nitril +atom_1=1-d +atom_2=1-b +atom_3=2-bhC +atom_4=2-dfA +atom_5=2-fF +atom_6=1-h +atom_7=4-H +feld_00=............... +feld_01=............... +feld_02=..#####.#####.. +feld_03=..#..1#.#5..#.. +feld_04=..#..#####7.#.. +feld_05=..#..#.3.#..#.. +feld_06=..#..#...#..#.. +feld_07=..#.........#.. +feld_08=..##..###..##.. +feld_09=...#...4...#... +feld_10=...#.2.#.6.#... +feld_11=...#.#.#.#.#... +feld_12=...#########... +feld_13=............... +feld_14=............... +mole_0=1.57 +mole_1=.3. +mole_2=.4. +mole_3=2.6 diff --git a/katomic/levels/level_61 b/katomic/levels/level_61 new file mode 100644 index 00000000..03a30071 --- /dev/null +++ b/katomic/levels/level_61 @@ -0,0 +1,30 @@ +[Level] +Name=Furan +atom_1=1-d +atom_2=1-b +atom_3=2-bhC +atom_4=2-cfA +atom_5=3-df +atom_6=A- +atom_7=2-dgA +atom_8=1-f +atom_9=1-h +feld_00=............... +feld_01=............... +feld_02=............... +feld_03=.#############. +feld_04=.#1..#...#..8#. +feld_05=.#.#3.4#7.3#.#. +feld_06=.#..#.#.#.#..#. +feld_07=.#...#.5.#...#. +feld_08=.##.........##. +feld_09=.#...##.##...#. +feld_10=.#2.#..6..#.9#. +feld_11=.#############. +feld_12=............... +feld_13=............... +feld_14=............... +mole_0=1.5.8 +mole_1=.3.3. +mole_2=.467. +mole_3=2...9 diff --git a/katomic/levels/level_62 b/katomic/levels/level_62 new file mode 100644 index 00000000..cd17f418 --- /dev/null +++ b/katomic/levels/level_62 @@ -0,0 +1,31 @@ +[Level] +Name=l-Lactic acid +atom_1=1-d +atom_2=1-b +atom_3=3-dh +atom_4=2-bdfh +atom_5=1-h +atom_6=3-C +atom_7=2-cfA +atom_8=3-cg +atom_9=1-g +feld_00=############### +feld_01=############### +feld_02=##1.#.......4## +feld_03=##..#3..#....## +feld_04=##.....#...#### +feld_05=##.....#...4.## +feld_06=##.#..7#6....## +feld_07=##..#######..## +feld_08=##....9#8..#.## +feld_09=##.1...#.....## +feld_10=####...#.....## +feld_11=##....#..5#..## +feld_12=##2.......#.5## +feld_13=############### +feld_14=############### +mole_0=1..6.. +mole_1=.3.789 +mole_2=1.4... +mole_3=.4.5.. +mole_4=2.5... diff --git a/katomic/levels/level_63 b/katomic/levels/level_63 new file mode 100644 index 00000000..8e4835bf --- /dev/null +++ b/katomic/levels/level_63 @@ -0,0 +1,34 @@ +[Level] +Name=Maleic Acid +atom_1=3-B +atom_2=3-be +atom_3=2-adD +atom_4=2-beD +atom_5=3-ad +atom_6=1-f +atom_7=2-bhC +atom_8=2-dfA +atom_9=1-h +feld_00=............... +feld_01=............... +feld_02=.#############. +feld_03=.#6...8#5...7#. +feld_04=.#.#..###..#.#. +feld_05=.##6...#...9##. +feld_06=..#..#1#3#..#.. +feld_07=..##..###..##.. +feld_08=..#....#....#.. +feld_09=.##1...#...2##. +feld_10=.#.#.......#.#. +feld_11=.#4.........9#. +feld_12=.#############. +feld_13=............... +feld_14=............... +mole_0=..6. +mole_1=.2.. +mole_2=13.6 +mole_3=..7. +mole_4=..8. +mole_5=14.9 +mole_6=.5.. +mole_7=..9. diff --git a/katomic/levels/level_64 b/katomic/levels/level_64 new file mode 100644 index 00000000..a2b06ed8 --- /dev/null +++ b/katomic/levels/level_64 @@ -0,0 +1,34 @@ +[Level] +Name=meso-Tartaric acid +atom_1=1-c +atom_2=3-dg +atom_3=2-chC +atom_4=3-C +atom_5=1-e +atom_6=2-aceg +atom_7=3-ae +atom_8=1-a +atom_9=2-dgA +atom_a=3-ch +atom_b=1-g +atom_c=3-A +feld_00=........###.... +feld_01=..#######2####. +feld_02=..#.3....5...#. +feld_03=.##.a#.....#.#. +feld_04=.#..#4#...#7.#. +feld_05=.#.#...#.#.5.#. +feld_06=.#...........#. +feld_07=.#...#.....#.#. +feld_08=.#87#6#...#..#. +feld_09=.#.#..6#.#bc.#. +feld_10=.#....#...#8.#. +feld_11=.###.......#.#. +feld_12=...#..9......#. +feld_13=...####1######. +feld_14=......###...... +mole_0=....5... +mole_1=12.574.. +mole_2=..3669.. +mole_3=..c78.ab +mole_4=...8.... diff --git a/katomic/levels/level_65 b/katomic/levels/level_65 new file mode 100644 index 00000000..8af4e9ae --- /dev/null +++ b/katomic/levels/level_65 @@ -0,0 +1,39 @@ +[Level] +Name=Crystal 5 +atom_1=o-cd +atom_2=o-bc +atom_3=o-de +atom_4=o-adg +atom_5=o-cfh +atom_6=o-beg +atom_7=o-ab +atom_8=o-beh +atom_9=o-adf +atom_a=o-ef +atom_b=o-acf +atom_c=o-bdg +atom_d=o-ceh +atom_e=o-ah +atom_f=o-fg +atom_g=o-gh +atom_h=o-abcdefgh +feld_00=............... +feld_01=............... +feld_02=............... +feld_03=...#########... +feld_04=...#########... +feld_05=...##..3.a##... +feld_06=...##19c6b##... +feld_07=...##d.7.f##... +feld_08=...##ghe58##... +feld_09=...##2...4##... +feld_10=...#########... +feld_11=...#########... +feld_12=............... +feld_13=............... +feld_14=............... +mole_0=.3.a. +mole_1=148bf +mole_2=.5hc. +mole_3=269dg +mole_4=.7.e. diff --git a/katomic/levels/level_66 b/katomic/levels/level_66 new file mode 100644 index 00000000..8d4e0fa5 --- /dev/null +++ b/katomic/levels/level_66 @@ -0,0 +1,31 @@ +[Level] +Name=Formic acid ethyl ester +atom_1=1-c +atom_2=2-bgC +atom_3=3-A +atom_4=1-d +atom_5=3-bf +atom_6=2-bdfh +atom_7=1-h +atom_8=1-f +feld_00=............... +feld_01=.....#########. +feld_02=..####.....2.#. +feld_03=..#..#..###.6#. +feld_04=..#.4.......##. +feld_05=.##.###1.#...#. +feld_06=.#..........6#. +feld_07=.#.......4...#. +feld_08=.#8#.#.###...#. +feld_09=.###........##. +feld_10=...#.....7#3#.. +feld_11=...#7###.####.. +feld_12=...#.5...#..... +feld_13=...#######..... +feld_14=............... +mole_0=...4.8 +mole_1=..4.6. +mole_2=...6.7 +mole_3=..5.7. +mole_4=12.... +mole_5=.3 diff --git a/katomic/levels/level_67 b/katomic/levels/level_67 new file mode 100644 index 00000000..29f1b390 --- /dev/null +++ b/katomic/levels/level_67 @@ -0,0 +1,31 @@ +[Level] +Name=1,4-Cyclohexadiene +atom_1=1-c +atom_2=1-d +atom_3=2-bdfh +atom_4=1-b +atom_5=2-fhB +atom_6=2-bdD +atom_7=1-f +atom_8=2-cfh +atom_9=1-h +feld_00=............... +feld_01=............... +feld_02=....#######.... +feld_03=....#2...4#.... +feld_04=..###..#..###.. +feld_05=..#6#..#..#5#.. +feld_06=..#.4.....9.#.. +feld_07=..##..7#5..##.. +feld_08=..#.3.....3.#.. +feld_09=..#6#..#..#7#.. +feld_10=..###..#..###.. +feld_11=....#2...9#.... +feld_12=....#######.... +feld_13=............... +feld_14=............... +mole_0=.2..7. +mole_1=2.56.7 +mole_2=.3..3. +mole_3=4.56.9 +mole_4=.4..9. diff --git a/katomic/levels/level_68 b/katomic/levels/level_68 new file mode 100644 index 00000000..86addd87 --- /dev/null +++ b/katomic/levels/level_68 @@ -0,0 +1,39 @@ +[Level] +Name=Squaric acid +atom_1=1-c +atom_2=3-cg +atom_3=1-g +atom_4=a-a +atom_5=a-c +atom_6=a-c +atom_7=o-c +atom_8=C-c +atom_9=D-c +atom_a=2-ceA +atom_b=2-egA +atom_c=2-ag +atom_d=2-afB +atom_e=2-adD +atom_f=3-bg +atom_g=3-ch +atom_h=3-C +feld_00=............... +feld_01=..###########.. +feld_02=..#e..#.....#.. +feld_03=..#3.b#....a#.. +feld_04=..#.....#####.. +feld_05=..#.........#.. +feld_06=..#.........#.. +feld_07=..#...#.....#.. +feld_08=..#.h.#d#...#.. +feld_09=..#.###h#...#.. +feld_10=..#.gf####..#.. +feld_11=..#...#.....#.. +feld_12=..#.1.#.....#.. +feld_13=..###########.. +feld_14=............... +mole_0=..hh.. +mole_1=..ab.. +mole_2=..de.. +mole_3=1f..g3 +mole_4=...... diff --git a/katomic/levels/level_69 b/katomic/levels/level_69 new file mode 100644 index 00000000..691aa3b5 --- /dev/null +++ b/katomic/levels/level_69 @@ -0,0 +1,43 @@ +[Level] +Name=Ascorbic acid +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=1-g +atom_6=3-ac +atom_7=2-bgB +atom_8=2-afB +atom_9=2-bdD +atom_a=2-fhB +atom_b=3-df +atom_c=2-befh +atom_d=3-D +atom_e=3-ch +atom_f=3-bg +atom_g=1-b +atom_h=2-bdfh +atom_i=3-bf +atom_j=1-f +atom_k=1-d +feld_00=############### +feld_01=#djgh.........# +feld_02=#k#........c#.# +feld_03=#a.#h......#b.# +feld_04=#...#.....#..g# +feld_05=#....#...#8..f# +feld_06=#.....#.#....5# +feld_07=#......#.....j# +feld_08=#.....#.#.....# +feld_09=#....#...#....# +feld_10=#...#.....#1..# +feld_11=#..#.......#..# +feld_12=#.#.........#9# +feld_13=#.i........eig# +feld_14=############### +mole_0=.k.j.j.. +mole_1=..h.i... +mole_2=.i.h.b.. +mole_3=g.g.c.ad +mole_4=...g89.. +mole_5=..1f..e5 diff --git a/katomic/levels/level_7 b/katomic/levels/level_7 new file mode 100644 index 00000000..35839e26 --- /dev/null +++ b/katomic/levels/level_7 @@ -0,0 +1,27 @@ +[Level] +Name=Iso-Propanol +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-ae +atom_6=1-g +feld_00=............... +feld_01=....###.###.... +feld_02=....#1#.#.#.... +feld_03=....#4.#..#.... +feld_04=.####....2####. +feld_05=.#...........#. +feld_06=.##.........##. +feld_07=..##...#...##.. +feld_08=.##.3.3....5##. +feld_09=.#.2....3....#. +feld_10=.####2...4####. +feld_11=....#6.#.4#.... +feld_12=....#.###.#.... +feld_13=....###.###.... +feld_14=............... +mole_0=.222. +mole_1=13336 +mole_2=.454. +mole_3=..4.. diff --git a/katomic/levels/level_70 b/katomic/levels/level_70 new file mode 100644 index 00000000..42d96c3f --- /dev/null +++ b/katomic/levels/level_70 @@ -0,0 +1,30 @@ +[Level] +Name=Phosgene +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-cg +atom_6=1-g +atom_7=3-C +atom_8=2-dfA +atom_9=7-b +atom_a=7-h +feld_00=............... +feld_01=.#############. +feld_02=.#....a#89...#. +feld_03=.#.....#....7#. +feld_04=.#...#####...#. +feld_05=.#.....#.....#. +feld_06=.#.....#.....#. +feld_07=.#...........#. +feld_08=.#...........#. +feld_09=.#...........#. +feld_10=.#############. +feld_11=............... +feld_12=............... +feld_13=............... +feld_14=............... +mole_0=.7. +mole_1=.8. +mole_2=9.a diff --git a/katomic/levels/level_71 b/katomic/levels/level_71 new file mode 100644 index 00000000..0eb656de --- /dev/null +++ b/katomic/levels/level_71 @@ -0,0 +1,37 @@ +[Level] +Name=Thiophene +atom_1=1-d +atom_2=1-b +atom_3=2-fhB +atom_4=2-bdD +atom_5=1-f +atom_6=1-h +atom_7=1-h +atom_8=5- +atom_9=5-bh +atom_a=2-cfA +atom_b=2-dgA +atom_c=2-cgC +atom_d=A- +atom_e=2-bgC +atom_f=2-chC +atom_g=2-dfA +feld_00=............... +feld_01=............... +feld_02=.############## +feld_03=.#..g#....6#..# +feld_04=.#..5#........# +feld_05=.#...#........# +feld_06=.#...#........# +feld_07=.#...#...#.#### +feld_08=.#de..........# +feld_09=.##.......g#..# +feld_10=.#.f....#....## +feld_11=.#.#..#.#.....# +feld_12=.#91..#.#2....# +feld_13=.############## +feld_14=............... +mole_0=1...5 +mole_1=.fde. +mole_2=.g.g. +mole_3=2.9.6 diff --git a/katomic/levels/level_72 b/katomic/levels/level_72 new file mode 100644 index 00000000..3dec1394 --- /dev/null +++ b/katomic/levels/level_72 @@ -0,0 +1,38 @@ +[Level] +Name=Urea +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=2-agB +atom_6=2-bdD +atom_7=1-f +atom_8=1-h +atom_9=2-fhB +atom_a=3-D +atom_b=4-adf +atom_c=4-bdg +atom_d=1-b +atom_e=4-beg +atom_f=4-adg +feld_00=.##########... +feld_01=.##1..f#..#... +feld_02=.#.#...#..#### +feld_03=.#.#.........# +feld_04=.#.##..#.#.#.# +feld_05=.#2........#.# +feld_06=.#.######.##.# +feld_07=.#.1e.......a# +feld_08=.#4.#.#...#### +feld_09=.#..#.#...#... +feld_10=.#..#.#...#... +feld_11=.#.9#.#...#... +feld_12=.#..#.#...#... +feld_13=.###......#... +feld_14=...########... +mole_0=.2.. +mole_1=1f.. +mole_2=..9a +mole_3=1e.. +mole_4=.4.. +le_4=.4.. diff --git a/katomic/levels/level_73 b/katomic/levels/level_73 new file mode 100644 index 00000000..21de96a8 --- /dev/null +++ b/katomic/levels/level_73 @@ -0,0 +1,32 @@ +[Level] +Name=Pyruvic Acid +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-cg +atom_6=1-g +atom_7=2-ceA +atom_8=2-aeB +atom_9=3-C +atom_a=3-D +feld_00=............... +feld_01=############### +feld_02=#...........6a# +feld_03=###...#....7### +feld_04=..#...#....#..# +feld_05=..#...#....#..# +feld_06=..#.###....#..# +feld_07=..#..1#....#..# +feld_08=..#####.......# +feld_09=..#89#........# +feld_10=..#..#........# +feld_11=###..#........# +feld_12=#5#.......##..# +feld_13=#43.......6#### +feld_14=###########.... +mole_0=.9.. +mole_1=.756 +mole_2=.8a. +mole_3=136. +mole_4=.4.. diff --git a/katomic/levels/level_74 b/katomic/levels/level_74 new file mode 100644 index 00000000..166debb4 --- /dev/null +++ b/katomic/levels/level_74 @@ -0,0 +1,34 @@ +[Level] +Name=Ethylene oxide +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-ae +atom_6=1-g +atom_7=2-cdfh +atom_8=2-bdfg +atom_9=A-bdfg +atom_a=3-bh +atom_b=1-h +atom_c=1-b +atom_d=1-f +atom_e=1-d +feld_00=............... +feld_01=............... +feld_02=............... +feld_03=##############. +feld_04=#..a#..#....c#. +feld_05=#.........#..#. +feld_06=#..#b........#. +feld_07=#9..d#.#.....#. +feld_08=##...##...#..#. +feld_09=##...#.#..#..#. +feld_10=#............#. +feld_11=#............#. +feld_12=#.#..........#. +feld_13=#.8.e#..#7...#. +feld_14=##############. +mole_0=e...d +mole_1=.798. +mole_2=c.a.b diff --git a/katomic/levels/level_75 b/katomic/levels/level_75 new file mode 100644 index 00000000..4dd37688 --- /dev/null +++ b/katomic/levels/level_75 @@ -0,0 +1,30 @@ +[Level] +Name=Phosphoric Acid +atom_1=1-c +atom_2=3-cg +atom_3=1-g +atom_4=9-g +atom_5=9-A +atom_6=3-C +atom_7=3-ae +atom_8=9-cegA +atom_9=1-a +feld_00=############### +feld_01=#.#...........# +feld_02=#........#.#..# +feld_03=#.........#...# +feld_04=#.#......#.#..# +feld_05=#6#...........# +feld_06=###...........# +feld_07=#......#.#....# +feld_08=#.......#.....# +feld_09=#......#.#...2# +feld_10=##31..........# +feld_11=#.....7.#...29# +feld_12=#....##.##..### +feld_13=#..##.###....8# +feld_14=#####....###### +mole_0=..6.. +mole_1=12823 +mole_2=..7.. +mole_3=..9.. diff --git a/katomic/levels/level_76 b/katomic/levels/level_76 new file mode 100644 index 00000000..d6de2065 --- /dev/null +++ b/katomic/levels/level_76 @@ -0,0 +1,37 @@ +[Level] +Name=Diacetyl +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=1-g +atom_6=2-ehB +atom_7=2-afB +atom_8=2-bdfh +atom_9=3-D +atom_a=3-ad +atom_b=1-d +atom_c=1-h +atom_d=1-f +atom_e=1-b +feld_00=#######........ +feld_01=#9...9########. +feld_02=#.....#.....d#. +feld_03=#.....#......#. +feld_04=#.....#......#. +feld_05=#............#. +feld_06=#.........####. +feld_07=#....e....7b8.# +feld_08=######.......e# +feld_09=..#8..........# +feld_10=..#.......#...# +feld_11=..#.......#...# +feld_12=..#b6....c#...# +feld_13=..#########...# +feld_14=..........##### +mole_0=b.d. +mole_1=.8.. +mole_2=e.69 +mole_3=b.79 +mole_4=.8.. +mole_5=e.c. diff --git a/katomic/levels/level_77 b/katomic/levels/level_77 new file mode 100644 index 00000000..7fcd8795 --- /dev/null +++ b/katomic/levels/level_77 @@ -0,0 +1,38 @@ +[Level] +Name=trans-Dichloroethene +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=1-g +atom_6=2-ehB +atom_7=2-afB +atom_8=2-bdfh +atom_9=3-D +atom_a=3-ad +atom_b=1-d +atom_c=1-h +atom_d=1-f +atom_e=1-b +atom_f=2-fhB +atom_g=2-bdD +atom_h=7-f +atom_i=7-b +feld_00=.............. +feld_01=.############# +feld_02=.#..i#.#.....# +feld_03=.#..g##...#..# +feld_04=.#...#...f#..# +feld_05=.#.....#..#### +feld_06=.#........#... +feld_07=.#........#... +feld_08=.#...#....#... +feld_09=.#...#....#... +feld_10=.#...#....#... +feld_11=.#...#.#.h#... +feld_12=.#...#..#c#... +feld_13=.#..b#...##... +feld_14=.##########... +mole_0=b..h +mole_1=.fg. +mole_2=i..c diff --git a/katomic/levels/level_78 b/katomic/levels/level_78 new file mode 100644 index 00000000..65624b05 --- /dev/null +++ b/katomic/levels/level_78 @@ -0,0 +1,41 @@ +[Level] +Name=Allylisothiocyanate +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=1-g +atom_6=2-ehB +atom_7=2-afB +atom_8=2-bdfh +atom_9=3-D +atom_a=3-ad +atom_b=1-d +atom_c=1-h +atom_d=1-f +atom_e=1-b +atom_f=2-fhB +atom_g=2-bdD +atom_h=7-f +atom_i=7-b +atom_j=4-fB +atom_k=2-BD +atom_l=5-D +feld_00=############### +feld_01=#gl.f#.......j# +feld_02=#..#####......# +feld_03=#...........#.# +feld_04=#......###..#.# +feld_05=#...#.......### +feld_06=#...#.......#.# +feld_07=#.#.#.....#.#.# +feld_08=#.#.......#...# +feld_09=###.......#...# +feld_10=#.#..###......# +feld_11=#.#...........# +feld_12=#......#####..# +feld_13=#......ce#b.k.# +feld_14=############### +mole_0=b..jkl +mole_1=.fg... +mole_2=e..c.. diff --git a/katomic/levels/level_79 b/katomic/levels/level_79 new file mode 100644 index 00000000..6e9e6cb5 --- /dev/null +++ b/katomic/levels/level_79 @@ -0,0 +1,57 @@ +[Level] +Name=Diketene +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=1-g +atom_6=2-ehB +atom_7=2-afB +atom_8=2-bdfh +atom_9=3-D +atom_a=3-ad +atom_b=1-d +atom_c=1-h +atom_d=1-f +atom_e=1-b +atom_f=2-fhB +atom_g=2-bdD +atom_h=7-f +atom_i=7-b +atom_j=4-fB +atom_k=2-BD +atom_l=5-D +atom_m=2-eg +atom_n=2-ce +atom_o=2-ac +atom_p=3-ag +atom_q=3-B +atom_r=C- +atom_s=A- +atom_t=2-ag +atom_u=2-acC +atom_v=3-ce +atom_w=3-A +atom_x=2-egA +atom_y=2-C +atom_z=2-bhC +feld_00=############### +feld_01=##4x........u## +feld_02=#..b........z.# +feld_03=#..#.#...#.#..# +feld_04=#...#.....#...# +feld_05=#5.#.#...#.#..# +feld_06=#w............# +feld_07=##...........## +feld_08=#v............# +feld_09=#..#.#...#.#..# +feld_10=#...#.....#...# +feld_11=#..#.#...#.#..# +feld_12=#3............# +feld_13=##.d.........## +feld_14=############### +mole_0=b.d +mole_1=.z. +mole_2=vx. +mole_3=u35 +mole_4=w4. diff --git a/katomic/levels/level_8 b/katomic/levels/level_8 new file mode 100644 index 00000000..5e762c7d --- /dev/null +++ b/katomic/levels/level_8 @@ -0,0 +1,26 @@ +[Level] +Name=Ethanal +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=2-agB +atom_6=3-D +feld_00=............... +feld_01=............... +feld_02=..###########.. +feld_03=..#.........#.. +feld_04=..#.##...##.#.. +feld_05=..#2.....3..#.. +feld_06=..#.###.###.#.. +feld_07=..#4...1....#.. +feld_08=..#5###2###.#.. +feld_09=..#.........#.. +feld_10=..#.##...##.#.. +feld_11=..#..6......#.. +feld_12=..###########.. +feld_13=............... +feld_14=............... +mole_0=.22. +mole_1=1356 +mole_2=.4.. diff --git a/katomic/levels/level_80 b/katomic/levels/level_80 new file mode 100644 index 00000000..5d6307b5 --- /dev/null +++ b/katomic/levels/level_80 @@ -0,0 +1,29 @@ +[Level] +Name=Acroleine +atom_1=1-f +atom_2=1-h +atom_3=1-b +atom_4=2-dfA +atom_5=2-bhC +atom_6=2-bdD +atom_7=3-B +feld_00=............... +feld_01=############### +feld_02=#.....1#1.....# +feld_03=#.....###.....# +feld_04=#.............# +feld_05=#.............# +feld_06=#7#.........#2# +feld_07=###.........### +feld_08=###.........### +feld_09=#6#.........#3# +feld_10=#.............# +feld_11=#.............# +feld_12=#.....###.....# +feld_13=#.....5#4.....# +feld_14=############### +mole_0=..1. +mole_1=76.1 +mole_2=..5. +mole_3=..4. +mole_4=.3.2 diff --git a/katomic/levels/level_81 b/katomic/levels/level_81 new file mode 100644 index 00000000..8c62106a --- /dev/null +++ b/katomic/levels/level_81 @@ -0,0 +1,37 @@ +[Level] +Name=Malonic Acid +atom_1=1-f +atom_2=1-h +atom_3=1-b +atom_4=2-dfA +atom_5=2-bhC +atom_6=2-bdD +atom_7=3-B +atom_8=2-bdfh +atom_9=2-cfA +atom_a=2-chC +atom_b=3-cg +atom_c=3-C +atom_d=3-A +atom_e=1-g +atom_f=1-d +feld_00=############### +feld_01=##c8d.......e## +feld_02=#e#.........#b# +feld_03=#............9# +feld_04=#.............# +feld_05=#.............# +feld_06=#.....#.#.....# +feld_07=#......#......# +feld_08=#.....#.#.....# +feld_09=#.............# +feld_10=#.............# +feld_11=#.............# +feld_12=#a#.........#f# +feld_13=##.........b3## +feld_14=############### +mole_0=..c.. +mole_1=f.9be +mole_2=.8... +mole_3=3.abe +mole_4=..d.. diff --git a/katomic/levels/level_82 b/katomic/levels/level_82 new file mode 100644 index 00000000..7d6fdfea --- /dev/null +++ b/katomic/levels/level_82 @@ -0,0 +1,44 @@ +[Level] +Name=Uracil +atom_1=1-f +atom_2=1-h +atom_3=1-b +atom_4=2-dfA +atom_5=2-bhC +atom_6=2-bdD +atom_7=3-B +atom_8=2-bdfh +atom_9=2-cfA +atom_a=2-chC +atom_b=3-cg +atom_c=3-C +atom_d=3-A +atom_e=1-g +atom_f=1-d +atom_g=2-bgC +atom_h=4-beh +atom_i=4-ceh +atom_j=2-afB +atom_k=3-D +atom_l=1-a +feld_00=############### +feld_01=#3..........4c# +feld_02=#l..........4.# +feld_03=#.#..#....#...# +feld_04=#.#..#........# +feld_05=#.#..#.#......# +feld_06=#..##k....#...# +feld_07=#.............# +feld_08=#....#je...#..# +feld_09=#..#.#i###....# +feld_10=#....###.#....# +feld_11=#.#...........# +feld_12=#...#.....#.f5# +feld_13=#............h# +feld_14=############### +mole_0=..c.. +mole_1=f.4.. +mole_2=.5.ie +mole_3=.4.jk +mole_4=3.h.. +mole_5=..l.. diff --git a/katomic/levels/level_83 b/katomic/levels/level_83 new file mode 100644 index 00000000..0c30ef6b --- /dev/null +++ b/katomic/levels/level_83 @@ -0,0 +1,46 @@ +[Level] +Name=Caffeine +atom_1=1-d +atom_2=1-e +atom_3=1-f +atom_4=2-abeh +atom_5=1-c +atom_6=2-adgh +atom_7=3-C +atom_8=4-adf +atom_9=4-ceh +atom_a=2-Acg +atom_b=2-bCg +atom_c=2-cCh +atom_d=1-g +atom_e=3-B +atom_f=2-acD +atom_g=4-ceg +atom_h=A- +atom_i=4-Ag +atom_j=2-adef +atom_k=1-b +atom_l=1-a +atom_m=1-h +feld_00=############### +feld_01=#1#.....#...3d# +feld_02=#2.........#### +feld_03=#......#.....4# +feld_04=#e#...........# +feld_05=#.#.#.a#.#....# +feld_06=###...#...#...# +feld_07=#5.#..........# +feld_08=#..9...#...#..# +feld_09=#.6.#...lm....# +feld_10=####.......j..# +feld_11=#12#.#kc.#....# +feld_12=#ab..#..####### +feld_13=#hi.....78..fg# +feld_14=############### +mole_0=....123. +mole_1=12...4.. +mole_2=56.7.8.. +mole_3=..9ab.cd +mole_4=.efgahi. +mole_5=...j.... +mole_6=..klm... diff --git a/katomic/levels/level_9 b/katomic/levels/level_9 new file mode 100644 index 00000000..ac49d58b --- /dev/null +++ b/katomic/levels/level_9 @@ -0,0 +1,27 @@ +[Level] +Name=Acetone +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=2-cgC +atom_6=3-A +atom_7=1-g +feld_00=............... +feld_01=.#############. +feld_02=.####..##....#. +feld_03=.##.....#.#..#. +feld_04=.#.....6#.####. +feld_05=.#.#3#.4.5..2#. +feld_06=.#.#4###..1..#. +feld_07=.###..2....###. +feld_08=.#.....###.#.#. +feld_09=.#.....3.#.#.#. +feld_10=.####.#.....7#. +feld_11=.#..#.#.....##. +feld_12=.#....##..####. +feld_13=.#############. +feld_14=............... +mole_0=.2.2. +mole_1=13537 +mole_2=.464. diff --git a/katomic/main.cpp b/katomic/main.cpp new file mode 100644 index 00000000..91072a8b --- /dev/null +++ b/katomic/main.cpp @@ -0,0 +1,68 @@ +/* main.cpp + + Copyright (C) 1998 Andreas Wüst (AndreasWuest@gmx.de) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + */ + + +#include "toplevel.h" + +#include +#include +#include +#include + + +static const char description[] = + I18N_NOOP("KDE Atomic Entertainment Game"); + +static const char version[] = "2.0"; + + +// ########################## +// # Main # +// ########################## + +int main(int argc, char **argv) +{ + KAboutData aboutData( "katomic", I18N_NOOP("KAtomic"), + version, description, KAboutData::License_GPL, + "(c) 1998, Andreas Wuest"); + aboutData.addAuthor("Andreas Wuest", 0, "AndreasWuest@gmx.de"); + aboutData.addAuthor("Stephan Kulow", 0, "coolo@kde.org"); + aboutData.addAuthor("Cristian Tibirna", 0, "tibirna@kde.org"); + aboutData.addCredit("Carsten Pfeiffer"); + aboutData.addCredit("Dave Corrie"); + aboutData.addCredit("Kai Jung", I18N_NOOP("6 new levels"), "jung@fh-fresenius.de"); + aboutData.addCredit("Danny Allen", I18N_NOOP("Game graphics and application icon"), "danny@dannyallen.co.uk"); + + KCmdLineArgs::init( argc, argv, &aboutData ); + + QApplication::setColorSpec(QApplication::ManyColor); + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + + if ( a.isRestored() ) + RESTORE(AtomTopLevel) + else { + AtomTopLevel *top = new AtomTopLevel; + top->show(); + a.setMainWidget(top); + } + return a.exec(); +} + diff --git a/katomic/molek.cpp b/katomic/molek.cpp new file mode 100644 index 00000000..3425ffe1 --- /dev/null +++ b/katomic/molek.cpp @@ -0,0 +1,158 @@ +/**************************************************************** +** +** Implementation Molek class, derieved from Qt tutorial 8 +** +****************************************************************/ + +// bemerkungen : wenn paintEvent aufgerufen wird, wird das komplette +// widget gelöscht und nur die sachen gezeichnet, die in +// paintEvent stehen ! sollen dinge z.b nur bei maustasten- +// druck gezeichnet werden, so muß dies in mousePressEvent +// stehen ! +// paintEvent wird aufgerufen, falls fenster überdeckt wird, +// oder auch einfach bewegt wird + +#include + +#include "molek.moc" +#include +#include +#include +#include +#include +#include + +#include + +extern int level; + +Molek::Molek( QWidget *parent, const char *name ) : QWidget( parent, name ), + data(locate("appdata", "pics/molek.png")) +{ + setBackgroundColor (QColor (0, 0, 0)); + setMinimumSize(240, 200); +} + +Molek::~Molek () +{ +} + +const atom& Molek::getAtom(uint index) const +{ + static atom none = { 0, "" }; + + if (index > atoms.count() || index == 0) + return none; + + return *atoms.at(index - 1); +} + +void Molek::load (const KSimpleConfig& config) +{ + atoms.clear(); + QString key; + + atom current; + + int atom_index = 1; + QString value; + while (true) { + key.sprintf("atom_%c", int2atom(atom_index)); + value = config.readEntry(key); + if (value.isEmpty()) + break; + + current.obj = value.at(0).latin1(); + value = value.mid(2); + if (value.isNull()) + value = ""; + + strlcpy(current.conn, value.ascii(), sizeof(current.conn)); + kdWarning( atoms.find(current) != atoms.end() ) + << "OOOPS, duplicate atom definition in " << key << endl; + atoms.append(current); + atom_index++; + } + + QString line; + + for (int j = 0; j < MOLEK_SIZE; j++) { + + key.sprintf("mole_%d", j); + line = config.readEntry(key); + + for (int i = 0; i < MOLEK_SIZE; i++) + molek[i][j] = atom2int(line.at(i).latin1()); + } + + mname = i18n(config.readEntry("Name", I18N_NOOP("Noname")).latin1()); + + int& height = _size.rheight(); + int& width = _size.rwidth(); + + height = 0; + width = 0; + + for (int i = 0; i < MOLEK_SIZE; i++) + for (int j = 0; j < MOLEK_SIZE; j++) { + if (molek [i][j] == 0) + continue; + if (i > width) width = i; + if (j > height) height = j; + } + height++; + width++; + + repaint (); +} + +void Molek::paintEvent( QPaintEvent * ) +{ + QString st = i18n("Level: %1").arg(level); + + QPainter paint (this); + paint.setPen (QColor (190, 190, 190)); + paint.drawText (7, height() - 36, mname); + paint.drawText (7, height() - 18, st); + // spielfeld gleich zeichnen + for (int i = 0; i < MOLEK_SIZE; i++) + for (int j = 0; j < MOLEK_SIZE; j++) { + int x = 10 + i * 15; + int y = 10 + j * 15; + + if (molek[i][j] == 0) + continue; + + // paints atoms + if (getAtom(molek [i] [j]).obj <= '9' && getAtom(molek [i] [j]).obj >= '1') + bitBlt (this, x, y, &data, (getAtom(molek [i] [j]).obj - '1') * 15, 0, 15, + 15, CopyROP); + + // paints cristals + if (getAtom(molek [i] [j]).obj == 'o') + bitBlt (this, x, y, &data, 10 * 15, 0, 15, 15, CopyROP); + + // paints connections + if (isdigit(getAtom(molek[i][j]).obj) || getAtom(molek[i][j]).obj == 'o') + for (int c = 0; c < MAX_CONNS_PER_ATOM; c++) { + char conn = getAtom(molek [i] [j]).conn[c]; + if (!conn) + break; + + if (conn >= 'a' && conn <= 'a' + 8) + bitBlt (this, x, y, &data, (conn - 'a') * 15, 16, 15, 15, XorROP); + else + bitBlt (this, x, y, &data, (conn - 'A') * 15, 34, 15, 15, XorROP); + + } + + + // paints connections + if (getAtom(molek[i][j]).obj >= 'A' && getAtom(molek[i][j]).obj <= 'F') + bitBlt (this, x, y, &data, (getAtom(molek[i][j]).obj - 'A' + 11) * 15 , 0, 15, 15, + CopyROP); + + } + + paint.end (); +} diff --git a/katomic/molek.h b/katomic/molek.h new file mode 100644 index 00000000..6189e4e8 --- /dev/null +++ b/katomic/molek.h @@ -0,0 +1,52 @@ +/**************************************************************** +** +** Definition of Molek class, +** +****************************************************************/ + +#ifndef MOLEK_H +#define MOLEK_H + +#include +#include +#include +#include +#include +#include +#include "atom.h" +#include + +class KSimpleConfig; + +#define MOLEK_SIZE 15 + +class Molek : public QWidget +{ + Q_OBJECT + +public: + Molek (QWidget *parent=0, const char *name=0); + ~Molek (); + + void load(const KSimpleConfig& config); + + const atom& getAtom(uint index) const; + int atomSize() const { return atoms.count(); } + + QSize molecSize() const { return _size; } + uint getAtom(int x, int y) const { return molek[x][y]; } + +protected: + void paintEvent( QPaintEvent * ); + +private: + QPixmap data; + uint molek[MOLEK_SIZE][MOLEK_SIZE]; // the indexes within atoms + QValueList atoms; + QString mname; + QSize _size; + + +}; + +#endif // MOLEK_H diff --git a/katomic/molek.png b/katomic/molek.png new file mode 100644 index 00000000..d816bcd9 Binary files /dev/null and b/katomic/molek.png differ diff --git a/katomic/settings.h b/katomic/settings.h new file mode 100644 index 00000000..e43471c2 --- /dev/null +++ b/katomic/settings.h @@ -0,0 +1,36 @@ +/* settings.h + + Copyright (C) 1999 Stephan Kulow (coolo@kde.org) + Cristian Tibirna (tibirna@kde.org) + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#ifndef SETTINGS_H +#define SETTINGS_H + +#include + +#define MAX_SPEED 10 + + +struct Options +{ + int anim_speed; + + bool changed; +}; + +#endif diff --git a/katomic/toplevel.cpp b/katomic/toplevel.cpp new file mode 100644 index 00000000..ca4de920 --- /dev/null +++ b/katomic/toplevel.cpp @@ -0,0 +1,119 @@ +/* toplevel.cpp + + Copyright (C) 1998 Andreas Wüst (AndreasWuest@gmx.de) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "gamewidget.h" +#include "toplevel.h" +#include "settings.h" +#include "configbox.h" +#include +#include +#include +#include + +extern Options settings; + +void AtomTopLevel::createMenu() +{ + KAction *act = KStdGameAction::highscores(main, SLOT(showHighscores()), actionCollection()); + act->setText(i18n("Show &Highscores")); + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + KStdGameAction::restart(main, SLOT(restartLevel()), actionCollection()); + + KStdAction::preferences(this, SLOT(configopts()), actionCollection()); + + undoAction = KStdGameAction::undo (main, SLOT(doUndo()), actionCollection()); + redoAction = KStdGameAction::redo (main, SLOT(doRedo()), actionCollection()); + undoAction->setEnabled(false); + redoAction->setEnabled(false); + connect (main, SIGNAL (enableRedo(bool)), SLOT(enableRedo(bool))); + connect (main, SIGNAL (enableUndo(bool)), SLOT(enableUndo(bool))); + + new KAction(i18n("Atom Up"), Key_Up, main, SLOT(moveUp()), actionCollection(), "atom_up"); + new KAction(i18n("Atom Down"), Key_Down, main, SLOT(moveDown()), actionCollection(), "atom_down"); + new KAction(i18n("Atom Left"), Key_Left, main, SLOT(moveLeft()), actionCollection(), "atom_left"); + new KAction(i18n("Atom Right"), Key_Right, main, SLOT(moveRight()), actionCollection(), "atom_right"); + + new KAction(i18n("Next Atom"), Key_Tab, main, SLOT(nextAtom()), actionCollection(), "next_atom"); + new KAction(i18n("Previous Atom"), SHIFT+Key_Tab, main, SLOT(previousAtom()), actionCollection(), "prev_atom"); +} + +void AtomTopLevel::configopts() +{ + (new ConfigBox(this, "Options"))->show(); +} + +void AtomTopLevel::initConfig() +{ + config = KGlobal::config(); +} + +void AtomTopLevel::saveConfig() +{ + config = KGlobal::config(); + + if (settings.changed) { + config->setGroup("Options"); + config->writeEntry("Animation Speed", settings.anim_speed); + config->setGroup("Colors"); + } + config->sync(); +} + + +AtomTopLevel::AtomTopLevel() +{ + main = new GameWidget(this, "gamewidget"); + createMenu(); + initConfig(); + setCentralWidget(main); + + setupGUI( KMainWindow::Save | Keys | Create ); +} + +AtomTopLevel::~AtomTopLevel() +{ +} + +bool AtomTopLevel::queryExit() +{ + saveConfig(); + return true; +} + +void AtomTopLevel::enableRedo(bool enable) +{ + redoAction->setEnabled(enable); +} + +void AtomTopLevel::enableUndo(bool enable) +{ + undoAction->setEnabled(enable); +} + +#include "toplevel.moc" diff --git a/katomic/toplevel.h b/katomic/toplevel.h new file mode 100644 index 00000000..38c613d0 --- /dev/null +++ b/katomic/toplevel.h @@ -0,0 +1,66 @@ +/* toplevel.h + * + * Andreas Wüst + * + */ + +#ifndef TOPLEVEL_H +#define TOPLEVEL_H + +class GameWidget; +class KAction; +class KConfig; + +#include + +/** + * This is the class AtomTopLevel. The class is used only for the program + * AtomTopLevel. + * + * @short Basic class for AtomTopLevel + * @author Andreas Wüst + */ + +class AtomTopLevel : public KMainWindow +{ + Q_OBJECT + + public: + + AtomTopLevel(); + + ~AtomTopLevel(); + + protected: + + // Creates the menubar and connects the menu-entries to the + // appropriate functions + void createMenu(); + + // Get the configuration from the config-file. + void initConfig(); + + // Save the current configuration to the config-file. + void saveConfig(); + + // called before exiting -> save configuration + virtual bool queryExit(); + + KConfig *config; + + GameWidget *main; + + KAction *redoAction, *undoAction; + + protected slots: + void enableRedo(bool enable); + void enableUndo(bool enable); + +public slots: + + // Shows a dialog for options other than keys + void configopts(); + +}; + +#endif diff --git a/kbackgammon/AUTHORS b/kbackgammon/AUTHORS new file mode 100644 index 00000000..65b967eb --- /dev/null +++ b/kbackgammon/AUTHORS @@ -0,0 +1 @@ +Jens Hoefkens diff --git a/kbackgammon/Makefile.am b/kbackgammon/Makefile.am new file mode 100644 index 00000000..6f21f2ca --- /dev/null +++ b/kbackgammon/Makefile.am @@ -0,0 +1,21 @@ +SUBDIRS = pics sounds icons engines + +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/kgame/ -I$(srcdir)/engines $(all_includes) +METASOURCES = AUTO + +bin_PROGRAMS = kbackgammon +kbackgammon_SOURCES = main.cpp kbg.cpp kbgboard.cpp kbgtextview.cpp \ + kbgstatus.cpp +kbackgammon_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kbackgammon_LDADD = $(LIB_KDEGAMES) $(LIB_KDEPRINT) ./engines/libkbgengines.la +kbackgammon_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +xdg_apps_DATA = kbackgammon.desktop + +rcdir = $(kde_datadir)/kbackgammon +rc_DATA = kbackgammonui.rc eventsrc + +messages: rc.cpp + LIST=`find . -name \*.h -o -name \*.hh -o -name \*.H -o -name \*.hxx -o -name \*.hpp -o -name \*.cpp -o -name \*.cc -o -name \*.c -o -name \*.ecpp -o -name \*.C`; \ + $(XGETTEXT) $$LIST -o $(podir)/kbackgammon.pot + diff --git a/kbackgammon/README b/kbackgammon/README new file mode 100644 index 00000000..6d818f2b --- /dev/null +++ b/kbackgammon/README @@ -0,0 +1,43 @@ +This file describes KBackgammon +------------------------------- + + +KBackgammon is a backgammon program for KDE2. It is based on the code, +ideas and concepts of KFibs (which is a FIBS client for KDE1). For a +short time, KBackgammon was called bacKgammon (if you know somebody +who is still using bacKgammon, please force them to upgrade :-)). + + +KBackgammon is a backgammon program built around a graphical backgammon +board. Since it uses moular backgammon engines, it can easily be extended +beyond the current set of engines. At the moment, the following types of +backgammon games are supported: + + FIBS - online games on the First Internet Backgammon Server. + the computer handles the network connection and + translates the textual messages from the server into + graphical representations. this engine offers separate + chat window and player list window to simplify the + interaction with the server + + Offline - play against yourself or a freind that is sitting next + to you. the most important role of the computer is rolling + the dice. + +In the near to mid future, the program will be extended with the following +two kinds of engines: + + GNUBg - the GNU backgammon program is a powerful neural network + and KBackgammon will soon allow you to play against it + from the convinience of your KDE desktop. + + NextGen - this extension of the offline engine will allow you to play + against other players on the network. the computer makes + sure that the dice are fair and it handles the netwok + communication. + +If you lust for other kinds of engines (besides FIBS, there are other +internet servers out there and it somebody might want to have access +to these as well), please contact the Jens Hoefkens +and make your wishes heard. + diff --git a/kbackgammon/TODO b/kbackgammon/TODO new file mode 100644 index 00000000..616fe58d --- /dev/null +++ b/kbackgammon/TODO @@ -0,0 +1,11 @@ +FIBS engine +----------- + +- add the accumulated online time in the lower right corner of the + main window status bar +- add user profiles with different username, etc. +- port the FIBS help system from KFibs +- add buttons for accept and reject ? +- automatically translate messages --> replace \"a by ae, etc ? +- toggle double is NOT automatically set at the beginning of 1 point games! +- clean the header file diff --git a/kbackgammon/engines/Makefile.am b/kbackgammon/engines/Makefile.am new file mode 100644 index 00000000..e599d9b2 --- /dev/null +++ b/kbackgammon/engines/Makefile.am @@ -0,0 +1,16 @@ +noinst_LTLIBRARIES = libkbgengines.la + +libkbgengines_la_SOURCES = dummy.cpp +libkbgengines_la_LIBADD = offline/libkbgoffline.la gnubg/libkbggnubg.la \ + generic/libkbggeneric.la fibs/libkbgfibs.la \ + nextgen/libkbgnextgen.la + +INCLUDES= $(all_includes) + +METASOURCES = AUTO + +SUBDIRS = offline generic fibs gnubg nextgen + +dummy.cpp: + echo > dummy.cpp + diff --git a/kbackgammon/engines/fibs/Makefile.am b/kbackgammon/engines/fibs/Makefile.am new file mode 100644 index 00000000..e32522de --- /dev/null +++ b/kbackgammon/engines/fibs/Makefile.am @@ -0,0 +1,9 @@ +noinst_LTLIBRARIES = libkbgfibs.la + +libkbgfibs_la_SOURCES = kbgfibs.cpp kplayerlist.cpp kbginvite.cpp kbgfibschat.cpp + +INCLUDES= -I$(top_srcdir)/kbackgammon/engines -I$(top_srcdir)/libkdegames \ + -I$(top_srcdir)/kbackgammon $(all_includes) + +METASOURCES = AUTO + diff --git a/kbackgammon/engines/fibs/clip.h b/kbackgammon/engines/fibs/clip.h new file mode 100644 index 00000000..e016eb5b --- /dev/null +++ b/kbackgammon/engines/fibs/clip.h @@ -0,0 +1,39 @@ +/* + + This file defines constants of the "CLIent Protocol" of FIBS. + It comes directly from Marvin and I guess it is copyrighted + by him. If you have questions regarding this file, try to + visit + + http://fibs.demon.co.uk/clip.html + +*/ + +#ifndef KFIBS_CLIP_H +#define KFIBS_CLIP_H + + +#define CLIP_VERSION 1008 + +#define CLIP_WELCOME 1 +#define CLIP_OWN_INFO 2 +#define CLIP_MOTD_BEGIN 3 +#define CLIP_MOTD_END 4 +#define CLIP_WHO_INFO 5 +#define CLIP_WHO_END 6 +#define CLIP_LOGIN 7 +#define CLIP_LOGOUT 8 +#define CLIP_MESSAGE 9 +#define CLIP_MESSAGE_DELIVERED 10 +#define CLIP_MESSAGE_SAVED 11 +#define CLIP_SAYS 12 +#define CLIP_SHOUTS 13 +#define CLIP_WHISPERS 14 +#define CLIP_KIBITZES 15 +#define CLIP_YOU_SAY 16 +#define CLIP_YOU_SHOUT 17 +#define CLIP_YOU_WHISPER 18 +#define CLIP_YOU_KIBITZ 19 + + +#endif // KFIBS_CLIP_H diff --git a/kbackgammon/engines/fibs/kbgfibs.cpp b/kbackgammon/engines/fibs/kbgfibs.cpp new file mode 100644 index 00000000..06fdaec7 --- /dev/null +++ b/kbackgammon/engines/fibs/kbgfibs.cpp @@ -0,0 +1,2314 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +/* + + TODO: popup dialog for accept/reject and join ?? + clear the chat history? + game over, clear the caption? + need show saved + need buddy list + need wait for player,... + +*/ + +#include "kbgfibs.h" +#include "kbgfibs.moc" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include "kbgboard.h" +#include "kbgstatus.h" + +#include "clip.h" +#include "version.h" + + +void KBgEngineFIBS::start() +{ + // FIXME: open the child windows here and not in the constructor +} + +// == configuration handling =================================================== + +/* + * Restore settings and ask children to do the same + */ +void KBgEngineFIBS::readConfig() +{ + KConfig *config = kapp->config(); + config->setGroup("fibs engine"); + + // history variables + lastAway = config->readEntry("away_hist", ""); + + // various options + showMsg = config->readBoolEntry("pers_msg", false); + whoisInvite = config->readBoolEntry("whois_invite", false); + + // connection information + infoFIBS[FIBSHost] = config->readEntry("server", "fibs.com"); + infoFIBS[FIBSPort] = config->readEntry("port", "4321"); + infoFIBS[FIBSUser] = config->readEntry("user", ""); + infoFIBS[FIBSPswd] = config->readEntry("password", ""); + + // automatic messages + useAutoMsg[MsgBeg] = config->readBoolEntry("auto-beg", false); + useAutoMsg[MsgLos] = config->readBoolEntry("auto-los", false); + useAutoMsg[MsgWin] = config->readBoolEntry("auto-win", false); + + autoMsg[MsgBeg] = config->readEntry("msg-beg", ""); + autoMsg[MsgLos] = config->readEntry("msg-los", ""); + autoMsg[MsgWin] = config->readEntry("msg-win", ""); + + // ask the children to read their config options + playerlist->readConfig(); + chatWindow->readConfig(); +} + +/* + * Save the engine specific settings and tell all clients + */ +void KBgEngineFIBS::saveConfig() +{ + KConfig *config = kapp->config(); + config->setGroup("fibs engine"); + + // history variables + config->writeEntry("away_hist", lastAway); + + // various options + config->writeEntry("pers_msg", showMsg); + config->writeEntry("whois_invite", whoisInvite); + + // connection information + config->writeEntry("server", infoFIBS[FIBSHost]); + config->writeEntry("port", infoFIBS[FIBSPort]); + config->writeEntry("user", infoFIBS[FIBSUser]); + config->writeEntry("password", infoFIBS[FIBSPswd]); + + // automatic messages + config->writeEntry("auto-beg", useAutoMsg[MsgBeg]); + config->writeEntry("auto-los", useAutoMsg[MsgLos]); + config->writeEntry("auto-win", useAutoMsg[MsgWin]); + + config->writeEntry("msg-beg", autoMsg[MsgBeg]); + config->writeEntry("msg-los", autoMsg[MsgLos]); + config->writeEntry("msg-win", autoMsg[MsgWin]); + + // ask the children to read their config options + playerlist->saveConfig(); + chatWindow->saveConfig(); +} + +void KBgEngineFIBS::setupDefault() +{ + + cbp->setChecked(false); + cbi->setChecked(false); + + lec[FIBSHost]->setText("fibs.com"); + lec[FIBSPort]->setText("4321"); + + lec[FIBSUser]->clear(); + lec[FIBSPswd]->clear(); + + + chatWindow->setupDefault(); + playerlist->setupDefault(); +} + +void KBgEngineFIBS::setupCancel() +{ + chatWindow->setupCancel(); + playerlist->setupCancel(); +} + +/* + * Called when the setup dialog is positively closed + */ +void KBgEngineFIBS::setupOk() +{ + // various options + showMsg = cbp->isChecked(); + whoisInvite = cbi->isChecked(); + + // connection information + for (int i = 0; i < NumFIBS; i++) + infoFIBS[i] = lec[i]->text(); + + // automatic messages + for (int i = 0; i < NumMsg; i++) { + useAutoMsg[i] = cbm[i]->isChecked(); + autoMsg[i] = lem[i]->text(); + } + + chatWindow->setupOk(); + playerlist->setupOk(); + + // save settings + saveConfig(); +} + +/* + * Puts the FIBS specific setup into the dialog nb + */ +void KBgEngineFIBS::getSetupPages(KDialogBase *nb) +{ + /* + * Main Widget + */ + QVBox *vbp = nb->addVBoxPage(i18n("FIBS Engine"), i18n("Here you can configure the FIBS backgammon engine"), + kapp->iconLoader()->loadIcon(PROG_NAME "_engine", KIcon::Desktop)); + + /* + * Get a multi page work space + */ + KTabCtl *tc = new KTabCtl(vbp, "fibs tabs"); + + /* + * FIBS, local options + */ + QWidget *w = new QWidget(tc); + QGridLayout *gl = new QGridLayout(w, 3, 1, nb->spacingHint()); + + /* + * Group boxes + */ + QGroupBox *gbo = new QGroupBox(i18n("Options"), w); + QGroupBox *gbm = new QGroupBox(i18n("Automatic Messages"), w); + + gl->addWidget(gbo, 0, 0); + gl->addWidget(gbm, 1, 0); + + /* + * Options + */ + cbp = new QCheckBox(i18n("Show copy of personal messages in main window"), gbo); + cbi = new QCheckBox(i18n("Automatically request player info on invitation"), gbo); + + QWhatsThis::add(cbp, i18n("Usually, all messages sent directly to you by other players " + "are displayed only in the chat window. Check this box if you " + "would like to get a copy of these messages in the main window.")); + QWhatsThis::add(cbi, i18n("Check this box if you would like to receive information on " + "players that invite you to games.")); + + cbp->setChecked(showMsg); + cbi->setChecked(whoisInvite); + + gl = new QGridLayout(gbo, 2, 1, 20); + gl->addWidget(cbp, 0, 0); + gl->addWidget(cbi, 1, 0); + + /* + * Automatic messages + */ + gl = new QGridLayout(gbm, NumMsg, 2, 20); + + cbm[MsgBeg] = new QCheckBox(i18n("Start match:"), gbm); + cbm[MsgWin] = new QCheckBox(i18n("Win match:"), gbm); + cbm[MsgLos] = new QCheckBox(i18n("Lose match:"), gbm); + + QWhatsThis::add(cbm[MsgBeg], i18n("If you want to send a standard greeting to your " + "opponent whenever you start a new match, check " + "this box and write the message into the entry " + "field.")); + QWhatsThis::add(cbm[MsgWin], i18n("If you want to send a standard message to your " + "opponent whenever you won a match, check this box " + "and write the message into the entry field.")); + QWhatsThis::add(cbm[MsgLos], i18n("If you want to send a standard message to your " + "opponent whenever you lost a match, check this box " + "and write the message into the entry field.")); + + for (int i = 0; i < NumMsg; i++) { + lem[i] = new QLineEdit(autoMsg[i], gbm); + gl->addWidget(cbm[i], i, 0); + gl->addWidget(lem[i], i, 1); + connect(cbm[i], SIGNAL(toggled(bool)), lem[i], SLOT(setEnabled(bool))); + cbm[i]->setChecked(useAutoMsg[i]); + lem[i]->setEnabled(useAutoMsg[i]); + QWhatsThis::add(lem[i], QWhatsThis::textFor(cbm[i])); + } + + /* + * Put the page into the notebook + */ + gl->activate(); + tc->addTab(w, i18n("&Local")); + + + /* + * FIBS, connection setup + */ + w = new QWidget(tc); + gl = new QGridLayout(w, 3, 1, nb->spacingHint()); + + QGroupBox *gbc = new QGroupBox(i18n("Server"), w); + QGroupBox *gbk = new QGroupBox(i18n("Other"), w); + + gl->addWidget(gbc, 0, 0); + gl->addWidget(gbk, 1, 0); + + /* + * Server box + */ + gl = new QGridLayout(gbc, 4, 2, 20); + + QLabel *lbc[NumFIBS]; + + lbc[FIBSHost] = new QLabel(i18n("Server name:"), gbc); + lbc[FIBSPort] = new QLabel(i18n("Server port:"), gbc); + lbc[FIBSUser] = new QLabel(i18n("User name:"), gbc); + lbc[FIBSPswd] = new QLabel(i18n("Password:"), gbc); + + for (int i = 0; i < NumFIBS; i++) { + lec[i] = new QLineEdit(infoFIBS[i], gbc); + gl->addWidget(lbc[i], i, 0); + gl->addWidget(lec[i], i, 1); + } + lec[FIBSPswd]->setEchoMode(QLineEdit::Password); + + QWhatsThis::add(lec[FIBSHost], i18n("Enter here the host name of FIBS. With almost " + "absolute certainty this should be \"fibs.com\". " + "If you leave this blank, you will be asked again " + "at connection time.")); + QWhatsThis::add(lec[FIBSPort], i18n("Enter here the port number of FIBS. With almost " + "absolute certainty this should be \"4321\". " + "If you leave this blank, you will be asked again " + "at connection time.")); + QWhatsThis::add(lec[FIBSUser], i18n("Enter your login on FIBS here. If you do not have a " + "login yet, you should first create an account using " + "the corresponding menu entry. If you leave this blank, " + "you will be asked again at connection time.")); + QWhatsThis::add(lec[FIBSPswd], i18n("Enter your password on FIBS here. If you do not have a " + "login yet, you should first create an account using " + "the corresponding menu entry. If you leave this blank, " + "you will be asked again at connection time. The password " + "will not be visible.")); + + /* + * Connection keepalive + */ + cbk = new QCheckBox(i18n("Keep connections alive"), gbk); + + QWhatsThis::add(cbk, i18n("Usually, FIBS drops the connection after one hour of inactivity. When " + "you check this box, %1 will try to keep the connection alive, even " + "if you are not actually playing or chatting. Use this with caution " + "if you do not have flat-rate Internet access.").arg(PROG_NAME)); + + cbk->setChecked(keepalive); + + gl = new QGridLayout(gbk, 1, 1, nb->spacingHint()); + gl->addWidget(cbk, 0, 0); + + /* + * Done with the page, put it in + */ + gl->activate(); + tc->addTab(w, i18n("&Connection")); + + /* + * Ask children for settings + */ + chatWindow->getSetupPages(tc, nb->spacingHint()); + playerlist->getSetupPages(tc, nb->spacingHint()); + + /* + * TODO: future extensions + */ + w = new QWidget(tc); + tc->addTab(w, i18n("&Buddy List")); +} + + +// == functions related to the invitation menu ================================= + +/* + * Remove a player from the invitation list in the join menu + */ +void KBgEngineFIBS::cancelJoin(const QString &info) +{ + QRegExp patt = QRegExp("^" + info + " "); + + for (int i = 0; i <= numJoin; i++) { + if (actJoin[i]->text().contains(patt)) { + // move all entries starting at i+1 up by one... + for (int j = i; j < numJoin; j++) + actJoin[j]->setText(actJoin[j+1]->text()); + actJoin[numJoin--]->unplug(joinMenu); + break; + } + } +} + +/* + * Parse the information in info for the purposes of the invitation + * submenu + */ +void KBgEngineFIBS::changeJoin(const QString &info) +{ + char name_p[100], name_o[100]; + float rate; + int expi; + + /* + * Extract the name of the player, her opponent, rating and experience. + * It is okay to use latin1(), since the string is coming from FIBS. + */ + sscanf(info.latin1(), "%99s %99s %*s %*s %*s %f %i %*s %*s %*s %*s %*s", + name_p, name_o, &rate, &expi); + + QString name = name_p; + QString oppo = name_o; + + QString rate_s; rate_s.setNum(rate); + QString expi_s; expi_s.setNum(expi); + + QRegExp patt = QRegExp("^" + name + " "); + + /* + * We have essentially two lists of names to check against: the ones + * that have invited us and are not yet in the menu and the ones that + * are already in the menu. + */ + + if (numJoin > -1 && oppo != "-") + cancelJoin(name); + + for (QStringList::Iterator it = invitations.begin(); it != invitations.end(); ++it) { + + if ((*it).contains(patt)) { + + QString text, menu; + + if ((*it).contains(QRegExp(" r$"))) { + menu = i18n("R means resume", "%1 (R)").arg(name); + text = i18n("%1 (experience %2, rating %3) wants to resume a saved match with you. " + "If you want to play, use the corresponding menu entry to join (or type " + "'join %4').").arg(name).arg(expi_s).arg(rate_s).arg(name); + KNotifyClient::event("invitation", i18n("%1 wants to resume a saved match with you"). + arg(name)); + } else if ((*it).contains(QRegExp(" u$"))) { + menu = i18n("U means unlimited", "%1 (U)").arg(name); + text = i18n("%1 (experience %2, rating %3) wants to play an unlimited match with you. " + "If you want to play, use the corresponding menu entry to join (or type " + "'join %4').").arg(name).arg(expi_s).arg(rate_s).arg(name); + KNotifyClient::event("invitation", i18n("%1 has invited you to an unlimited match"). + arg(name)); + } else { + QString len = (*it).right((*it).length() - name.length() - 1); + menu = i18n("If the format of the (U) and (R) strings is changed, it should also be changed here", + "%1 (%2)").arg(name).arg(len); + text = i18n("%1 (experience %2, rating %3) wants to play a %4 point match with you. " + "If you want to play, use the corresponding menu entry to join (or type " + "'join %5').").arg(name).arg(expi_s).arg(rate_s).arg(len).arg(name); + KNotifyClient::event("invitation", i18n("%1 has invited you for a %2 point match"). + arg(name).arg(len)); + } + emit serverString("rawwho " + name); // this avoids a race + if (whoisInvite) { + emit serverString("whois " + name); + emit infoText("" + text + ""); + } else + emit infoText("" + text + "
"); + + for (int i = 0; i <=numJoin; i++) + actJoin[i]->unplug(joinMenu); + + if (++numJoin > 7) numJoin = 7; + + for (int i = numJoin; i > 0; i--) + actJoin[i]->setText(actJoin[i-1]->text()); + + actJoin[0]->setText(menu); + + for (int i = 0; i <= numJoin; i++) + actJoin[i]->plug(joinMenu); + + invitations.remove(it); + break; + } + } + + /* + * If there are entries in the menu, enable it + */ + menu->setItemEnabled(joinMenuID, numJoin > -1); +} + + +// == various slots and functions ============================================== + +/* + * Keep the connection alive. + */ +void KBgEngineFIBS::keepAlive() +{ + emit serverString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); +} + +/* + * Several bookkeeping operations that have to be done at the + * end of every game. Some of these may or may not be necessary + * at a particular time, but they don't hurt either. + */ +void KBgEngineFIBS::endGame() +{ + playing = false; + + emit serverString("rawwho " + infoFIBS[FIBSUser]); + + actConti->setEnabled(false); + actLeave->setEnabled(false); + + actAccept->setEnabled(false); + actReject->setEnabled(false); + + emit allowCommand(Load, false); + emit allowCommand(Undo, false); + emit allowCommand(Done, false); + emit allowCommand(Cube, false); + emit allowCommand(Roll, false); +} + +/* + * Toggle visibility of the player list + */ +void KBgEngineFIBS::showList() +{ + playerlist->isVisible() ? playerlist->hide() : playerlist->show(); +} + +/* + * Toggle visibility of the chat window + */ +void KBgEngineFIBS::showChat() +{ + chatWindow->isVisible() ? chatWindow->hide() : chatWindow->show(); +} + +/* + * Process the last move coming from the board + */ +void KBgEngineFIBS::handleMove(QString *s) +{ + lastMove = *s; + QString t = lastMove.left(1); + int moves = t.toInt(); + + emit allowCommand(Done, moves == toMove); + emit allowCommand(Undo, moves > 0); + + /* + * Allow undo and possibly start the commit timer + */ + redoPossible &= ((moves < toMove) && (undoCounter > 0)); + emit allowCommand(Redo, redoPossible); + if (moves == toMove && cl >= 0) { + emit allowMoving(false); + ct->start(cl, true); + } +} + +/* + * Done with the move + */ +void KBgEngineFIBS::done() +{ + // prevent the timer from expiring again + ct->stop(); + + // no more moves + emit allowMoving(false); + + // no more commands until it's our turn + emit allowCommand(Load, false); + emit allowCommand(Undo, false); + emit allowCommand(Done, false); + emit allowCommand(Cube, false); + emit allowCommand(Roll, false); + + // Transform the string to FIBS cormat + lastMove.replace(0, 2, "move "); + lastMove.replace(pat[PlsChar], "-"); + + // sent it to the server + emit serverString(lastMove); +} + +/* + * Undo the last move + */ +void KBgEngineFIBS::undo() +{ + ct->stop(); + + redoPossible = true; + ++undoCounter; + + emit allowMoving(true); + emit allowCommand(Done, false); + emit allowCommand(Redo, true); + emit undoMove(); +} + +/* + * Redo the last undone move + */ +void KBgEngineFIBS::redo() +{ + --undoCounter; + emit redoMove(); +} + +/* + * Double the cube - coming from the board + */ +void KBgEngineFIBS::doubleCube(const int w) +{ + if (playing && w == US) cube(); +} + +/* + * Roll the dice - coming from the board + */ +void KBgEngineFIBS::rollDice(const int w) +{ + if (playing && w == US) roll(); +} + +/* + * This engine passes all commands unmodified to the server + */ +void KBgEngineFIBS::handleCommand(QString const &cmd) +{ + emit serverString(cmd); +} + +/* + * If we have a connection, we don't quit right away + */ +bool KBgEngineFIBS::queryClose() +{ + if (connection->state() == QSocket::Idle) + return true; + + switch (KMessageBox::warningYesNoCancel((QWidget *)parent(),i18n("Still connected. Log out first?"),QString::null,i18n("Log Out"), i18n("Stay Connected"))) { + case KMessageBox::Yes : + disconnectFIBS(); + return true; + case KMessageBox::No : + return true; + default: // cancel + return false; + } +} + +/* + * If we have a connection, we don't quit right away + */ +bool KBgEngineFIBS::queryExit() +{ + if( kapp->sessionSaving()) + return true; + if (connection->state() != QSocket::Idle) + disconnectFIBS(); + return true; +} + +/* + * This displays a copy of personal messages in the main window. + * Normally, these only get displayed in the chat window. + */ +void KBgEngineFIBS::personalMessage(const QString &msg) +{ + if (showMsg) + emit infoText(msg); +} + + +// == slots and functions for FIBS commands ==================================== + +/* + * Accept the offer + */ +void KBgEngineFIBS::accept() +{ + actAccept->setEnabled(false); + actReject->setEnabled(false); + + emit serverString("accept"); +} + +/* + * Reject the offer + */ +void KBgEngineFIBS::reject() +{ + actAccept->setEnabled(false); + actReject->setEnabled(false); + + emit serverString("reject"); +} + +/* + * Continue a multi game match + */ +void KBgEngineFIBS::match_conti() +{ + actConti->setEnabled(false); + actLeave->setEnabled(false); + + emit serverString("join"); +} + +/* + * Leave a multi game match + */ +void KBgEngineFIBS::match_leave() +{ + actConti->setEnabled(false); + actLeave->setEnabled(false); + + emit serverString("leave"); +} + +/* + * Go away from the server for a little while. Offer the last know away + * message as a default to the user. + */ +void KBgEngineFIBS::away() +{ + bool ret; + QString msg = KLineEditDlg::getText(i18n("Please type the message that should be displayed to other\n" + "users while you are away."), + lastAway, &ret, (QWidget *)parent()); + if (ret) { + lastAway = msg; + emit serverString("away " + msg); + actAway->setEnabled(false); + } +} + +/* + * Toggle being ready for games + */ +void KBgEngineFIBS::toggle_ready() +{ + emit serverString("toggle ready"); +} + +/* + * Toggle the use of greedy bearoffs + */ +void KBgEngineFIBS::toggle_greedy() +{ + emit serverString("toggle greedy"); +} + +/* + * Toggle whether we will be asked to double/roll or not + */ +void KBgEngineFIBS::toggle_double() +{ + emit serverString("toggle double"); +} + +/* + * Toggle whether we want to see details on rating computations + */ +void KBgEngineFIBS::toggle_ratings() +{ + emit serverString("toggle ratings"); +} + +/* + * Come back after being away. + */ +void KBgEngineFIBS::back() +{ + emit serverString("back"); +} + +/* + * Double the cube + */ +void KBgEngineFIBS::cube() +{ + emit serverString("double"); +} + +/* + * Roll the dice + */ +void KBgEngineFIBS::roll() +{ + emit serverString("roll"); +} + +/* + * Reload the board + */ +void KBgEngineFIBS::load() +{ + emit serverString("board"); +} + +/* + * Handle the menu short cuts for joining. This is not as pretty as it + * could or should be, but it works and is easy to understand. + */ +void KBgEngineFIBS::join(const QString &msg) +{ + emit serverString("join " + msg.left(msg.find('('))); +} +void KBgEngineFIBS::join_0() { join(actJoin[0]->text()); } +void KBgEngineFIBS::join_1() { join(actJoin[1]->text()); } +void KBgEngineFIBS::join_2() { join(actJoin[2]->text()); } +void KBgEngineFIBS::join_3() { join(actJoin[3]->text()); } +void KBgEngineFIBS::join_4() { join(actJoin[4]->text()); } +void KBgEngineFIBS::join_5() { join(actJoin[5]->text()); } +void KBgEngineFIBS::join_6() { join(actJoin[6]->text()); } +void KBgEngineFIBS::join_7() { join(actJoin[7]->text()); } + + +// == invitation handling ====================================================== + +/* + * Show the invitation dialog and set the name to player + */ +void KBgEngineFIBS::inviteDialog() +{ + fibsRequestInvitation(""); +} + +/* + * Show the invitation dialog and set the name to player + */ +void KBgEngineFIBS::fibsRequestInvitation(const QString &player) +{ + if (!invitationDlg) { + QString p = player; + invitationDlg = new KBgInvite("invite"); + connect(invitationDlg, SIGNAL(inviteCommand(const QString &)), this, SLOT(handleCommand(const QString &))); + connect(invitationDlg, SIGNAL(dialogDone()), this, SLOT(invitationDone())); + } + invitationDlg->setPlayer(player); + invitationDlg->show(); +} + +/* + * Finish off the invitation dialog + */ +void KBgEngineFIBS::invitationDone() +{ + delete invitationDlg; + invitationDlg = 0; +} + + +// == connection handling ====================================================== + +/* + * Establish a connection to the server and log in if the parameter login + * is true. + */ +void KBgEngineFIBS::connectFIBS() +{ + /* + * Make sure the connection parameter are properly set. + */ + if (!queryConnection(false)) + return; + + conAction->setEnabled(false); + newAction->setEnabled(false); + disAction->setEnabled(false); + + /* + * Connect + */ + emit infoText(i18n("Looking up %1").arg(infoFIBS[FIBSHost])); + connection->connectToHost(infoFIBS[FIBSHost], infoFIBS[FIBSPort].toUShort()); + + return; +} + +/* + * Hostname has been resolved. + */ +void KBgEngineFIBS::hostFound() +{ + emit infoText(i18n("Connecting to %1").arg(infoFIBS[FIBSHost])); +} + +/* + * An error has occurred. Reset and inform the user. + */ +void KBgEngineFIBS::connError(int f) +{ + switch (f) { + case QSocket::ErrConnectionRefused: + emit infoText(i18n("Error, connection has been refused")); + break; + case QSocket::ErrHostNotFound: + emit infoText(i18n("Error, nonexistent host or name server down.")); + break; + case QSocket::ErrSocketRead: + emit infoText(i18n("Error, reading data from socket")); + break; + } + connectionClosed(); + return; +} + +void KBgEngineFIBS::readData() +{ + QString line; + while(connection->canReadLine()) { + line = connection->readLine(); + if (line.length() > 2) { + line.truncate(line.length()-2); + handleServerData(line); + } + } +} + +/* + * Transmit the string s to the server + */ +void KBgEngineFIBS::sendData(const QString &s) +{ + connection->writeBlock((s+"\r\n").latin1(),2+s.length()); +} + +/* + * Connection has been established. Log in and update the menus & actions. + */ +void KBgEngineFIBS::connected() +{ + conAction->setEnabled(false); + newAction->setEnabled(false); + disAction->setEnabled(true); + + menu->setItemEnabled( cmdMenuID, true); + menu->setItemEnabled(respMenuID, true); + menu->setItemEnabled(optsMenuID, true); + + /* + * Initialize the rx state machine + */ + rxStatus = RxConnect; + rxCollect = ""; + + /* + * Depending on whether somebody else wants to handle the login or not + */ + if (login) { + + /* + * Make sure the player list is empty when the whole list comes + * right after login + */ + playerlist->clear(); + + /* + * Login, using the autologin feature of FIBS, before we even receive anything. + */ + QString entry; + entry.setNum(CLIP_VERSION); + emit serverString(QString("login ") + PROG_NAME + "-" + PROG_VERSION + " " + entry + " " + + infoFIBS[FIBSUser] + " " + infoFIBS[FIBSPswd]); + + } else { + + emit serverString("guest"); + login = true; + + } + + /* + * Some visual feedback and done + */ + emit infoText(i18n("Connected") + "
"); +} + +/* + * Create a new account on FIBS. Obviously, this will also create + * a connection. The actual login is handled in the message parsing + * state machine. + */ +void KBgEngineFIBS::newAccount() +{ + if (!queryConnection(true)) + return; + + rxStatus = RxNewLogin; + rxCollect = ""; + login = false; + connectFIBS(); +} + +/* + * Send a disconnection request to the server. The server will disconnect + * and we will receive a connectionClosed() signal. + */ +void KBgEngineFIBS::disconnectFIBS() +{ + // send two lines in case we are stuck in the login phase + emit serverString("quit"); + emit serverString("quit"); +} + +/* + * Connection to the server is closed for some (unknown) reason. Delete + * the connection object and get the actions into a proper state. + */ +void KBgEngineFIBS::connectionClosed() +{ + /* + * Read remaining input + */ + readData(); + + /* + * Flush whatever is left in the rxBuffer and send a note + */ + emit infoText(rxCollect + "

"); + emit infoText(i18n("Disconnected.") + "
"); + + conAction->setEnabled(true); + newAction->setEnabled(true); + disAction->setEnabled(false); + + menu->setItemEnabled(joinMenuID, false); + menu->setItemEnabled( cmdMenuID, false); + menu->setItemEnabled(respMenuID, false); + menu->setItemEnabled(optsMenuID, false); +} + +/* + * To establish a connection, we need to query the server name, the port + * number, the login and the password. + */ +bool KBgEngineFIBS::queryConnection(const bool newlogin) +{ + QString text, msg; + bool first, ret = true; + + /* + * query the connection parameter + */ + if (newlogin || infoFIBS[FIBSHost].isEmpty()) { + + msg = KLineEditDlg::getText(i18n("Enter the name of the server you want to connect to.\n" + "This should almost always be \"fibs.com\"."), + infoFIBS[FIBSHost], &ret, (QWidget *)parent()); + + if (ret) + infoFIBS[FIBSHost] = msg; + else + return false; + + } + if (newlogin || infoFIBS[FIBSPort].isEmpty()) { + + msg = KLineEditDlg::getText(i18n("Enter the port number on the server. " + "It should almost always be \"4321\"."), + infoFIBS[FIBSPort], &ret, (QWidget *)parent()); + + if (ret) + infoFIBS[FIBSPort] = msg; + else + return false; + } + if (newlogin || infoFIBS[FIBSUser].isEmpty()) { + + if (newlogin) + + text = i18n("Enter the login you would like to use on the server %1. The login may not\n" + "contain spaces or colons. If the login you choose is not available, you'll later be\n" + "given the opportunity to pick another one.\n\n").arg(infoFIBS[FIBSHost]); + + else + + text = i18n("Enter your login on the server %1. If you don't have a login, you\n" + "should create one using the corresponding menu option.\n\n").arg(infoFIBS[FIBSHost]); + + + first = true; + do { + msg = (KLineEditDlg::getText(text, infoFIBS[FIBSUser], &ret, + (QWidget *)parent())).stripWhiteSpace(); + if (first) { + text += i18n("The login may not contain spaces or colons!"); + first = false; + } + + } while (ret && (msg.isEmpty() || msg.contains(' ') || msg.contains(':'))); + + if (ret) + infoFIBS[FIBSUser] = msg; + else + return false; + } + if (newlogin || infoFIBS[FIBSPswd].isEmpty()) { + + if (newlogin) + + text = i18n("Enter the password you would like to use with the login %1\n" + "on the server %2. It may not contain colons.\n\n"). + arg(infoFIBS[FIBSUser]).arg(infoFIBS[FIBSHost]); + + else + + text = i18n("Enter the password for the login %1 on the server %2.\n\n"). + arg(infoFIBS[FIBSUser]).arg(infoFIBS[FIBSHost]); + + first = true; + do { + QCString password; + if (newlogin) + ret = (KPasswordDialog::getNewPassword(password, text) == KPasswordDialog::Accepted); + else + ret = (KPasswordDialog::getPassword(password, text) == KPasswordDialog::Accepted); + + password.stripWhiteSpace(); + msg = password; + + if (first) { + text += i18n("The password may not contain colons or spaces!"); + first = false; + } + + } while (ret && (msg.isEmpty() || msg.contains(' ') || msg.contains(':'))); + + if (ret) + infoFIBS[FIBSPswd] = msg; + else + return false; + } + + /* + * Made it here, all parameters acquired + */ + return true; +} + + +// == message parsing ========================================================== + +/* + * Pattern setup - rather long and boring + */ +void KBgEngineFIBS::initPattern() +{ + QString pattern; + + /* + * Initialize the search pattern array + */ + pat[Welcome] = QRegExp(pattern.sprintf("^%d ", CLIP_WELCOME)); + pat[OwnInfo] = QRegExp(pattern.sprintf("^%d ", CLIP_OWN_INFO)); + pat[WhoInfo] = QRegExp(pattern.sprintf("^%d ", CLIP_WHO_INFO)); + pat[WhoEnde] = QRegExp(pattern.sprintf("^%d$", CLIP_WHO_END)); + pat[MotdBeg] = QRegExp(pattern.sprintf("^%d" , CLIP_MOTD_BEGIN)); + pat[MotdEnd] = QRegExp(pattern.sprintf("^%d" , CLIP_MOTD_END)); + pat[MsgPers] = QRegExp(pattern.sprintf("^%d ", CLIP_MESSAGE)); + pat[MsgDeli] = QRegExp(pattern.sprintf("^%d ", CLIP_MESSAGE_DELIVERED)); + pat[MsgSave] = QRegExp(pattern.sprintf("^%d ", CLIP_MESSAGE_SAVED)); + pat[ChatSay] = QRegExp(pattern.sprintf("^%d ", CLIP_SAYS)); + pat[ChatSht] = QRegExp(pattern.sprintf("^%d ", CLIP_SHOUTS)); + pat[ChatWis] = QRegExp(pattern.sprintf("^%d ", CLIP_WHISPERS)); + pat[ChatKib] = QRegExp(pattern.sprintf("^%d ", CLIP_KIBITZES)); + pat[SelfSay] = QRegExp(pattern.sprintf("^%d ", CLIP_YOU_SAY)); + pat[SelfSht] = QRegExp(pattern.sprintf("^%d ", CLIP_YOU_SHOUT)); + pat[SelfWis] = QRegExp(pattern.sprintf("^%d ", CLIP_YOU_WHISPER)); + pat[SelfKib] = QRegExp(pattern.sprintf("^%d ", CLIP_YOU_KIBITZ)); + pat[UserLin] = QRegExp(pattern.sprintf("^%d ", CLIP_LOGIN)); + pat[UserLot] = QRegExp(pattern.sprintf("^%d ", CLIP_LOGOUT)); + + pat[NoLogin] = QRegExp("\\*\\* Unknown command: 'login'"); + pat[BegRate] = QRegExp("^rating calculation:$"); + pat[EndRate] = QRegExp("^change for "); + pat[HTML_lt] = QRegExp("<"); + pat[HTML_gt] = QRegExp(">"); + pat[BoardSY] = QRegExp("^Value of 'boardstyle' set to 3"); + pat[BoardSN] = QRegExp("^Value of 'boardstyle' set to [^3]"); + pat[WhoisBG] = QRegExp("^Information about "); + pat[WhoisE1] = QRegExp("^ No email address\\.$"); + pat[WhoisE2] = QRegExp("^ Email address: "); + pat[SelfSlf] = QRegExp("^You say to yourself:"); + pat[Goodbye] = QRegExp("^ Goodbye\\."); + pat[GameSav] = QRegExp("The game was saved\\.$"); + pat[RawBord] = QRegExp("^board:"); + pat[YouTurn] = QRegExp("^It's your turn\\. Please roll or double"); + pat[PlsMove] = QRegExp("^Please move [1-6]+ pie"); + pat[EndWtch] = QRegExp("^You stop watching "); + pat[BegWtch] = QRegExp("^You're now watching "); + pat[BegGame] = QRegExp("^Starting a new game with "); + pat[Reload1] = QRegExp("^You are now playing with "); + pat[Reload2] = QRegExp(" has joined you. Your running match was loaded\\.$"); + pat[OneWave] = QRegExp(" waves goodbye.$"); + pat[TwoWave] = QRegExp(" waves goodbye again.$"); + pat[YouWave] = QRegExp("^You wave goodbye.$"); + pat[GameBG1] = QRegExp("start a [0-9]+ point match"); + pat[GameBG2] = QRegExp("start an unlimited match"); + pat[GameRE1] = QRegExp("are resuming their [0-9]+-point match"); + pat[GameRE2] = QRegExp("are resuming their unlimited match"); + pat[GameEnd] = QRegExp("point match against"); + pat[TabChar] = QRegExp("\\t"); + pat[PlsChar] = QRegExp("\\+"); + pat[Invite0] = QRegExp(" wants to play a [0-9]+ point match with you\\.$"); + pat[Invite1] = QRegExp("^.+ wants to play a "); + pat[Invite2] = QRegExp(" wants to resume a saved match with you\\.$"); + pat[Invite3] = QRegExp(" wants to play an unlimited match with you\\.$"); + pat[TypJoin] = QRegExp("^Type 'join "); + pat[OneName] = QRegExp("^ONE USERNAME PER PERSON ONLY!!!"); + pat[YouAway] = QRegExp("^You're away. Please type 'back'"); + pat[YouBack] = QRegExp("^Welcome back\\.$"); + pat[YouMove] = QRegExp("^It's your turn to move\\."); + pat[YouRoll] = QRegExp("^It's your turn to roll or double\\."); + pat[TwoStar] = QRegExp("^\\*\\* "); + pat[OthrNam] = QRegExp("^\\*\\* Please use another name\\. "); + pat[BoxHori] = QRegExp("^ *\\+-*\\+ *$"); + pat[BoxVer1] = QRegExp("^ *\\|"); + pat[BoxVer2] = QRegExp("\\| *$"); + pat[YourNam] = QRegExp("Your name will be "); + pat[GivePwd] = QRegExp("Please give your password:"); + pat[RetypeP] = QRegExp("Please retype your password:"); + pat[HelpTxt] = QRegExp("^NAME$"); + pat[MatchB1] = QRegExp(" has joined you for a [0-9]+ point match\\.$"); + pat[MatchB2] = QRegExp(" has joined you for an unlimited match\\.$"); + pat[EndLose] = QRegExp(" wins the [0-9]+ point match [0-9]+-[0-9]+"); + pat[EndVict] = QRegExp(" win the [0-9]+ point match [0-9]+-[0-9]+"); + pat[RejAcpt] = QRegExp("Type 'accept' or 'reject'\\.$"); + pat[YouAcpt] = QRegExp("^You accept the double\\. The cube shows [0-9]+\\."); + + pat[KeepAlv] = QRegExp("^\\*\\* Unknown command: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"); + pat[RatingY] = QRegExp("You'll see how the rating changes are calculated\\.$"); + pat[RatingN] = QRegExp("You won't see how the rating changes are calculated\\.$"); + + // FIXME same problem as in previous line + // mpgnu accepts the double.5 arthur_tn - gnu 1 0 1243.32 365 6 983722411 adsl-61-168-141.bna.bellsouth.net - - + + // FIXME: can't move. -- needs board reload... + + /* + + opponent matchlength score (your points first) + **gnu 1 0 - 0 + *blah 1 0 - 0 + kraut 1 0 - 0 + + logged in and ready ** + logged in * + otherwise " " + + */ + + pat[ConLeav] = QRegExp("^Type 'join' if you want to play the next game, type 'leave' if you don't\\.$"); + pat[GreedyY] = QRegExp("^\\*\\* Will use automatic greedy bearoffs\\."); + pat[GreedyN] = QRegExp("^\\*\\* Won't use automatic greedy bearoffs\\."); + pat[BegBlnd] = QRegExp("^\\*\\* You blind "); + pat[EndBlnd] = QRegExp("^\\*\\* You unblind "); + pat[MatchB3] = QRegExp("^\\*\\* You are now playing a [0-9]+ point match with "); + pat[MatchB4] = QRegExp("^\\*\\* You are now playing an unlimited match with "); + pat[RejCont] = QRegExp("^You reject\\. The game continues\\."); + pat[AcptWin] = QRegExp("^You accept and win "); + pat[YouGive] = QRegExp("^You give up\\."); + pat[DoubleY] = QRegExp("^\\*\\* You will be asked if you want to double\\."); + pat[DoubleN] = QRegExp("^\\*\\* You won't be asked if you want to double\\."); +} + +/* + * Parse an incoming line and notify all interested parties - first match + * decides. + */ +void KBgEngineFIBS::handleServerData(QString &line) +{ + QString rawline = line; // contains the line before it is HTML'fied + + /* + * Fix-up any HTML-like tags in the line + */ + line.replace(pat[HTML_lt], "<"); + line.replace(pat[HTML_gt], ">"); + + /* + * FIBS sometimes sends tabs, where it should send 8 spaces... + */ + line.replace(pat[TabChar], " "); + + switch (rxStatus) { + + case RxConnect: + handleMessageConnect(line, rawline); + break; + + case RxMotd: + handleMessageMotd(line); + return; + + case RxWhois: + handleMessageWhois(line); + break; + + case RxRating: + handleMessageRating(line); + break; + + case RxNewLogin: + handleMessageNewLogin(line); + break; + + case RxIgnore: + /* + * Ignore _ALL_ incoming strings - this is needed during the + * login phase, when the message box is open. + */ + break; + + case RxGoodbye: + /* + * Receive the logout sequence. The string will be flushed by the + * disconnectFIBS() callback + */ + rxCollect += QString("
") + line + "

"; + break; + + case RxNormal: + handleMessageNormal(line, rawline); + break; + + default: + /* + * This is a serious problem - latin1() is fine since the line comes from FIBS. + */ + std::cerr << "PROBLEM in KBgEngineFIBS::handleServerData: " << line.latin1() << std::endl; + } +} + +/* + * Handle messages during the RxWhois state + */ +void KBgEngineFIBS::handleMessageWhois(const QString &line) +{ + rxCollect += "
    " + line; + if (line.contains(pat[WhoisE1]) || line.contains(pat[WhoisE2])) { + rxStatus = RxNormal; + emit infoText("" + rxCollect + "
"); + } +} + +/* + * Handle messages during the RxRating state + */ +void KBgEngineFIBS::handleMessageRating(const QString &line) +{ + rxCollect += "
" + line; + if (line.contains(pat[EndRate]) && ++rxCount == 2) { + emit infoText("" + rxCollect + "
"); + rxStatus = RxNormal; + } +} + +/* + * Handle messages during the RxMotd state + */ +void KBgEngineFIBS::handleMessageMotd(const QString &line) +{ + if (line.contains(pat[MotdEnd])) { + rxStatus = RxNormal; + emit infoText("
" + rxCollect + "
"); + /* + * just to be on the safe side, we set the value of boardstyle. + * we do it here, since this is reasonably late in the login + * procedure + */ + emit serverString("set boardstyle 3"); + } else { + QString tline = line; + tline.replace(pat[BoxHori], "

"); + tline.replace(pat[BoxVer1], ""); + tline.replace(pat[BoxVer2], ""); + rxCollect += "
" + tline; + } +} + +/* + * Handle messages during the RxConnect state + */ +void KBgEngineFIBS::handleMessageConnect(const QString &line, const QString &rawline) +{ + /* + * Two possibilities: either we are logged in or we sent bad password/login + */ + if (line.contains("login:")) { + /* + * This can only happen if the password/login is wrong. + */ + if (rxCollect.isEmpty()) { + rxStatus = RxIgnore; + int ret = KMessageBox::warningContinueCancel + ((QWidget *)parent(), i18n("There was a problem with " + "your login and password. " + "You can reenter\n" + "your login and password and " + "try to reconnect."), + i18n("Wrong Login/Password"), + i18n("Reconnect")); + if (ret == KMessageBox::Continue) { + infoFIBS[FIBSUser] = ""; + infoFIBS[FIBSPswd] = ""; + login = true; + connectFIBS(); // will reset the rxStatus + } else { + rxStatus = RxConnect; + emit serverString(""); + emit serverString(""); + } + return; + } + emit infoText("
" + rxCollect + "

"); + rxCollect = ""; + return; + } + + /* + * Ok, we are logged in! Now receive personal information. These + * are completely useless but what the heck. + */ + if (line.contains(pat[Welcome])) { + char p[3][256]; + time_t tmp; + // Using latin1() is okay, since the string comes from FIBS. + int words = sscanf (line.latin1(), "%255s%255s%li%255s", p[0], p[1], &tmp, p[2]); + if (words >= 4) { + QDateTime d; d.setTime_t(tmp); + QString text = i18n("%1, last logged in from %2 at %3.").arg(p[1]).arg(p[2]).arg(d.toString()); + emit infoText("

" + text); + playerlist->setName(p[1]); + } + return; + } + + /* + * Initial parsing of user options and making sure that settings needed + * by us are at the correct value. We use and ignore values according + * to the following list: + * + * p[ 0] - CLIP_OWN_INFO + * p[ 1] - name -- IGNORE + * OptAllowPip + * n[ 0] - autoboard -- IGNORE + * OptAutoDouble + * OptAutoMove + * n[ 1] - away -- IGNORE + * n[ 2] - bell -- IGNORE + * OptCrawford + * n[ 3] - double -- IGNORE + * n[ 4] - expierience -- IGNORE + * OptGreedy + * n[ 6] - moreboards -- IGNORE and set to YES + * OptMoves + * n[ 8] - notify -- IGNORE and set to YES + * rating - rating -- IGNORE + * OptRatings + * OptReady + * n[10] - redoubles -- IGNORE + * n[11] - report -- IGNORE and set to YES + * OptSilent + * p[3] - timezone + * + */ + if (line.contains(pat[OwnInfo])) { + + rxStatus = RxNormal; + + int fibsOptions[NumFIBSOpt]; + + char p[3][256]; + int n[12]; + double rating; + + // Using latin1() is okay, since the string comes from FIBS. + int words = sscanf (line.latin1(), "%255s%255s%i%i%i%i%i%i%i%i%i%i%i%i%i%lf%i%i%i%i%i%255s", + p[0], p[1], + &fibsOptions[OptAllowPip], + &n[0], + &fibsOptions[OptDouble], + &fibsOptions[OptAutoMove], // equivalent to OptDouble, can be ignored + &n[1], &n[2], + &fibsOptions[OptCrawford], + &n[3], &n[4], + &fibsOptions[OptGreedy], + &n[6], + &fibsOptions[OptMoves], + &n[8], + &rating, + &fibsOptions[OptRatings], + &fibsOptions[OptReady], + &n[10], &n[11], + &fibsOptions[OptSilent], + p[2]); + + if (words >= 22 && n[6] != 1) { + /* + * need to get boards after new dice have arrived + */ + emit infoText("" + i18n("The moreboards toggle has been set.") + ""); + emit serverString("toggle moreboards"); + } + if (words >= 22 && n[8] != 1) { + /* + * need to know who logs out + */ + emit infoText("" + i18n("The notify toggle has been set.") + ""); + emit serverString("toggle notify"); + } + if (words >= 22 && n[11] != 1) { + /* + * want to know who starts playing games + */ + emit infoText("" + i18n("The report toggle has been set.") + ""); + emit serverString("toggle report"); + } + + /* + * Set the correct toggles in the options menu + */ + fibsOpt[OptReady]->setChecked(fibsOptions[OptReady]); + fibsOpt[OptDouble]->setChecked(!fibsOptions[OptDouble]); + fibsOpt[OptRatings]->setChecked(fibsOptions[OptRatings]); + + return; + } + + /* + * The beginning of a new login procedure starts starts here + */ + if (line.contains(pat[OneName])) { + rxStatus = RxNewLogin; + emit infoText(QString("") + rxCollect + ""); + rxCollect = ""; + QString tmp = rawline; + handleServerData(tmp); + return; + } + + /* + * Still in the middle of the login sequence, still collecting information + */ + rxCollect += "
" + line; +} + +/* + * Handle messages during the RxNewLogin state + */ +void KBgEngineFIBS::handleMessageNewLogin(const QString &line) +{ + /* + * Request the new login + */ + if (line.contains(pat[OneName])) { + emit serverString(QString("name ") + infoFIBS[FIBSUser]); + return; + } + /* + * Ooops, user name already exists + */ + if (line.contains(pat[OthrNam])) { + QString text = i18n("The selected login is alreay in use! Please select another one."); + bool ret, first = true; + QString msg; + + do { + msg = (KLineEditDlg::getText(text, infoFIBS[FIBSUser], &ret, + (QWidget *)parent())).stripWhiteSpace(); + if (first) { + text += i18n("\n\nThe login may not contain spaces or colons!"); + first = false; + } + } while (msg.contains(' ') || msg.contains(':')); + + if (ret) { + infoFIBS[FIBSUser] = msg; + emit serverString("name " + msg); + } else + emit serverString("bye"); + + return; + } + /* + * first time we send the password + */ + if (line.contains(pat[YourNam])) { + emit serverString(infoFIBS[FIBSPswd]); + return; + } + /* + * second time we send the password + */ + if (line.contains(pat[GivePwd])) { + emit serverString(infoFIBS[FIBSPswd]); + return; + } + /* + * at this point we are done creating the account + */ + if (line.contains(pat[RetypeP])) { + + QString text = i18n("Your account has been created. Your new login is %1. To fully activate " + "this account, I will now close the connection. Once you reconnect, you can start " + "playing backgammon on FIBS.").arg(infoFIBS[FIBSUser]); + emit infoText("

" + text + "

"); + emit serverString("bye"); + rxStatus = RxNormal; + rxCollect = ""; + return; + } + return; +} + +/* + * Handle all normal messages - during the RxNormal state + */ +void KBgEngineFIBS::handleMessageNormal(QString &line, QString &rawline) +{ + + // - ignored ---------------------------------------------------------------------- + + /* + * For now, the waves are ignored. They should probably go into + * the chat window -- but only optional + */ + if (line.contains(pat[OneWave]) || line.contains(pat[TwoWave]) || line.contains(pat[YouWave])) { + + return; + } + + /* + * These messages used to go into the games window. If KBackgammon + * ever gets a games window, they should be in there. For now, they + * are ignored. + */ + else if (line.contains(pat[GameBG1]) || line.contains(pat[GameBG2]) || line.contains(pat[GameRE1]) || + line.contains(pat[GameRE2]) || line.contains(pat[GameEnd])) { + + return; + } + + /* + * Artefact caused by the login test procedure utilized. + */ + else if (line.contains(pat[NoLogin])) { + + return; + } + + /* + * Connection keep-alive response + */ + else if (line.contains(pat[KeepAlv])) { + + return; + } + + // -------------------------------------------------------------------------------- + + /* + * Chat and personal messages - note that the chat window sends these messages + * back to us so we can display them if the user wants that. + */ + else if (line.contains(pat[ChatSay]) || line.contains(pat[ChatSht]) || line.contains(pat[ChatWis]) || + line.contains(pat[ChatKib]) || line.contains(pat[SelfSay]) || line.contains(pat[SelfSht]) || + line.contains(pat[SelfWis]) || line.contains(pat[SelfKib]) || line.contains(pat[SelfSlf]) || + line.contains(pat[MsgPers]) || line.contains(pat[MsgDeli]) || line.contains(pat[MsgSave])) { + + emit chatMessage(line); + return; + } + + // -------------------------------------------------------------------------------- + + /* + * Beginning of games. In all these cases we are playing and not watching. + */ + else if (line.contains(pat[MatchB1]) || line.contains(pat[MatchB2])) { + + if (useAutoMsg[MsgBeg] && !autoMsg[MsgBeg].stripWhiteSpace().isEmpty()) + emit serverString("kibitz " + autoMsg[MsgBeg]); + } + else if (line.contains(pat[MatchB3]) || line.contains(pat[MatchB4])) { + + if (useAutoMsg[MsgBeg] && !autoMsg[MsgBeg].stripWhiteSpace().isEmpty()) + emit serverString("kibitz " + autoMsg[MsgBeg]); + line = "" + line + ""; + } + + // -------------------------------------------------------------------------------- + + /* + * The help should be handled separately. A fairly complete implementation of a + * help parsing can be found in KFibs. + */ + else if (line.contains(pat[HelpTxt])) { + + // do nothing + } + + // -------------------------------------------------------------------------------- + + /* + * Simple cases without the need for many comments... + */ + else if (line.contains(pat[RawBord])) { + + /* + * Save the board string and create a new game state + */ + KBgStatus *st = new KBgStatus(currBoard = rawline); + + /* + * Save important state data and stop the timeout + */ + ct->stop(); + undoCounter = 0; + + pname[US ] = st->player(US); + pname[THEM] = st->player(THEM); + + playing = (QString("You") == pname[US]); + + toMove = st->moves(); + + /* + * Update the caption string + */ + if (st->turn() < 0) + caption = i18n("%1 (%2) vs. %3 (%4) - game over").arg(pname[US]). + arg(st->points(US)).arg(pname[THEM]).arg(st->points(THEM)); + else if (st->length() < 0) + caption = i18n("%1 (%2) vs. %3 (%4) - unlimited match").arg(pname[US]). + arg(st->points(US)).arg(pname[THEM]).arg(st->points(THEM)); + else + caption = i18n("%1 (%2) vs. %3 (%4) - %5 point match").arg(pname[US]). + arg(st->points(US)).arg(pname[THEM]).arg(st->points(THEM)). + arg(st->length()); + + emit statText(caption); + + /* + * Emit information and drop the state object + */ + emit allowMoving(playing && (st->turn() == US)); + emit newState(*st); + + delete st; + + /* + * Set the actions correctly + */ + emit allowCommand(Load, true ); + emit allowCommand(Undo, false); + emit allowCommand(Redo, false); + emit allowCommand(Done, false); + + return; + } + else if (line.contains(pat[PlsMove]) || line.contains(pat[YouMove])) { + + KNotifyClient::event("move", i18n("Please make your move")); + + } + + // -------------------------------------------------------------------------------- + + /* + * Being away and coming back + */ + else if (line.contains(pat[YouAway])) { + + emit changePlayerStatus(infoFIBS[FIBSUser], KFibsPlayerList::Away, true); + actBack->setEnabled(true); + line += "
  
" + i18n("(or use the corresponding menu entry to join the match)"); + } + else if (line.contains(pat[YouBack])) { + + emit changePlayerStatus(infoFIBS[FIBSUser], KFibsPlayerList::Away, false); + actBack->setEnabled(false); + actAway->setEnabled(true); + } + + // -------------------------------------------------------------------------------- + + /* + * Catch the response of the user responding to double or resign + */ + else if (line.contains(pat[YouGive]) || line.contains(pat[RejCont]) || line.contains(pat[AcptWin])) { + + actAccept->setEnabled(false); + actReject->setEnabled(false); + } + + // -------------------------------------------------------------------------------- + + /* + * Catch the responses to newly set toggles + */ + else if (line.contains(pat[GreedyY]) || line.contains(pat[GreedyN])) { + + fibsOpt[OptGreedy]->setChecked(line.contains(pat[GreedyY])); + line = "" + line + ""; + } + else if (line.contains(pat[DoubleY]) || line.contains(pat[DoubleN])) { + + fibsOpt[OptDouble]->setChecked(line.contains(pat[DoubleY])); + line = "" + line + ""; + } + + else if (line.contains(pat[RatingY]) || line.contains(pat[RatingN])) { + + fibsOpt[OptRatings]->setChecked(line.contains(pat[RatingY])); + line = "" + line + ""; + } + + // -------------------------------------------------------------------------------- + + /* + * It's our turn to roll or double + */ + else if (line.contains(pat[YouTurn]) || line.contains(pat[YouRoll])) { + + emit allowCommand(Cube, playing); + emit allowCommand(Roll, playing); + + emit statText(caption); // force a pip count recomputation by the board + + KNotifyClient::event("roll or double", i18n("It's your turn to roll the dice or double the cube")); + } + + // -------------------------------------------------------------------------------- + + /* + * Got an invitation for a match + */ + else if (line.contains(pat[Invite0]) || line.contains(pat[Invite2]) || line.contains(pat[Invite3])) { + + rxCollect = rawline.left(rawline.find(' ')); + emit serverString("rawwho " + rxCollect); + + if (line.contains(pat[Invite0])) { + rawline.replace(pat[Invite1], ""); + rawline = rxCollect + " "+ rawline.left(rawline.find(' ')); + } else if (line.contains(pat[Invite2])) { + rawline = rxCollect + " r"; + } else if (line.contains(pat[Invite3])) { + invitations += rxCollect + " u"; + } + invitations += rawline; + return; // will be printed once the rawwho is received + } + + // - rx status changes ------------------------------------------------------------ + + else if (line.contains(pat[WhoisBG])) { + rxStatus = RxWhois; + rxCollect = QString("
") + line + ""; + return; + } + else if (line.contains(pat[MotdBeg])) { + rxStatus = RxMotd; + rxCollect = ""; + return; + } + else if (line.contains(pat[BegRate])) { + rxStatus = RxRating; + rxCount = 0; + rxCollect = "
" + line; + return; + } + else if (line.contains(pat[Goodbye])) { + rxStatus = RxGoodbye; + rxCollect = "


"; + handleServerData(rawline); // danger: recursion! + return; + } + + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + + /* + * Continue a mutli game match? We have to either leave or continue + */ + else if (line.contains(pat[ConLeav])) { + actConti->setEnabled(true); + actLeave->setEnabled(true); + line.append("
  
" + i18n("(or use the corresponding menu " + "entry to leave or continue the match)")); + } + /* + * Beginning and end of user updates + */ + else if (line.contains(pat[WhoInfo])) { + rawline.replace(pat[WhoInfo], ""); + if (rawline.contains(QRegExp("^" + infoFIBS[FIBSUser] + " "))) { + int ready; + // Using latin1() is fine, since the string is coming from FIBS. + sscanf(rawline.latin1(), "%*s %*s %*s %i %*s %*s %*s %*s %*s %*s %*s %*s", &ready); + fibsOpt[OptReady]->setChecked(ready); + } + emit fibsWhoInfo(rawline); + return; + } + else if (line.contains(pat[WhoEnde])) { + emit fibsWhoEnd(); + return; + } + /* + * This message is ignored. The instruction is given elsewhere (and slightly + * delayed in the flow of time). + */ + if (line.contains(pat[TypJoin])) { + return; + } + /* + * Watching other players + */ + else if (line.contains(pat[BegWtch])) { + emit allowCommand(Load, true); + rawline.replace(pat[BegWtch], ""); + rawline.truncate(rawline.length()-1); + emit fibsStartNewGame(rawline); + load(); + } + else if (line.contains(pat[EndWtch])) { + emit gameOver(); + } + /* + * Blinding of players, the actual blind is handled by + * the player list + */ + else if (line.contains(pat[BegBlnd])) { + rawline.replace(pat[BegBlnd], ""); + rawline.truncate(rawline.length()-1); + emit changePlayerStatus(rawline, KFibsPlayerList::Blind, true); + line = "" + line + ""; + } + else if (line.contains(pat[EndBlnd])) { + rawline.replace(pat[EndBlnd], ""); + rawline.truncate(rawline.length()-1); + emit changePlayerStatus(rawline, KFibsPlayerList::Blind, false); + line = "" + line + ""; + } + /* + * Starting or reloading games or matches + */ + else if (line.contains(pat[BegGame])) { + rawline.replace(pat[BegGame], ""); + rawline.truncate(rawline.length()-1); + emit fibsStartNewGame(rawline); + fibsOpt[OptDouble]->setChecked(true); + fibsOpt[OptGreedy]->setChecked(false); + actConti->setEnabled(false); + actLeave->setEnabled(false); + } + else if (line.contains(pat[Reload1])) { + rawline.replace(pat[Reload1], ""); + rawline = rawline.left(rawline.find(' ')); + rawline.truncate(rawline.length()-1); + emit fibsStartNewGame(rawline); + fibsOpt[OptDouble]->setChecked(true); + fibsOpt[OptGreedy]->setChecked(false); + actConti->setEnabled(false); + actLeave->setEnabled(false); + load(); + } + else if (line.contains(pat[Reload2])) { + rawline.replace(pat[Reload2], ""); + emit fibsStartNewGame(rawline); + fibsOpt[OptDouble]->setChecked(true); + fibsOpt[OptGreedy]->setChecked(false); + actConti->setEnabled(false); + actLeave->setEnabled(false); + load(); + } + /* + * Opponent offered resignation or the cube. We have to accept + * or reject the offer. + */ + else if (line.contains(pat[RejAcpt])) { + actAccept->setEnabled(true); + actReject->setEnabled(true); + line += "
  
" + i18n("(or use the corresponding menu " + "entry to accept or reject the offer)"); + } + /* + * This is strange: FIBS seems to not send a newline at the + * end of this pattern. So we work around that. + */ + else if (line.contains(pat[YouAcpt])) { + actAccept->setEnabled(false); + actReject->setEnabled(false); + rawline.replace(pat[YouAcpt], ""); + line.truncate(line.length()-rawline.length()); + if (!rawline.stripWhiteSpace().isEmpty()) { + handleServerData(rawline); + } + } + /* + * Ending of games + */ + else if (line.contains(pat[EndLose])) { + if (playing) { + KNotifyClient::event("game over l", i18n("Sorry, you lost the game.")); + if (useAutoMsg[MsgLos] && !autoMsg[MsgLos].stripWhiteSpace().isEmpty()) + emit serverString(QString("tell ") + pname[THEM] + " " + autoMsg[MsgLos]); + } + emit gameOver(); + } + else if (line.contains(pat[EndVict])) { + if (playing) { + KNotifyClient::event("game over w", i18n("Congratulations, you won the game!")); + if (useAutoMsg[MsgWin] && !autoMsg[MsgWin].stripWhiteSpace().isEmpty()) + emit serverString(QString("tell ") + pname[THEM] + " " + autoMsg[MsgWin]); + } + emit gameOver(); + } + else if (line.contains(pat[GameSav])) { + emit gameOver(); + } + /* + * User logs out. This has to be signalled to the player + * list. Get the true user names by working on the rawline. + */ + else if (line.contains(pat[UserLot])) { + rawline.replace(pat[UserLot], ""); + emit fibsLogout(rawline.left(rawline.find(' '))); + return; + } + /* + * Emit the name of the newly logged in user. + */ + else if (line.contains(pat[UserLin])) { + rawline.replace(pat[UserLin], ""); + emit fibsLogin(rawline.left(rawline.find(' '))); + return; + } + /* + * Special attention has to be paid to the proper setting of + * the 'boardstyle' variable, since we will not be able to display + * the board properly without it. + */ + else if (line.contains(pat[BoardSY])) { + // ignored + return; + } + else if (line.contains(pat[BoardSN])) { + emit serverString("set boardstyle 3"); + emit infoText(QString("
") + + i18n("You should never set the 'boardstyle' variable " + "by hand! It is vital for proper functioning of " + "this program that it remains set to 3. It has " + "been reset for you.") + + "
"); + return; + } + /* + * This is the final fall through: if the line started with ** and + * hasn't been processed, make it red, since it is a server resp. + * to something we just did. + */ + else if (line.contains(pat[TwoStar])) { + line = "" + line + ""; + } + + // -------------------------------------------------------------------------------- + + /* + * Print whatever part of the line made it here + */ + emit infoText(line); +} + +// EOF + + +// == constructor, destructor and setup ======================================== + +/* + * Constructor + */ +KBgEngineFIBS::KBgEngineFIBS(QWidget *parent, QString *name, QPopupMenu *pmenu) + : KBgEngine(parent, name, pmenu) +{ + /* + * No connection, not playing, ready for login + */ + connection = new QSocket(parent, "fibs connection"); + playing = false; + login = true; + + connect(connection, SIGNAL(hostFound()), this, SLOT(hostFound())); + connect(connection, SIGNAL(connected()), this, SLOT(connected())); + connect(connection, SIGNAL(error(int)), this, SLOT(connError(int))); + connect(connection, SIGNAL(connectionClosed()), this, SLOT(connectionClosed())); + connect(connection, SIGNAL(delayedCloseFinished()), this, SLOT(connectionClosed())); + connect(connection, SIGNAL(readyRead()), this, SLOT(readData())); + + connect(this, SIGNAL(serverString(const QString &)), this, SLOT(sendData(const QString &))); + + /* + * No invitation dialog + */ + invitationDlg = 0; + + connect(this, SIGNAL(fibsWhoInfo(const QString &)), this, SLOT(changeJoin(const QString &))); + connect(this, SIGNAL(fibsLogout (const QString &)), this, SLOT(cancelJoin(const QString &))); + connect(this, SIGNAL(gameOver()), this, SLOT(endGame())); + + /* + * Creating, initializing and connecting the player list + */ + playerlist = new KFibsPlayerList(0, "fibs player list"); + + connect(this, SIGNAL(fibsWhoInfo(const QString &)), playerlist, SLOT(changePlayer(const QString &))); + connect(this, SIGNAL(fibsLogout (const QString &)), playerlist, SLOT(deletePlayer(const QString &))); + connect(this, SIGNAL(fibsWhoEnd()), playerlist, SLOT(stopUpdate())); + connect(this, SIGNAL(fibsConnectionClosed()), playerlist, SLOT(stopUpdate())); + connect(this, SIGNAL(changePlayerStatus(const QString &, int, bool)), + playerlist, SLOT(changePlayerStatus(const QString &, int, bool))); + connect(playerlist, SIGNAL(fibsCommand(const QString &)), this, SLOT(handleCommand(const QString &))); + connect(playerlist, SIGNAL(fibsInvite(const QString &)), this, SLOT(fibsRequestInvitation(const QString &))); + + /* + * Create, initialize and connect the chat window + */ + chatWindow = new KBgChat(0, "chat window"); + + connect(this, SIGNAL(chatMessage(const QString &)), chatWindow, SLOT(handleData(const QString &))); + connect(this, SIGNAL(fibsStartNewGame(const QString &)), chatWindow, SLOT(startGame(const QString &))); + connect(this, SIGNAL(gameOver()), chatWindow, SLOT(endGame())); + connect(this, SIGNAL(fibsLogout (const QString &)), chatWindow, SLOT(deletePlayer(const QString &))); + connect(chatWindow, SIGNAL(fibsCommand(const QString &)), this, SLOT(handleCommand(const QString &))); + connect(chatWindow, SIGNAL(fibsRequestInvitation(const QString &)), this, SLOT(fibsRequestInvitation(const QString &))); + connect(chatWindow, SIGNAL(personalMessage(const QString &)), this, SLOT(personalMessage(const QString &))); + connect(playerlist, SIGNAL(fibsTalk(const QString &)), chatWindow, SLOT(fibsTalk(const QString &))); + + /* + * Creating, initializing and connecting the menu + * ---------------------------------------------- + */ + respMenu = new QPopupMenu(); + joinMenu = new QPopupMenu(); + cmdMenu = new QPopupMenu(); + optsMenu = new QPopupMenu(); + + /* + * Initialize the FIBS submenu - this is also put in the play menu + */ + conAction = new KAction(i18n("&Connect"), 0, this, SLOT( connectFIBS()), this); + newAction = new KAction(i18n("New Account"), 0, this, SLOT( newAccount()), this); + disAction = new KAction(i18n("&Disconnect"), 0, this, SLOT(disconnectFIBS()), this); + + conAction->setEnabled(true ); conAction->plug(menu); + disAction->setEnabled(false); disAction->plug(menu); + newAction->setEnabled(true ); newAction->plug(menu); + + menu->insertSeparator(); + + (invAction = new KAction(i18n("&Invite..."), 0, this, SLOT(inviteDialog()), this))->plug(menu); + + /* + * Create and fill the response menu. This is for all these: type this or + * that messages from FIBS. + */ + cmdMenuID = menu->insertItem(i18n("&Commands"), cmdMenu); { + + (actAway = new KAction(i18n("Away"), 0, this, SLOT(away()), this))->plug(cmdMenu); + (actBack = new KAction(i18n("Back"), 0, this, SLOT(back()), this))->plug(cmdMenu); + + actAway->setEnabled(true); + actBack->setEnabled(false); + } + + /* + * Create the server side options. This is preliminary and needs more work. + * The available options are skewed, since they refelect the needs of the + * author. Contact jens@hoefkens.com if your favorite option is missing. + */ + optsMenuID = menu->insertItem(i18n("&Options"), optsMenu); { + + for (int i = 0; i < NumFIBSOpt; i++) + fibsOpt[i] = 0; + + fibsOpt[OptReady] = new KToggleAction(i18n("Ready to Play"), + 0, this, SLOT(toggle_ready()), this); + fibsOpt[OptRatings] = new KToggleAction(i18n("Show Rating Computations"), + 0, this, SLOT(toggle_ratings()), this); + fibsOpt[OptRatings]->setCheckedState(i18n("Hide Rating Computations")); + fibsOpt[OptGreedy] = new KToggleAction(i18n("Greedy Bearoffs"), + 0, this, SLOT(toggle_greedy()), this); + fibsOpt[OptDouble] = new KToggleAction(i18n("Ask for Doubles"), + 0, this, SLOT(toggle_double()), this); + + for (int i = 0; i < NumFIBSOpt; i++) + if (fibsOpt[i]) + fibsOpt[i]->plug(optsMenu); + + } + + /* + * Create and fill the response menu. This is for all these: type this or + * that messages from FIBS. + */ + respMenuID = menu->insertItem(i18n("&Response"), respMenu); { + + (actAccept = new KAction(i18n("Accept"), 0, this, SLOT(accept()), this))->plug(respMenu); + (actReject = new KAction(i18n("Reject"), 0, this, SLOT(reject()), this))->plug(respMenu); + + actAccept->setEnabled(false); + actReject->setEnabled(false); + + respMenu->insertSeparator(); + + (actConti = new KAction(i18n("Join"), 0, this, SLOT(match_conti()), this))->plug(respMenu); + (actLeave = new KAction(i18n("Leave"), 0, this, SLOT(match_leave()), this))->plug(respMenu); + + actConti->setEnabled(false); + actLeave->setEnabled(false); + } + + /* + * Create the join menu and do not fill it (this happens at first + * action setup. + */ + joinMenuID = menu->insertItem(i18n("&Join"), joinMenu); { + numJoin = -1; + + actJoin[0] = new KAction("", 0, this, SLOT(join_0()), this); + actJoin[1] = new KAction("", 0, this, SLOT(join_1()), this); + actJoin[2] = new KAction("", 0, this, SLOT(join_2()), this); + actJoin[3] = new KAction("", 0, this, SLOT(join_3()), this); + actJoin[4] = new KAction("", 0, this, SLOT(join_4()), this); + actJoin[5] = new KAction("", 0, this, SLOT(join_5()), this); + actJoin[6] = new KAction("", 0, this, SLOT(join_6()), this); + actJoin[7] = new KAction("", 0, this, SLOT(join_7()), this); + } + + menu->setItemEnabled(joinMenuID, false); + menu->setItemEnabled( cmdMenuID, false); + menu->setItemEnabled(respMenuID, false); + menu->setItemEnabled(optsMenuID, false); + + /* + * Continue with the FIBS menu + */ + menu->insertSeparator(); + + (listAct = new KToggleAction(i18n("&Player List"), 0, this, SLOT(showList()), this))->plug(menu); + (chatAct = new KToggleAction(i18n("&Chat"), 0, this, SLOT(showChat()), this))->plug(menu); + + connect(playerlist, SIGNAL(windowVisible(bool)), listAct, SLOT(setChecked(bool))); + connect(chatWindow, SIGNAL(windowVisible(bool)), chatAct, SLOT(setChecked(bool))); + + /* + * Create message IDs. This sets up a lot of regular expressions. + */ + initPattern(); + + /* + * Restore old settings + */ + readConfig(); + + // FIXME: open the child windows in start() and not here + + /* + * Update the menu actions + */ + listAct->setChecked(playerlist->isVisible()); + chatAct->setChecked(chatWindow->isVisible()); + + /* + * Initialize the keepalive timer FIXME: make this a setting + */ + keepalive = true; + + // FIXME: move the start to connect... + + keepaliveTimer = new QTimer(this); + connect(keepaliveTimer, SIGNAL(timeout()), this, SLOT(keepAlive())); + keepaliveTimer->start(1200000); +} + +/* + * Destructor deletes child objects if necessary + */ +KBgEngineFIBS::~KBgEngineFIBS() +{ + delete joinMenu; + delete respMenu; + delete cmdMenu; + delete optsMenu; + + delete connection; + delete invitationDlg; + + delete playerlist; + delete chatWindow; +} + + diff --git a/kbackgammon/engines/fibs/kbgfibs.h b/kbackgammon/engines/fibs/kbgfibs.h new file mode 100644 index 00000000..1c14e0f3 --- /dev/null +++ b/kbackgammon/engines/fibs/kbgfibs.h @@ -0,0 +1,479 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + + +#ifndef __KBGFIBS_H +#define __KBGFIBS_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "kplayerlist.h" +#include "kbgfibschat.h" +#include "kbginvite.h" // TODO + +#include +#include +#include + +#include + +class QTimer; +class QSocket; +class QPopupMenu; +class QCheckBox; + +class KAction; +class KToggleAction; + +/** + * + * Special backgammon engine for games on the First Internet Backgammon Server + * + * @short The FIBS backgammon engine + * @author Jens Hoefkens + * + */ +class KBgEngineFIBS : public KBgEngine +{ + Q_OBJECT + +public: + + /** + * Constructor + */ + KBgEngineFIBS(QWidget *parent = 0, QString *name = 0, QPopupMenu *pmenu = 0); + + /** + * Destructor + */ + virtual ~KBgEngineFIBS(); + + /** + * Fills the engine-specific page into the notebook + */ + virtual void getSetupPages(KDialogBase *nb); + + virtual void setupOk(); + virtual void setupDefault(); + virtual void setupCancel(); + + /* + * Check with the engine if we can quit. This may require user + * interaction. + */ + virtual bool queryClose(); + + /** + * About to be closed. Let the engine exit properly. + */ + virtual bool queryExit(); + + virtual void start(); + + +public slots: + + /** + * Read and save user settings to the config file + */ + virtual void readConfig(); + virtual void saveConfig(); + + /** + * Roll dice for the player w + */ + virtual void rollDice(const int w); + + /** + * Double the cube of player w + */ + virtual void doubleCube(const int w); + + /** + * A move has been made on the board - see the board class + * for the format of the string s + */ + virtual void handleMove(QString *s); + + /** + * Undo the last move + */ + virtual void undo(); + + /** + * Redo the last move + */ + virtual void redo(); + + /** + * Commit a move + */ + virtual void done(); + + + // ########################################################################### + // + // + // + // TODO TODO TODO TODO TODO TODO TODO + // + // + // + // ########################################################################### + + /* + * Process the string cmd + */ + void handleCommand(const QString &cmd); + + void fibsRequestInvitation(const QString &player); + + void personalMessage(const QString &msg); + + + + /* + * Local configuration handling + */ + + void keepAlive(); + +signals: + + void serverString(const QString &s); + + void fibsWhoInfo(const QString &line); + void fibsWhoEnd(); + void fibsLogout(const QString &p); + void fibsLogin(const QString &p); + + void fibsConnectionClosed(); + + void changePlayerStatus(const QString &, int, bool); + + void chatMessage(const QString &msg); + + void fibsStartNewGame(const QString &msg); + void gameOver(); + +protected slots: + + void invitationDone(); + void inviteDialog(); + void showList(); + void showChat(); + + void endGame(); + +private: + + QTimer *keepaliveTimer; + + QString pname[2]; + + QString currBoard, caption; + + //KBgStatus *currBoard + //KBgFIBSBoard *boardHandler; + + QStringList invitations; + + /* + * special menu entries + */ + int respMenuID, cmdMenuID, joinMenuID, optsMenuID; + QPopupMenu *respMenu, *cmdMenu, *joinMenu, *optsMenu; + + /* + * child windows + */ + KFibsPlayerList *playerlist; + KBgChat *chatWindow; + KBgInvite *invitationDlg; + + /* + * Other stuff + */ + QString lastMove; + int toMove; + + QString lastAway; + bool playing; + bool redoPossible; + int undoCounter; + + KAction *conAction, *disAction, *newAction, *invAction; + + KAction *actAccept, *actReject, *actConti, *actLeave, *actAway, *actBack; + + KToggleAction *chatAct, *listAct; + + + // ########################################################################### + // + // + // + // DONE DONE DONE DONE DONE DONE DONE + // + // + // + // ########################################################################### + +private: + + /** + * Actions for responding to invitations. numJoin is he current + * number of active actions. The max. number of pending invitations + * is eight and this is hardcoded in a lot of places (not the least + * of which are the slots join_N(). + */ + KAction *actJoin[8]; + int numJoin; + +protected slots: + + /** + * Handle rawwho information for the purposes of the invitation + * submenu and the join entries + */ + void changeJoin(const QString &info); + + /** + * A player will be removed from the menu of pending invitations + * if necessary. + */ + void cancelJoin(const QString &info); + + /** + * We have up to 8 names in the join menu. They are the + * players that invited us to play games. Each action + * has its own slot and all slots call the common backend + * join(). + */ + void join(const QString &msg); + + void join_0(); + void join_1(); + void join_2(); + void join_3(); + void join_4(); + void join_5(); + void join_6(); + void join_7(); + + /** + * Simple slots that toggle FIBS server-side settings. The + * names of the functions reflect the name of the toggle on + * FIBS. + */ + void toggle_greedy(); + void toggle_ready(); + void toggle_double(); + void toggle_ratings(); + +private: + + /** + * Toggle actions for the FIBS server-side settings + */ + enum FIBSOpt {OptReady, OptGreedy, OptDouble, + OptAllowPip, OptAutoMove, OptCrawford, OptSilent, OptRatings, OptMoves, NumFIBSOpt}; + KToggleAction *fibsOpt[NumFIBSOpt]; + +public slots: + + /* + * Connection handling + * ------------------- + */ + + // initiate asynchronous connection establishment + void connectFIBS(); + + // take the connection down + void disconnectFIBS(); + + // create a new account and connect + void newAccount(); + + // called when the connection is down + void connectionClosed(); + + // the hostname has been resolved + void hostFound(); + + // a connection error occurred + void connError(int f); + + // connection has been established + void connected(); + + // data can be read from the socket + void readData(); + + // send the string s to the server + void sendData(const QString &s); + +protected: + + // get the connection parameters + bool queryConnection(const bool newlogin); + +private: + + // actual connection object + QSocket *connection; + + // flag if we have login information or new account + bool login; + +protected slots: + + /* + * FIBS command slots + * ------------------ + */ + + // go away and leave a message + void away(); + + // come back after being away + void back(); + + // roll dice + virtual void roll(); + + // double the cube + virtual void cube(); + + // reload the board to the last known sane state + virtual void load(); + + // accept an offer + void accept(); + + // reject an offer + void reject(); + + // continue a multi game match + void match_conti(); + + // leave a multi game match + void match_leave(); + +protected slots: + + /* + * All strings received from the server are given to handleServerData() for + * identification and processing. It implements a limited state machine to + * handle the incoming data correctly. The whole function could probably be + * made more efficient, but it is not time critical (and it appears to be + * easier to understand this way). + */ + void handleServerData(QString &line); + +protected: + + enum RxStatus {RxIgnore, RxConnect, RxWhois, RxMotd, RxRating, + RxNewLogin, RxGoodbye, RxNormal}; + + int rxStatus, rxCount; + + QString rxCollect; + + /* + * The following functions handle the individual states + * of the handleServerData() state machine, + */ + void handleMessageWhois(const QString &line); + void handleMessageRating(const QString &line); + void handleMessageMotd(const QString &line); + void handleMessageNewLogin(const QString &line); + void handleMessageConnect(const QString &line, const QString &rawline); + void handleMessageNormal(QString &line, QString &rawline); + + /* + * The next enumeration and the array of regular expressions is needed for the + * message identification in handleServerData(). + */ + enum Pattern {Welcome, OwnInfo, NoLogin, BegRate, EndRate, HTML_lt, HTML_gt, + BoardSY, BoardSN, WhoisBG, WhoisE1, WhoisE2, WhoEnde, WhoInfo, + MotdBeg, MotdEnd, MsgPers, MsgDeli, MsgSave, ChatSay, ChatSht, + ChatWis, ChatKib, SelfSay, SelfSlf, SelfSht, SelfWis, SelfKib, + UserLin, UserLot, Goodbye, GameSav, RawBord, YouTurn, PlsMove, + BegWtch, EndWtch, BegBlnd, EndBlnd, BegGame, OneWave, TwoWave, + YouWave, Reload1, Reload2, GameBG1, GameBG2, GameRE1, GameRE2, + GameEnd, EndLose, EndVict, MatchB1, MatchB2, MatchB3, MatchB4, + RejAcpt, YouAway, YouAcpt, HelpTxt, Invite0, Invite1, Invite2, + Invite3, ConLeav, TabChar, PlsChar, OneName, TypJoin, YouBack, + YouMove, YouRoll, TwoStar, BoxHori, BoxVer1, BoxVer2, OthrNam, + YourNam, GivePwd, RetypeP, GreedyY, GreedyN, RejCont, AcptWin, + YouGive, DoubleY, DoubleN, KeepAlv, RatingY, RatingN, + NumPattern}; + + QRegExp pat[NumPattern]; + + /* + * This function is simply filling the pat[] array with the proper values. + */ + void initPattern(); + +private: + + /* + * Local setup and config variables + * ================================ + */ + + /* + * Various options + */ + bool showMsg, whoisInvite; + QCheckBox *cbp, *cbi; + + QCheckBox *cbk; + bool keepalive; + + /* + * Connection setup + */ + enum FIBSInfo {FIBSHost, FIBSPort, FIBSUser, FIBSPswd, NumFIBS}; + QString infoFIBS[NumFIBS]; + QLineEdit *lec[NumFIBS]; + + /* + * Auto messages + */ + enum AutoMessages {MsgBeg, MsgLos, MsgWin, NumMsg}; + QLineEdit *lem[NumMsg]; + QCheckBox *cbm[NumMsg]; + bool useAutoMsg[NumMsg]; + QString autoMsg[NumMsg]; +}; + +#endif // __KBGFIBS_H diff --git a/kbackgammon/engines/fibs/kbgfibschat.cpp b/kbackgammon/engines/fibs/kbgfibschat.cpp new file mode 100644 index 00000000..45ba2bb7 --- /dev/null +++ b/kbackgammon/engines/fibs/kbgfibschat.cpp @@ -0,0 +1,828 @@ + +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + + +#include "kbgfibschat.h" +#include "kbgfibschat.moc" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clip.h" +#include "version.h" + + +/* + * Private utility class that might become more generally useful in + * the future. Basically, it implements rich text QListBox items. + */ +class KLBT : public QListBoxText +{ + +public: + + /* + * Constructor + */ + KLBT(QWidget *parent, const QString &text = QString::null, const QString &player = QString::null) + : QListBoxText(text) + { + w = parent; + n = new QString(player); + t = new QSimpleRichText(text, w->font()); + + // FIXME: this is not yet perfect + t->setWidth(w->width()-20); + } + + /* + * Destructor + */ + virtual ~KLBT() + { + delete t; + delete n; + } + + /* + * Overloaded required members returning height + */ + virtual int height(const QListBox *) const + { + return (1+t->height()); + } + + /* + * Overloaded required members returning width + */ + virtual int width(const QListBox *) const + { + return t->width(); + } + + /* + * The context menu needs the name of the player. It's easier + * than extracting it from the text. + */ + QString player() const + { + return *n; + } + +protected: + + /* + * Required overloaded member to paint the text on the painter p. + */ + virtual void paint(QPainter *p) + { + t->draw(p, 1, 1, QRegion(p->viewport()), w->colorGroup()); + } + +private: + + QSimpleRichText *t; + QWidget *w; + QString *n; + +}; + + +class KBgChatPrivate +{ +public: + + /* + * Name of the users + */ + QString mName[2]; + + /* + * Hold and assemble info text + */ + QString mText; + + /* + * Numbers of the private action list. + */ + enum Privact {Inquire, InviteD, Invite1, Invite2, Invite3, Invite4, + Invite5, Invite6, Invite7, InviteR, InviteU, Silent, + Talk, Gag, Ungag, Cleargag, Copy, Clear, Close, MaxAction}; + + /* + * Available actions + */ + KAction *mAct[MaxAction]; + + /* + * Context menu and invitation menu + */ + QPopupMenu *mChat, *mInvt; + + /* + * list of users we do not want to hear shouting + */ + QStringList mGag; + + /* + * Listbox needed by the setup dialog + */ + QListBox *mLb; + + /* + * Internal ID to name mapping + */ + QDict *mName2ID; + +}; + + +// == constructor, destructor ================================================== + +/* + * Constructor of the chat window. + */ +KBgChat::KBgChat(QWidget *parent, const char *name) + : KChat(parent, false) +{ + d = new KBgChatPrivate(); + KActionCollection* actions = new KActionCollection(this); + + d->mName[0] = QString::null; + d->mChat = 0; + d->mInvt = new QPopupMenu(); + + setAutoAddMessages(false); // we get an echo from FIBS + setFromNickname(i18n("%1 user").arg(PROG_NAME)); + + if (!addSendingEntry(i18n("Kibitz to watchers and players"), CLIP_YOU_KIBITZ)) + kdDebug(10500) << "adding kibitz" << endl; + if (!addSendingEntry(i18n("Whisper to watchers only"), CLIP_YOU_WHISPER)) + kdDebug(10500) << "adding whisper" << endl; + + connect(this, SIGNAL(rightButtonClicked(QListBoxItem *, const QPoint &)), + this, SLOT(contextMenu(QListBoxItem *, const QPoint &))); + connect(this, SIGNAL(signalSendMessage(int, const QString &)), + this, SLOT(handleCommand(int, const QString &))); + + d->mName2ID = new QDict(17, true); + d->mName2ID->setAutoDelete(true); + + /* + * some eye candy :) + */ + setIcon(kapp->miniIcon()); + setCaption(i18n("Chat Window")); + + QWhatsThis::add(this, i18n("This is the chat window.\n\n" + "The text in this window is colored depending on whether " + "it is directed at you personally, shouted to the general " + "FIBS population, has been said by you, or is of general " + "interest. If you select the name of a player, the context " + "contains entries specifically geared towards that player.")); + /* + * Define set of available actions + */ + d->mAct[KBgChatPrivate::Inquire] = new KAction(i18n("Info On"), + QIconSet(kapp->iconLoader()->loadIcon( + "help.xpm", KIcon::Small)), + 0, this, SLOT(slotInquire()), actions); + d->mAct[KBgChatPrivate::Talk] = new KAction(i18n("Talk To"), + QIconSet(kapp->iconLoader()->loadIcon( + PROG_NAME "-chat.png", KIcon::Small)), + 0, this, SLOT(slotTalk()), actions); + + d->mAct[KBgChatPrivate::InviteD] = new KAction(i18n("Use Dialog"), 0, this, + SLOT(slotInviteD()), actions); + d->mAct[KBgChatPrivate::Invite1] = new KAction(i18n("1 Point Match"), 0, this, + SLOT(slotInvite1()), actions); + d->mAct[KBgChatPrivate::Invite2] = new KAction(i18n("2 Point Match"), 0, this, + SLOT(slotInvite2()), actions); + d->mAct[KBgChatPrivate::Invite3] = new KAction(i18n("3 Point Match"), 0, this, + SLOT(slotInvite3()), actions); + d->mAct[KBgChatPrivate::Invite4] = new KAction(i18n("4 Point Match"), 0, this, + SLOT(slotInvite4()), actions); + d->mAct[KBgChatPrivate::Invite5] = new KAction(i18n("5 Point Match"), 0, this, + SLOT(slotInvite5()), actions); + d->mAct[KBgChatPrivate::Invite6] = new KAction(i18n("6 Point Match"), 0, this, + SLOT(slotInvite6()), actions); + d->mAct[KBgChatPrivate::Invite7] = new KAction(i18n("7 Point Match"), 0, this, + SLOT(slotInvite7()), actions); + d->mAct[KBgChatPrivate::InviteU] = new KAction(i18n("Unlimited"), 0, this, + SLOT(slotInviteU()), actions); + d->mAct[KBgChatPrivate::InviteR] = new KAction(i18n("Resume"), 0, this, + SLOT(slotInviteR()), actions); + + d->mAct[KBgChatPrivate::InviteD]->plug(d->mInvt); + + d->mInvt->insertSeparator(); + + d->mAct[KBgChatPrivate::Invite1]->plug(d->mInvt); + d->mAct[KBgChatPrivate::Invite2]->plug(d->mInvt); + d->mAct[KBgChatPrivate::Invite3]->plug(d->mInvt); + d->mAct[KBgChatPrivate::Invite4]->plug(d->mInvt); + d->mAct[KBgChatPrivate::Invite5]->plug(d->mInvt); + d->mAct[KBgChatPrivate::Invite6]->plug(d->mInvt); + d->mAct[KBgChatPrivate::Invite7]->plug(d->mInvt); + + d->mInvt->insertSeparator(); + + d->mAct[KBgChatPrivate::InviteU]->plug(d->mInvt); + d->mAct[KBgChatPrivate::InviteR]->plug(d->mInvt); + + d->mAct[KBgChatPrivate::Gag] = new KAction(i18n("Gag"), 0, this, SLOT(slotGag()), actions); + d->mAct[KBgChatPrivate::Ungag] = new KAction(i18n("Ungag"), 0, this, SLOT(slotUngag()), actions); + d->mAct[KBgChatPrivate::Cleargag] = new KAction(i18n("Clear Gag List"), 0, this, SLOT(slotCleargag()), actions); + d->mAct[KBgChatPrivate::Copy] = KStdAction::copy(this, SLOT(slotCopy()), actions); + d->mAct[KBgChatPrivate::Clear] = new KAction(i18n("Clear"), 0, this, SLOT(slotClear()), actions); + d->mAct[KBgChatPrivate::Close] = KStdAction::close(this, SLOT(hide()), actions); + d->mAct[KBgChatPrivate::Silent] = new KToggleAction(i18n("Silent"), 0, this, SLOT(slotSilent()), actions); +} + + +/* + * Destructor + */ +KBgChat::~KBgChat() +{ + delete d->mName2ID; + delete d->mChat; // save to delete NULL pointers + delete d->mInvt; + delete d; +} + + +// == configuration handling =================================================== + +/* + * Restore the previously stored settings + */ +void KBgChat::readConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("chat window"); + + QPoint pos(10, 10); + + pos = config->readPointEntry("ori", &pos); + setGeometry(pos.x(), pos.y(), config->readNumEntry("wdt",460), config->readNumEntry("hgt",200)); + + config->readBoolEntry("vis", false) ? show() : hide(); + + ((KToggleAction *)d->mAct[KBgChatPrivate::Silent])->setChecked(config->readBoolEntry("sil", false)); + + d->mGag = config->readListEntry("gag"); +} + +/* + * Save the current settings to disk + */ +void KBgChat::saveConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("chat window"); + + config->writeEntry("ori", pos()); + config->writeEntry("hgt", height()); + config->writeEntry("wdt", width()); + config->writeEntry("vis", isVisible()); + + config->writeEntry("sil", ((KToggleAction *)d->mAct[KBgChatPrivate::Silent])->isChecked()); + + config->writeEntry("gag", d->mGag); +} + + +/* + * Setup dialog page of the player list - allow the user to select the + * columns to show + * + * FIXME: need to be able to set font here KChatBase::setBothFont(const QFont& font) + */ +void KBgChat::getSetupPages(KTabCtl *nb, int space) +{ + /* + * Main Widget + * =========== + */ + QWidget *w = new QWidget(nb); + QGridLayout *gl = new QGridLayout(w, 2, 1, space); + + d->mLb = new QListBox(w); + d->mLb->setMultiSelection(true); + + d->mLb->insertStringList(d->mGag); + + QLabel *info = new QLabel(w); + info->setText(i18n("Select users to be removed from the gag list.")); + + QWhatsThis::add(w, i18n("Select all the users you want " + "to remove from the gag list " + "and then click OK. Afterwards " + "you will again hear what they shout.")); + + gl->addWidget(d->mLb, 0, 0); + gl->addWidget(info, 1, 0); + + /* + * put in the page + * =============== + */ + gl->activate(); + w->adjustSize(); + w->setMinimumSize(w->size()); + nb->addTab(w, i18n("&Gag List")); +} + +/* + * Remove all the selected entries from the gag list + */ +void KBgChat::setupOk() +{ + for (uint i = 0; i < d->mLb->count(); ++i) { + if (d->mLb->isSelected(i)) + d->mGag.remove(d->mLb->text(i)); + } + d->mLb->clear(); + d->mLb->insertStringList(d->mGag); +} + +/* + * Don't do anything + */ +void KBgChat::setupCancel() +{ + // empty +} + +/* + * By default, all players stay in the gag list + */ +void KBgChat::setupDefault() +{ + d->mLb->clearSelection(); +} + + +// == various slots and functions ============================================== + +/* + * Overloaded member to create a QListBoxItem for the chat window. + */ +QListBoxItem* KBgChat::layoutMessage(const QString& fromName, const QString& text) +{ + QListBoxText* message = new KLBT(this, text, fromName); + return message; +} + +/* + * Catch hide events, so the engine's menu can be update. + */ +void KBgChat::showEvent(QShowEvent *e) +{ + QFrame::showEvent(e); + emit windowVisible(true); +} + +/* + * Catch hide events, so the engine's menu can be update. + */ +void KBgChat::hideEvent(QHideEvent *e) +{ + emit windowVisible(false); + QFrame::hideEvent(e); +} + +/* + * At the beginning of a game, add the name to the list and switch to + * kibitz mode. + */ +void KBgChat::startGame(const QString &name) +{ + int *id = d->mName2ID->find(d->mName[1] = name); + if (!id) { + id = new int(nextId()); + d->mName2ID->insert(name, id); + addSendingEntry(i18n("Talk to %1").arg(name), *id); + } + setSendingEntry(CLIP_YOU_KIBITZ); +} + +/* + * At the end of a game, we switch to talk mode. + */ +void KBgChat::endGame() +{ + int *id = d->mName2ID->find(d->mName[1]); + if (id) + setSendingEntry(*id); + else + setSendingEntry(SendToAll); +} + +/* + * Set the chat window ready to talk to name + */ +void KBgChat::fibsTalk(const QString &name) +{ + int *id = d->mName2ID->find(name); + if (!id) { + id = new int(nextId()); + d->mName2ID->insert(name, id); + addSendingEntry(i18n("Talk to %1").arg(name), *id); + } + setSendingEntry(*id); +} + +/* + * Remove the player from the combo box when he/she logs out. + */ +void KBgChat::deletePlayer(const QString &name) +{ + int *id = d->mName2ID->find(name); + if (id) { + removeSendingEntry(*id); + d->mName2ID->remove(name); + } +} + +/* + * Take action when the user presses return in the line edit control. + */ +void KBgChat::handleCommand(int id, const QString& msg) +{ + int realID = sendingEntry(); + + switch (realID) { + case SendToAll: + emit fibsCommand("shout " + msg); + break; + case CLIP_YOU_KIBITZ: + emit fibsCommand("kibitz " + msg); + break; + case CLIP_YOU_WHISPER: + emit fibsCommand("whisper " + msg); + break; + default: + QDictIterator it(*d->mName2ID); + while (it.current()) { + if (*it.current() == realID) { + emit fibsCommand("tell " + it.currentKey() + " " + msg); + return; + } + ++it; + } + kdDebug(10500) << "unrecognized ID in KBgChat::handleCommand" << endl; + } +} + + +// == handle strings from the server =========================================== + +/* + * A message from the server that should be handled by us. If necessary, + * we replace the CLIP number by a string and put the line into the window. + * + * This function emits the string in rich text format with the signal + * personalMessage - again: the string contains rich text! + */ +void KBgChat::handleData(const QString &msg) +{ + QString clip = msg.left(msg.find(' ')), user, cMsg = msg; + QDateTime date; + + bool flag = false; + int cmd = clip.toInt(&flag); + + if (flag) { + cMsg.replace(0, cMsg.find(' ')+1, ""); + + user = cMsg.left(cMsg.find(' ')); + + switch (cmd) { + case CLIP_SAYS: + if (!d->mGag.contains(user)) { + cMsg = i18n("%1 tells you: %2").arg(user).arg(cMsg.replace(QRegExp("^" + user), "")); + cMsg = "" + cMsg + ""; + emit personalMessage(cMsg); + } else + cMsg = ""; + break; + + case CLIP_SHOUTS: + if ((!((KToggleAction *)d->mAct[KBgChatPrivate::Silent])->isChecked()) && (!d->mGag.contains(user))) { + cMsg = i18n("%1 shouts: %2").arg(user).arg(cMsg.replace(QRegExp("^" + user), "")); + cMsg = "" + cMsg + ""; + } else + cMsg = ""; + break; + + case CLIP_WHISPERS: + if (!d->mGag.contains(user)) { + cMsg = i18n("%1 whispers: %2").arg(user).arg(cMsg.replace(QRegExp("^" + user), "")); + cMsg = "" + cMsg + ""; + emit personalMessage(cMsg); + } else + cMsg = ""; + break; + + case CLIP_KIBITZES: + if (!d->mGag.contains(user)) { + cMsg = i18n("%1 kibitzes: %2").arg(user).arg(cMsg.replace(QRegExp("^" + user), "")); + cMsg = "" + cMsg + ""; + emit personalMessage(cMsg); + } else + cMsg = ""; + break; + + case CLIP_YOU_SAY: + cMsg = i18n("You tell %1: %2").arg(user).arg(cMsg.replace(QRegExp("^" + user), "")); + cMsg = "" + cMsg + ""; + emit personalMessage(cMsg); + user = QString::null; + break; + + case CLIP_YOU_SHOUT: + cMsg = i18n("You shout: %1").arg(cMsg); + cMsg = "" + cMsg + ""; + emit personalMessage(cMsg); + user = QString::null; + break; + + case CLIP_YOU_WHISPER: + cMsg = i18n("You whisper: %1").arg(cMsg); + cMsg = "" + cMsg + ""; + emit personalMessage(cMsg); + user = QString::null; + break; + + case CLIP_YOU_KIBITZ: + cMsg = i18n("You kibitz: %1").arg(cMsg); + cMsg = "" + cMsg + ""; + emit personalMessage(cMsg); + user = QString::null; + break; + + case CLIP_MESSAGE: + user = cMsg.left(cMsg.find(' ')+1); + cMsg.remove(0, cMsg.find(' ')+1); + date.setTime_t(cMsg.left(cMsg.find(' ')+1).toUInt()); + cMsg.remove(0, cMsg.find(' ')); + cMsg = i18n("User %1 left a message at %2: %3").arg(user).arg(date.toString()).arg(cMsg); + cMsg = "" + cMsg + ""; + emit personalMessage(cMsg); + user = QString::null; + break; + + case CLIP_MESSAGE_DELIVERED: + cMsg = i18n("Your message for %1 has been delivered.").arg(user); + cMsg = QString("") + cMsg + ""; + emit personalMessage(cMsg); + user = QString::null; + break; + + case CLIP_MESSAGE_SAVED: + cMsg = i18n("Your message for %1 has been saved.").arg(user); + cMsg = QString("") + cMsg + ""; + emit personalMessage(cMsg); + user = QString::null; + break; + + default: // ignore the message + return; + } + + } else { + + /* + * Special treatment for non-CLIP messages + */ + if (cMsg.contains(QRegExp("^You say to yourself: "))) { + cMsg.replace(QRegExp("^You say to yourself: "), + i18n("You say to yourself: ")); + } else { + kdDebug(user.isNull(), 10500) << "KBgChat::handleData unhandled message: " + << cMsg.latin1() << endl; + return; + } + } + + if (!cMsg.isEmpty()) + addMessage(user, cMsg); +} + + +// == context menu and related slots =========================================== + +/* + * RMB opens a context menu. + */ +void KBgChat::contextMenu(QListBoxItem *i, const QPoint &p) +{ + /* + * Even if i is non-null, user might still be QString::null + */ + d->mName[0] = (i == 0) ? QString::null : ((KLBT *)i)->player(); + d->mText = (i == 0) ? QString::null : ((KLBT *)i)->text(); + + /* + * Get a new context menu every time. Safe to delete the 0 + * pointer. + */ + delete d->mChat; d->mChat = new QPopupMenu(); + + /* + * Fill the context menu with actions + */ + if (!d->mName[0].isNull()) { + + d->mAct[KBgChatPrivate::Talk]->setText(i18n("Talk to %1").arg(d->mName[0])); + d->mAct[KBgChatPrivate::Talk]->plug(d->mChat); + + d->mAct[KBgChatPrivate::Inquire]->setText(i18n("Info on %1").arg(d->mName[0])); + d->mAct[KBgChatPrivate::Inquire]->plug(d->mChat); + + // invite menu is always the same + d->mChat->insertItem(i18n("Invite %1").arg(d->mName[0]), d->mInvt); + + d->mChat->insertSeparator(); + + if (d->mGag.contains(d->mName[0]) <= 0) { + d->mAct[KBgChatPrivate::Gag]->setText(i18n("Gag %1").arg(d->mName[0])); + d->mAct[KBgChatPrivate::Gag]->plug(d->mChat); + } else { + d->mAct[KBgChatPrivate::Ungag]->setText(i18n("Ungag %1").arg(d->mName[0])); + d->mAct[KBgChatPrivate::Ungag]->plug(d->mChat); + } + } + if (d->mGag.count() > 0) + d->mAct[KBgChatPrivate::Cleargag]->plug(d->mChat); + + if ((d->mGag.count() > 0) || (!d->mName[0].isNull())) + d->mChat->insertSeparator(); + + d->mAct[KBgChatPrivate::Silent]->plug(d->mChat); + + d->mChat->insertSeparator(); + + d->mAct[KBgChatPrivate::Copy ]->plug(d->mChat); + d->mAct[KBgChatPrivate::Clear]->plug(d->mChat); + d->mAct[KBgChatPrivate::Close]->plug(d->mChat); + + d->mChat->popup(p); +} + +/* + * Clear the gag list + */ +void KBgChat::slotCleargag() +{ + d->mGag.clear(); + + QString msg(""); + msg += i18n("The gag list is now empty."); + msg += ""; + + addMessage(QString::null, msg); +} + +/* + * Gag the selected user + */ +void KBgChat::slotGag() +{ + d->mGag.append(d->mName[0]); + + QString msg(""); + msg += i18n("You won't hear what %1 says and shouts.").arg(d->mName[0]); + msg += ""; + + addMessage(QString::null, msg); +} + +/* + * Simple interface to the actual talk slot + */ +void KBgChat::slotTalk() +{ + fibsTalk(d->mName[0]); +} + +/* + * Remove selected user from gag list + */ +void KBgChat::slotUngag() +{ + d->mGag.remove(d->mName[0]); + + QString msg(""); + msg += i18n("You will again hear what %1 says and shouts.").arg(d->mName[0]); + msg += ""; + + addMessage(QString::null, msg); +} + +/* + * Get information on selected user + */ +void KBgChat::slotInquire() +{ + kdDebug(d->mName[0].isNull(), 10500) << "KBgChat::slotInquire: user == null" << endl; + emit fibsCommand("whois " + d->mName[0]); +} + +/* + * Block all shouts from the chat window + */ +void KBgChat::slotSilent() +{ + QString msg; + if (((KToggleAction *)d->mAct[KBgChatPrivate::Silent])->isChecked()) + msg = "" + i18n("You will not hear what people shout.") + ""; + else + msg = "" + i18n("You will hear what people shout.") + ""; + addMessage(QString::null, msg); +} + +/* + * Copy the selected line to the clipboard. Strip the additional HTML + * from the text before copying. + */ +void KBgChat::slotCopy() +{ + d->mText.replace(QRegExp(""), ""); + d->mText.replace(QRegExp(""), ""); + d->mText.replace(QRegExp(""), ""); + d->mText.replace(QRegExp("^.*\">"), ""); + + kapp->clipboard()->setText(d->mText); +} + +/* + * Invite the selected player. + */ +void KBgChat::slotInviteD() +{ + kdDebug(d->mName[0].isNull(), 10500) << "KBgChat::slotInvite: user == null" << endl; + emit fibsRequestInvitation(d->mName[0]); +} +void KBgChat::slotInvite1() { emit fibsCommand("invite " + d->mName[0] + " 1"); } +void KBgChat::slotInvite2() { emit fibsCommand("invite " + d->mName[0] + " 2"); } +void KBgChat::slotInvite3() { emit fibsCommand("invite " + d->mName[0] + " 3"); } +void KBgChat::slotInvite4() { emit fibsCommand("invite " + d->mName[0] + " 4"); } +void KBgChat::slotInvite5() { emit fibsCommand("invite " + d->mName[0] + " 5"); } +void KBgChat::slotInvite6() { emit fibsCommand("invite " + d->mName[0] + " 6"); } +void KBgChat::slotInvite7() { emit fibsCommand("invite " + d->mName[0] + " 7"); } + +void KBgChat::slotInviteU() { emit fibsCommand("invite " + d->mName[0] + " unlimited"); } +void KBgChat::slotInviteR() { emit fibsCommand("invite " + d->mName[0]); } + + +// EOF diff --git a/kbackgammon/engines/fibs/kbgfibschat.h b/kbackgammon/engines/fibs/kbgfibschat.h new file mode 100644 index 00000000..c3a1d670 --- /dev/null +++ b/kbackgammon/engines/fibs/kbgfibschat.h @@ -0,0 +1,273 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KBGCHAT_H +#define __KBGCHAT_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +class QString; +class QPoint; +class QListBox; +class QListBoxItem; +class QPopupMenu; + +class KTabCtl; +class KAction; + +class KBgChatPrivate; + +/** + * Class of the FIBS Chat Windows + * + * This class inherits from KChat and represents a widget for a chat + * window. It has rich text entries and supports a powerful context + * menu. + * + * @short An extension of the KGame chat window for the FIBS engine + * @author Jens Hoefkens + * + */ +class KBgChat : public KChat +{ + Q_OBJECT + +public: + + /** + * Constructor + */ + KBgChat(QWidget *parent = 0, const char *name = 0); + + /** + * Destructor + */ + virtual ~KBgChat(); + +public slots: + + /** + * Catch the RMB signal to display a context menu at p. The + * menu shows entries specific to the selected item i. + */ + void contextMenu(QListBoxItem *i, const QPoint &p); + + /** + * Add chat window specific pages to the setup dialog + */ + void getSetupPages(KTabCtl *nb, int space); + + /** + * Save and apply the changes made in the setup dialog + */ + void setupOk(); + + /** + * Do not save any of the changes made in the setup dialog + */ + void setupCancel(); + + /** + * Load default values from the setup dialog + */ + void setupDefault(); + + /** + * Player name has logges out. Remove name from the chat + * window combo box if necessary. + */ + void deletePlayer(const QString &name); + + /** + * Process and append msg to the text. + */ + void handleData(const QString &msg); + + /** + * Restore previously saved setting or provides defaults + */ + void readConfig(); + + /** + * Save current settings + */ + void saveConfig(); + + /** + * Set the opponents name and select whisper + */ + void startGame(const QString &name); + + /** + * Game is over. We won (or not) and have been playing (or not) + */ + void endGame(); + + /** + * Start talking to name + */ + void fibsTalk(const QString &name); + +signals: + + /** + * Emits a string that can be sent to the server + */ + void fibsCommand(const QString &cmd); + + /** + * Request an invitation of player + */ + void fibsRequestInvitation(const QString &player); + + /** + * Text of a personal message + */ + void personalMessage(const QString &msg); + + /** + * Dialog is visible or not + */ + void windowVisible(bool v); + +protected: + + /** + * Catch show events, so the engine's menu can be updated. + */ + virtual void showEvent(QShowEvent *e); + + /** + * Catch hide events, so the engine's menu can be updated. + */ + virtual void hideEvent(QHideEvent *e); + + /** + * Create a custom ListBoxItem that contains a formated string + * for the chat window. + */ + virtual QListBoxItem* layoutMessage(const QString& fromName, const QString& text); + +protected slots: + + /** + * Invite the selected player using the dialog + */ + void slotInviteD(); + + /** + * Invite the selected player to resume a match + */ + void slotInviteR(); + + /** + * Invite the selected player to an unlimited match + */ + void slotInviteU(); + + /** + * Invite the selected player to a 1 point match + */ + void slotInvite1(); + + /** + * Invite the selected player to a 2 point match + */ + void slotInvite2(); + + /** + * Invite the selected player to a 3 point match + */ + void slotInvite3(); + + /** + * Invite the selected player to a 4 point match + */ + void slotInvite4(); + + /** + * Invite the selected player to a 5 point match + */ + void slotInvite5(); + + /** + * Invite the selected player to a 6 point match + */ + void slotInvite6(); + + /** + * Invite the selected player to a 7 point match + */ + void slotInvite7(); + + /** + * Request information on the selected player + */ + void slotInquire(); + + /** + * Copy the selected line to the clipboard + */ + void slotCopy(); + + /** + * Talk to the selected player + */ + void slotTalk(); + + /** + * Add the selected player to the gag list + */ + void slotGag(); + + /** + * Remove the selected player from the gag list + */ + void slotUngag(); + + /** + * Clear the gag list + */ + void slotCleargag(); + + /** + * Toggle everybody silent + */ + void slotSilent(); + + /** + * Slot for return pressed. Time to send the text to FIBS. + */ + void handleCommand(int id, const QString& msg); + +private: + + KBgChatPrivate *d; + +}; + +#endif // __KBGCHAT_H diff --git a/kbackgammon/engines/fibs/kbginvite.cpp b/kbackgammon/engines/fibs/kbginvite.cpp new file mode 100644 index 00000000..cb455f0a --- /dev/null +++ b/kbackgammon/engines/fibs/kbginvite.cpp @@ -0,0 +1,185 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#include "kbginvite.h" +#include "kbginvite.moc" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +class KBgInvitePrivate { + +public: + + KLineEdit *mLe; + QSpinBox *mSb; + QPushButton *mInvite, *mResume, *mUnlimited, *mCancel, *mClose; + +}; + +/* + * Constructor is quite simple - most positioning is left to + * the toolkit. + */ +KBgInvite::KBgInvite(const char *name) + : KDialog(0, name, false) +{ + setCaption(i18n("Invite Players")); + + d = new KBgInvitePrivate(); + + QLabel *info = new QLabel(this); + + d->mLe = new KLineEdit(this, "invitation dialog"); + d->mSb = new QSpinBox(1, 999, 1, this, "spin box"); + + d->mInvite = new QPushButton(i18n("&Invite"), this); + d->mResume = new QPushButton(i18n("&Resume"), this); + d->mUnlimited = new QPushButton(i18n("&Unlimited"), this); + + d->mClose = new KPushButton(KStdGuiItem::close(), this); + d->mCancel = new KPushButton(KStdGuiItem::clear(), this); + + info->setText(i18n("Type the name of the player you want to invite in the first entry\n" + "field and select the desired match length in the spin box.")); + + QFrame *hLine = new QFrame(this); + hLine->setFrameStyle(QFrame::Sunken|QFrame::HLine); + + /* + * Set up layouts + */ + QBoxLayout *vbox = new QVBoxLayout(this); + + QBoxLayout *hbox_1 = new QHBoxLayout(vbox); + QBoxLayout *hbox_2 = new QHBoxLayout(vbox); + QBoxLayout *hbox_3 = new QHBoxLayout(vbox); + QBoxLayout *hbox_4 = new QHBoxLayout(vbox); + QBoxLayout *hbox_5 = new QHBoxLayout(vbox); + + hbox_1->addWidget(info); + + hbox_2->addWidget(d->mLe); + hbox_2->addWidget(d->mSb); + + hbox_3->addWidget(hLine); + + hbox_4->addWidget(d->mInvite); + hbox_4->addWidget(d->mResume); + hbox_4->addWidget(d->mUnlimited); + + hbox_5->addWidget(d->mClose); + hbox_5->addWidget(d->mCancel); + + /* + * Adjust widget sizes and resize the dialog + */ + KDialog::resizeLayout(this, marginHint(), spacingHint()); + setMinimumSize(childrenRect().size()); + vbox->activate(); + resize(minimumSize()); + + /* + * Set focus and default buttons + */ + d->mInvite->setDefault(true); + d->mInvite->setAutoDefault(true); + d->mLe->setFocus(); + + /* + * Connect the buttons + */ + connect(d->mUnlimited, SIGNAL(clicked()), SLOT(unlimitedClicked())); + connect(d->mResume, SIGNAL(clicked()), SLOT(resumeClicked())); + connect(d->mInvite, SIGNAL(clicked()), SLOT(inviteClicked())); + connect(d->mClose, SIGNAL(clicked()), SLOT(hide())); + connect(d->mCancel, SIGNAL(clicked()), SLOT(cancelClicked())); +} + +/* + * Destructor + */ +KBgInvite::~KBgInvite() +{ + delete d; +} + +/* + * After hiding, we tell our creator that we are ready to die. + */ +void KBgInvite::hide() +{ + emit dialogDone(); +} + +/* + * Set player name + */ +void KBgInvite::setPlayer(const QString &player) +{ + d->mLe->setText(player); +} + +/* + * Invitation with number + */ +void KBgInvite::inviteClicked() +{ + QString tmp; + emit inviteCommand(QString("invite ") + d->mLe->text() + " " + tmp.setNum(d->mSb->value())); +} + +/* + * Invitation for unlimited match + */ +void KBgInvite::unlimitedClicked() +{ + emit inviteCommand(QString("invite ") + d->mLe->text() + " unlimited"); +} + +/* + * Resume a game + */ +void KBgInvite::resumeClicked() +{ + emit inviteCommand(QString("invite ") + d->mLe->text()); +} + +/* + * Slot for Cancel. clear everything to default. + */ +void KBgInvite::cancelClicked() +{ + d->mSb->setValue(1); + d->mLe->clear(); +} + +// EOF diff --git a/kbackgammon/engines/fibs/kbginvite.h b/kbackgammon/engines/fibs/kbginvite.h new file mode 100644 index 00000000..992ee445 --- /dev/null +++ b/kbackgammon/engines/fibs/kbginvite.h @@ -0,0 +1,113 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KBGINVITE_H +#define __KBGINVITE_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +class KBgInvitePrivate; + +/** + * + * This class implements a dialog for inviting players for games. It + * is quite simple (but follows the default style guide). The dialog + * offers specific numbers, unlimited and resume as invitation + * options. + * + * @short Simple dialog that allows to invite somebody on FIBS + * @author Jens Hoefkens + * + */ +class KBgInvite : public KDialog +{ + Q_OBJECT + +public: + + /** + * Constructor + */ + KBgInvite(const char *name = 0); + + /** + * Destructor + */ + virtual ~KBgInvite(); + +public slots: + + /** + * After hiding, we tell our creator that we are ready to die. + */ + virtual void hide(); + + /** + * Set the name of the player in the line editor + */ + void setPlayer(const QString &name); + +protected slots: + + /** + * Emits the FIBS invitation command if the Ok button was clicked. + */ + void inviteClicked(); + + /** + * Ask FIBS to resume a match + */ + void resumeClicked(); + + /** + * Ask FIBS for an unlimited match + */ + void unlimitedClicked(); + + /** + * Clear the entry field + */ + void cancelClicked(); + +signals: + + /** + * Emits the text of an invitation + */ + void inviteCommand(const QString &cmd); + + /** + * Delete the dialog after it is closed. + */ + void dialogDone(); + +private: + + KBgInvitePrivate *d; +}; + +#endif // __KBGINVITE_H diff --git a/kbackgammon/engines/fibs/kplayerlist.cpp b/kbackgammon/engines/fibs/kplayerlist.cpp new file mode 100644 index 00000000..102c354d --- /dev/null +++ b/kbackgammon/engines/fibs/kplayerlist.cpp @@ -0,0 +1,902 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#include "kplayerlist.moc" +#include "kplayerlist.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "kbgfibs.h" +#include "version.h" + + +/* + * Simple container for information on columns of the list view. + * + * index : the internal index in the list + * width : width of the column in pixel + * show : whether the column is visible + * cb : check box for the setup dialog + */ +class KFibsPlayerListCI { + +public: + + int index, width; + bool show; + QCheckBox *cb; + QString key, name; + +}; + +/* + * Extension of the QListViewItem class that has a custom key function + * that can deal with the different items of the player list. + */ +class KFibsPlayerListLVI : public KListViewItem { + +public: + + /* + * Constructor + */ + KFibsPlayerListLVI(KFibsPlayerList *parent) : KListViewItem(parent) { _plist = parent; } + + /* + * Destructor + */ + virtual ~KFibsPlayerListLVI() {} + + /* + * Overloaded key function for sorting + */ + virtual QString key(int col, bool) const + { + int real_col = _plist->cIndex(col); + + QString s = text(col); + + switch (real_col) { + case KFibsPlayerList::Player: + case KFibsPlayerList::Opponent: + case KFibsPlayerList::Watches: + case KFibsPlayerList::Client: + case KFibsPlayerList::Email: + case KFibsPlayerList::Status: + case KFibsPlayerList::Host: + s = s.lower(); + break; + case KFibsPlayerList::Idle: + case KFibsPlayerList::Experience: + s.sprintf("%08d", s.toInt()); + break; + case KFibsPlayerList::Rating: + s.sprintf("%08d", (int)(1000*s.toDouble())); + break; + case KFibsPlayerList::Time: + s = s.lower(); + break; + default: + kdDebug(10500) << "KFibsPlayerListLVI::key(): illegal column" << endl; + break; + } + return s; + } + +private: + + KFibsPlayerList *_plist; + +}; + +/* + * Private data of the player list + */ +class KFibsPlayerListPrivate { + +public: + + /* + * Named constants for the popup menu actions + */ + enum MenuID {Info, Talk, Mail, InviteD, Invite1, Invite2, Invite3, Invite4, + Invite5, Invite6, Invite7, InviteR, InviteU, + Look, Watch, Unwatch, BlindAct, Update, Reload, Close, ActionEnd}; + + /* + * Various actions for the context menu + */ + KAction *mAct[ActionEnd]; + + /* + * All relevant information on the columns + */ + KFibsPlayerListCI *mCol[KFibsPlayerList::LVEnd]; + + /* + * Context menus for player related commands + */ + QPopupMenu *mPm[2]; + + /* + * ID of the invite menu in the context menu + */ + int mInID; + + /* + * Are we watching? + */ + bool mWatch; + + /* + * count similar clients - KFibs & kbackgammon + */ + int mCount[2]; + + /* + * Short abbreviations for Blind, Ready, and Away. + */ + QString mAbrv[KFibsPlayerList::MaxStatus]; + + /* + * Name of the last selected player - for internal purposes + */ + QString mUser; + + /* + * Our own name + */ + QString mName; + + /* + * Email address of the last selected player - for internal purposes + */ + QString mMail; + +}; + + +// == constructor, destructor and setup ======================================== + +/* + * Construct the playerlist and do some initial setup + */ +KFibsPlayerList::KFibsPlayerList(QWidget *parent, const char *name) + : KListView(parent, name) +{ + d = new KFibsPlayerListPrivate(); + KActionCollection* actions = new KActionCollection(this); + + /* + * Allocate the column information + */ + for (int i = 0; i < LVEnd; i++) + d->mCol[i] = new KFibsPlayerListCI(); + + /* + * Initialize variables + */ + d->mCol[Player]->name = i18n("Player"); + d->mCol[Opponent]->name = i18n("Opponent"); + d->mCol[Watches]->name = i18n("Watches"); + d->mCol[Status]->name = i18n("Status"); + d->mCol[Rating]->name = i18n("Rating"); + d->mCol[Experience]->name = i18n("Exp."); + d->mCol[Idle]->name = i18n("Idle"); + d->mCol[Time]->name = i18n("Time"); + d->mCol[Host]->name = i18n("Host name"); + d->mCol[Client]->name = i18n("Client"); + d->mCol[Email]->name = i18n("Email"); + + // These strings shouldn't be translated!! + d->mCol[Player]->key = "player"; + d->mCol[Opponent]->key = "opponent"; + d->mCol[Watches]->key = "watches"; + d->mCol[Status]->key = "status"; + d->mCol[Rating]->key = "rating"; + d->mCol[Experience]->key = "experience"; + d->mCol[Idle]->key = "idle"; + d->mCol[Time]->key = "time"; + d->mCol[Host]->key = "hostname"; + d->mCol[Client]->key = "client"; + d->mCol[Email]->key = "email"; + + d->mCount[0] = d->mCount[1] = 0; + + d->mAbrv[Blind] = i18n("abreviate blind", "B"); + d->mAbrv[Away ] = i18n("abreviate away", "A"); + d->mAbrv[Ready] = i18n("abreviate ready", "R"); + + d->mName = QString::null; + + d->mWatch = false; + + /* + * Get a sane caption, initialize some eye candy and read the + * configuration - needed for the column information. + */ + updateCaption(); + setIcon(kapp->miniIcon()); + QWhatsThis::add(this, i18n("This window contains the player list. It shows " + "all players that are currently logged into FIBS." + "Use the right mouse button to get a context " + "menu with helpful information and commands.")); + + readColumns(); + + /* + * Put the columns into the list view + */ + for (int i = 0; i < LVEnd; i++) { + if (d->mCol[i]->show) { + d->mCol[i]->index = addColumn(d->mCol[i]->name, d->mCol[i]->width); + if (i == Experience || i == Rating || i == Time || i == Idle) + setColumnAlignment(d->mCol[i]->index, AlignRight); + } else { + d->mCol[i]->index = -1; + } + } + setAllColumnsShowFocus(true); + + /* + * Create context menus + */ + d->mPm[0] = new QPopupMenu(); + d->mPm[1] = new QPopupMenu(); + + /* + * Create the whole set of actions + */ + d->mAct[KFibsPlayerListPrivate::Info] = new KAction(i18n("Info"), + QIconSet(kapp->iconLoader()->loadIcon + ("help.xpm", KIcon::Small)), + 0, this, SLOT(slotInfo()), actions); + d->mAct[KFibsPlayerListPrivate::Talk] = new KAction(i18n("Talk"), + QIconSet(kapp->iconLoader()->loadIcon + (PROG_NAME "-chat.png", KIcon::Small)), + 0, this, SLOT(slotTalk()), actions); + + d->mAct[KFibsPlayerListPrivate::Look] = new KAction(i18n("Look"), 0, this, SLOT(slotLook()), actions); + d->mAct[KFibsPlayerListPrivate::Watch] = new KAction(i18n("Watch"), 0, this, SLOT(slotWatch()), actions); + d->mAct[KFibsPlayerListPrivate::Unwatch] = new KAction(i18n("Unwatch"), 0, this, SLOT(slotUnwatch()),actions); + d->mAct[KFibsPlayerListPrivate::BlindAct] = new KAction(i18n("Blind"), 0, this, SLOT(slotBlind()), actions); + d->mAct[KFibsPlayerListPrivate::Update] = new KAction(i18n("Update"), 0, this, SLOT(slotUpdate()), actions); + + d->mAct[KFibsPlayerListPrivate::Reload] = KStdAction::redisplay(this, SLOT(slotReload()), actions); + d->mAct[KFibsPlayerListPrivate::Mail] = KStdAction::mail(this, SLOT(slotMail()), actions); + d->mAct[KFibsPlayerListPrivate::Close] = KStdAction::close(this, SLOT(hide()), actions); + + d->mAct[KFibsPlayerListPrivate::InviteD] = new KAction(i18n("Use Dialog"), 0, this, + SLOT(slotInviteD()), actions); + d->mAct[KFibsPlayerListPrivate::Invite1] = new KAction(i18n("1 Point Match"), 0, this, + SLOT(slotInvite1()), actions); + d->mAct[KFibsPlayerListPrivate::Invite2] = new KAction(i18n("2 Point Match"), 0, this, + SLOT(slotInvite2()), actions); + d->mAct[KFibsPlayerListPrivate::Invite3] = new KAction(i18n("3 Point Match"), 0, this, + SLOT(slotInvite3()), actions); + d->mAct[KFibsPlayerListPrivate::Invite4] = new KAction(i18n("4 Point Match"), 0, this, + SLOT(slotInvite4()), actions); + d->mAct[KFibsPlayerListPrivate::Invite5] = new KAction(i18n("5 Point Match"), 0, this, + SLOT(slotInvite5()), actions); + d->mAct[KFibsPlayerListPrivate::Invite6] = new KAction(i18n("6 Point Match"), 0, this, + SLOT(slotInvite6()), actions); + d->mAct[KFibsPlayerListPrivate::Invite7] = new KAction(i18n("7 Point Match"), 0, this, + SLOT(slotInvite7()), actions); + d->mAct[KFibsPlayerListPrivate::InviteU] = new KAction(i18n("Unlimited"), 0, this, + SLOT(slotInviteU()), actions); + d->mAct[KFibsPlayerListPrivate::InviteR] = new KAction(i18n("Resume"), 0, this, + SLOT(slotInviteR()), actions); + + /* + * Fill normal context menu + */ + d->mAct[KFibsPlayerListPrivate::Info]->plug(d->mPm[0]); + d->mAct[KFibsPlayerListPrivate::Talk]->plug(d->mPm[0]); + d->mAct[KFibsPlayerListPrivate::Mail]->plug(d->mPm[0]); + d->mPm[0]->insertSeparator(); + d->mInID = d->mPm[0]->insertItem(i18n("Invite"), d->mPm[1]); // save ID for later + d->mAct[KFibsPlayerListPrivate::Look ]->plug(d->mPm[0]); + d->mAct[KFibsPlayerListPrivate::Watch ]->plug(d->mPm[0]); + d->mAct[KFibsPlayerListPrivate::Unwatch ]->plug(d->mPm[0]); + d->mAct[KFibsPlayerListPrivate::BlindAct]->plug(d->mPm[0]); + d->mPm[0]->insertSeparator(); + d->mAct[KFibsPlayerListPrivate::Update]->plug(d->mPm[0]); + d->mAct[KFibsPlayerListPrivate::Reload]->plug(d->mPm[0]); + d->mPm[0]->insertSeparator(); + d->mAct[KFibsPlayerListPrivate::Close]->plug(d->mPm[0]); + + /* + * Fill the invitation menu + */ + d->mAct[KFibsPlayerListPrivate::InviteD]->plug(d->mPm[1]); + d->mPm[1]->insertSeparator(); + d->mAct[KFibsPlayerListPrivate::Invite1]->plug(d->mPm[1]); + d->mAct[KFibsPlayerListPrivate::Invite2]->plug(d->mPm[1]); + d->mAct[KFibsPlayerListPrivate::Invite3]->plug(d->mPm[1]); + d->mAct[KFibsPlayerListPrivate::Invite4]->plug(d->mPm[1]); + d->mAct[KFibsPlayerListPrivate::Invite5]->plug(d->mPm[1]); + d->mAct[KFibsPlayerListPrivate::Invite6]->plug(d->mPm[1]); + d->mAct[KFibsPlayerListPrivate::Invite7]->plug(d->mPm[1]); + d->mPm[1]->insertSeparator(); + d->mAct[KFibsPlayerListPrivate::InviteU]->plug(d->mPm[1]); + d->mAct[KFibsPlayerListPrivate::InviteR]->plug(d->mPm[1]); + + /* + * Right mouse button gets context menu, double click gets player info + */ + connect(this, SIGNAL(contextMenu(KListView *, QListViewItem *, const QPoint &)), + this, SLOT(showContextMenu(KListView *, QListViewItem *, const QPoint &))); + connect(this, SIGNAL(doubleClicked(QListViewItem *, const QPoint &, int)), + this, SLOT(getPlayerInfo(QListViewItem *, const QPoint &, int))); +} + +/* + * Destructor deletes members + */ +KFibsPlayerList::~KFibsPlayerList() +{ + for (int i = 0; i < LVEnd; i++) + delete d->mCol[i]; + delete d->mPm[0]; + delete d->mPm[1]; + delete d; +} + + +// == settings and config ====================================================== + +/* + * Called when the setup dialog is positively closed + */ +void KFibsPlayerList::setupOk() +{ + int i; + bool change = false; + + for (i = 1; i < LVEnd; i++) + change |= (d->mCol[i]->cb->isChecked() != d->mCol[i]->show); + + /* + * Only juggle with the columns if something changed + */ + if (change) { + + /* + * It's important to remove the columns in reverse order + */ + for (i = LVEnd-1; i > 0; i--) + if (d->mCol[i]->show) + removeColumn(d->mCol[i]->index); + + /* + * Now add all columns that are selected + */ + for (i = 1; i < LVEnd; i++) { + if ((d->mCol[i]->show = d->mCol[i]->cb->isChecked())) { + d->mCol[i]->index = addColumn(d->mCol[i]->name, d->mCol[i]->width); + if (i == Experience || i == Rating || i == Time || i == Idle) + setColumnAlignment(d->mCol[i]->index, AlignRight); + } else { + d->mCol[i]->index = -1; + } + } + + /* + * Reload the list + */ + slotReload(); + } + + /* + * store the new settings + */ + saveConfig(); + +} + +/* + * Setup dialog page of the player list - allow the user to select the + * columns to show + */ +void KFibsPlayerList::getSetupPages(KTabCtl *nb, int space) +{ + int i; + + /* + * Main Widget + */ + QWidget *w = new QWidget(nb); + QGridLayout *gl = new QGridLayout(w, 2, 1, space); + + /* + * Label + */ + QGroupBox *gbl = new QGroupBox(w); + gbl->setTitle(i18n("Column Selection")); + + gl->addWidget(gbl, 0, 0); + + /* + * Note that the first column (Player == 0) is always there + */ + QLabel *lb = new QLabel(i18n("Select all the columns that you would\n" + "like to be shown in the player list."), gbl); + + for (i = 1; i < LVEnd; i++) { + d->mCol[i]->cb = new QCheckBox(d->mCol[i]->name, gbl); + d->mCol[i]->cb->setChecked(d->mCol[i]->show); + } + + gl = new QGridLayout(gbl, LVEnd, 2, 20); + gl->addWidget(lb, 0, 0); + + // two column layout.... + for (i = 1; i < LVEnd/2; i++) { + gl->addWidget(d->mCol[2*i-1]->cb, i, 0); + gl->addWidget(d->mCol[2*i ]->cb, i, 1); + } + gl->addWidget(d->mCol[2*i-1]->cb, i, 0); + if (2*i < LVEnd) + gl->addWidget(d->mCol[2*i]->cb, i, 1); + + /* + * put in the page and connect + */ + nb->addTab(w, i18n("&Playerlist")); + + connect(nb, SIGNAL(applyButtonPressed()), this, SLOT(setupOk())); +} + +/* + * Nothing to cancel/undo + */ +void KFibsPlayerList::setupCancel() +{ + // do nothing +} + +/* + * By default all entries are checked + */ +void KFibsPlayerList::setupDefault() +{ + for (int i = 0; i < LVEnd; i++) + d->mCol[i]->cb->setChecked(true); +} + +/* + * Restore the columns + */ +void KFibsPlayerList::readColumns() +{ + KConfig* config = kapp->config(); + config->setGroup(name()); + + for (int i = 0; i < LVEnd; i++) { + d->mCol[i]->show = config->readBoolEntry("col-" + d->mCol[i]->key, true); + d->mCol[i]->width = config->readNumEntry("col-w-" + d->mCol[i]->key, -1); + } +} + +/* + * Restore the saved settings + */ +void KFibsPlayerList::readConfig() +{ + KConfig* config = kapp->config(); + config->setGroup(name()); + + QPoint pos, defpos(10, 10); + pos = config->readPointEntry("ori", &defpos); + setGeometry(pos.x(), pos.y(), config->readNumEntry("wdt",460), + config->readNumEntry("hgt",190)); + + (config->readBoolEntry("vis", false)) ? show() : hide(); + + readColumns(); +} + +/* + * Save current settings + */ +void KFibsPlayerList::saveConfig() +{ + KConfig* config = kapp->config(); + config->setGroup(name()); + + config->writeEntry("ori", pos()); + config->writeEntry("hgt", height()); + config->writeEntry("wdt", width()); + + config->writeEntry("vis", isVisible()); + + for (int i = 0; i < LVEnd; i++) { + config->writeEntry("col-" + d->mCol[i]->key, d->mCol[i]->show); + config->writeEntry("col-w-" + d->mCol[i]->key, + (d->mCol[i]->show) ? columnWidth(d->mCol[i]->index) : -1); + } +} + + +// == popup menu slots and functions =========================================== + +/* + * Save selected player, update the menu entries and show the popup menu + */ +void KFibsPlayerList::showContextMenu(KListView *, QListViewItem *i, const QPoint &p) +{ + /* + * Get the name of the selected player + */ + d->mUser = (i ? i->text(Player) : QString::null); + + d->mAct[KFibsPlayerListPrivate::Info ]->setText(i18n("Info on %1" ).arg(d->mUser)); + d->mAct[KFibsPlayerListPrivate::Talk ]->setText(i18n("Talk to %1" ).arg(d->mUser)); + d->mAct[KFibsPlayerListPrivate::Mail ]->setText(i18n("Email to %1").arg(d->mUser)); + d->mAct[KFibsPlayerListPrivate::Look ]->setText(i18n("Look at %1" ).arg(d->mUser)); + d->mAct[KFibsPlayerListPrivate::Watch ]->setText(i18n("Watch %1" ).arg(d->mUser)); + d->mAct[KFibsPlayerListPrivate::Update]->setText(i18n("Update %1" ).arg(d->mUser)); + + d->mAct[KFibsPlayerListPrivate::Info ]->setEnabled(i); + d->mAct[KFibsPlayerListPrivate::Talk ]->setEnabled(i); + d->mAct[KFibsPlayerListPrivate::Mail ]->setEnabled(i); + d->mAct[KFibsPlayerListPrivate::Look ]->setEnabled(i); + d->mAct[KFibsPlayerListPrivate::Watch ]->setEnabled(i); + d->mAct[KFibsPlayerListPrivate::Update ]->setEnabled(i); + d->mAct[KFibsPlayerListPrivate::BlindAct]->setEnabled(i); + + d->mAct[KFibsPlayerListPrivate::Unwatch]->setEnabled(d->mWatch); + + d->mPm[0]->setItemEnabled(d->mInID, i && d->mName != d->mUser); + d->mPm[0]->changeItem(d->mInID, i18n("Invite %1").arg(d->mUser)); + + d->mMail = (i && d->mCol[Email]->show ? i->text(d->mCol[Email]->index) : QString::null); + d->mAct[KFibsPlayerListPrivate::Mail]->setEnabled(!d->mMail.isEmpty()); + + if (i && d->mCol[Status]->show) + d->mAct[KFibsPlayerListPrivate::BlindAct]->setText + ((i->text(d->mCol[Status]->index).contains(d->mAbrv[Blind])) ? + i18n("Unblind %1").arg(d->mUser) : i18n("Blind %1").arg(d->mUser)); + else + d->mAct[KFibsPlayerListPrivate::BlindAct]->setText(i18n("Blind")); + + // show the menu + d->mPm[0]->popup(p); +} + +/* + * Reload the entire list + */ +void KFibsPlayerList::slotReload() +{ + emit fibsCommand("rawwho"); + clear(); +} + +/* + * Stop watching + */ +void KFibsPlayerList::slotUnwatch() +{ + emit fibsCommand("unwatch"); +} + +/* + * Blind/Unblind user + */ +void KFibsPlayerList::slotBlind() +{ + emit fibsCommand("blind " + d->mUser); +} + +/* + * Start talking to user + */ +void KFibsPlayerList::slotTalk() +{ + emit fibsTalk(d->mUser); +} + +/* + * Request information on user + */ +void KFibsPlayerList::slotInfo() +{ + emit fibsCommand("whois " + d->mUser); +} + +/* + * Look at user + */ +void KFibsPlayerList::slotLook() +{ + emit fibsCommand("look " + d->mUser); +} + +/* + * Send an email to player user at address email + */ +void KFibsPlayerList::slotMail() +{ + kapp->invokeMailer(d->mMail, QString::null); +} + +/* + * Request a new entry for user + */ +void KFibsPlayerList::slotUpdate() +{ + emit fibsCommand("rawwho " + d->mUser); +} + +/* + * Watch user and get an updated board + */ +void KFibsPlayerList::slotWatch() +{ + emit fibsCommand("watch " + d->mUser); + emit fibsCommand("board"); +} + +/* + * Request information about the selected user + */ +void KFibsPlayerList::getPlayerInfo(QListViewItem *i, const QPoint &, int col) +{ + int num = cIndex(col); + if (col < 0 || num < 0 || num > 2 || i->text(num).isEmpty()) + num = 0; + emit fibsCommand("whois " + i->text(num)); +} + +/* + * Invite the selected user. + */ +void KFibsPlayerList::slotInviteD() +{ + emit fibsInvite(d->mUser); +} +void KFibsPlayerList::slotInvite1() { emit fibsCommand("invite " + d->mUser + " 1"); } +void KFibsPlayerList::slotInvite2() { emit fibsCommand("invite " + d->mUser + " 2"); } +void KFibsPlayerList::slotInvite3() { emit fibsCommand("invite " + d->mUser + " 3"); } +void KFibsPlayerList::slotInvite4() { emit fibsCommand("invite " + d->mUser + " 4"); } +void KFibsPlayerList::slotInvite5() { emit fibsCommand("invite " + d->mUser + " 5"); } +void KFibsPlayerList::slotInvite6() { emit fibsCommand("invite " + d->mUser + " 6"); } +void KFibsPlayerList::slotInvite7() { emit fibsCommand("invite " + d->mUser + " 7"); } + +void KFibsPlayerList::slotInviteU() { emit fibsCommand("invite " + d->mUser + " unlimited"); } +void KFibsPlayerList::slotInviteR() { emit fibsCommand("invite " + d->mUser); } + + +// == inserting and updating the list ========================================== + +/* + * Add or change the entry of player with the corresponding string + * from the server - rawwho + */ +void KFibsPlayerList::changePlayer(const QString &line) +{ + char entry[LVEnd][100]; + char ready[2], away[2]; + QListViewItem *i; + QDateTime fromEpoch; + QString str_entry[LVEnd], tmp; + + entry[Status][0] = '\0'; + + // the line comes from FIBS and is 7 bit ASCII + sscanf(line.latin1(), "%99s %99s %99s %1s %1s %99s %99s %99s %99s %99s %99s %99s", entry[Player], entry[Opponent], + entry[Watches], ready, away, entry[Rating], entry[Experience], entry[Idle], entry[Time], + entry[Host], entry[Client], entry[Email]); + + // convert time + tmp = entry[Time]; + fromEpoch.setTime_t(tmp.toUInt()); + strcpy(entry[Time], fromEpoch.toString().latin1()); + + // clear empty strings and copy + for (int j = 0; j < LVEnd; j++) { + if ((str_entry[j] = entry[j]) == "-") + str_entry[j] = ""; + } + str_entry[Status].replace(Ready, 1, ready[0] == '0' ? "-" : d->mAbrv[Ready]); + str_entry[Status].replace(Away, 1, away [0] == '0' ? "-" : d->mAbrv[Away ]); + str_entry[Status].replace(Blind, 1, "-"); + + // disable drawing until the end of update + setUpdatesEnabled(false); + + // try to find the item in the list + QListViewItemIterator it(this); + for ( ; it.current(); ++it) { + if (it.current()->text(0) == str_entry[Player]) { + i = it.current(); + goto found; + } + } + + // getting here means we have to create a new entry + i = new KFibsPlayerListLVI(this); + + // count the KFibs and KBackgammon clients + if (str_entry[Client].contains("KFibs")) + d->mCount[0]++; + else if (str_entry[Client].contains(PROG_NAME)) + d->mCount[1]++; + + // new entry requires an update to the player count + updateCaption(); + + goto update; + + found: + + // getting here means the player is in the list - update private status + str_entry[Status].replace(Blind,1,i->text(Status).contains + (d->mAbrv[Blind]) ? d->mAbrv[Blind] : "-"); + + update: + + for (int j = 0; j < LVEnd; j++) { + if (d->mCol[j]->show) + i->setText(d->mCol[j]->index, str_entry[j]); + } + + // find out if we are watching somebody + if (d->mName == str_entry[Player]) + d->mWatch = !str_entry[Watches].isEmpty(); +} + +/* + * Remove player from the list + */ +void KFibsPlayerList::deletePlayer(const QString &player) +{ + QListViewItemIterator it(this); + for ( ; it.current(); ++it) { + if (it.current()->text(0) == player) { + if (it.current()->text(Client).contains(PROG_NAME)) + --d->mCount[1]; + else if (it.current()->text(Client).contains("KFibs")) + --d->mCount[0]; + delete it.current(); + updateCaption(); + return; + } + } +} + +/* + * Set/Unset the status stat in the corresponding column of the list + */ +void KFibsPlayerList::changePlayerStatus(const QString &player, int stat, bool flag) +{ + QListViewItem *i = 0; + + /* + * Find the correct line + */ + QListViewItemIterator it(this); + for ( ; it.current(); ++it) { + if (it.current()->text(Player) == player) { + i = it.current(); + break; + } + } + if (!i) return; + + /* + * Update the status flag + */ + i->setText(Status, i->text(Status).replace(stat, 1, (flag) ? d->mAbrv[stat] : "-")); +} + + +// == various slots and functions ============================================== + +/* + * Reverse column to index mapping. Return negative on error. + */ +int KFibsPlayerList::cIndex(int col) +{ + for (int i = 0; i < LVEnd; i++) + if (d->mCol[i]->index == col) + return i; + return -1; +} + +/* + * Catch hide events, so the engine's menu can be update. + */ +void KFibsPlayerList::showEvent(QShowEvent *e) +{ + KListView::showEvent(e); + emit windowVisible(true); +} + +/* + * Catch hide events, so the engine's menu can be update. + */ +void KFibsPlayerList::hideEvent(QHideEvent *e) +{ + emit windowVisible(false); + KListView::hideEvent(e); +} + +/* + * Called at the end of updates to re-enable the UI + */ +void KFibsPlayerList::stopUpdate() +{ + setUpdatesEnabled(true); + triggerUpdate(); +} + +/* + * Knowing our own name allows us to disable certain menu entries for + * ourselves. + */ +void KFibsPlayerList::setName(const QString &name) +{ + d->mName = name; +} + +/* + * Update the caption of the list by including the current client + * count + */ +void KFibsPlayerList::updateCaption() +{ + setCaption(i18n("Player List - %1 - %2/%3").arg(childCount()).arg(d->mCount[0]).arg(d->mCount[1])); +} + +/* + * Clear the list and reset the client counters + */ +void KFibsPlayerList::clear() +{ + d->mCount[0] = 0; + d->mCount[1] = 0; + QListView::clear(); +} + +// EOF diff --git a/kbackgammon/engines/fibs/kplayerlist.h b/kbackgammon/engines/fibs/kplayerlist.h new file mode 100644 index 00000000..701f9ace --- /dev/null +++ b/kbackgammon/engines/fibs/kplayerlist.h @@ -0,0 +1,298 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KPLAYERLIST_H +#define __KPLAYERLIST_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +class KTabCtl; +class KFibsPlayerListPrivate; + +/** + * + * A class that keeps track of players on the server. The server is flooding + * us with user information. At any given time we are able to have an current + * list of all loged-in players and their status. + * + * @short The FIBS backgammon engine player list + * @author Jens Hoefkens + * + */ +class KFibsPlayerList : public KListView +{ + Q_OBJECT + +public: + + /** + * Enumerate player status + */ + enum PStatus {Ready, Away, Blind, MaxStatus}; + + /** + * Enumerate the different columns of the list + */ + enum {Player, Opponent, Watches, Status, Rating, Experience, + Idle, Time, Host, Client, Email, LVEnd}; + + /** + * Constructor + */ + KFibsPlayerList(QWidget *parent = 0, const char *name = 0); + + /** + * Destructor + */ + virtual ~KFibsPlayerList(); + + /** + * Clear the list and reset the client counter + */ + virtual void clear(); + +public slots: + + /** + * Remove the player with the name given by the first word + */ + void deletePlayer(const QString &player); + + /** + * Change/Add the entry for the given player + */ + void changePlayer(const QString &line); + + /** + * Enables list redraws after an update + */ + void stopUpdate(); + + /** + * Read the UI settings from disk + */ + void readConfig(); + + /** + * Read the column info from disk + */ + void readColumns(); + + /** + * Restore settings from previously stored settings + */ + void saveConfig(); + + /** + * Change the status of a player + */ + void changePlayerStatus(const QString &player, int stat, bool flag); + + /** + * Fills the playerlist page into the notebook + */ + virtual void getSetupPages(KTabCtl *nb, int space); + + /** + * Save setting changes + */ + void setupOk(); + + /** + * Setup changes have been cancelled + */ + void setupCancel(); + + /** + * Set default settings + */ + void setupDefault(); + + /** + * Set our own name. This allows us to special case the context + * menu. + */ + void setName(const QString &name); + + /** + * Return the column index + */ + int cIndex(int col); + +protected: + + /** + * Catch show events, so the engine's menu can be update. + */ + virtual void showEvent(QShowEvent *e); + + /** + * Catch hide events, so the engine's menu can be update. + */ + virtual void hideEvent(QHideEvent *e); + +protected slots: + + /** + * Double click handler, requests information on a player + */ + void getPlayerInfo(QListViewItem *i, const QPoint &p, int col); + + /** + * Display a popup menu for the current player + */ + void showContextMenu(KListView *, QListViewItem *, const QPoint &); + + /** + * Reload the whole list + */ + void slotReload(); + + /** + * Upate the caption + */ + void updateCaption(); + + /** + * Watch user + */ + void slotWatch(); + + /** + * Update line of user + */ + void slotUpdate(); + + /** + * Request information on user + */ + void slotInfo(); + + /** + * Look at user + */ + void slotLook(); + + /** + * Send an email to user + */ + void slotMail(); + + /** + * Stop watching + */ + void slotUnwatch(); + + /** + * Blind user + */ + void slotBlind(); + + /** + * Talk to user + */ + void slotTalk(); + + /** + * Invite using the dialog + */ + void slotInviteD(); + + /** + * Invite to a 1 point match + */ + void slotInvite1(); + + /** + * Invite to a 2 point match + */ + void slotInvite2(); + + /** + * Invite to a 3 point match + */ + void slotInvite3(); + + /** + * Invite to a 4 point match + */ + void slotInvite4(); + + /** + * Invite to a 5 point match + */ + void slotInvite5(); + + /** + * Invite to a 6 point match + */ + void slotInvite6(); + + /** + * Invite to a 7 point match + */ + void slotInvite7(); + + /** + * Invite to resume a saved match + */ + void slotInviteR(); + + /** + * Invite to an unlimited match + */ + void slotInviteU(); + +signals: + + /** + * Send a command to the server + */ + void fibsCommand(const QString &); + + /** + * Initiate an invitation of a player + */ + void fibsInvite(const QString &); + + /** + * Request talking to player user + */ + void fibsTalk(const QString &); + + /** + * Allow the engine's menu to be updated + */ + void windowVisible(bool); + +private: + + KFibsPlayerListPrivate *d; + +}; + +#endif // __KPLAYERLIST_H diff --git a/kbackgammon/engines/generic/Makefile.am b/kbackgammon/engines/generic/Makefile.am new file mode 100644 index 00000000..da0f2fdb --- /dev/null +++ b/kbackgammon/engines/generic/Makefile.am @@ -0,0 +1,8 @@ +noinst_LTLIBRARIES = libkbggeneric.la + +libkbggeneric_la_SOURCES = kbgengine.cpp + +INCLUDES= -I$(top_srcdir)/kbackgammon $(all_includes) + +METASOURCES = AUTO + diff --git a/kbackgammon/engines/generic/kbgengine.cpp b/kbackgammon/engines/generic/kbgengine.cpp new file mode 100644 index 00000000..bbe528a6 --- /dev/null +++ b/kbackgammon/engines/generic/kbgengine.cpp @@ -0,0 +1,62 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#include +#include + +#include + +#include +#include "kbgengine.h" + + +/* + * Constructor initializes the QObject + */ +KBgEngine::KBgEngine(QWidget *parent, QString *name, QPopupMenu *pmenu) + : QObject(parent, name->local8Bit()) +{ + menu = pmenu; + cl = -1; + ct = new QTimer(this); + connect(ct, SIGNAL(timeout()), this, SLOT(done())); +} + +/* + * Destructor is empty + */ +KBgEngine::~KBgEngine() +{ + // empty +} + +/* + * Set the length of the commit timeout. Negative values disable the + * feature. + */ +void KBgEngine::setCommit(const double com) +{ + cl = int(1000*com); +} + +// EOF diff --git a/kbackgammon/engines/generic/kbgengine.h b/kbackgammon/engines/generic/kbgengine.h new file mode 100644 index 00000000..ee672f40 --- /dev/null +++ b/kbackgammon/engines/generic/kbgengine.h @@ -0,0 +1,298 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KBGENGINE_H +#define __KBGENGINE_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +class QTimer; +class QPopupMenu; + +class KDialogBase; + +class KBgStatus; + +/** + * + * Abstract class for a generic backgammon engine. Real engines have + * to inherit this and implement the interfaces. + * + * Engines can and will use the following global events described in + * the file eventsrc: + * + * "game over l" + * "game over w" + * + * "roll" + * "roll or double" + * + * "move" + * "invitation" + * + * @short Abstract base class for backgammon engines + * @author Jens Hoefkens + * + */ +class KBgEngine:public QObject +{ + Q_OBJECT public: + + enum Command { Redo, Undo, Roll, Cube, Done, Load }; + + /** + * Constructor + */ + KBgEngine (QWidget * parent = 0, QString * name = 0, QPopupMenu * pmenu = 0); + + /** + * Destructor + */ + virtual ~KBgEngine (); + + /** + * Fills the engine-specific page into the notebook + */ + virtual void getSetupPages (KDialogBase * nb) = 0; + + /** + * Called after the user clicked ok in the setup dialog. Time + * to save settings. + */ + virtual void setupOk () = 0; + + /** + * The user cancelled the setup + */ + virtual void setupCancel () = 0; + + /** + * Set engine defaults + */ + virtual void setupDefault () = 0; + + /** + * Called when the windows are about to be hidden. Engines + * should hide all their child windows. + * + * The default implementation does nothing. + */ + virtual void hideEvent () + { + } + + /** + * Called when the windows are about to be shown. Engines + * should show all visible child windows. + * + * The default implementation does nothing. + */ + virtual void showEvent () + { + } + + /** + * Start the engine. This is called pretty much right after + * the constructor. While the constructor may not have any + * user interaction, it is possible to display dialogs in + * start. + * + * The default implementation does nothing. + */ + virtual void start () + { + } + + /** + * Check with the engine if we can quit. This may require user + * interaction. + * + * The default implementation returns true. + */ + virtual bool queryClose () + { + return true; + } + + /** + * About to be closed. Let the engine exit properly. + * + * The default implementation returns true. + */ + virtual bool queryExit () + { + return true; + } + + /** + * Set the length of the commit timeout. Negative values + * disable the feature. + */ + void setCommit (const double com = 2.5); + +public slots: + /** + * Read user settings from the config file + */ + virtual void readConfig () = 0; + + /** + * Save user settings to the config file + */ + virtual void saveConfig () = 0; + + /** + * Roll dice for the player w + */ + virtual void rollDice (const int w) = 0; + + /** + * Double the cube of player w + */ + virtual void doubleCube (const int w) = 0; + + /** + * A move has been made on the board - see the board class + * for the format of the string s + */ + virtual void handleMove (QString * s) = 0; + + /** + * Undo the last move + */ + virtual void undo () = 0; + + /** + * Redo the last move + */ + virtual void redo () = 0; + + /** + * Roll dice for whoevers turn it is + */ + virtual void roll () = 0; + + /** + * Double the cube for whoevers can double right now + */ + virtual void cube () = 0; + + /** + * Reload the board to the last known sane state + */ + virtual void load () = 0; + + /** + * Commit a move + */ + virtual void done () = 0; + + /** + * Process the string cmd + */ + virtual void handleCommand (const QString & cmd) = 0; + + /** + * Start a new game + */ + virtual void newGame () + { + } + + /** + * Can we start a new game? + */ + virtual bool haveNewGame () + { + return false; + } + +signals: + + /** + * The text identifies the current game status - could be put + * into the main window caption + */ + void statText (const QString & msg); + + /** + * Text that should be displayed in the ongoing message window + */ + void infoText (const QString & msg); + + /** + * Emit the most recent game state + */ + void newState (const KBgStatus &); + + /** + * Tell the board that we need the current state of the board. + */ + void getState (KBgStatus *); + + /** + * Starts/ends the edit mode of the board + */ + void setEditMode (const bool f); + + /** + * Toggle RO/RW flag of the board + */ + void allowMoving (const bool fl); + + /** + * Announce that we will accept/reject the command cmd from + * now on + */ + void allowCommand (int cmd, bool f); + + /** + * Tell the board to undo the last move + */ + void undoMove (); + + /** + * Tell the board to redo the last undone move + */ + void redoMove (); + +protected: + + /** + * Context menu for the board + */ + QPopupMenu * menu; + + /** + * Commit timer + */ + QTimer *ct; + int cl; + +}; + +#endif // __KBGENGINE_H diff --git a/kbackgammon/engines/gnubg/Makefile.am b/kbackgammon/engines/gnubg/Makefile.am new file mode 100644 index 00000000..7c0c9e1e --- /dev/null +++ b/kbackgammon/engines/gnubg/Makefile.am @@ -0,0 +1,9 @@ +noinst_LTLIBRARIES = libkbggnubg.la + +libkbggnubg_la_SOURCES = kbggnubg.cpp + +INCLUDES= -I$(top_srcdir)/kbackgammon -I$(top_srcdir)/kbackgammon/engines \ + $(all_includes) + +METASOURCES = AUTO + diff --git a/kbackgammon/engines/gnubg/kbggnubg.cpp b/kbackgammon/engines/gnubg/kbggnubg.cpp new file mode 100644 index 00000000..eaaa4bdf --- /dev/null +++ b/kbackgammon/engines/gnubg/kbggnubg.cpp @@ -0,0 +1,710 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#include "kbggnubg.moc" +#include "kbggnubg.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "kbgstatus.h" +#include "kbgboard.h" +#include "version.h" + + +// == cube ===================================================================== + +/* + * Double the cube for the player that can double - asks player + */ +void KBgEngineGNU::cube() +{ + handleCommand("double"); +} + +/* + * Double the cube for player w + */ +void KBgEngineGNU::doubleCube(const int w) +{ + dummy = w; // avoid compiler warning + cube(); +} + + + + + +void KBgEngineGNU::handleLine(const QString &l) +{ + if (l.isEmpty()) + return; + + QString line(l); + + /* + * Start of a new game/match + */ + if (line.contains(QRegExp("^gnubg rolls [1-6], .* rolls [1-6]\\."))) { + KRegExp e("^gnubg rolls ([1-6]), .* rolls ([1-6])\\."); + e.match(line.latin1()); + if (int r = strcmp(e.group(1), e.group(2))) + turn = (r < 0) ? uRoll : tRoll; + } + + /* + * Bug fixes for older versions of GNUBG - to be removed + */ + if (line.contains(QRegExp("^.* cannot move\\..+$"))) { + KRegExp e("(^.* cannot move.)(.*$)"); + e.match(line.latin1()); + handleLine(e.group(1)); + handleLine(e.group(2)); + return; + } + if (line.contains(QRegExp("^Are you sure you want to start a new game, and discard the one in progress\\?"))) { + KRegExp e("(^Are you sure you want to start a new game, and discard the one in progress\\? )(.+$)"); + e.match(line.latin1()); + handleLine(e.group(1)); + handleLine(e.group(2)); + return; + } + + /* + * Cube handling + */ + if (line.contains(QRegExp("^gnubg accepts and immediately redoubles to [0-9]+\\.$"))) { + + // redoubles mess up the game counter "turn" + + //KBgStatus st(board); + //st.setCube(32, BOTH); + //emit newState(st); + + } + if (line.contains(QRegExp("^gnubg doubles\\.$"))) { + + // TODO: we need some generic class for this. the class + // can be shared between all engines + +#if 0 + KBgStatus st(board); + + int ret = KMessageBox::warningYesNoCancel + (0, i18n("gnubg doubles the cube to %1.").arg(2*st.cube(THEM)), + i18n("gnubg doubles"), + i18n("&Accept"), i18n("Re&double"), i18n("&Reject"), true); + + switch (ret) { + + case KMessageBox::Yes: + handleCommand("accept"); + break; + + case KMessageBox::No: + handleCommand("redouble"); + break; + + case KMessageBox::Cancel: + handleCommand("reject"); + break; + } +#endif + } + + /* + * Ignore the following messages + */ + if (line.contains(QRegExp("^TTY boards will be given in raw format"))) { + line = " "; + } + + /* + * Board messages + */ + if (line.contains(QRegExp("^board:"))) { + + KBgStatus st(line); + + /* + * Do preliminary analysis of board + */ + if (st.doubled()) { + --turn; + return; + } + if (strcmp(board.latin1(),line.latin1())) + ++turn %= maxTurn; + board = line; + + /* + * Act according to the current state in the move/roll cycle + */ + switch (turn) { + + case uRoll: + + if (st.cube() > 0) { + emit infoText(i18n("Please roll or double.")); + KNotifyClient::event("roll or double"); + } else { + emit infoText(i18n("Please roll.")); + KNotifyClient::event("roll"); + } + + emit allowCommand(Roll, true); + emit allowCommand(Cube, true); + break; + + case uMove: + st.setDice(THEM, 0, 0); + st.setDice(THEM, 1, 0); + emit infoText(i18n("You roll %1 and %2.").arg(st.dice(US, 0)).arg(st.dice(US, 1))); + switch (st.moves()) { + case 0: + // get a message + break; + case 1: + emit infoText(i18n("Please move 1 piece.")); + break; + default: + emit infoText(i18n("Please move %1 pieces.").arg(st.moves())); + break; + } + emit allowCommand(Roll, false); + break; + + case tRoll: + break; + + case tMove: + st.setDice(US, 0, 0); + st.setDice(US, 1, 0); + emit infoText(i18n("gnubg rolls %1 and %2.").arg(st.dice(THEM, 0)).arg(st.dice(THEM, 1))); + if (st.moves() == 0) + emit infoText(i18n("gnubg cannot move.")); + + break; + + } + + /* + * Bookkeeping + */ + undoCounter = 0; + toMove = st.moves(); + emit allowMoving(st.turn() == US); + emit newState(st); + + emit statText(i18n("%1 vs. %2").arg(st.player(US)).arg(st.player(THEM))); + + emit allowCommand(Load, true ); + emit allowCommand(Undo, false); + emit allowCommand(Redo, false); + emit allowCommand(Done, false); + return; + } + + /* + * Show the line... + */ + line.replace(QRegExp(" "), " "); + if (!line.isEmpty()) + emit infoText(line); +} + + +/* + * Handle textual commands. All commands are passed to gnubg. + */ +void KBgEngineGNU::handleCommand(const QString& cmd) +{ + cmdList += cmd; + nextCommand(); +} + + + +// == start and init games ===================================================== + +/* + * Start a new game. + */ +void KBgEngineGNU::newGame() +{ + /* + * If there is a game running we warn the user first + */ + if (gameRunning && (KMessageBox::warningYesNo((QWidget *)parent(), + i18n("A game is currently in progress. " + "Starting a new one will terminate it."), + QString::null, i18n("Start New Game"), i18n("Continue Old Game")) + == KMessageBox::No)) + return; + + /* + * Start new game + */ + handleCommand("new game"); + if (gameRunning) + handleCommand("yes"); + + gameRunning = true; + + emit infoText(i18n("Starting a new game.")); +} + + + +// == various slots & functions ================================================ + +/* + * Quitting is fine at any time + */ +bool KBgEngineGNU::queryClose() +{ + return true; +} + +/* + * Quitting is fine at any time + */ +bool KBgEngineGNU::queryExit() +{ + return true; +} + +/* + * Load the last known sane state of the board + */ +void KBgEngineGNU::load() +{ + handleCommand("show board"); +} + +/* + * Store if cmd is allowed or not + */ +void KBgEngineGNU::setAllowed(int cmd, bool f) +{ + switch (cmd) { + case Roll: + rollingAllowed = f; + return; + case Undo: + undoPossible = f; + return; + case Cube: + doublePossible = f; + return; + case Done: + donePossible = f; + return; + } +} + + + + + + + + + + + + + + + +// == configuration handling =================================================== + +void KBgEngineGNU::setupOk() +{ + // nothing yet +} + +void KBgEngineGNU::setupCancel() +{ + // nothing yet +} + +void KBgEngineGNU::setupDefault() +{ + // nothing yet +} + +void KBgEngineGNU::getSetupPages(KDialogBase *nb) +{ + /* + * Main Widget + */ + QVBox *w = nb->addVBoxPage(i18n("GNU Engine"), i18n("Here you can configure the GNU backgammon engine"), + kapp->iconLoader()->loadIcon(PROG_NAME "_engine", KIcon::Desktop)); +} + +/* + * Restore settings + */ +void KBgEngineGNU::readConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("gnu engine"); + + // nothing yet +} + +/* + * Save the engine specific settings + */ +void KBgEngineGNU::saveConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("gnu engine"); + + // nothing yet +} + + + +// ***************************************************************************** +// ***************************************************************************** +// ***************************************************************************** +// ***************************************************************************** +// ***************************************************************************** +// ***************************************************************************** + + + + +// == constructor, destructor and other ======================================== + +/* + * Constructor + */ +KBgEngineGNU::KBgEngineGNU(QWidget *parent, QString *name, QPopupMenu *pmenu) + : KBgEngine(parent, name, pmenu) +{ + // obsolete + nameUS = "US"; + nameTHEM = "THEM"; + random.setSeed(getpid()*time(NULL)); + + /* + * internal statue variables + */ + rollingAllowed = undoPossible = gameRunning = donePossible = false; + connect(this, SIGNAL(allowCommand(int, bool)), this, SLOT(setAllowed(int, bool))); + + /* + * Setup of menu + */ + resAction = new KAction(i18n("&Restart GNU Backgammon"), 0, this, SLOT(startGNU()), this); + resAction->setEnabled(false); resAction->plug(menu); + + /* + * Restore last stored settings + */ + readConfig(); +} + +/* + * Destructor. Kill the child process and that's it. + */ +KBgEngineGNU::~KBgEngineGNU() +{ + gnubg.kill(); +} + + +// == start, restart, termination of gnubg ===================================== + +/* + * Start the GNU Backgammon process in the background and set up + * some communication links. + */ +void KBgEngineGNU::start() +{ + /* + * Will be started later + */ + cmdTimer = new QTimer(this); + connect(cmdTimer, SIGNAL(timeout()), SLOT(nextCommand()) ); + + emit infoText(i18n("This is experimental code which currently requires a specially " + "patched version of GNU Backgammon.

")); + + /* + * Initialize variables + */ + partline = board = ""; + + /* + * Start the process - this requires that gnubg is in the PATH + */ + gnubg << "gnubg" << "--tty"; + + connect(&gnubg, SIGNAL(processExited(KProcess *)), this, SLOT(gnubgExit(KProcess *))); + connect(&gnubg, SIGNAL(receivedStderr(KProcess *, char *, int)), + this, SLOT(receiveData(KProcess *, char *, int))); + connect(&gnubg, SIGNAL(receivedStdout(KProcess *, char *, int)), + this, SLOT(receiveData(KProcess *, char *, int))); + connect(&gnubg, SIGNAL(wroteStdin(KProcess *)), this, SLOT(wroteStdin(KProcess *))); + + startGNU(); +} + +/* + * Actually start the background process. + */ +void KBgEngineGNU::startGNU() +{ + + resAction->setEnabled(false); + + if (!gnubg.start(KProcess::NotifyOnExit, KProcess::All)) + KMessageBox::information((QWidget *)parent(), + i18n("Could not start the GNU Backgammon process.\n" + "Make sure the program is in your PATH and is " + "called \"gnubg\".\n" + "Make sure that your copy is at least version 0.10")); + + /* + * Set required gnubg options + */ + handleCommand("set output rawboard on"); +} + +/* + * The gnubg process has died. Stop all user activity and allow a restart. + */ +void KBgEngineGNU::gnubgExit(KProcess *proc) +{ + ct->stop(); + + cmdTimer->stop(); + + emit allowCommand(Undo, false); + emit allowCommand(Roll, false); + emit allowCommand(Done, false); + emit allowCommand(Cube, false); + emit allowCommand(Load, false); + + emit allowMoving(false); + + emit infoText(QString("
") + i18n("The GNU Backgammon process (%1) has exited. ") + .arg(proc->pid()) + "
"); + + resAction->setEnabled(true); +} + + +// == communication callbacks with GNU bg ====================================== + +/* + * Last command has been sent. Try to send pending ones. + */ +void KBgEngineGNU::wroteStdin(KProcess *proc) +{ + if (!proc->isRunning()) + return; + nextCommand(); +} + +/* + * Try to send the next command from the command list to gnubg. + * If it fails, make sure we call ourselves again. + */ +void KBgEngineGNU::nextCommand() +{ + if (!gnubg.isRunning()) + return; + + for (QStringList::Iterator it = cmdList.begin(); it != cmdList.end(); ++it) { + QString s = (*it) + "\n"; + if (!gnubg.writeStdin(s.latin1(), strlen(s.latin1()))) { + cmdTimer->start(250, true); + cmdList.remove(QString::null); + return; + } + (*it) = QString::null; + } + cmdList.remove(QString::null); + cmdTimer->stop(); +} + +/* + * Get data from GNU Backgammon and process them. Note that we may have + * to buffer the last line and wait for the closing newline... + */ +void KBgEngineGNU::receiveData(KProcess *proc, char *buffer, int buflen) +{ + if (!proc->isRunning()) + return; + + char *buf = new char[buflen+1]; + + memcpy(buf, buffer, buflen); + buf[buflen] = '\0'; + + QStringList l(QStringList::split('\n', buf, true)); + + /* + * Restore partial lines from the previous time + */ + l.first() = partline + l.first(); + partline = ""; + if (buf[buflen-1] != '\n') { + partline = l.last(); + l.remove(partline); + } + + delete[] buf; + + /* + * Handle the information from gnubg + */ + for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) + handleLine(*it); +} + + +// == moving =================================================================== + +/* + * Finish the last move - called by the timer and directly by the user + */ +void KBgEngineGNU::done() +{ + ct->stop(); + + emit allowMoving(false); + + emit allowCommand(Done, false); + emit allowCommand(Undo, false); + emit allowCommand(Redo, false); + + // Transform the string to FIBS format + lastmove.replace(0, 2, "move "); + lastmove.replace(QRegExp("\\+"), " "); + lastmove.replace(QRegExp("\\-"), " "); + + // sent it to the server + handleCommand(lastmove); +} + +/* + * Undo the last move + */ +void KBgEngineGNU::undo() +{ + ct->stop(); + + redoPossible = true; + ++undoCounter; + + emit allowMoving(true); + + emit allowCommand(Done, false); + emit allowCommand(Redo, true); + + emit undoMove(); +} + +/* + * Redo the last move + */ +void KBgEngineGNU::redo() +{ + --undoCounter; + emit redoMove(); +} + +/* + * Take the move string and make the changes on the working copy + * of the state. + */ +void KBgEngineGNU::handleMove(QString *s) +{ + lastmove = *s; + + int index = 0; + QString t = s->mid(index, s->find(' ', index)); + index += 1 + t.length(); + int moves = t.toInt(); + + /* + * Allow undo and possibly start the commit timer + */ + redoPossible &= ((moves < toMove) && (undoCounter > 0)); + + emit allowCommand(Undo, moves > 0); + emit allowCommand(Redo, redoPossible); + emit allowCommand(Done, moves == toMove); + + if (moves == toMove && cl >= 0) { + emit allowMoving(false); + ct->start(cl, true); + } +} + + +// == dice & rolling =========================================================== + +/* + * Roll random dice for the player whose turn it is. We can ignore the + * value of w, since we have the turn value. + */ +void KBgEngineGNU::roll() +{ + if (turn == uRoll) + handleCommand("roll"); +} +void KBgEngineGNU::rollDice(const int w) +{ + roll(); +} + + + +// EOF diff --git a/kbackgammon/engines/gnubg/kbggnubg.h b/kbackgammon/engines/gnubg/kbggnubg.h new file mode 100644 index 00000000..3240b8b1 --- /dev/null +++ b/kbackgammon/engines/gnubg/kbggnubg.h @@ -0,0 +1,223 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KBGGNU_H +#define __KBGGNU_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include + +/** + * + * + */ +class KBgEngineGNU : public KBgEngine +{ + Q_OBJECT + +public: + + /* + * Constructor and destructor + */ + KBgEngineGNU(QWidget *parent = 0, QString *name = 0, QPopupMenu *pmenu = 0); + virtual ~KBgEngineGNU(); + + /** + * Fills the engine-specific page into the notebook + */ + virtual void getSetupPages(KDialogBase *nb); + + virtual void setupOk(); + virtual void setupDefault(); + virtual void setupCancel(); + + /* + * Check with the engine if we can quit. This may require user + * interaction. + */ + virtual bool queryClose(); + + /** + * About to be closed. Let the engine exit properly. + */ + virtual bool queryExit(); + + virtual void start(); + +public slots: + + /** + * Read user settings from the config file + */ + virtual void readConfig(); + + /** + * Save user settings to the config file + */ + virtual void saveConfig(); + + /** + * Double the cube of player w + */ + virtual void doubleCube(const int w); + + /** + * A move has been made on the board - see the board class + * for the format of the string s + */ + virtual void handleMove(QString *s); + + /** + * Undo the last move + */ + virtual void undo(); + + /** + * Redo the last move + */ + virtual void redo(); + + /** + * Roll dice for whoevers turn it is + */ + virtual void roll(); + + /** + * Double the cube for whoevers can double right now + */ + virtual void cube(); + + /** + * Reload the board to the last known sane state + */ + virtual void load(); + + /** + * Commit a move + */ + virtual void done(); + + /* + * Roll dice for the player w + */ + virtual void rollDice(const int w); + + /** + * Process the string cmd + */ + virtual void handleCommand(const QString& cmd); + + /** + * Start a new game. + */ + virtual void newGame(); + virtual bool haveNewGame() {return true;} + +protected slots: + + /** + * Store if cmd is allowed or not + */ + void setAllowed(int cmd, bool f); + + void startGNU(); + +private: + + /** + * Use the standard method of obtaining random numbers + */ + KRandomSequence random; + + /** + * Player's names + */ + QString nameUS, nameTHEM; + + /** + * Who did the last roll + */ + int lastRoll; + + /** + * How many checkers to move + */ + int toMove; + + /** + * Various flags, representing the current status of the game + */ + bool rollingAllowed, undoPossible, donePossible; + bool gameRunning, redoPossible, doublePossible; + + /** + * Count the number of available undos + */ + int dummy, undoCounter; + +private: + + enum Turn {uRoll, uMove, tRoll, tMove, maxTurn}; + + KProcess gnubg; + + QStringList cmdList; + + QTimer *cmdTimer; + + QString partline; + + QString board; + + QString lastmove; + + int turn; + + KAction *resAction; + +protected slots: + + void wroteStdin(KProcess *); + + void receiveData(KProcess *, char *buffer, int buflen); + + void handleLine(const QString &l); + + void gnubgExit(KProcess *proc); + + void nextCommand(); + +}; + +#endif // __KBGGNU_H diff --git a/kbackgammon/engines/nextgen/Makefile.am b/kbackgammon/engines/nextgen/Makefile.am new file mode 100644 index 00000000..ed58d2f4 --- /dev/null +++ b/kbackgammon/engines/nextgen/Makefile.am @@ -0,0 +1,9 @@ +noinst_LTLIBRARIES = libkbgnextgen.la + +libkbgnextgen_la_SOURCES = kbgng.cpp kbgplayer.cpp kbggame.cpp + +INCLUDES= -I$(top_srcdir)/kbackgammon -I$(top_srcdir)/kbackgammon/engines \ + -I$(top_srcdir)/libkdegames/kgame $(all_includes) + +METASOURCES = AUTO + diff --git a/kbackgammon/engines/nextgen/kbggame.cpp b/kbackgammon/engines/nextgen/kbggame.cpp new file mode 100644 index 00000000..6ee709e1 --- /dev/null +++ b/kbackgammon/engines/nextgen/kbggame.cpp @@ -0,0 +1,47 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#include "kbggame.moc" +#include "kbggame.h" + +#include + +#include + +/* + * Constructor + */ +KBgGame::KBgGame(int cookie, QObject *parent) + : KGame(cookie, parent) +{ + // do nothing... +} + +bool KBgGame::playerInput(QDataStream &msg,KPlayer *player) +{ + Q_INT32 move; + msg >> move; + cerr << " Player " << player->id() << " moved to " << move << endl; + return true; +} + diff --git a/kbackgammon/engines/nextgen/kbggame.h b/kbackgammon/engines/nextgen/kbggame.h new file mode 100644 index 00000000..fea5f516 --- /dev/null +++ b/kbackgammon/engines/nextgen/kbggame.h @@ -0,0 +1,57 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KBGGAME_H +#define __KBGGAME_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +class QObject; +class KPlayer; + +/** + * + * + */ +class KDE_EXPORT KBgGame : public KGame +{ + Q_OBJECT + +public: + + enum MsgID {Text, Cmd, MaxMsg}; + + KBgGame(int cookie = 42, QObject *parent = 0); + +protected: + + virtual bool playerInput(QDataStream &msg,KPlayer *player); + +}; + +#endif // __KBGGAME_H + diff --git a/kbackgammon/engines/nextgen/kbgng.cpp b/kbackgammon/engines/nextgen/kbgng.cpp new file mode 100644 index 00000000..6518147c --- /dev/null +++ b/kbackgammon/engines/nextgen/kbgng.cpp @@ -0,0 +1,622 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#include "kbgng.moc" +#include "kbgng.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + + +/* + * Constructor + */ +KBgEngineNg::KBgEngineNg(QWidget *parent, QString *name, QPopupMenu *pmenu) + : KBgEngine(parent, name, pmenu) +{ + // get a new game + initGame(); + + // create actions and menus + QString label[MaxTypes]; + + label[Local ] = i18n("Local Games"); + label[NetServer] = i18n("Offer Network Games"); + label[NetClient] = i18n("Join Network Games"); + + QStringList list; + for (int i = 0; i < MaxTypes; i++) + list.append(label[i]); + + _gameSelect = new KSelectAction(i18n("&Types"), 0, this, SLOT(setGame()), this); + _gameSelect->setItems(list); + _gameSelect->plug(menu); + + menu->insertSeparator(); + + _connectAction = new KAction(i18n("&Names..."), 0, this, SLOT(changeName()), this); + _connectAction->plug(menu); + + // Restore last settings + readConfig(); + + // initialize to local games + _player[0] = _player[1] = 0; + _currGame = None; + _gameSelect->setCurrentItem(Local); + setGame(); +} + + +/* + * Switch the local game type. This is called by the menu... + * + * TODO: lots of work and testing needed... + */ +void KBgEngineNg::setGame() +{ + // shutdown old game + switch (_currGame) { + + case Local: + // nothing to do... + break; + + case NetServer: + _game->stopServerConnection(); + break; + + case NetClient: + _game->disconnect(); + break; + + default: + // ignore + break; + } + + // reset the game and delete the players + delete _game; + initGame(); + + emit infoText("
"); + + // initialize a new game + bool ret = false; + QString label, port_s, host_s; + Q_UINT16 port; + + switch (_currGame = _gameSelect->currentItem()) { + + case Local: + + _game->addPlayer(createPlayer(0, _name[0])); + _game->addPlayer(createPlayer(1, _name[1])); + break; + + case NetServer: + label = i18n("Type the port number on which you want to listen to " + "connections.\nThe number should be between 1024 and " + "65535."); + port_s.setNum(_port); + do { + port_s = KLineEditDlg::getText(label, port_s, &ret, (QWidget *)parent()); + if (!ret) + return; + port = port_s.toUShort(&ret); + } while (port_s.isEmpty() && !ret); + + if (_game->offerConnections(port)) + emit infoText(i18n("Now waiting for incoming connections on port %1."). + arg(_port = port)); + else + emit infoText(i18n("Failed to offer connections on port %1.").arg(port)); + + _game->addPlayer(createPlayer(0, _name[0])); + break; + + case NetClient: + label = i18n("Type the name of the server you want to connect to:"); + host_s = _host; + do { + host_s = KLineEditDlg::getText(label, host_s, &ret, (QWidget *)parent()); + if (!ret) + return; + } while (host_s.isEmpty()); + + label = i18n("Type the port number on %1 you want to connect to.\nThe " + "number should be between 1024 and 65535.").arg(host_s); + port_s.setNum(_port); + do { + port_s = KLineEditDlg::getText(label, port_s, &ret, (QWidget *)parent()); + if (!ret) + return; + port = port_s.toUShort(&ret); + } while (port_s.isEmpty() && !ret); + + /* + * Hi Martin: another thing you night want to try is to move this to the + * place marked by (about 10 lines further down. If you do that, the + * players are created properly on the server, but a total of three players + * is created on the client. + */ + _game->addPlayer(createPlayer(0, _name[0])); + + if (_game->connectToServer(host_s, port)) + emit infoText(i18n("Now connected to %1:%2.").arg(_host = host_s). + arg(_port = port)); + else + emit infoText(i18n("Failed to connect to %1:%2.").arg(_host = host_s). + arg(_port = port)); + + // + + break; + + default: + kdDebug(true, PROG_COOKIE) << "setGame parameter invalid: " + << _currGame << endl; + _currGame = None; + return; + } + + // we are still having problems with player creation... + + // FIXME - which status _game->setGameStatus(KGame::End); +} + +void KBgEngineNg::slotPlayerJoinedGame(KPlayer *p) +{ + emit infoText(i18n("Player %1 (%2) has joined the game.").arg(p->name()).arg(p->id())); + cerr << i18n("Player %1 (%2) has joined the game.").arg(p->name()).arg(p->id()).latin1() << endl; +} + +void KBgEngineNg::slotCreatePlayer(KPlayer *&p, int rtti, int io, bool v, KGame *g) +{ + Q_UNUSED(rtti) + Q_UNUSED(g) + Q_UNUSED(io) + emit infoText(i18n("creating player. virtual=%1").arg(v)); + p = createPlayer(1); +} + +void KBgEngineNg::slotClientConnected(Q_UINT32) +{ + cerr << "client has joint the game..." << endl; +} + +void KBgEngineNg::slotClientDisconnected(Q_UINT32, bool) +{ + cerr << "KBgEngineNg::slotClientDisconnected" << endl; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +// == start and init games ===================================================== + +/* + * Start a new game. ... + */ +void KBgEngineNg::newGame() +{ + // TODO + cerr << "games are not yet working..." << endl; +} + +/* + * Finish the last move - called by the timer and directly by the used + */ +void KBgEngineNg::done() +{ + // empty +} + +/* + * Undo the last move + */ +void KBgEngineNg::undo() +{ + // TODO +} + +/* + * Redo the last move + */ +void KBgEngineNg::redo() +{ + // TODO +} + +/* + * Take the move string and make the changes on the working copy + * of the state. + */ +void KBgEngineNg::handleMove(QString *s) +{ + Q_UNUSED(s) + // TODO +} + +/* + * Roll random dice for the player whose turn it is + */ +void KBgEngineNg::roll() +{ + // empty +} + +/* + * If possible, roll random dice for player w + */ +void KBgEngineNg::rollDice(const int w) +{ + Q_UNUSED(w) + // empty +} + +/* + * Double the cube for the player that can double - asks player + */ +void KBgEngineNg::cube() +{ + // TODO +} + +/* + * Double the cube for player w + */ +void KBgEngineNg::doubleCube(const int) +{ + cube(); +} + +/* + * Put the engine specific details in the setup dialog + */ +void KBgEngineNg::getSetupPages(KDialogBase *) +{ + // FIXME: do nothing... +} + +/* + * Called when the setup dialog is positively closed + */ +void KBgEngineNg::setupOk() +{ + // FIXME: do nothing... +} +void KBgEngineNg::setupDefault() +{ + // FIXME: do nothing... +} +void KBgEngineNg::setupCancel() +{ + // FIXME: do nothing... +} + + +// == various slots & functions ================================================ + +/* + * Check with the user if we should really quit in the middle of a + * game. + */ +bool KBgEngineNg::queryClose() +{ + return true; +} + +/* + * Quitting is fine at any time + */ +bool KBgEngineNg::queryExit() +{ + return true; +} + +/* + * Load the last known sane state of the board + */ +void KBgEngineNg::load() +{ + // TODO +} + +/* + * Store if cmd is allowed or not + */ +void KBgEngineNg::setAllowed(int cmd, bool f) +{ + switch (cmd) { + case Roll: + rollingAllowed = f; + return; + case Undo: + undoPossible = f; + return; + case Cube: + doublePossible = f; + return; + case Done: + donePossible = f; + return; + } +} + + +// ******************************************************************************** +// ******************************************************************************** + +// DONE + +// ******************************************************************************** +// ******************************************************************************** + + +/* + * Destructor. + */ +KBgEngineNg::~KBgEngineNg() +{ + saveConfig(); + delete _game; +} + +/* + * Restore settings + */ +void KBgEngineNg::readConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("next generation engine"); + + _port = config->readNumEntry("port", PROG_COOKIE); + _host = config->readEntry("host", "localhost"); + + _name[0] = config->readEntry("name_0", i18n("one")); + _name[1] = config->readEntry("name_1", i18n("two")); +} + +/* + * Save the engine specific settings + */ +void KBgEngineNg::saveConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("next generation engine"); + + config->writeEntry("port", _port); + config->writeEntry("host", _host); + + config->writeEntry("name_0", _name[0]); + config->writeEntry("name_1", _name[1]); +} + +/* + * Read the users input from the command line and send it to all + * players. Although the message gets the Cmd ID, it is currently + * handled as a regular text message. + */ +void KBgEngineNg::handleCommand(const QString& text) +{ + QByteArray msg; + QTextStream ts(msg, IO_WriteOnly); + ts << text; + if (!_game->sendMessage(msg, KBgGame::Cmd)) + kdDebug(true, PROG_COOKIE) << "couldn't send message: " + << text.latin1() << endl; +} + +/* + * Return a random integer between 1 and 6. Use the KGame random + * number generator. + */ +int KBgEngineNg::getRandom() +{ + return 1+_game->random()->getLong(6); +} + +/* + * A player propert has changed - check if we care + */ +void KBgEngineNg::slotPropertyChanged(KGamePropertyBase *p, KPlayer *me) +{ + int player = (me->id() == _player[1]->id()); + + switch (p->id()) { + + case KGamePropertyBase::IdName: + emit infoText(i18n("Player %1 has changed the name to %2.") + .arg(_name[player]).arg(me->name())); + _name[player] = me->name(); + break; + + default: + kdDebug(true, PROG_COOKIE) << "KBgPlayer (" << me << ") property change (" + << p->id() << ") ignored" << endl; + break; + } +} + +/* + * A game property has changed + */ +void KBgEngineNg::slotPropertyChanged(KGamePropertyBase *p, KGame *me) +{ + Q_UNUSED(me) + switch (p->id()) { + + default: + kdDebug(true, PROG_COOKIE) << "Change in GameProperty " << p->id() + << " has been ignored." << endl; + break; + } +} + +/* + * Change the names of all local players + */ +void KBgEngineNg::changeName() +{ + bool ok = false; + QString name; + + for (int i = 0; i < 2; i++) { + name = QString::null; + while (!_player[i]->isVirtual() && name.isEmpty()) { + if (i == 0) + name = KLineEditDlg::getText(i18n("Type the name of the first player:"), + _name[i], &ok, (QWidget *)parent()); + else + name = KLineEditDlg::getText(i18n("Type the name of the second player:"), + _name[i], &ok, (QWidget *)parent()); + if (!ok) return; + _player[i]->setName(name); + } + } +} + +/* + * Receive data sent via KBgGame::sendMessage(...) + */ +void KBgEngineNg::slotNetworkData(int msgid, const QByteArray &msg, Q_UINT32 r, Q_UINT32 s) +{ + Q_UNUSED(r); + Q_UNUSED(s); + switch (msgid) { + + case KBgGame::Cmd: + emit infoText(msg); + emit infoText(i18n("Players are %1 and %2").arg(_player[0]->name()) + .arg(_player[1]->name())); + break; + + default: + kdDebug(true, PROG_COOKIE) << "Ignored message ID: " << msgid << endl; + break; + } +} + +/* + * Create the i-th player + */ +KBgPlayer * KBgEngineNg::createPlayer(int i, QString name) +{ + KBgPlayer *p = new KBgPlayer(); + + if (!name.isNull()) + p->setName(name); + + p->findProperty(KGamePropertyBase::IdName)->setEmittingSignal(true); + + connect(p, SIGNAL(signalPropertyChanged(KGamePropertyBase *, KPlayer *)), + this, SLOT(slotPropertyChanged(KGamePropertyBase *, KPlayer *))); + + return (_player[i] = p); +} + +/* + * Create and connect the game object + */ +void KBgEngineNg::initGame() +{ + _game = new KBgGame(PROG_COOKIE); + _game->random()->setSeed(getpid()*time(NULL)); + + connect(_game, SIGNAL(signalPlayerJoinedGame(KPlayer *)), + this, SLOT(slotPlayerJoinedGame(KPlayer *))); + connect(_game, SIGNAL(signalCreatePlayer(KPlayer *&, int, int, bool, KGame *)), + this, SLOT(slotCreatePlayer(KPlayer *&, int, int, bool, KGame *))); + + connect(_game, SIGNAL(signalClientConnected(Q_UINT32)), + this, SLOT(slotClientConnected(Q_UINT32))); + connect(_game, SIGNAL(signalClientDisconnected(Q_UINT32, bool)), + this, SLOT(slotClientDisconnected(Q_UINT32, bool))); + + connect(_game, SIGNAL(signalPropertyChanged(KGamePropertyBase *, KGame *)), + this, SLOT(slotPropertyChanged(KGamePropertyBase *, KGame *))); + connect(_game, SIGNAL(signalNetworkData(int,const QByteArray &, Q_UINT32, Q_UINT32)), + this, SLOT(slotNetworkData(int,const QByteArray &, Q_UINT32, Q_UINT32))); +} + +// EOF diff --git a/kbackgammon/engines/nextgen/kbgng.h b/kbackgammon/engines/nextgen/kbgng.h new file mode 100644 index 00000000..149f3bf6 --- /dev/null +++ b/kbackgammon/engines/nextgen/kbgng.h @@ -0,0 +1,263 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KBGNG_H +#define __KBGNG_H + +#ifdef HAVE_CONFIG_H +#include +#endif + + +#include +#include +#include +#include +#include + +#include + +#include "kbgboard.h" +#include "kbgstatus.h" +#include "kbgplayer.h" +#include "kbggame.h" + + +/** + * + * The interface of the next generation backgammon engine. + * + */ +class KBgEngineNg : public KBgEngine +{ + Q_OBJECT + +public: + + /* + * Constructor and destructor + */ + KBgEngineNg( QWidget *parent = 0, QString *name = 0, QPopupMenu *pmenu = 0); + virtual ~KBgEngineNg(); + + /** + * Fills the engine-specific page into the notebook + */ + virtual void getSetupPages(KDialogBase *nb); + + virtual void setupOk(); + virtual void setupDefault(); + virtual void setupCancel(); + + /* + * Check with the engine if we can quit. This may require user + * interaction. + */ + virtual bool queryClose(); + + /** + * About to be closed. Let the engine exit properly. + */ + virtual bool queryExit(); + + +public slots: + + /** + * Read user settings from the config file + */ + virtual void readConfig(); + + /** + * Save user settings to the config file + */ + virtual void saveConfig(); + + /** + * Roll dice for the player w + */ + virtual void rollDice(const int w); + + /** + * Double the cube of player w + */ + virtual void doubleCube(const int w); + + /** + * A move has been made on the board - see the board class + * for the format of the string s + */ + virtual void handleMove(QString *s); + + /** + * Undo the last move + */ + virtual void undo(); + + /** + * Redo the last move + */ + virtual void redo(); + + /** + * Roll dice for whoevers turn it is + */ + virtual void roll(); + + /** + * Double the cube for whoevers can double right now + */ + virtual void cube(); + + /** + * Reload the board to the last known sane state + */ + virtual void load(); + + /** + * Commit a move + */ + virtual void done(); + + /** + * Process the string text + */ + virtual void handleCommand(const QString& text); + + /** + * Start a new game. + */ + virtual void newGame(); + virtual bool haveNewGame() {return true;} + + + void slotPlayerJoinedGame(KPlayer *p); + void slotNetworkData(int msgid, const QByteArray &msg, Q_UINT32 receiver, Q_UINT32 sender); + void slotCreatePlayer(KPlayer *&, int, int, bool, KGame *); + + void slotClientDisconnected(Q_UINT32, bool); + void slotClientConnected(Q_UINT32); + + void slotPropertyChanged(KGamePropertyBase *p, KGame *me); + void slotPropertyChanged(KGamePropertyBase *p, KPlayer *me); + +protected slots: + + void initGame(); + + void setGame(); + + void changeName(); + +protected: + + void setAllowed(int cmd, bool f); + +private: + + + /** + * Who did the last roll + */ + int lastRoll; + + /** + * How many checkers to move + */ + int toMove; + + /** + * Various flags, representing the current status of the game + */ + bool rollingAllowed, undoPossible, donePossible; + bool gameRunning, redoPossible, doublePossible; + + /** + * Count the number of available undos + */ + int dummy, undoCounter; + + + + + + + + + + + + + + + enum GameTypes {None = -1, Local, NetServer, NetClient, MaxTypes}; + KSelectAction * _gameSelect; + KAction* _connectAction; + KAction* _nameAction; + int _currGame; + int _nLocalPlayers; + + int _nplayers; + + QString _host; + Q_UINT16 _port; + + // ************************************************************ + // ************************************************************ + + // DONE + + // ************************************************************ + // ************************************************************ + + +protected: + + /** + * Return a random integer between 1 and 6. The random numer + * is based on the @ref KRandomSequence of @ref KGame. Thus, + * the numbers should be synchronized across the network. + */ + int getRandom(); + +private: + + /** + * Create the i-th player. Legal values for i are 0 and 1. The + * name of the player is taken from @ref _name and the parent of + * the player is @ref _player. That means that the players are + * automatically deleted. + */ + KBgPlayer * createPlayer(int i, QString name = QString::null); + +private: + + KBgGame* _game; + + QString _name[2]; + + KBgPlayer* _player[2]; + +}; + +#endif // __KBGNG_H diff --git a/kbackgammon/engines/nextgen/kbgplayer.cpp b/kbackgammon/engines/nextgen/kbgplayer.cpp new file mode 100644 index 00000000..f0b3a7ed --- /dev/null +++ b/kbackgammon/engines/nextgen/kbgplayer.cpp @@ -0,0 +1,62 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#include "kbgplayer.moc" +#include "kbgplayer.h" + +#include + +#include + + +/* + * Constructors + */ +KBgPlayer::KBgPlayer() + : KPlayer() +{ + // do nothing... +} +KBgPlayer::KBgPlayer(KGame *game) + : KPlayer(game) +{ + // do nothing... +} + +int KBgPlayer::rtti() const +{ + return 10500; +} + +bool KBgPlayer::load(QDataStream &stream) +{ + KPlayer::load(stream); + cerr << "-------- KBgPlayer::load" << endl; + return false; +} +bool KBgPlayer::save(QDataStream &stream) +{ + KPlayer::save(stream); + cerr << "-------- KBgPlayer::save" << endl; + return false; +} diff --git a/kbackgammon/engines/nextgen/kbgplayer.h b/kbackgammon/engines/nextgen/kbgplayer.h new file mode 100644 index 00000000..7c11d83c --- /dev/null +++ b/kbackgammon/engines/nextgen/kbgplayer.h @@ -0,0 +1,58 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KBGPLAYER_H +#define __KBGPLAYER_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +class KGame; + + +/** + * + * + */ +class KBgPlayer : public KPlayer +{ + Q_OBJECT + +public: + + KBgPlayer(); + KBgPlayer(KGame* game); + + virtual int rtti() const; + + virtual bool load(QDataStream &stream); + virtual bool save(QDataStream &stream); + +}; + +#endif // __KBGPLAYER_H + diff --git a/kbackgammon/engines/offline/Makefile.am b/kbackgammon/engines/offline/Makefile.am new file mode 100644 index 00000000..82d7a681 --- /dev/null +++ b/kbackgammon/engines/offline/Makefile.am @@ -0,0 +1,9 @@ +noinst_LTLIBRARIES = libkbgoffline.la + +libkbgoffline_la_SOURCES = kbgoffline.cpp + +INCLUDES= -I$(top_srcdir)/kbackgammon -I$(top_srcdir)/kbackgammon/engines \ + $(all_includes) + +METASOURCES = AUTO + diff --git a/kbackgammon/engines/offline/kbgoffline.cpp b/kbackgammon/engines/offline/kbgoffline.cpp new file mode 100644 index 00000000..920dc741 --- /dev/null +++ b/kbackgammon/engines/offline/kbgoffline.cpp @@ -0,0 +1,810 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#include "kbgoffline.moc" +#include "kbgoffline.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "version.h" + +class KBgEngineOfflinePrivate +{ +public: + + /* + * Various flags, representing the current status of the game + */ + bool mRollFlag, mUndoFlag, mDoneFlag, mCubeFlag, mGameFlag, mRedoFlag; + + /* + * Store two copies of the game: one backup and a working copy + */ + KBgStatus mGame[2]; + + /* + * Use the standard method of obtaining random numbers + */ + KRandomSequence *mRandom; + + /* + * Game actions + */ + KAction *mNew, *mSwap; + KToggleAction *mEdit; + + /* + * Player's names + */ + QString mName[2]; + + /* + * Who did the last roll + */ + int mRoll; + + /* + * How many checkers to move + */ + int mMove; + + /* + * Count the number of available undos + */ + int mUndo; + + /* + * Entry fields for the names + */ + QLineEdit *mLe[2]; + +}; + + +// == constructor, destructor and other ======================================== + +/* + * Constructor + */ +KBgEngineOffline::KBgEngineOffline(QWidget *parent, QString *name, QPopupMenu *pmenu) + : KBgEngine(parent, name, pmenu) +{ + d = new KBgEngineOfflinePrivate(); + + /* + * get some entropy for the dice + */ + d->mRandom = new KRandomSequence; + d->mRandom->setSeed(0); + + /* + * Create engine specific actions + */ + d->mNew = new KAction(i18n("&New Game..."), 0, this, SLOT(newGame()), this); + d->mSwap = new KAction(i18n("&Swap Colors"), 0, this, SLOT(swapColors()), this); + + d->mEdit = new KToggleAction(i18n("&Edit Mode"), 0, this, + SLOT(toggleEditMode()), this); + d->mEdit->setChecked(false); + + /* + * create & initialize the menu + */ + d->mNew->plug(menu); + d->mEdit->plug(menu); + d->mSwap->plug(menu); + + /* + * get standard board and set it + */ + initGame(); + emit newState(d->mGame[0]); + + /* + * initialize the commit timeout + */ + ct = new QTimer(this); + connect(ct, SIGNAL(timeout()), this, SLOT(done())); + + /* + * internal statue variables + */ + d->mRollFlag = d->mUndoFlag = d->mGameFlag = d->mDoneFlag = false; + connect(this, SIGNAL(allowCommand(int, bool)), this, SLOT(setAllowed(int, bool))); + + /* + * Restore last stored settings + */ + readConfig(); +} + +/* + * Destructor. The only child is the popup menu. + */ +KBgEngineOffline::~KBgEngineOffline() +{ + saveConfig(); + delete d->mRandom; + delete d; +} + + +// == configuration handling =================================================== + +/* + * Put the engine specific details in the setup dialog + */ +void KBgEngineOffline::getSetupPages(KDialogBase *nb) +{ + /* + * Main Widget + */ + QVBox *vbp = nb->addVBoxPage(i18n("Offline Engine"), i18n("Use this to configure the Offline engine"), + kapp->iconLoader()->loadIcon(PROG_NAME "_engine", KIcon::Desktop)); + + /* + * Get a multi page work space + */ + KTabCtl *tc = new KTabCtl(vbp, "offline tabs"); + + /* + * Player names + */ + QWidget *w = new QWidget(tc); + QGridLayout *gl = new QGridLayout(w, 2, 1, nb->spacingHint()); + + /* + * Group boxes + */ + QGroupBox *gbn = new QGroupBox(i18n("Names"), w); + + gl->addWidget(gbn, 0, 0); + + gl = new QGridLayout(gbn, 2, 2, 20); + + d->mLe[0] = new QLineEdit(d->mName[0], gbn); + d->mLe[1] = new QLineEdit(d->mName[1], gbn); + + QLabel *lb[2]; + lb[0] = new QLabel(i18n("First player:"), gbn); + lb[1] = new QLabel(i18n("Second player:"), gbn); + + for (int i = 0; i < 2; i++) { + gl->addWidget(lb[i], i, 0); + gl->addWidget(d->mLe[i], i, 1); + } + + QWhatsThis::add(d->mLe[0], i18n("Enter the name of the first player.")); + QWhatsThis::add(d->mLe[1], i18n("Enter the name of the second player.")); + + /* + * Done with the page, put it in + */ + gl->activate(); + tc->addTab(w, i18n("&Player Names")); +} + +/* + * Called when the setup dialog is positively closed + */ +void KBgEngineOffline::setupOk() +{ + d->mName[0] = d->mLe[0]->text(); + d->mName[1] = d->mLe[1]->text(); +} +void KBgEngineOffline::setupDefault() +{ + d->mName[0] = i18n("South"); + d->mName[1] = i18n("North"); +} +void KBgEngineOffline::setupCancel() +{ + // do nothing +} + +/* + * Restore settings + */ +void KBgEngineOffline::readConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("offline engine"); + + d->mName[0] = config->readEntry("player-one", i18n("South")); // same as above + d->mName[1] = config->readEntry("player-two", i18n("North")); // same as above + cl = config->readNumEntry("timer", 2500); +} + +/* + * Save the engine specific settings + */ +void KBgEngineOffline::saveConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("offline engine"); + + config->writeEntry("player-one", d->mName[0] ); + config->writeEntry("player-two", d->mName[1]); + config->writeEntry("timer", cl); +} + + +// == start and init games ===================================================== + +/* + * Start a new game. + */ +void KBgEngineOffline::newGame() +{ + int u = 0; + int t = 0; + + /* + * If there is a game running we warn the user first + */ + if (d->mGameFlag && (KMessageBox::warningYesNo((QWidget *)parent(), + i18n("A game is currently in progress. " + "Starting a new one will terminate it."), + QString::null, i18n("Start New Game"), + i18n("Continue Old Game")) + == KMessageBox::No)) + return; + + /* + * Separate from the previous game + */ + emit infoText("


"); + + /* + * Get player's names - user can still cancel + */ + if (!queryPlayerName(US) || !queryPlayerName(THEM)) + return; + + /* + * let the games begin + */ + d->mGameFlag = true; + + /* + * Initialize the board + */ + initGame(); + + /* + * Figure out who starts by rolling + */ + while (u == t) { + u = getRandom(); + t = getRandom(); + emit infoText(i18n("%1 rolls %2, %3 rolls %4."). + arg(d->mName[0]).arg(u).arg(d->mName[1]).arg(t)); + } + + if (u > t) { + emit infoText(i18n("%1 makes the first move.").arg(d->mName[0])); + d->mRoll = US; + } else { + emit infoText(i18n("%1 makes the first move.").arg(d->mName[1])); + d->mRoll = THEM; + int n = u; u = t; t = n; + } + + /* + * set the dice and tell the board + */ + rollDiceBackend(d->mRoll, u, t); + + /* + * tell the user + */ + emit statText(i18n("%1 vs. %2").arg(d->mName[0]).arg(d->mName[1])); +} + +/* + * Initialize the state descriptors mGame[0|1] + */ +void KBgEngineOffline::initGame() +{ + /* + * nobody rolled yet + */ + d->mRoll = -1; + + /* + * set up a standard game + */ + d->mGame[0].setCube(1, true, true); + d->mGame[0].setDirection(+1); + d->mGame[0].setColor(+1); + for (int i = 1; i < 25; i++) + d->mGame[0].setBoard(i, US, 0); + d->mGame[0].setBoard( 1, US, 2); d->mGame[0].setBoard( 6, THEM, 5); + d->mGame[0].setBoard( 8, THEM, 3); d->mGame[0].setBoard(12, US, 5); + d->mGame[0].setBoard(13, THEM, 5); d->mGame[0].setBoard(17, US, 3); + d->mGame[0].setBoard(19, US, 5); d->mGame[0].setBoard(24, THEM, 2); + d->mGame[0].setHome(US, 0); d->mGame[0].setHome(THEM, 0); + d->mGame[0].setBar(US, 0); d->mGame[0].setBar(THEM, 0); + d->mGame[0].setDice(US , 0, 0); d->mGame[0].setDice(US , 1, 0); + d->mGame[0].setDice(THEM, 0, 0); d->mGame[0].setDice(THEM, 1, 0); + + /* + * save backup of the game state + */ + d->mGame[1] = d->mGame[0]; + + emit allowCommand(Load, true); +} + +/* + * Open a dialog to query for the name of player w. Return true unless + * the dialog was canceled. + */ +bool KBgEngineOffline::queryPlayerName(int w) +{ + bool ret = false; + QString *name; + QString text; + + if (w == US) { + name = &d->mName[0]; + text = i18n("Please enter the nickname of the player whose home\n" + "is in the lower half of the board:"); + } else { + name = &d->mName[1]; + text = i18n("Please enter the nickname of the player whose home\n" + "is in the upper half of the board:"); + } + + do { + *name = KLineEditDlg::getText(text, *name, &ret, (QWidget *)parent()); + if (!ret) break; + + } while (name->isEmpty()); + + return ret; +} + + +// == moving =================================================================== + +/* + * Finish the last move - called by the timer and directly by the used + */ +void KBgEngineOffline::done() +{ + ct->stop(); + + emit allowMoving(false); + emit allowCommand(Done, false); + emit allowCommand(Undo, false); + + if (abs(d->mGame[0].home(d->mRoll)) == 15) { + + emit infoText(i18n("%1 wins the game. Congratulations!"). + arg((d->mRoll == US) ? d->mName[0] : d->mName[1])); + d->mGameFlag = false; + emit allowCommand(Roll, false); + emit allowCommand(Cube, false); + + } else { + + emit allowCommand(Roll, true); + if (d->mGame[0].cube((d->mRoll == US ? THEM : US)) > 0) { + + d->mGame[0].setDice(US , 0, 0); d->mGame[0].setDice(US , 1, 0); + d->mGame[0].setDice(THEM, 0, 0); d->mGame[0].setDice(THEM, 1, 0); + + emit newState(d->mGame[0]); + emit getState(&d->mGame[0]); + + d->mGame[1] = d->mGame[0]; + + emit infoText(i18n("%1, please roll or double."). + arg((d->mRoll == THEM) ? d->mName[0] : d->mName[1])); + emit allowCommand(Cube, true); + + } else { + + roll(); + emit allowCommand(Cube, false); + } + } +} + +/* + * Undo the last move + */ +void KBgEngineOffline::undo() +{ + ct->stop(); + + d->mRedoFlag = true; + ++d->mUndo; + + emit allowMoving(true); + emit allowCommand(Done, false); + emit allowCommand(Redo, true); + emit undoMove(); +} + +/* + * Redo the last move + */ +void KBgEngineOffline::redo() +{ + --d->mUndo; + emit redoMove(); +} + +/* + * Take the move string and make the changes on the working copy + * of the state. + */ +void KBgEngineOffline::handleMove(QString *s) +{ + int index = 0; + QString t = s->mid(index, s->find(' ', index)); + index += 1 + t.length(); + int moves = t.toInt(); + + /* + * Allow undo and possibly start the commit timer + */ + d->mRedoFlag &= ((moves < d->mMove) && (d->mUndo > 0)); + emit allowCommand(Undo, moves > 0); + emit allowCommand(Redo, d->mRedoFlag); + emit allowCommand(Done, moves == d->mMove); + if (moves == d->mMove && cl) { + emit allowMoving(false); + ct->start(cl, true); + } + + /* + * Apply moves to d->mGame[1] and store results in d->mGame[0] + */ + d->mGame[0] = d->mGame[1]; + + /* + * process each individual move + */ + for (int i = 0; i < moves; i++) { + bool kick = false; + t = s->mid(index, s->find(' ', index) - index); + index += 1 + t.length(); + char c = '-'; + if (t.contains('+')) { + c = '+'; + kick = true; + } + QString r = t.left(t.find(c)); + if (r.contains("bar")) { + d->mGame[0].setBar(d->mRoll, abs(d->mGame[0].bar(d->mRoll)) - 1); + } else { + int from = r.toInt(); + d->mGame[0].setBoard(from, d->mRoll, abs(d->mGame[0].board(from)) - 1); + } + t.remove(0, 1 + r.length()); + if (t.contains("off")) { + d->mGame[0].setHome(d->mRoll, abs(d->mGame[0].home(d->mRoll)) + 1); + } else { + int to = t.toInt(); + if (kick) { + d->mGame[0].setBoard(to, d->mRoll, 0); + int el = ((d->mRoll == US) ? THEM : US); + d->mGame[0].setBar(el, abs(d->mGame[0].bar(el)) + 1); + } + d->mGame[0].setBoard(to, d->mRoll, abs(d->mGame[0].board(to)) + 1); + } + } +} + + +// == dice & rolling =========================================================== + +/* + * Roll random dice for the player whose turn it is + */ +void KBgEngineOffline::roll() +{ + rollDice((d->mRoll == US) ? THEM : US); +} + +/* + * If possible, roll random dice for player w + */ +void KBgEngineOffline::rollDice(const int w) +{ + if ((d->mRoll != w) && d->mRollFlag) { + rollDiceBackend(w, getRandom(), getRandom()); + return; + } + emit infoText(i18n("It's not your turn to roll!")); +} + +/* + * Return a random integer between 1 and 6. According to the man + * page of rand(), this is the way to go... + */ +int KBgEngineOffline::getRandom() +{ + return 1+d->mRandom->getLong(6); +} + +/* + * Set the dice for player w to a and b. Reload the board and determine the + * maximum number of moves + */ +void KBgEngineOffline::rollDiceBackend(const int w, const int a, const int b) +{ + /* + * This is a special case that stems from leaving the edit + * mode. + */ + if (a == 0) + return; + + /* + * Set the dice and tel the board about the new state + */ + d->mGame[0].setDice(w, 0, a); + d->mGame[0].setDice(w, 1, b); + d->mGame[0].setDice((w == US) ? THEM : US, 0, 0); + d->mGame[0].setDice((w == US) ? THEM : US, 1, 0); + d->mGame[0].setTurn(w); + + d->mGame[1] = d->mGame[0]; + + d->mRoll = w; + emit newState(d->mGame[0]); + + /* + * No more roling until Done and no Undo yet + */ + emit allowCommand(Undo, false); + emit allowCommand(Roll, false); + d->mRedoFlag = false; + d->mUndo = 0; + + /* + * Tell the players how many checkers to move + */ + switch (d->mMove = d->mGame[0].moves()) { + case -1: + emit infoText(i18n("Game over!")); + d->mGameFlag = false; + emit allowCommand(Roll, false); + emit allowCommand(Cube, false); + emit allowMoving(false); + break; + case 0: + emit infoText(i18n("%1, you cannot move."). + arg((w == US) ? d->mName[0] : d->mName[1])); + if (cl) + ct->start(cl, true); + emit allowMoving(false); + break; +// case 1: + default: + emit infoText(QString((w == US) ? d->mName[0] : d->mName[1]) + + i18n(", please move 1 piece.",", please move %n pieces.",d->mMove)); + emit allowMoving(true); + break; + } +} + + +// == cube ===================================================================== + +/* + * Double the cube for the player that can double - asks player + */ +void KBgEngineOffline::cube() +{ + int w = ((d->mRoll == US) ? THEM : US); + + if (d->mRollFlag && d->mGame[0].cube(w) > 0) { + emit allowCommand(Cube, false); + if (KMessageBox::questionYesNo((QWidget *)parent(), + i18n("%1 has doubled. %2, do you accept the double?"). + arg((w == THEM) ? d->mName[1] : d->mName[0]). + arg((w == US) ? d->mName[1] : d->mName[0]), + i18n("Doubling"), i18n("Accept"), i18n("Reject")) != KMessageBox::Yes) { + d->mGameFlag = false; + emit allowCommand(Roll, false); + emit allowCommand(Cube, false); + emit infoText(i18n("%1 wins the game. Congratulations!"). + arg((w == US) ? d->mName[0] : d->mName[1])); + return; + } + + emit infoText(i18n("%1 has accepted the double. The game continues."). + arg((w == THEM) ? d->mName[0] : d->mName[1])); + + if (d->mGame[0].cube(US)*d->mGame[0].cube(THEM) > 0) + d->mGame[0].setCube(2, w == THEM, w == US); + else + d->mGame[0].setCube(2*d->mGame[0].cube(w), w == THEM, w == US); + + emit newState(d->mGame[0]); + emit getState(&d->mGame[0]); + + d->mGame[1] = d->mGame[0]; + + roll(); + } +} + +/* + * Double the cube for player w + */ +void KBgEngineOffline::doubleCube(const int) +{ + cube(); +} + + +// == various slots & functions ================================================ + +/* + * Check with the user if we should really quit in the middle of a + * game. + */ +bool KBgEngineOffline::queryClose() +{ + if (!d->mGameFlag) + return true; + + switch (KMessageBox::warningContinueCancel((QWidget *)parent(), + i18n("In the middle of a game. " + "Really quit?"), QString::null, KStdGuiItem::quit())) { + case KMessageBox::Continue : + return TRUE; + case KMessageBox::Cancel : + return FALSE; + default: // cancel + return FALSE; + } + return true; +} + +/* + * Quitting is fine at any time + */ +bool KBgEngineOffline::queryExit() +{ + return true; +} + +/* + * Handle textual commands. Right now, all commands are ignored + */ +void KBgEngineOffline::handleCommand(const QString& cmd) +{ + emit infoText(i18n("Text commands are not yet working. " + "The command '%1' has been ignored.").arg(cmd)); +} + +/* + * Load the last known sane state of the board + */ +void KBgEngineOffline::load() +{ + if (d->mEdit->isChecked()) + emit newState(d->mGame[1]); + else { + // undo up to four moves + undo(); + undo(); + undo(); + undo(); + } +} + +/* + * Store if cmd is allowed or not + */ +void KBgEngineOffline::setAllowed(int cmd, bool f) +{ + switch (cmd) { + case Roll: + d->mRollFlag = f; + return; + case Undo: + d->mUndoFlag = f; + return; + case Cube: + d->mCubeFlag = f; + return; + case Done: + d->mDoneFlag = f; + return; + } +} + +/* + * Swaps the used colors on the board + */ +void KBgEngineOffline::swapColors() +{ + d->mGame[1].setDice(US, 0, d->mGame[0].dice(US, 0)); + d->mGame[1].setDice(US, 1, d->mGame[0].dice(US, 1)); + d->mGame[1].setDice(THEM, 0, d->mGame[0].dice(THEM, 0)); + d->mGame[1].setDice(THEM, 1, d->mGame[0].dice(THEM, 1)); + d->mGame[1].setColor(d->mGame[1].color(THEM), US); + emit newState(d->mGame[1]); + emit getState(&d->mGame[1]); + d->mGame[0] = d->mGame[1]; +} + +/* + * Switch back and forth between edit and play mode + */ +void KBgEngineOffline::toggleEditMode() +{ + emit setEditMode(d->mEdit->isChecked()); + if (d->mEdit->isChecked()) { + ct->stop(); + d->mNew->setEnabled(false); + d->mSwap->setEnabled(false); + emit allowCommand(Undo, false); + emit allowCommand(Roll, false); + emit allowCommand(Done, false); + emit allowCommand(Cube, false); + emit statText(i18n("%1 vs. %2 - Edit Mode").arg(d->mName[0]).arg(d->mName[1])); + } else { + d->mNew->setEnabled(true); + d->mSwap->setEnabled(true); + emit statText(i18n("%1 vs. %2").arg(d->mName[0]).arg(d->mName[1])); + emit getState(&d->mGame[1]); + d->mGame[0] = d->mGame[1]; + emit allowCommand(Done, d->mDoneFlag); + emit allowCommand(Cube, d->mCubeFlag); + emit allowCommand(Undo, d->mUndoFlag); + emit allowCommand(Roll, d->mRollFlag); + int w =((d->mGame[0].dice(US, 0) && d->mGame[0].dice(US, 1)) ? US : THEM); + rollDiceBackend(w, d->mGame[0].dice(w, 0), d->mGame[0].dice(w, 1)); + } +} + +// EOF diff --git a/kbackgammon/engines/offline/kbgoffline.h b/kbackgammon/engines/offline/kbgoffline.h new file mode 100644 index 00000000..db2bdc03 --- /dev/null +++ b/kbackgammon/engines/offline/kbgoffline.h @@ -0,0 +1,213 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KBGOFFLINE_H +#define __KBGOFFLINE_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "kbgboard.h" +#include "kbgstatus.h" + +class KBgEngineOfflinePrivate; + +/** + * + * The interface of an offline backgammon engine. The engine is inherently + * stupid and doesn't play - it just manages the games betweeen two humans + * sitting at the same computer. Network enabled games will be part of the + * next generation engine (KBgNg). + * + * @short The offline backgammon engine + * @author Jens Hoefkens + * + */ +class KBgEngineOffline : public KBgEngine +{ + Q_OBJECT + +public: + + /** + * Constructor + */ + KBgEngineOffline(QWidget *parent = 0, QString *name = 0, QPopupMenu *pmenu = 0); + + /** + * Destructor + */ + virtual ~KBgEngineOffline(); + + /** + * Fills the engine-specific page into the notebook + */ + virtual void getSetupPages(KDialogBase *nb); + + /** + * Save new steup + */ + virtual void setupOk(); + + /** + * Load default setup + */ + virtual void setupDefault(); + + /** + * Cancel the changes to the setup + */ + virtual void setupCancel(); + + /** + * Check with the engine if we can quit. This may require user + * interaction. + */ + virtual bool queryClose(); + + /** + * About to be closed. Let the engine exit properly. + */ + virtual bool queryExit(); + +public slots: + + /** + * Read user settings from the config file + */ + virtual void readConfig(); + + /** + * Save user settings to the config file + */ + virtual void saveConfig(); + + /** + * Roll dice for the player w + */ + virtual void rollDice(const int w); + + /** + * Double the cube of player w + */ + virtual void doubleCube(const int w); + + /** + * A move has been made on the board - see the board class + * for the format of the string s + */ + virtual void handleMove(QString *s); + + /** + * Undo the last move + */ + virtual void undo(); + + /** + * Redo the last move + */ + virtual void redo(); + + /** + * Roll dice for whoevers turn it is + */ + virtual void roll(); + + /** + * Double the cube for whoevers can double right now + */ + virtual void cube(); + + /** + * Reload the board to the last known sane state + */ + virtual void load(); + + /** + * Commit a move + */ + virtual void done(); + + /** + * Process the string cmd + */ + virtual void handleCommand(const QString& cmd); + + /** + * Start a new game. + */ + virtual void newGame(); + virtual bool haveNewGame() {return true;} + + +protected slots: + + /** + * Initialize the state descriptors game[0] and game[1] + */ + void initGame(); + + /** + * Switch back and forth between edit and play mode + */ + void toggleEditMode(); + + /** + * Store if cmd is allowed or not + */ + void setAllowed(int cmd, bool f); + + /** + * Swaps the used colors on the board + */ + void swapColors(); + +protected: + + /** + * Returns a random integer between 1 and 6 + */ + int getRandom(); + + /** + * Set the dice for player w to a and b. Reload the board and determine the + * maximum number of moves + */ + void rollDiceBackend(const int w, const int a, const int b); + + /** + * Open a dialog to query for the name of player w. Return true unless + * the dialog was canceled. + */ + bool queryPlayerName(int w); + +private: + + KBgEngineOfflinePrivate *d; + +}; + +#endif // __KBGOFFLINE_H diff --git a/kbackgammon/eventsrc b/kbackgammon/eventsrc new file mode 100644 index 00000000..de6f5f90 --- /dev/null +++ b/kbackgammon/eventsrc @@ -0,0 +1,802 @@ +[!Global!] +IconName=kbackgammon +Comment=KBackgammon +Comment[af]=Kbackgammon +Comment[ar]=لعبة النرد/الطاولة (KBackgammon) +Comment[be]=Ðарды +Comment[bn]=কে-বà§à¦¯à¦¾à¦•à¦—à§à¦¯à¦¾à¦®à§‹à¦¨ +Comment[cs]=Vrchcáby +Comment[eo]=Bakgamono +Comment[hi]=के-बैकगेमॉन +Comment[ne]=केडीई बà¥à¤¯à¤¾à¤•à¤—ामोन +Comment[pt_BR]=KGamão +Comment[ro]=Joc de table +Comment[sv]=Kbackgammon +Comment[ta]=கேபேகà¯à®•à®®à®¾à®©à¯ +Comment[tg]=KÐардбозӣ +Comment[tr]=KTavla +Comment[zh_TW]=KBackgammon 西洋雙陸棋 + + +[game over w] +Name=Game over, you won +Name[af]=Speletjie bo, jy wen +Name[ar]=اللعبة انتهت، لقد Ùزت +Name[az]=Oyun Qurtardı, siz uddunuz +Name[be]=Канец гульні, вы выйгралі +Name[bg]=Спечелихте +Name[bn]=খেল খতম, আপনি জিতেছেন +Name[br]=Echu an abadenn, aet out ar maout +Name[bs]=Igra zavrÅ¡ena, vi ste pobjednik +Name[ca]=Final del joc, heu guanyat +Name[cs]=Konec hry, vyhrál(a) jste +Name[cy]=Gêm drosodd, ennill wnaethoch chi +Name[da]=Spillet forbi, du vandt +Name[de]=Spiel beendet, Sie haben gewonnen +Name[el]=Τέλος παιχνιδιοÏ, νικήσατε +Name[eo]=Ludo finita, vi gajnis +Name[es]=Fin de la partida, usted ganó +Name[et]=Mäng läbi, sina võitsid +Name[eu]=Jokoa amaitu da, irabazi duzu +Name[fa]=بازی تمام شد، شما بردید +Name[fi]=Peli loppu, voitit +Name[fr]=Fin de la partie, vous avez gagné +Name[gl]=Fin do xogo, vostede gaña +Name[he]=המשחק הסתיי×, ניצחת +Name[hi]=खेल ख़तà¥à¤®, आप जीते +Name[hr]=Igra je zavrÅ¡ena. Pobijedili ste! +Name[hu]=Vége a játéknak, Ön nyert +Name[is]=Leik lokið, þú vannst +Name[it]=Partita finita, hai vinto +Name[ja]=ゲームオーãƒãƒ¼ã€ã‚ãªãŸã®å‹ã¡ +Name[km]=ល្បែង​ចប់, អ្នក​ឈ្នះ​ហើយ +Name[lt]=Žaidimas baigtas, jÅ«s laimÄ—jote +Name[lv]=SpÄ“les beigas, jÅ«s uzvarÄ“jÄt +Name[mk]=Играта заврши, вие победивте +Name[mt]=Logħba spiÄ‹Ä‹at - int irbaħt +Name[nb]=Spillet er slutt, du vant +Name[nds]=Speel vörbi, Du hest wunnen +Name[ne]=खेल समापà¥à¤¤, तपाईà¤à¤²à¥‡ जितà¥à¤¨à¥ भयो +Name[nl]=Spel is afgelopen, u hebt gewonnen. +Name[nn]=Spelet er slutt, du vann +Name[nso]=Papadi e fedile, o fentse +Name[pa]=ਖੇਡ ਖਤਮ, ਤà©à¨¸à©€à¨‚ ਜਿੱਤ ਗਠ+Name[pl]=Koniec gry, wygraÅ‚eÅ› +Name[pt]=Fim do jogo, ganhou +Name[pt_BR]=Fim do jogo; você ganhou +Name[ro]=Joc terminat. Ai cîştigat. +Name[ru]=Конец игры, вы выиграли +Name[se]=Speallu nogai, don vuitet +Name[sk]=Koniec hry, vyhrali ste +Name[sl]=Konec igre, zmagali ste +Name[sr]=Крај игре, победили Ñте +Name[sr@Latn]=Kraj igre, pobedili ste +Name[sv]=Spelet är slut, du vann +Name[ta]=விளையாடà¯à®Ÿà¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯, நீஙà¯à®•à®³à¯ வெனà¯à®±à¯à®µà®¿à®Ÿà¯à®Ÿà¯€à®°à¯à®•à®³à¯ +Name[tg]=Бозӣ ба итмом раÑид, шумо ғолиб омадед +Name[th]=จบเà¸à¸¡ - คุณชนะ +Name[tr]=Oyun bitti, sen kazandın +Name[uk]=Гру завершено, ви виграли +Name[uz]=OÊ»yin tugadi, siz gÊ»alaba qozondingiz +Name[uz@cyrillic]=Ўйин тугади, Ñиз ғалаба қозондингиз +Name[ven]=Mutambo wo fhela, no wina +Name[vi]=Trò chÆ¡i kết thúc, bạn thắng +Name[wa]=Li djeu est houte, vos avoz wangnî +Name[xh]=Umdlalo uphelile, uphumelele +Name[zh_CN]=游æˆç»“æŸï¼Œæ‚¨èµ¢äº† +Name[zh_TW]=éŠæˆ²çµæŸï¼Œæ‚¨è´äº† +Name[zu]=Umdlalo uphelile, uphemelele +Comment=You have won the current game of backgammon +Comment[af]=Jy het wen die huidige speletjie van backgammon +Comment[ar]=لقد Ùزت اللعبة الحالية من لعبة النرد/الطاولة +Comment[az]=Hazırkı nÉ™rdtaxta oyununu uddunuz +Comment[be]=Ð’Ñ‹ выйгралі партыю Ñž нарды +Comment[bg]=Спечелихте +Comment[bn]=à¦à¦‡ বà§à¦¯à¦¾à¦•à¦—à§à¦¯à¦¾à¦®à§‹à¦¨ খেলাটি আপনি জিতেছেন +Comment[bs]=Pobjedili ste u trenutnoj backgammon igri +Comment[ca]=Heu guanyat aquesta partida de backgamon +Comment[cs]=Tuto hru ve vrchcáby jste vyhráli +Comment[cy]=Rydych wedi ennill y gêm gyfredol o dawlbwrdd +Comment[da]=Du har vundet dette spil backgammon +Comment[de]=Sie haben die Backgammon-Partie gewonnen! +Comment[el]=ΚεÏδίσατε αυτή την παÏτίδα backgammon +Comment[eo]=Vi gajnis la nunan bakgamonludon +Comment[es]=Usted ha ganado la partida actual de backgammon +Comment[et]=Sa võitsid selle mängu +Comment[eu]=Uneko Backgammon jokoa irabazi duzu +Comment[fa]=شما بازی جاری تخته نرد را بردید +Comment[fi]=Olet voittanut backgammon pelin +Comment[fr]=Vous avez gagné cette partie de backgammon +Comment[gl]=Vostede gañou esta partida de backgammon +Comment[he]=ניצחת במשחק השש־בש הנוכחי +Comment[hi]=आप बैकगेमॉन का हालिया खेल जीत गठ+Comment[hr]=Pobijedili ste u ovoj partiji backgammona +Comment[hu]=Ön megnyerte ezt a backgammon játékot +Comment[is]=Þú vannst þennan Backgammon leik +Comment[it]=Hai vinto questa partita di backgammon +Comment[ja]=ç¾åœ¨ã®backgammonゲームã«å‹ã¡ã¾ã—㟠+Comment[km]=អ្នក​បានឈ្មះ​ល្បែង​បច្ចុប្បន្ន​នៃ backgammon +Comment[lt]=JÅ«s laimÄ—jote šį backgammon žaidimÄ… +Comment[lv]=JÅ«s uzvarÄ“jÄt tekoÅ¡ajÄ bekgemona spÄ“lÄ“ +Comment[mk]=Ја добивте тековната игра на табла +Comment[mt]=Int irbaħt il-logħba preżenti tal-backgammon +Comment[nb]=Du vant det gjeldende backgammon-spillet +Comment[nds]=Du hest den Backgammon-Törn wunnen +Comment[ne]=बà¥à¤¯à¤¾à¤•à¤—ामोनको हालको खेल तपाईà¤à¤²à¥‡ जितà¥à¤¨à¥ भयो +Comment[nl]=U hebt het huidige Backgammon-spel gewonnen. +Comment[nn]=Du har vunne denne backgammon-runden +Comment[nso]=O fentse papadi ya bjale ya backgammon +Comment[pa]=ਤà©à¨¸à©€à¨‚ ਮੌਜੂਦਾ ਬੈਕਗਮੋਮ ਦੀ ਮੌਜੂਦਾ ਖੇਡ ਜਿੱਤ ਗਠ+Comment[pl]=WygraÅ‚eÅ› bieżącÄ… grÄ™ backgammon +Comment[pt]=Ganhou o jogo de gamão +Comment[pt_BR]=Você ganhou o jogo atual de gamão +Comment[ro]=AÅ£i cîştigat jocul de table curent +Comment[ru]=Ð’Ñ‹ выиграли партию в нарды +Comment[se]=Don leat vuoitán dán backgammon-vuoru +Comment[sk]=Vyhrali ste aktuálnu hru v backgammone +Comment[sl]=Zmagali ste trenutno igro backgammona +Comment[sr]=Победили Ñте у овој игри бекгемона +Comment[sr@Latn]=Pobedili ste u ovoj igri bekgemona +Comment[sv]=Du har vunnit det aktuella spelet av backgammon +Comment[ta]=பேகà¯à®•à®¾à®®à¯à®®à¯‹à®©à®¾à®©à®¿à®©à¯ தறà¯à®ªà¯‹à®¤à¯ˆà®¯ விளையாடà¯à®Ÿà¯ˆ நீஙà¯à®•à®³à¯ வெனà¯à®±à¯à®µà®¿à®Ÿà¯à®Ÿà¯€à®°à¯à®•à®³à¯ +Comment[tg]=Шумо дар нардбозии ҷорӣ ғолиб омадед +Comment[tr]=Åžu anki tavla oyununu kazandınız +Comment[uk]=Ви виграли поточну гру backgammon +Comment[ven]=No khunda kha mutambo wa backgammon +Comment[vi]=Bạn thắng trong trò chÆ¡i backgammon này +Comment[wa]=Vos avoz wangnî l' djeu d' backgammon +Comment[xh]=Uphumelele emdlalweni wangoku we backgammon +Comment[zh_CN]=您赢了这盘åŒé™†æ£‹æ¸¸æˆ +Comment[zh_TW]=您è´äº†é€™ä¸€ç›¤è¥¿æ´‹é›™é™¸æ£‹ +Comment[zu]=Uphumelele emdlalweni wamanje we-backgammon +default_sound=kbackgammon-won.wav +default_presentation=1 + +[game over l] +Name=Gamo over, you lost +Name[af]=Speletjie bo, jy verloor +Name[ar]=اللعبة انتهت، لقد خسرت +Name[az]=Oyun Qurtardı, siz uduzdunuz +Name[be]=Канец гульні, вы прайгралі +Name[bg]=Загубихте +Name[bn]=খেল খতম, আপনি হেরে গিয়েছেন +Name[br]=Echu eo an abadenn, kollet out +Name[bs]=Igra zavrÅ¡ena, izgubili ste +Name[ca]=Final del joc, heu perdut +Name[cs]=Konec hry, prohrál(a) jste +Name[cy]=Gêm drosodd, colli wnaethoch chi +Name[da]=Spillet forbi, du tabte +Name[de]=Spiel beendet, Sie haben verloren +Name[el]=Τέλος παιχνιδιοÏ, χάσατε +Name[en_GB]=Game over, you lost +Name[eo]=Ludo finita, vi malgajnis +Name[es]=Fin de la partida, usted perdió +Name[et]=Mäng läbi, sina kaotasid +Name[eu]=Jokoa amaitu da, galdu duzu +Name[fa]=بازی تمام شد، شما باختید +Name[fi]=Peli loppu, hävisit +Name[fr]=Fin de la partie, vous avez perdu +Name[gl]=Fin do xogo, vostede perde +Name[he]=המשחק הסתיי×, הפסדת +Name[hi]=खेल ख़तà¥à¤®, आप हारे +Name[hr]=Igra je zavrÅ¡ena. Izgubili ste. +Name[hu]=Vége a játéknak, Ön vesztett +Name[is]=Leik lokið, þú tapaðir +Name[it]=Partita finita, hai perso +Name[ja]=ゲームオーãƒãƒ¼ã€ã‚ãªãŸã®è² ã‘ +Name[km]=ល្បែងចប់​, អ្នក​ចាញ់​ហើយ +Name[lt]=Žaidimas baigtas, jÅ«s pralaimÄ—jote +Name[lv]=SpÄ“les beigas, jÅ«s zaudÄ“jÄt +Name[mk]=Играта заврши, вие изгубивте +Name[mt]=Logħba spiÄ‹Ä‹at - int tlift +Name[nb]=Spillet er slutt, du tapte +Name[nds]=Speel vörbi, Du hest verloren +Name[ne]=खेल समापà¥à¤¤, तपाईठहारà¥à¤¨à¥ भयो +Name[nl]=Spel is afgelopen, u hebt verloren. +Name[nn]=Spelet er slutt, du tapte +Name[nso]=Papadi e fedile, o paletswe +Name[pa]=ਖੇਡ ਖਤਮ, ਤà©à¨¹à¨¾à¨¡à©€ ਵਾਰੀ ਖਤਮ +Name[pl]=Koniec gry, przegraÅ‚eÅ› +Name[pt]=Fim do jogo, perdeu +Name[pt_BR]=Fim do jogo; você perdeu +Name[ro]=Joc terminat. Ai pierdut. +Name[ru]=Конец игры, вы проиграли +Name[se]=Speallu nogai, don vuoittehallet +Name[sk]=Koniec hry, prehrali ste +Name[sl]=Konec igre, izgubili ste +Name[sr]=Крај игре, изгубили Ñте +Name[sr@Latn]=Kraj igre, izgubili ste +Name[sv]=Spelet är slut, du förlorade +Name[ta]=விளையாடà¯à®Ÿà¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯, நீஙà¯à®•à®³à¯ தோறà¯à®±à¯à®µà®¿à®Ÿà¯à®Ÿà¯€à®°à¯à®•à®³à¯ +Name[tg]=Бозӣ ба итмом раÑид, шумо мағлуб шудед +Name[th]=จบเà¸à¸¡ - คุณà¹à¸žà¹‰ +Name[tr]=Oyun bitti, sen kaybettin +Name[uk]=Гру завершено, ви програли +Name[uz]=OÊ»yin tugadi, siz yutqazdingiz +Name[uz@cyrillic]=Ўйин тугади, Ñиз ютқаздингиз +Name[ven]=Mutambo wo fhela, no liwa +Name[vi]=Trò chÆ¡i kết thúc, bạn thua +Name[wa]=Li djeu est houte, vos avoz pierdou +Name[xh]=Umdlalo uphelile, wohluliwe +Name[zh_CN]=游æˆç»“æŸï¼Œæ‚¨è¾“了 +Name[zh_TW]=éŠæˆ²çµæŸï¼Œæ‚¨è¼¸äº† +Name[zu]=Umdlalo uphellile, uhluliwe +Comment=You have lost the current game of backgammon +Comment[af]=Jy het verloor die huidige speletjie van backgammon +Comment[ar]=لقد خسرت اللعبة الحالية من لعبة النرد/الطاولة +Comment[az]=Hazırkı nÉ™rdtaxta oyununu uduzdunuz +Comment[be]=Ð’Ñ‹ прайгралі партыю Ñž нарды +Comment[bg]=Загубихте +Comment[bn]=à¦à¦‡ বà§à¦¯à¦¾à¦•à¦—à§à¦¯à¦¾à¦®à§‹à¦¨ খেলাটি আপনি হেরেছেন +Comment[bs]=Izgubili ste u trenutnoj backgammon igri +Comment[ca]=Heu perdut aquesta partida de backgamon +Comment[cs]=Tuto hru ve vrchcáby jste prohráli +Comment[cy]=Rydych wedi colli y gêm gyfredol o dawlbwrdd +Comment[da]=Du har tabt dette spil backgammon +Comment[de]=Sie haben die Backgammon-Partie verloren! +Comment[el]=Χάσατε αυτή την παÏτίδα backgammon +Comment[eo]=Vi malgajnis la nunan bakgamonludon +Comment[es]=Usted ha perdido la partida actual de backgammon +Comment[et]=Sa kaotasid selle mängu +Comment[eu]=Uneko Backgammon jokoa galdu duzu +Comment[fa]=شما بازی جاری تخته نرد را باختید +Comment[fi]=Olet hävinnyt backgammon pelin +Comment[fr]=Vous avez perdu cette partie de backgammon +Comment[gl]=Vostede perdeu esta partida de backgammon +Comment[he]=הפסדת במשחק השש־בש הנוכחי +Comment[hi]=आप बैकगेमॉन का हालिया खेल हार गठ+Comment[hr]=Izgubili ste u ovoj partiji backgammona +Comment[hu]=Ön elvesztette ezt a backgammon játékot +Comment[is]=Þú tapaðir þessum Backgammon leik +Comment[it]=Hai perso questa partita di backgammon +Comment[ja]=ç¾åœ¨ã®backgammonゲームã«è² ã‘ã¾ã—㟠+Comment[km]=អ្នក​បានចាញ់​ល្បែង​បច្ចុប្បន្ន​នៃ backgammon +Comment[lt]=JÅ«s pralaimÄ—jote šį backgammon žaidimÄ… +Comment[lv]=JÅ«s zaudÄ“jÄt tekoÅ¡ajÄ bekgemona spÄ“lÄ“ +Comment[mk]=Ја изгубивте тековната игра на табла +Comment[mt]=Int tlift il-logħba preżenti tal-backgammon +Comment[nb]=Du tapte det gjeldende backgammon-spillet +Comment[nds]=Du hest den Backgammon-Törn verloren +Comment[ne]=बà¥à¤¯à¤¾à¤•à¤—ामोनको हालको खेल तपाईà¤à¤²à¥‡ हारà¥à¤¨à¥ भयो +Comment[nl]=U hebt het huidige Backgammon-spel verloren. +Comment[nn]=Du har tapt denne backgammon-runden +Comment[nso]=O paletswe ke papadi ya bjale ya backgammon +Comment[pl]=PrzegraÅ‚eÅ› bieżącÄ… grÄ™ backgammon +Comment[pt]=Perdeu o jogo de gamão +Comment[pt_BR]=Infelizmente você perdeu o jogo atual de gamão +Comment[ro]=AÅ£i pierdut jocul de table curent +Comment[ru]=Ð’Ñ‹ проиграли партию в нарды +Comment[se]=Don leat vuoittehallan dán backgammon-vuoru +Comment[sk]=Prehrali ste aktuálnu hru v backgammone +Comment[sl]=Izgubili ste trenutno igro backgammona +Comment[sr]=Изгубили Ñте у овој игри бекгемона +Comment[sr@Latn]=Izgubili ste u ovoj igri bekgemona +Comment[sv]=Du har förlorat det aktuella spelet av backgammon +Comment[ta]=பேகà¯à®•à®¾à®®à¯à®®à¯‹à®©à®¾à®©à®¿à®©à¯ தறà¯à®ªà¯‹à®¤à¯ˆà®¯ விளையாடà¯à®Ÿà®¿à®²à¯ நீஙà¯à®•à®³à¯ தோலà¯à®µà®¿ அடைநà¯à®¤à¯à®³à¯à®³à¯€à®°à¯à®•à®³à¯. +Comment[tg]=Шумо дар нардбозии ҷорӣ мағлуб шудед +Comment[tr]=Åžu anki tavla oyununu kaybettiniz +Comment[uk]=Ви програли поточну гру backgammon +Comment[ven]=No kunda kha mutambo wa backgammon +Comment[vi]=Bạn thua trong trò chÆ¡i backgammon này +Comment[wa]=Vos avoz pierdou l' djeu d' backgammon +Comment[xh]=Wahlulekile emdlalweni wangoku we backgammon +Comment[zh_CN]=您输了这盘åŒé™†æ£‹æ¸¸æˆ +Comment[zh_TW]=您輸了這一盤西洋雙陸棋 +Comment[zu]=Uhluliwe emdlallweni wamanje we-backgammon +default_sound=kbackgammon-lost.wav +default_presentation=1 + +[roll or double] +Name=Roll or double +Name[af]=Rol of dubbel +Name[az]=At vÉ™ ya CütlÉ™ +Name[be]=Кінуць коÑці або падвоіць +Name[bg]=ХвърлÑне или удвоÑване +Name[bn]=গড়ান অথবা দà§à¦¬à¦¿à¦—à§à¦¨ করà§à¦¨ +Name[bs]=Ponovo ili duplo +Name[ca]=Tirar o doblar +Name[cs]=HoÄte nebo double +Name[cy]=Taflu neu dwbl +Name[da]=Kast eller fordobl +Name[de]=Würfeln oder verdoppeln +Name[el]=Ρίξτε το ζάÏι ή διπλασιάστε +Name[eo]=Rulu aÅ­ duobligu +Name[es]=Tirar o doblar +Name[et]=Veereta või duubelda +Name[eu]=Jaurti edo bikoiztu +Name[fa]=غلتاندن یا دو برابر کردن +Name[fi]=Heitä tai tuplaa +Name[fr]=Jeter les dés ou doubler +Name[gl]=Botar ou dobrar +Name[he]=הטלה ×ו הכפלה +Name[hi]=पाà¤à¤¸à¤¾ फेंकें या दोगà¥à¤¨à¤¾ करें +Name[hr]=Bacaj ili dvostruko +Name[hu]=Dobás vagy duplázás +Name[is]=Kastaðu eða tvöfaldaðu +Name[it]=Lancia o raddoppia +Name[ja]=振るã‹ãƒ€ãƒ–ル +Name[km]=ក្រឡុក ឬ ទ្វ០+Name[lt]=Ridenti ar dvigubinti +Name[lv]=Mest vai dubultot +Name[mk]=Фрлете или удвојте +Name[mt]=Waddab damem jew Irdoppja +Name[nb]=Kast eller doble +Name[nds]=Wörpeln oder verdubbeln +Name[ne]=घà¥à¤®à¤¾à¤‰à¤¨à¥à¤¹à¥‹à¤¸à¥ वा दोबà¥à¤¬à¤° पारà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Werpen of verdubbelen +Name[nn]=Rull eller dobla +Name[nso]=Tokolosa goba gabedi +Name[pa]=ਰੋਲ ਜਾਂ ਦà©à¨¹à¨°à¨¾ +Name[pl]=Rzucaj lub podwój +Name[pt]=Lançar ou duplicar +Name[pt_BR]=Jogar ou dobrar +Name[ro]=Aruncă sau dublează +Name[ru]=БроÑить коÑти или удвоить +Name[se]=ÄŒaskke dahje duppalastte +Name[sk]=HodiÅ¥ alebo zdvojiÅ¥ +Name[sl]=MeÄi ali podvoji +Name[sr]=Баците коцкице или удвоÑтручите +Name[sr@Latn]=Bacite kockice ili udvostruÄite +Name[sv]=SlÃ¥ eller dubblera +Name[ta]=சà¯à®±à¯à®±à¯ அலà¯à®²à®¤à¯ இரடà¯à®Ÿà®¿à®ªà¯à®ªà®¾à®•à¯à®•à¯ +Name[tg]=Партофтани мӯҳр Ñ‘ дубора партофтан +Name[th]=ทอดหรือได้ทอดใหม่ +Name[tr]=At ya da çift +Name[uk]=Кинути коÑÑ‚Ñ– або подвоїти +Name[ven]=Kungulusani kha ni kou pada +Name[vi]=Cuá»™n hay gấp +Name[xh]=Jikeleza okanye phindaphinda +Name[zh_CN]=掷骰å­æˆ–åŠ å€ +Name[zh_TW]=擲骰å­æˆ–åŠ å€ +Name[zu]=Gingqa noma phinda kabili +Comment=It's your turn to roll the dice or double the cube +Comment[af]=Dit is jou skakel na rol die dobbelsteen of dubbel die kubus +Comment[az]=ZÉ™rlÉ™ri atmaqda ya da kubları cütlÉ™mÉ™kdÉ™ sizin sıranız gÉ™ldi +Comment[be]=Ваша чарга кідаць коÑці або падвойваць +Comment[bg]=ХвърлÑне или удвоÑване +Comment[bn]=à¦à¦–ন আপনার পাশা গড়ানোর বা কিউব দà§à¦¬à¦¿à¦—à§à¦¨ করার পালা +Comment[bs]=Na vas je red da bacite kocke ili poduplate cube +Comment[ca]=És el vostre torn per a tirar els daus o doblar el cub +Comment[cs]=Jste na tahu, buÄ hoÄte kostkou nebo double +Comment[cy]=Eich tro chi yw hi i daflu'r dîs neu ddwblu'r ciwb +Comment[da]=Det er din tur til at kaste terningerne eller fordoble kuben +Comment[de]=Sie sind dran, entweder zu würfeln oder zu verdoppeln +Comment[el]=Είναι η σειÏά σας να Ïίξετε τα ζάÏια +Comment[eo]=Estas via vico ĵeti la du kubojn aÅ­ duobligi la kubon +Comment[es]=Es su turno para tirar los dados o doblar el cubo +Comment[et]=Sinu kord veeretada täringut või kahekordistada panust +Comment[eu]=Kuboa jaurti edo bikoizteko zure txanda da +Comment[fa]=نوبت شماست Ú©Ù‡ طاس را بغلتانید، یا مکعب را دو برابر کنید +Comment[fi]=On sinun vuoro heittää noppaa tai tuplata +Comment[fr]=C'est à votre tour de jeter les dés ou de doubler le videau. +Comment[gl]=É a súa quenda de botar o dado ou dobrar o cubo +Comment[he]=תורך להטיל ×ת הקוביות ×ו להכפיל ×ת הקובייה +Comment[hi]=पाà¤à¤¸à¤¾ फेंकने की या कà¥à¤¯à¥‚ब को दोगà¥à¤¨à¤¾ करने की यह आपकी बारी है +Comment[hr]=Vi ste na redu da bacite kocku ili je udvostruÄite +Comment[hu]=Dobjon a kockával vagy duplázzon +Comment[is]=Þú átt leik. Kastaðu teningnum eða tvöfaldaðu +Comment[it]=È il tuo turno di lanciare i dadi o di raddoppiare il cubo +Comment[ja]=ã‚ãªãŸãŒã•ã„ã“ã‚を振るã‹ãƒ€ãƒ–ルã™ã‚‹ç•ªã§ã™ +Comment[km]=វា​គឺ​ជា​វáŸáž“​របស់​អ្នក ដើម្បី​ក្រឡុក​គ្រាប់​ឡុកឡាក់ ឬ ដើម្បី​ទ្វáŸâ€‹áž‚ូប +Comment[lt]=JÅ«sų eilÄ— ridenti kauliukÄ… ar dvigubinti kubÄ… +Comment[lv]=Å is ir jÅ«su gÄjiens, lai mestu kauliņu vai dubultotu +Comment[mk]=Вие Ñте на ред да ги фрлите коцките или да ја удвоите коцката +Comment[mt]=Imiss lilek twaddab id-damem jew tirdoppja l-kubu +Comment[nb]=Det er din tur til Ã¥ kaste terningene eller doble kuben +Comment[nds]=Du büst mit Wörpeln oder Verdubbeln an de Reeg +Comment[ne]=पासा घà¥à¤®à¤¾à¤‰à¤¨à¥‡ वा घन दोबà¥à¤¬à¤° पारà¥à¤¨à¥‡ तपाईà¤à¤•à¥‹ पालो हो +Comment[nl]=U bent aan de beurt om de dobbelsteen te werpen of te verdubbelen. +Comment[nn]=Det er din tur til Ã¥ rulla terningen eller dobla kuben +Comment[nso]=Ke nako ya gago yago tokolosa mataese goba wa double cube +Comment[pl]=Twój ruch, by rzucić kostkÄ… lub podwoić szeÅ›cian +Comment[pt]=É a sua vez de lançar os dados ou duplicar o cubo +Comment[pt_BR]=É a sua vez de jogar os dados ou dobrar o cubo +Comment[ro]=Este rîndul dumneavoastră să aruncaÅ£i zarurile sau să dublaÅ£i cubul +Comment[ru]=Ваша очередь броÑать коÑти или удваивать +Comment[se]=Lea du vuorru birccu Äaskit dahje duppalastit kuba +Comment[sk]=Ste na Å¥ahu, buÄ hoÄte kockou alebo zdvojte +Comment[sl]=Na vrsti ste za met kock ali podvojitev vrednosti +Comment[sr]=Ваш је ред да баците коцкице или да дуплирате коцку +Comment[sr@Latn]=VaÅ¡ je red da bacite kockice ili da duplirate kocku +Comment[sv]=Det är din tur att slÃ¥ tärningen eller dubblera kuben +Comment[ta]=நீஙà¯à®•à®³à¯ தாயதà¯à®¤à¯ˆ உரà¯à®Ÿà¯à®Ÿà¯à®®à¯ அலà¯à®²à®¤à¯ படà¯à®Ÿà®•à®¤à¯à®¤à¯ˆ இரடà¯à®Ÿà®¿à®•à¯à®•à¯à®®à¯ à®®à¯à®±à¯ˆ +Comment[tg]=Ðавбати шумо барои партофтани мӯҳр Ñ‘ партофтани дубора +Comment[tr]=Zar atma sırası sizde +Comment[uk]=Ваша черга кидати коÑÑ‚Ñ– +Comment[ven]=Ndi tshifhinga tshavho tsha u kungulusa dice kana pada cube +Comment[vi]=Äến lượt bạn gieo xúc sắc hay double cube +Comment[xh]=Lithuba lakho lokujikelezisa idayisi okanye phinda kabini ityhubhu +Comment[zh_CN]=该您掷骰å­æˆ–åŠ å€ +Comment[zh_TW]=該您擲骰å­æˆ–åŠ å€ +Comment[zu]=Ithuba lakho lokugingqa idayisi noma uphinde kabili iqhuzu +default_sound=kbackgammon-roll.wav +default_presentation=1 + +[roll] +Name=Roll the dice +Name[af]=Rol die dobbelsteen +Name[ar]=ارمي النرد +Name[az]=ZÉ™rlÉ™ri at +Name[be]=Кінуць коÑці +Name[bg]=ХвърлÑне на заровете +Name[bn]=পাশা গড়ান +Name[bs]=Baci kocke +Name[ca]=Tirar els daus +Name[cs]=Hodit kostkou +Name[cy]=Taflwch y dîs +Name[da]=Kast terningerne +Name[de]=Würfeln +Name[el]=Ρίξτε το ζάÏι +Name[eo]=Rulu aÅ­ ĵetu kubojn +Name[es]=Tirar los dados +Name[et]=Veereta täringut +Name[eu]=Jaurti dadoa +Name[fa]=غلتاندن طاس +Name[fi]=Heitä noppaa +Name[fr]=Lancer les dés +Name[gl]=Botar o dado +Name[he]=הטלת הקוביות +Name[hi]=पाà¤à¤¸à¤¾ फेंकें +Name[hr]=Baci kocke +Name[hu]=Dobás a kockával +Name[is]=Kastaðu teningnum +Name[it]=Lancia i dadi +Name[ja]=サイコロを振ã£ã¦ãã ã•ã„ +Name[km]=ក្រឡុក​គ្រាប់​ឡុកឡាក់ +Name[lt]=Ridenti kauliukÄ… +Name[lv]=Mest kauliņu +Name[mk]=Фрлете ги коцките +Name[mt]=Waddab id-damem +Name[nb]=Kast terningene +Name[nds]=Wörpeln +Name[ne]=पासा घà¥à¤®à¤¾à¤‰à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Dobbelsteen werpen +Name[nn]=Rull terningen +Name[nso]=Tokolosa mataese +Name[pa]=ਗੋਟੀ ਘà©à©°à¨®à¨¾à¨“ +Name[pl]=Rzuć kostkÄ… +Name[pt]=Lançar os dados +Name[pt_BR]=Jogar os dados +Name[ro]=Aruncă zarurile +Name[ru]=БроÑить коÑти +Name[se]=ÄŒaskke birccu +Name[sk]=HodiÅ¥ kocku +Name[sl]=Meci kocke +Name[sr]=Баците коцкице +Name[sr@Latn]=Bacite kockice +Name[sv]=Kasta tärningen +Name[ta]=தாயதà¯à®¤à¯ˆ உரà¯à®Ÿà¯à®Ÿà¯à®• +Name[tg]=Партофтани мӯҳр +Name[th]=ทอดเต๋า +Name[tr]=Zarı at +Name[uk]=Кинути коÑÑ‚Ñ– +Name[uz]=Toshni otish +Name[uz@cyrillic]=Тошни отиш +Name[ven]=Kungulusani dice +Name[vi]=Cuá»™n hay xắc +Name[wa]=Taper les dés +Name[xh]= Jikelezisa idayisi +Name[zh_CN]=æŽ·éª°å­ +Name[zh_TW]=æ“²éª°å­ +Name[zu]=Gingqa idayisi +Comment=It's your turn to roll the dice +Comment[af]=Dit is jou skakel na rol die dobbelsteen +Comment[ar]=لقد حان دورك لرمي النرد +Comment[az]=ZÉ™rlÉ™ri atmaqda sizin sıranız gÉ™ldi +Comment[be]=Ваша чарга кідаць коÑці +Comment[bg]=ХвърлÑне на заровете +Comment[bn]=à¦à¦–ন আপনার পাশা গড়ানোর পালা +Comment[bs]=Na vas je red da bacate kocke +Comment[ca]=És el vostre torn per a tirar els daus +Comment[cs]=Jste na tahu, hoÄte kostkou +Comment[cy]=Eich tro chi yw hi i daflu'r dîs +Comment[da]=Det er din tur til at kaste terningerne +Comment[de]=Sie sind dran mit Würfeln! +Comment[el]=Είναι η σειÏά σας να Ïίξετε τα ζάÏια +Comment[eo]=Estas via vico ĵeti la kubojn +Comment[es]=Es su turno para tirar los dados +Comment[et]=Sinu kord täringut veeretada +Comment[eu]=Dadoa jaurtizeko zure txanda da +Comment[fa]=نوبت شماست Ú©Ù‡ طاس را بغلتانید +Comment[fi]=Sinun vuoro heittää noppaa +Comment[fr]=C'est à votre tour de jeter les dés +Comment[gl]=É a súa quenda de botar o dado +Comment[he]=תורך להטיל ×ת הקוביות +Comment[hi]=पाà¤à¤¸à¤¾ फेंकने की यह आपकी बारी है +Comment[hr]=Vi ste na redu da bacite kocku +Comment[hu]=Dobjon a kockával +Comment[is]=Þú átt að kasta +Comment[it]=È il tuo turno di lanciare i dadi +Comment[ja]=ã‚ãªãŸãŒã‚µã‚¤ã‚³ãƒ­ã‚’振る番ã§ã™ +Comment[km]=វា​ជា​វáŸáž“​របស់​អ្នក​ដើម្បី​ប្រមៀលឡុក​គ្រាប់​ឡុក​ឡាក់ +Comment[lt]=JÅ«sų eilÄ— ridenti kauliukÄ… +Comment[lv]=Å is ir jÅ«su gÄjiens mest kauliņu +Comment[mk]=Вие Ñте на ред да ги фрлите коцките +Comment[mt]=Imiss lilek twaddab id-damem +Comment[nb]=Det er din tur til Ã¥ kaste terningene +Comment[nds]=Du büst mit Wörpeln an de Reeg +Comment[ne]=पासा घà¥à¤®à¤¾à¤‰à¤¨à¥‡ तपाईà¤à¤•à¥‹ पालो हो +Comment[nl]=U bent aan de beurt om de dobbelsteen te werpen. +Comment[nn]=Det er din tur til Ã¥ rulla terningen +Comment[nso]=Ke nako ya gago yago tokolosa mataese +Comment[pa]=ਗੋਟੀ ਘà©à©°à¨®à¨¾à¨‰à¨£ ਦੀ ਵਾਰੀ ਤà©à¨¹à¨¾à¨¡à©€ ਹੈ +Comment[pl]=Twój ruch do rzutu kostkÄ… +Comment[pt]=É a sua vez de lançar os dados +Comment[pt_BR]=É a sua vez de jogar os dados +Comment[ro]=Este rîndul dumneavoastră să aruncaÅ£i zarurile +Comment[ru]=Ваша очередь броÑать коÑти +Comment[se]=Lea du vuorru birccu Äaskit +Comment[sk]=Ste na Å¥ahu, hoÄte kockou +Comment[sl]=Na vrsti ste za met kock +Comment[sr]=Ваш је ред да баците коцкице +Comment[sr@Latn]=VaÅ¡ je red da bacite kockice +Comment[sv]=Det är din tur att kasta tärningen +Comment[ta]=நீஙà¯à®•à®³à¯ இபà¯à®ªà¯‹à®¤à¯ தாயதà¯à®¤à¯ˆ உரà¯à®Ÿà¯à®Ÿà¯à®®à¯ à®®à¯à®±à¯ˆ +Comment[tg]=Ðавбати шумо барои партофтани мӯҳрҳо +Comment[tr]=Zar atma sırası sizde +Comment[uk]=Ваша черга кидати коÑÑ‚Ñ– +Comment[ven]=Ndi tshifhinga tshanu tsha u tamba daisi +Comment[vi]=Äến lượt bạn reo xúc sắc +Comment[wa]=C' est a vos d' taper les dés asteure +Comment[zh_CN]=该您掷骰å­äº† +Comment[zh_TW]=該您擲骰å­äº† +Comment[zu]=Ithuba lakho lokugingqa idayisi +default_sound=kbackgammon-roll.wav +default_presentation=1 + +[move] +Name=Move checkers +Name[af]=Beweeg skuifstukke +Name[az]=DaÅŸları HÉ™rÉ™kÉ™t etdir +Name[be]=ПераÑунуць фішку +Name[bg]=ПремеÑтване на пулове +Name[bn]=চেকারà§à¦¸ চালà§à¦¨ +Name[ca]=Moure fitxes +Name[cs]=PÅ™esunout kameny +Name[cy]=Symud drafftiau +Name[da]=Flyt brikkerne +Name[de]=Steine ziehen +Name[el]=Μετακινήστε ποÏλια +Name[eo]=Movu pecojn +Name[es]=Mover fichas +Name[et]=Liiguta nuppe +Name[eu]=Mugitu fitxak +Name[fa]=حرکت بازبینها +Name[fi]=Siirrä tammea +Name[fr]=Déplacer des pions +Name[gl]=Mover fichas +Name[he]=הזזת ×—×œ×§×™× +Name[hi]=चेकरà¥à¤¸ खिसकाà¤à¤ +Name[hr]=Pomakni figure +Name[hu]=Lépés +Name[is]=Færðu +Name[it]=Sposta le pedine +Name[ja]=ãƒã‚§ãƒƒã‚«ãƒ¼ã®ç§»å‹• +Name[km]=ផ្លាស់​ទី​អ្នក​ពិនិážáŸ’áž™ +Name[lt]=Eiti Å¡aÅ¡kÄ—mis +Name[lv]=PÄrvietot kauliņus +Name[mk]=ПремеÑтете ги пуловите +Name[mt]=Mexxi checkers +Name[nb]=Flytt brikker +Name[nds]=Steen trecken +Name[ne]=चाल परिकà¥à¤·à¤• +Name[nl]=Stukken verplaatsen +Name[nn]=Flytt brikker +Name[nso]=Sutisa checkers +Name[pl]=Rusz pionkiem +Name[pt]=Mover as peças +Name[pt_BR]=Mover peças +Name[ro]=MutaÅ£i piesele +Name[ru]=Передвинуть фишку +Name[se]=Sirdde bihtáid +Name[sk]=Presunúť kamene +Name[sl]=Premakni figure +Name[sr]=Померите чекере +Name[sr@Latn]=Pomerite Äekere +Name[sv]=Flytta brickor +Name[ta]=கடà¯à®Ÿà®®à¯ கடà¯à®Ÿà®®à®¾à®• நகறà¯à®±à¯à®• +Name[tg]=Ҷойивазкунии домнаҳо +Name[th]=ย้ายตัวหมาภ+Name[tr]=Pulu oynat +Name[uk]=ПереÑунути шашки +Name[ven]=Tshimbidzani checkers +Name[vi]=Di chuyển checkers +Name[xh]= Hambisa icheckers +Name[zh_CN]=ç§»åŠ¨æ£‹å­ +Name[zh_TW]=ç§»å‹•æ£‹å­ +Name[zu]=Nyakazisa i-checkers +Comment=The dice have been rolled and it's your turn to move checkers +Comment[af]=Die dobbelsteen het al gerol en dit is jou skakel na beweeg skuifstukke +Comment[az]=ZÉ™ri atdınız vÉ™ daÅŸları oynatmaq vaxtı gÉ™ldi +Comment[be]=Ð’Ñ‹ ўжо кінулі коÑці, Ñ– зараз ваш ход +Comment[bg]=ПремеÑтване на пулове +Comment[bn]=পাশা গড়ানো হয়েছে à¦à¦¬à¦‚ à¦à¦–ন আপনার চেকারà§à¦¸ চালার পালা +Comment[ca]=S'han llançat els daus i és el vostre torn per a moure les fitxes +Comment[cs]=Kostka byla hozená a teÄ máte pÅ™esunout kameny +Comment[cy]=Mae'r dîs wedi eu taflu ac eich tro chi yw hi i symud y drafftiau +Comment[da]=Terningerne er kastet og det er din tur til at flytte brikkerne +Comment[de]=Die Würfel sind gefallen, Sie müssen ziehen. +Comment[el]=Τα ζάÏια Ïίχτηκαν και είναι η σειÏά σας να μετακινήσετε τα ποÏλια +Comment[eo]=La kuboj estas ĵetitaj kaj estas via vico movi la pecojn +Comment[es]=Se han lanzado los dados y es su turno para mover fichas +Comment[et]=Täring sai visatud, liiguta nüüd nuppe +Comment[eu]=Dadoak jaurti dira eta fitxak mugitzeko zure txanda da +Comment[fa]=طاس غلتانیده شد، Ùˆ نوبت شماست Ú©Ù‡ بازبینها را حرکت بدهید +Comment[fi]=Noppaa on heitetty ja on sinun vuoro siirtää tammea +Comment[fr]=Les dés ont été jetés et c'est à votre tour de déplacer des pions +Comment[gl]=O dado xa rodou e é a súa quenda de mover as fichas +Comment[he]=הקוביות הוטלו וכעת תורך להזיז ×ת ×בני המשחק +Comment[hi]=पाà¤à¤¸à¤¾ फेंक दिया गया है और यह चेकरà¥à¤¸ को चलने की आपकी बारी है +Comment[hr]=Kocke su baÄene i vaÅ¡ je red da pomaknete figure +Comment[hu]=A dobás megtörtént, most Ön lép +Comment[is]=Það er búið að kasta og þú átt að færa +Comment[it]=I dadi sono stati lanciati e tocca a te muovere le pedine +Comment[ja]=サイã¯æŠ•ã’られã¾ã—ãŸã€ãƒã‚§ãƒƒã‚«ãƒ¼ã‚’å‹•ã‹ã™ç•ªã§ã™ +Comment[km]=គ្រាប់​ឡុក​ឡាក់​ážáŸ’រូវ​បាន​ប្រមៀល ហើយ​វា​ជា​វáŸáž“​របស់​អ្នក​ដើម្បី​ផ្លាស់​ទីអ្នក​ពិនិážáŸ’áž™ +Comment[lt]=Kauliukas nuridentas ir dabar jÅ«sų eilÄ— stumti Å¡aÅ¡kes +Comment[lv]=Kauliņi ir mesti un ir jÅ«su kÄrta izdarÄ«t gÄjienu +Comment[mk]=Коцките Ñе фрлени и вие Ñте на ред да ги премеÑтите пуловите +Comment[mt]=Id-damem twaddbu u jmiss lilek tmexxi Ä‹-checkers +Comment[nb]=Terningene er kastet og det er din tur til Ã¥ flytte +Comment[nds]=Du hest wörpelt un muttst nu trecken +Comment[ne]=पासा घà¥à¤®à¤¾à¤‡à¤à¤•à¥‹ छ र परीकà¥à¤·à¤• सारà¥à¤¨à¥‡ अब तपाईà¤à¤•à¥‹ पालो छ । +Comment[nl]=De dobbelsteen is geworpen, en u bent aan de beurt om de stukken te verplaatsen. +Comment[nn]=Terningen er rulla, og det er din tur til Ã¥ flytta brikker +Comment[nso]=Mataese a tokolositswe gomme ke nako ya gago yago sutisa checkers +Comment[pl]=Kość zostaÅ‚a rzucona, Twój ruch do ruchu pionkiem +Comment[pt]=Os dados foram lançados e é a sua vez de mexer as peças +Comment[pt_BR]=Os dados foram jogados e é a sua vez de mover as peças +Comment[ro]=Zarurile au fost aruncate ÅŸi e rîndul dumneavoastră să mutaÅ£i piesele +Comment[ru]=Ð’Ñ‹ уже броÑили коÑти, и теперь ваша очередь ходить +Comment[se]=Bircu lea Äaskon, ja dál lea du vuorru bihtáid sirdit +Comment[sk]=Kocka bola hodená a teraz máte presunúť kamene +Comment[sl]=Kocke so vržene in zdaj ste na vrsti za premik figur +Comment[sr]=Коцкица је бачена и ваш је ред да померите чекере +Comment[sr@Latn]=Kockica je baÄena i vaÅ¡ je red da pomerite Äekere +Comment[sv]=Tärningen har slagits och det är din tur att flytta brickor +Comment[ta]=தாயம௠உரà¯à®Ÿà¯à®Ÿà®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯.இபà¯à®ªà¯‹à®¤à¯ நீஙà¯à®•à®³à¯ கடà¯à®Ÿà®¤à¯à®¤à¯ˆ நகரà¯à®¤à¯à®¤à®²à®¾à®®à¯. +Comment[tg]=Мӯҳрҳо партофта шудаанд ва ҳоло навбати шумо барои ҷойивазкунии дамнаҳо +Comment[tr]=Zar atıldı ve ÅŸimdi pulları oynatma sırası sizde +Comment[uk]=КоÑÑ‚Ñ– кинуто; ваша черга переÑунути шашки +Comment[ven]=Dice lo kunguluswa zwino ndi tshifhinga tshavho tsha u tshimbidza checkers +Comment[vi]=xúc sắc đã được gieo và đến lượt bạn di chuyển checkers +Comment[xh]=Amadayisi aqengqiwe lithuba lakho lukuhambisa iicheckersi +Comment[zh_CN]=å·²ç»æŽ·äº†éª°å­ï¼Œè¯¥æ‚¨ç§»åŠ¨äº† +Comment[zh_TW]=已經擲了骰å­ï¼Œè©²æ‚¨ç§»å‹•äº† +Comment[zu]=Idayisi ligingqiwe, ithuba lakho lokunyakazisa i-checkers +default_sound=kbackgammon-roll.wav +default_presentation=1 + +[invitation] +Name=Game invitation +Name[af]=Speletjie uitnodiging +Name[ar]=دعوة إلى لعبة +Name[az]=Oyuna DÉ™vÉ™t +Name[be]=ЗапрашÑнне да гульні +Name[bg]=Покана за игра +Name[bn]=খেলার আমনà§à¦¤à§à¦°à¦£ +Name[bs]=Poziv u igru +Name[ca]=Invitació a una partida +Name[cs]=Výzva ke hÅ™e +Name[cy]=Gwahoddiad gêm +Name[da]=Spilinvitation +Name[de]=Einladung zum Spiel +Name[el]=ΠÏόσκληση για παιχνίδι +Name[eo]=Ludinvito +Name[es]=Invitación al juego +Name[et]=Mängu tervitus +Name[eu]=Jokora gonbidapena +Name[fa]=دعوت به بازی +Name[fi]=Pelikutsu +Name[fr]=Invitation à une partie +Name[gl]=Invitación ao xogo +Name[he]=הזמנה למשחק +Name[hi]=खेल निमंतà¥à¤°à¤£ +Name[hr]=Poziv za igru +Name[hu]=Játék kezdeményezése (meghívás) +Name[is]=Býð þér í nýjan leik +Name[it]=Invito a giocare +Name[ja]=ゲームã«æ‹›å¾… +Name[km]=សំបុážáŸ’រ​អញ្ជើញ​ល្បែង +Name[lt]=Kvietimas į žaidimÄ… +Name[lv]=IelÅ«gums uz spÄ“li +Name[mk]=Покана за игра +Name[mt]=Stedina għal logħba +Name[nb]=Spillinvitasjon +Name[nds]=Speelinladen +Name[ne]=खेल निमनà¥à¤¤à¥à¤°à¤£à¤¾ +Name[nl]=Speluitnodiging +Name[nn]=Spelinvitasjon +Name[nso]=Memo ya papadi +Name[pa]=ਖੇਡ ਸੱਦਾ +Name[pl]=Zaproszenie do gry +Name[pt]=Convite para jogo +Name[pt_BR]=Convite para jogar +Name[ro]=InvitaÅ£ie joc +Name[ru]=Приглашение в игру +Name[se]=Speallanbovdehus +Name[sk]=Výzva na hru +Name[sl]=Povabilo k igri +Name[sr]=Позив у игру +Name[sr@Latn]=Poziv u igru +Name[sv]=Spelinbjudan +Name[ta]= விளையாடà¯à®Ÿà¯ அழைபà¯à®ªà®¿à®¤à®´à¯ +Name[tg]=Ташрифот ба бозӣ +Name[th]=เชิà¸à¹ƒà¸«à¹‰à¹€à¸¥à¹ˆà¸™à¹€à¸à¸¡à¸”้วย +Name[tr]=Oyuna davet +Name[uk]=Ð—Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð´Ð¾ гри +Name[uz]=OÊ»yinga taklif qilish +Name[uz@cyrillic]=Ўйинга таклиф қилиш +Name[ven]=U rambiwa ha mutambo +Name[vi]=Má»i chÆ¡i game +Name[xh]=Isimemo somdlalo +Name[zh_CN]=游æˆé‚€è¯· +Name[zh_TW]=邀請別人加入éŠæˆ² +Name[zu]=Isimemo somdlalo +Comment=Somebody has invited you to a match +Comment[af]=Iemand het uitgenooi jy na 'n ooreenstem +Comment[ar]=لقد دعاك أحد لمباراة +Comment[az]=Biri sizi oyuna dÉ™vÉ™t etdi +Comment[be]=Ð’Ð°Ñ Ð·Ð°Ð¿Ñ€Ð°ÑˆÐ°ÑŽÑ†ÑŒ паўдзельнічаць у гульні +Comment[bg]=Покана за игра +Comment[bn]=কেউ à¦à¦•à¦œà¦¨ আপনাকে à¦à¦•à¦Ÿà¦¿ মà§à¦¯à¦¾à¦šà§‡ আমনà§à¦¤à§à¦°à¦£ জানিয়েছে +Comment[bs]=Neko vas je pozvao u meÄ +Comment[ca]=Algú us ha invitat a una partida +Comment[cs]=NÄ›kdo vás vyzval na zápas +Comment[cy]=Mae rhywun wedi eich gwahodd i gêm +Comment[da]=Der er nogen som har inviteret dig til et spil +Comment[de]=Jemand hat Sie zu einer Partie eingeladen. +Comment[el]=Κάποιος σας κάλεσε για ένα παιχνίδι +Comment[eo]=Iu invitis vin al ludo +Comment[es]=Alguien le ha invitado a una partida +Comment[et]=Keegi kutsus sind duellile +Comment[eu]=Norbaitek joko batera gonbidatu zaitu +Comment[fa]=شخصی شما را به یک مسابقه دعوت کرده است +Comment[fi]=Joku on kutsunut sinut otteluun +Comment[fr]=Quelqu'un vient de vous proposer une partie +Comment[gl]=Alguén convidouno a botar unha partida +Comment[he]=מישהו הזמין ×ותך למשחק +Comment[hi]=किसी ने आपको पà¥à¤°à¤¤à¤¿à¤¯à¥‹à¤—िता के लिठनà¥à¤¯à¥Œà¤¤à¤¾ दिया है +Comment[hr]=Netko vas je pozvao na igru +Comment[hu]=Valaki kihívta Önt egy játszmára +Comment[is]=Það bauð þér einhver í leik +Comment[it]=Qualcuno ti ha invitato ad una partita +Comment[ja]=誰ã‹ãŒã‚ãªãŸã‚’マッãƒã«æ‹›å¾…ã—ã¾ã—㟠+Comment[km]=មាន​មនុស្ស​អញ្ជើញ​អ្នក​ទៅ​កាន់​ការ​ប្រកួហ+Comment[lt]=Kažkas pakvietÄ— jus maÄui +Comment[lv]=KÄds jÅ«s ir uzaicinÄjis uz spÄ“li +Comment[mk]=Ðекој ве покани на натпревар +Comment[mt]=Xi ħadd stiednek għal logħba +Comment[nb]=Noen har invitert deg til et spill +Comment[nds]=Een hett Di to en Törn inlaadt +Comment[ne]=कसैले तपाईà¤à¤²à¤¾à¤ˆ खेलका लागि निमनà¥à¤¤à¥à¤°à¤£à¤¾ गरेकोछ । +Comment[nl]=Iemand heeft u voor een spel uitgenodigd. +Comment[nn]=Nokon har invitert deg til ein runde +Comment[nso]=Motho o mongwe ogo laleditse papading +Comment[pa]=ਤà©à¨¹à¨¾à¨¨à©‚à©° ਕਿਸੇ ਨੇ ਮੈਚ ਖੇਡਣ ਲਈ ਸੱਦਿਆ ਹੈ +Comment[pl]=KtoÅ› zaproponowaÅ‚ pojedynek +Comment[pt]=Alguém o convidou para um jogo +Comment[pt_BR]=Alguém o convidou para uma partida +Comment[ro]=Cineva va invitat la o partidă +Comment[ru]=Ð’Ð°Ñ Ð¿Ñ€Ð¸Ð³Ð»Ð°ÑˆÐ°ÑŽÑ‚ начать игру +Comment[se]=Giinu lea bovden du speallat +Comment[sk]=Niekto vás vyzval na zápas +Comment[sl]=Nekdo vas je povabil k igri +Comment[sr]=Ðеко Ð²Ð°Ñ Ñ˜Ðµ позвао у игру +Comment[sr@Latn]=Neko vas je pozvao u igru +Comment[sv]=NÃ¥gon har bjudit in dig till en match +Comment[ta]=எவரோ à®’à®°à¯à®µà®°à¯ உஙà¯à®•à®³à¯ˆ ஆடà¯à®Ÿà®¤à¯à®¤à®¿à®±à¯à®•à¯ அழைகà¯à®•à®¿à®±à®¾à®°à¯ +Comment[tg]=КаÑе шуморо ба муÑобиқа даъват мекунад +Comment[tr]=Birileri sizi maça davet etti +Comment[uk]=ХтоÑÑŒ запрошує Ð²Ð°Ñ Ð½Ð° матч +Comment[uz]=Kimdir sizni oÊ»yinga taklif qildi +Comment[uz@cyrillic]=Кимдир Ñизни ўйинга таклиф қилди +Comment[ven]=Munwe muthu o ni ramba uri ni tambe naye +Comment[vi]=Có ngÆ°á»i má»i bạn chÆ¡i +Comment[xh]=Ukhona umntu okumemele emdlalweni +Comment[zh_CN]=æœ‰äººé‚€è¯·æ‚¨è¿›è¡Œæ¸¸æˆ +Comment[zh_TW]=有人邀請您進行éŠæˆ² +Comment[zu]=Kukhona okumemele emdlalweni +default_sound=kbackgammon-move.wav +default_presentation=1 + diff --git a/kbackgammon/icons/Makefile.am b/kbackgammon/icons/Makefile.am new file mode 100644 index 00000000..1980b970 --- /dev/null +++ b/kbackgammon/icons/Makefile.am @@ -0,0 +1,3 @@ + +KDE_ICON = kbackgammon kbackgammon_engine + diff --git a/kbackgammon/icons/hi128-app-kbackgammon.png b/kbackgammon/icons/hi128-app-kbackgammon.png new file mode 100644 index 00000000..794c94e0 Binary files /dev/null and b/kbackgammon/icons/hi128-app-kbackgammon.png differ diff --git a/kbackgammon/icons/hi16-app-kbackgammon.png b/kbackgammon/icons/hi16-app-kbackgammon.png new file mode 100644 index 00000000..39640210 Binary files /dev/null and b/kbackgammon/icons/hi16-app-kbackgammon.png differ diff --git a/kbackgammon/icons/hi16-app-kbackgammon_engine.png b/kbackgammon/icons/hi16-app-kbackgammon_engine.png new file mode 100644 index 00000000..bd2ba6a1 Binary files /dev/null and b/kbackgammon/icons/hi16-app-kbackgammon_engine.png differ diff --git a/kbackgammon/icons/hi22-app-kbackgammon.png b/kbackgammon/icons/hi22-app-kbackgammon.png new file mode 100644 index 00000000..882d0f9b Binary files /dev/null and b/kbackgammon/icons/hi22-app-kbackgammon.png differ diff --git a/kbackgammon/icons/hi32-app-kbackgammon.png b/kbackgammon/icons/hi32-app-kbackgammon.png new file mode 100644 index 00000000..8d4b3f1d Binary files /dev/null and b/kbackgammon/icons/hi32-app-kbackgammon.png differ diff --git a/kbackgammon/icons/hi32-app-kbackgammon_engine.png b/kbackgammon/icons/hi32-app-kbackgammon_engine.png new file mode 100644 index 00000000..871ef577 Binary files /dev/null and b/kbackgammon/icons/hi32-app-kbackgammon_engine.png differ diff --git a/kbackgammon/icons/hi48-app-kbackgammon.png b/kbackgammon/icons/hi48-app-kbackgammon.png new file mode 100644 index 00000000..b6b5f154 Binary files /dev/null and b/kbackgammon/icons/hi48-app-kbackgammon.png differ diff --git a/kbackgammon/icons/hi48-app-kbackgammon_engine.png b/kbackgammon/icons/hi48-app-kbackgammon_engine.png new file mode 100644 index 00000000..8291e82a Binary files /dev/null and b/kbackgammon/icons/hi48-app-kbackgammon_engine.png differ diff --git a/kbackgammon/icons/hi64-app-kbackgammon.png b/kbackgammon/icons/hi64-app-kbackgammon.png new file mode 100644 index 00000000..f76e4158 Binary files /dev/null and b/kbackgammon/icons/hi64-app-kbackgammon.png differ diff --git a/kbackgammon/icons/hi64-app-kbackgammon_engine.png b/kbackgammon/icons/hi64-app-kbackgammon_engine.png new file mode 100644 index 00000000..f29cb603 Binary files /dev/null and b/kbackgammon/icons/hi64-app-kbackgammon_engine.png differ diff --git a/kbackgammon/kbackgammon.desktop b/kbackgammon/kbackgammon.desktop new file mode 100644 index 00000000..f81fd922 --- /dev/null +++ b/kbackgammon/kbackgammon.desktop @@ -0,0 +1,75 @@ +[Desktop Entry] +Exec=kbackgammon %i %m -caption "%c" +Name=KBackgammon +Name[af]=Kbackgammon +Name[ar]=لعبة النرد/الطاولة (KBackgammon) +Name[be]=Ðарды +Name[bn]=কে-বà§à¦¯à¦¾à¦•à¦—à§à¦¯à¦¾à¦®à§‹à¦¨ +Name[cs]=Vrchcáby +Name[eo]=Bakgamono +Name[hi]=के-बैकगेमॉन +Name[is]=Kotra +Name[ja]=ãƒãƒƒã‚¯ã‚®ãƒ£ãƒ¢ãƒ³ +Name[ne]=केडीई बà¥à¤¯à¤¾à¤•à¤—ामोन +Name[pt_BR]=KGamão +Name[ro]=Joc de table +Name[sv]=Kbackgammon +Name[ta]=கேபேகà¯à®•à®®à®¾à®©à¯ +Name[tg]=KÐардбозӣ +Name[tr]=Tavla +Name[zh_TW]=KBackgammon 西洋雙陸棋 +Type=Application +DocPath=kbackgammon/index.html +GenericName=Backgammon Game +GenericName[be]=Ð“ÑƒÐ»ÑŒÐ½Ñ Ñž нарды +GenericName[bg]=Табла +GenericName[bn]=বà§à¦¯à¦¾à¦•à¦—à§à¦¯à¦¾à¦®à§‹à¦¨ খেলা +GenericName[bs]=Igra tavle (Backgammon) +GenericName[ca]=Joc de Backgammon +GenericName[cs]=Backgammon hra +GenericName[cy]=Gêm Dawlbwrdd +GenericName[da]=Backgammon-spil +GenericName[de]=Backgammon Spiel +GenericName[el]=Παιχνίδι τάβλι +GenericName[eo]=Triktrakludo +GenericName[es]=Juego de Backgammon +GenericName[et]=Backgammoni mäng +GenericName[eu]=Backgammon jokoa +GenericName[fa]=بازی Backgammon +GenericName[fi]=Backgammon lautapeli +GenericName[fr]=Jeu de Backgammon +GenericName[he]=משחק שש־בש +GenericName[hr]=Backgammon +GenericName[hu]=Backgammon +GenericName[is]=Kotruleikur +GenericName[it]=Gioco del Backgammon +GenericName[ja]=ãƒãƒƒã‚¯ã‚®ãƒ£ãƒ¢ãƒ³ +GenericName[km]=ល្បែង Backgammon +GenericName[lt]=Backgammon žaidimas +GenericName[lv]=Backgammon spÄ“le +GenericName[mk]=Игра на табла +GenericName[nb]=Backgammon-spill +GenericName[nds]=Backgammon-Speel +GenericName[ne]=बà¥à¤¯à¤¾à¤•à¤—ामोन खेल +GenericName[nl]=Backgammonspel +GenericName[nn]=Backgammon-spel +GenericName[pa]=ਬੈਕਗਾਮੋਨ ਖੇਡ +GenericName[pl]=Backgammon +GenericName[pt]=Jogo de Gamão +GenericName[pt_BR]=Jogo de Gamão +GenericName[ru]=Ðарды +GenericName[se]=Backgammon-speallu +GenericName[sk]=Backgammon hra +GenericName[sl]=Igra backgammona +GenericName[sr]=Игра бекгемона +GenericName[sr@Latn]=Igra bekgemona +GenericName[sv]=Backgammonspel +GenericName[ta]=பாகà¯à®•à®¾à®®à®¾à®©à¯ விளையாடà¯à®Ÿà¯ +GenericName[uk]=Гра в нарди +GenericName[wa]=Djeu d' backgammon +GenericName[zh_TW]=Backgammon 西洋雙陸棋éŠæˆ² +Terminal=false +Icon=kbackgammon +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;BoardGame; diff --git a/kbackgammon/kbackgammonui.rc b/kbackgammon/kbackgammonui.rc new file mode 100644 index 00000000..24791882 --- /dev/null +++ b/kbackgammon/kbackgammonui.rc @@ -0,0 +1,51 @@ + + + + + &Move + + + + + &Command + + &Settings + + + &Help + + + + +Main Toolbar + + + + + + + + + + + +Command Toolbar + + + + + + + + + + + + + + + + + + + diff --git a/kbackgammon/kbg.cpp b/kbackgammon/kbg.cpp new file mode 100644 index 00000000..55aef32a --- /dev/null +++ b/kbackgammon/kbg.cpp @@ -0,0 +1,830 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#include "kbg.h" +#include "kbg.moc" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kbgtextview.h" +#include "offline/kbgoffline.h" +#include "fibs/kbgfibs.h" +#include "gnubg/kbggnubg.h" +#include "nextgen/kbgng.h" +#include "version.h" + + +// == setup ==================================================================== + +/* + * Constructor creates user interface, actions and first engine. + */ +KBg::KBg() +{ + /* + * Initialize menu strings + */ + engineString[Offline] = i18n("Open Board"); + engineString[FIBS ] = i18n("FIBS"); + engineString[GNUbg ] = i18n("GNU Backgammon (Experimental)"); + engineString[NextGen] = i18n("Next Generation (Experimental)"); + + helpTopic[FIBSHome][0] = i18n("FIBS Home"); + helpTopic[FIBSHome][1] = "http://www.fibs.com/"; + + helpTopic[RuleHome][0] = i18n("Backgammon Rules"); + helpTopic[RuleHome][1] = "http://www.bkgm.com/rules.html"; + + /* + * The main view is shared between the board and a small text window + */ + panner = new QSplitter(Vertical, this, "panner"); + board = new KBgBoardSetup(panner, "board"); + status = new KBgTextView(panner, "status"); + setCentralWidget(panner); + + /* + * Create all actions needed by the application + */ + newAction = KStdGameAction::gameNew(this, SLOT(openNew()), actionCollection()); + newAction->setEnabled(false); + KStdGameAction::print(this, SLOT(print()), actionCollection()); + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + + QStringList list; + for (int i = 0; i < MaxEngine; i++) + list.append(engineString[i]); + engineSet = new KSelectAction(i18n("&Engine"), 0, this, SLOT(setupEngine()), actionCollection(), + "move_engine"); + engineSet->setItems(list); + + // AB: what the heck has this to do with redisplay? perhaps use reload instead? + loadAction = KStdAction::redisplay(this, SLOT(load()), actionCollection(), "move_load"); + loadAction->setEnabled(false); + undoAction = KStdGameAction::undo(this, SLOT(undo()), actionCollection()); + undoAction->setEnabled(false); + redoAction = KStdGameAction::redo(this, SLOT(redo()), actionCollection()); + redoAction->setEnabled(false); + + rollAction = KStdGameAction::roll(this, SLOT(roll()), actionCollection()); + rollAction->setEnabled(false); + endAction = KStdGameAction::endTurn(this, SLOT(done()), actionCollection()); + endAction->setEnabled(false); + cubeAction = new KAction(i18n("Double Cube"), QIconSet(kapp->iconLoader()->loadIcon + (PROG_NAME "-double.xpm", KIcon::Toolbar)), + 0, this, SLOT(cube()), actionCollection(), "move_cube"); + cubeAction->setEnabled(false); + + KStdAction::showMenubar(this, SLOT(toggleMenubar()), actionCollection()); + KStdAction::preferences(this, SLOT(setupDlg()), actionCollection()); + KStdAction::saveOptions(this, SLOT(saveConfig()), actionCollection()); + + KPopupMenu *p = (new KActionMenu(i18n("&Backgammon on the Web"), + actionCollection(), "help_www"))->popupMenu(); + + (new KAction(helpTopic[FIBSHome][0], 0, this, SLOT(wwwFIBS()), + actionCollection(), "help_www_fibs"))->plug(p); + (new KAction(helpTopic[RuleHome][0], 0, this, SLOT(wwwRule()), + actionCollection(), "help_www_rule"))->plug(p); + + /* + * Set up the command line - using actions, otherwise recreating the GUI will delete them + * (e.g. using KEditToolbar) + */ + cmdLabel = new QLabel(i18n("Command: "), this); + new KWidgetAction( cmdLabel, cmdLabel->text(), 0, 0, 0, actionCollection(), "command_label"); + cmdLine = new KLineEdit(this, "commandline"); + KWidgetAction* actionCmdLine = new KWidgetAction( cmdLine, QString::null, 0, 0, 0, actionCollection(), "command_lineedit"); + actionCmdLine->setAutoSized(true); + + cmdLine->completionObject()->setOrder(KCompletion::Weighted); + connect(cmdLine, SIGNAL(returnPressed(const QString &)), this, SLOT(handleCmd(const QString &))); + /* + * Done with the actions, create the XML-defined parts of the + * user interface + */ + statusBar(); + setupGUI(); + + cmdLine->setFocus(); + + /* + * Initialize the engine to the default (offline). If the user + * prefers a different engine, it will be started later + */ + for (int i = 0; i < MaxEngine; i++) + engine[i] = 0; + currEngine = None; + engineSet->setCurrentItem(Offline); + setupEngine(); + + /* + * Set up configuration handling. + * FIXME: support session management + */ + connect(this, SIGNAL(readSettings()), board, SLOT(readConfig())); + connect(this, SIGNAL(saveSettings()), board, SLOT(saveConfig())); + + /* + * Set up some whatis messages for the online help + */ + QWhatsThis::add(status, i18n("This area contains the status messages for the game. " + "Most of these messages are sent to you from the current " + "engine.")); + QWhatsThis::add(toolBar("cmdToolBar"), + i18n("This is the command line. You can type special " + "commands related to the current engine in here. " + "Most relevant commands are also available " + "through the menus.")); + QWhatsThis::add(toolBar("mainToolBar"), + i18n("This is the button bar tool bar. It gives " + "you easy access to game related commands. " + "You can drag the bar to a different location " + "within the window.")); + QWhatsThis::add(statusBar(), + i18n("This is the status bar. It shows you the currently " + "selected engine in the left corner.")); + + /* + * Create and initialize the context menu + */ + QPopupMenu* menu = (QPopupMenu*)factory()->container("popup", this); + board->setContextMenu(menu); +} + +/* + * Destructor is empty + */ +KBg::~KBg() {} + + +// == engine handling ========================================================== + +/* + * Set the engine according to the currently selected item in the + * engineSet action. Additional engines have to be added to the switch + * statement (and only there). + */ +void KBg::setupEngine() +{ + /* + * Get new engine type + */ + int type = engineSet->currentItem(); + + /* + * Engine doesn't need to be changed? + */ + if (engine[type]) return; + + /* + * Check with the engine if it can be terminated + */ + if (currEngine != None && engine[currEngine] && !engine[currEngine]->queryClose()) { + engineSet->setCurrentItem(currEngine); + return; + } + + /* + * Remove the old engine, create a new one, and hook up menu and slots/signals + */ + QPopupMenu *commandMenu = (QPopupMenu *)factory()->container("command_menu", this); + QString s = PROG_NAME; + commandMenu->clear(); + + if (currEngine != None) { + delete engine[currEngine]; + engine[currEngine] = 0; + } + + switch (currEngine = type) { + case Offline: + engine[currEngine] = new KBgEngineOffline(this, &s, commandMenu); + break; + case FIBS: + engine[currEngine] = new KBgEngineFIBS(this, &s, commandMenu); + break; + case GNUbg: + engine[currEngine] = new KBgEngineGNU(this, &s, commandMenu); + break; + case NextGen: + engine[currEngine] = new KBgEngineNg(this, &s, commandMenu); + break; + default: // FIXME: we need some kind of catch here... + currEngine = Offline; + engine[currEngine] = new KBgEngineOffline(this, &s, commandMenu); + break; + } + + statusBar()->message(engineString[currEngine]); + KConfig* config = kapp->config(); + config->setGroup("global settings"); + if (config->readBoolEntry("enable timeout", true)) + engine[currEngine]->setCommit(config->readDoubleNumEntry("timeout", 2.5)); + newAction->setEnabled(engine[currEngine]->haveNewGame()); + + // engine -> this + connect(engine[currEngine], SIGNAL(statText(const QString &)), this, SLOT(updateCaption(const QString &))); + connect(engine[currEngine], SIGNAL(infoText(const QString &)), status, SLOT(write(const QString &))); + connect(engine[currEngine], SIGNAL(allowCommand(int, bool)), this, SLOT(allowCommand(int, bool))); + + // this -> engine + connect(this, SIGNAL(readSettings()), engine[currEngine], SLOT(readConfig())); + connect(this, SIGNAL(saveSettings()), engine[currEngine], SLOT(saveConfig())); + + // board -> engine + connect(board, SIGNAL(rollDice(const int)), engine[currEngine], SLOT(rollDice(const int))); + connect(board, SIGNAL(doubleCube(const int)), engine[currEngine], SLOT(doubleCube(const int))); + connect(board, SIGNAL(currentMove(QString *)), engine[currEngine], SLOT(handleMove(QString *))); + + // engine -> board + connect(engine[currEngine], SIGNAL(undoMove()), board, SLOT(undoMove())); + connect(engine[currEngine], SIGNAL(redoMove()), board, SLOT(redoMove())); + connect(engine[currEngine], SIGNAL(setEditMode(const bool)), board, SLOT(setEditMode(const bool))); + connect(engine[currEngine], SIGNAL(allowMoving(const bool)), board, SLOT(allowMoving(const bool))); + connect(engine[currEngine], SIGNAL(getState(KBgStatus *)), board, SLOT(getState(KBgStatus *))); + connect(engine[currEngine], SIGNAL(newState(const KBgStatus &)), board, SLOT(setState(const KBgStatus &))); + + // now that all signals are connected, start the engine + engine[currEngine]->start(); +} + + +// == configuration handing ==================================================== + +/* + * Save all settings that should be saved for the next start. + */ +void KBg::saveConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("global settings"); + + /* + * Save the main window options unless the user has asked not + * to do so. + */ + if (config->readBoolEntry("autosave on exit", true)) { + + config->setGroup("main window"); + + config->writeEntry("origin", pos()); + + config->writeEntry("font", status->font()); + config->writeEntry("panner", (double)board->height()/(double)panner->height()); + + saveMainWindowSettings(config, "main window"); + } + + /* + * Save the history + */ + config->setGroup("command line"); + config->writeEntry("history", cmdLine->completionObject()->items()); + + /* + * Save current engine + */ + config->setGroup("engine settings"); + config->writeEntry("last engine", currEngine); + + /* + * Tell other objects to save their settings, too. + */ + emit saveSettings(); + + config->sync(); +} + +/* + * Read the stored configuration and apply it + */ +void KBg::readConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("global settings"); + + /* + * Restore the main window settings unless the user has asked + * not to do so. + */ + if (config->readBoolEntry("autosave on exit", true)) { + + config->setGroup("main window"); + + QPoint pos, defpos(10, 10); + QFont kappFont = kapp->font(); + + pos = config->readPointEntry("origin", &defpos); + + status->setFont(config->readFontEntry("font", &kappFont)); + + QValueList l; + l.append(qRound( config->readDoubleNumEntry("panner", 0.75) *panner->height())); + l.append(qRound((1-config->readDoubleNumEntry("panner", 0.75))*panner->height())); + panner->setSizes(l); + + applyMainWindowSettings(config, "main window"); + } + + /* + * Restore the history + */ + config->setGroup("command line"); + cmdLine->completionObject()->setItems(config->readListEntry("history")); + + /* + * Tell other objects to read their configurations + */ + emit readSettings(); + + /* + * Restore last engine + */ + config->setGroup("engine settings"); + engineSet->setCurrentItem((Engines)config->readNumEntry("last engine", Offline)); + setupEngine(); +} + + +// == configuration ============================================================ + +/* + * Connected to the setup dialog applyButtonPressed signal. Make sure + * that all changes are saved. + */ +void KBg::setupOk() +{ + // global settings + KConfig* config = kapp->config(); + config->setGroup("global settings"); + + config->writeEntry("enable timeout", cbt->isChecked()); + config->writeEntry("timeout", sbt->value()); + config->writeEntry("autosave on exit", cbs->isChecked()); + + // tell engine about commit timer + engine[currEngine]->setCommit(cbt->isChecked() ? sbt->value() : -1); + + // one time requests + if (cbm->isChecked()) + KMessageBox::enableAllMessages(); + + // tell children to read their changes + board->setupOk(); + + // engines + for (int i = 0; i < MaxEngine; i++) + engine[i]->setupOk(); + + // save it all + saveConfig(); +} + +/* + * Load default values for the user settings + */ +void KBg::setupDefault() +{ + // timeout + cbt->setChecked(true); + sbt->setValue(2.5); + + // messages + cbm->setChecked(false); + + // auto save + cbs->setChecked(true); + + // board + board->setupDefault(); + + // engines + for (int i = 0; i < MaxEngine; i++) + engine[i]->setupDefault(); +} + +/* + * Connected to the setup dialog cancelButtonPressed signal. There + * isn't much to do. We tell the board to undo the changes. + */ +void KBg::setupCancel() +{ + // board + board->setupCancel(); + + // engines + for (int i = 0; i < MaxEngine; i++) + engine[i]->setupCancel(); +} + +/* + * Setup dialog is ready to be deleted. Do it later... + */ +void KBg::setupDone() +{ + nb->delayedDestruct(); + for (int i = 0; i < MaxEngine; i++) + if (i != currEngine) engine[i] = 0; +} + +// FIXME make more general... + +void KBg::startKCM(const QString &url) +{ + KRun::runCommand(url); +} + +/* + * Initialize and display the setup dialog + */ +void KBg::setupDlg() +{ + /* + * Get a new notebook in which all other members can put their + * config pages + */ + nb = new KDialogBase(KDialogBase::IconList, i18n("Configuration"), + KDialogBase::Ok|KDialogBase::Cancel|KDialogBase::Default| + KDialogBase::Apply|KDialogBase::Help, + KDialogBase::Ok, this, "setup", true, true); + + KConfig* config = kapp->config(); + config->setGroup("global settings"); + + /* + * Main Widget + */ + QVBox *w = nb->addVBoxPage(i18n("General"), i18n("Here you can configure general settings of %1"). + arg(kapp->aboutData()->programName()), + kapp->iconLoader()->loadIcon("go", KIcon::Desktop)); + + /* + * Group boxes + */ + QGroupBox *gbm = new QGroupBox(i18n("Messages"), w); + QGroupBox *gbt = new QGroupBox(i18n("Timer"), w); + QGroupBox *gbs = new QGroupBox(i18n("Autosave"), w); + QGroupBox *gbe = new QGroupBox(i18n("Events"), w); + + /* + * Timer box + */ + QWhatsThis::add(gbt, i18n("After you finished your moves, they have to be sent to the engine. " + "You can either do that manually (in which case you should not enable " + "this feature), or you can specify an amount of time that has to pass " + "before the move is committed. If you undo a move during the timeout, the " + "timeout will be reset and restarted once you finish the move. This is " + "very useful if you would like to review the result of your move.")); + + cbt = new QCheckBox(i18n("Enable timeout"), gbt); + cbt->setChecked(config->readBoolEntry("enable timeout", true)); + + sbt = new KDoubleNumInput(gbt); + sbt->setRange(0.0, 60.0, 0.5); + sbt->setLabel(i18n("Move timeout in seconds:")); + sbt->setValue(config->readDoubleNumEntry("timeout", 2.5)); + + connect(cbt, SIGNAL(toggled(bool)), sbt, SLOT(setEnabled(bool))); + sbt->setEnabled(cbt->isChecked()); + + QGridLayout *gl = new QGridLayout(gbt, 2, 1, 20); + gl->addWidget(cbt, 0, 0); + gl->addWidget(sbt, 1, 0); + + /* + * Enable messages + */ + QWhatsThis::add(gbm, i18n("Check the box to enable all the messages that you have previously " + "disabled by choosing the \"Don't show this message again\" option.")); + + QGridLayout *glm = new QGridLayout(gbm, 1, 1, nb->spacingHint()); + cbm = new QCheckBox(i18n("Reenable all messages"), gbm); + glm->addWidget(cbm, 0, 0); + + /* + * Save options on exit ? + */ + QWhatsThis::add(gbm, i18n("Check the box to automatically save all window positions on program " + "exit. They will be restored at next start.")); + + QGridLayout *gls = new QGridLayout(gbs, 1, 1, nb->spacingHint()); + cbs = new QCheckBox(i18n("Save settings on exit"), gbs); + cbs->setChecked(config->readBoolEntry("autosave on exit", true)); + gls->addWidget(cbs, 0, 0); + + /* + * Event vonfiguration + */ + QWhatsThis::add(gbe, i18n("Event notification of %1 is configured as part of the " + "system-wide notification process. Click here, and you " + "will be able to configure system sounds, etc."). + arg(kapp->aboutData()->programName())); + + QGridLayout *gle = new QGridLayout(gbe, 1, 1, nb->spacingHint()); + KURLLabel *lab = new KURLLabel("kcmshell kcmnotify", + i18n("Klick here to configure the event notification"), gbe); + lab->setMaximumSize(lab->sizeHint()); + + gle->addWidget(lab, 0, 0); + connect(lab, SIGNAL(leftClickedURL(const QString &)), SLOT(startKCM(const QString &))); + + /* + * Board settings + */ + board->getSetupPages(nb); + + /* + * Hack alert: this little trick makes sure that ALL engines + * have their settings available in the dialog. + */ + QPopupMenu *dummyPopup = new QPopupMenu(nb); + QString s = PROG_NAME; + for (int i = 0; i < MaxEngine; i++) { + if (currEngine != i) { + switch (i) { + case Offline: + engine[i] = new KBgEngineOffline(nb, &s, dummyPopup); + break; + case FIBS: + engine[i] = new KBgEngineFIBS(nb, &s, dummyPopup); + break; + case GNUbg: + engine[i] = new KBgEngineGNU(nb, &s, dummyPopup); + break; + case NextGen: + engine[i] = new KBgEngineNg(nb, &s, dummyPopup); + break; + } + connect(this, SIGNAL(saveSettings()), engine[i], SLOT(saveConfig())); + } + engine[i]->getSetupPages(nb); + } + + /* + * Connect the signals of nb + */ + connect(nb, SIGNAL(okClicked()), this, SLOT(setupOk())); + connect(nb, SIGNAL(applyClicked()), this, SLOT(setupOk())); + connect(nb, SIGNAL(cancelClicked()), this, SLOT(setupCancel())); + connect(nb, SIGNAL(defaultClicked()),this, SLOT(setupDefault())); + + connect(nb, SIGNAL(finished()), this, SLOT(setupDone())); + + nb->resize(nb->minimumSize()); + nb->show(); +} + + +// == action slots ============================================================= + +/* + * Tell the board to print itself - restore and save user settings for + * the print dialog. + */ +void KBg::print() +{ + KPrinter *prt = new KPrinter(); + + KConfig* config = kapp->config(); + config->setGroup("printing"); + + prt->setNumCopies(config->readNumEntry("numcopies", 1)); + prt->setOutputFileName(config->readPathEntry("outputfile")); + prt->setOutputToFile(config->readBoolEntry("tofile", false)); + prt->setPageSize((KPrinter::PageSize) config->readNumEntry("pagesize", KPrinter::A4)); + prt->setOrientation((KPrinter::Orientation)config->readNumEntry("orientation", KPrinter::Landscape)); + + if (prt->setup(this, i18n("Print %1").arg(baseCaption))) { + QPainter p; + p.begin(prt); + board->print(&p); + p.end(); + config->writeEntry("tofile", prt->outputToFile()); + config->writePathEntry("outputfile", prt->outputFileName()); + config->writeEntry("pagesize", (int)prt->pageSize()); + config->writeEntry("orientation", (int)prt->orientation()); + config->writeEntry("numcopies", prt->numCopies()); + } + delete prt; +} + +/* + * Toggle visibility of the menubar - be careful that the menu doesn't + * get lost + */ +void KBg::toggleMenubar() +{ + if (menuBar()->isVisible()) { + + KMessageBox::information(this, i18n("You can enable the menubar again with the " + "right mouse button menu of the board."), + i18n("Information"), "conf_menubar_information"); + menuBar()->hide(); + + } else { + + menuBar()->show(); + } +} + +/* + * Display a standard dialog for the toolbar content + */ +void KBg::configureToolbars() +{ + saveMainWindowSettings(KGlobal::config(), "kedittoolbar settings"); // temp group + KEditToolbar dlg(actionCollection(), xmlFile(), true, this); + connect(&dlg,SIGNAL(newToolbarConfig()),this,SLOT(newToolbarConfig())); + dlg.exec(); + KGlobal::config()->deleteGroup( "kedittoolbar settings" ); // delete temp group +} + +/* + * Called when clicking OK or Apply in the toolbar editor + */ +void KBg::newToolbarConfig() +{ + createGUI(); + applyMainWindowSettings(KGlobal::config(), "kedittoolbar settings"); +} + +/* + * Help slots + */ +void KBg::wwwFIBS() {showWWW(FIBSHome);} +void KBg::wwwRule() {showWWW(RuleHome);} + +void KBg::showWWW(int t) +{ + kapp->invokeBrowser(helpTopic[t][1]); +} + +/* + * Edit slots + */ +void KBg::undo() {engine[currEngine]->undo();} +void KBg::redo() {engine[currEngine]->redo();} +void KBg::roll() {engine[currEngine]->roll();} +void KBg::cube() {engine[currEngine]->cube();} +void KBg::done() {engine[currEngine]->done();} +void KBg::load() {engine[currEngine]->load();} + +/* + * Start a new game with the current engine + */ +void KBg::openNew() +{ + engine[currEngine]->newGame(); +} + + +// == various slots - not for actions ========================================== + +/* + * Check with the engine if it is okay to close the window. + * If so, save settings. + */ +bool KBg::queryClose() +{ + bool ret = engine[currEngine]->queryClose(); + if ( ret ) + saveConfig(); + return ret; +} + +/* + * Set the caption of the main window. If the user has requested pip + * counts, they are appended, too. + */ +void KBg::updateCaption(const QString &s) +{ + baseCaption = s; + QString msg; + if (!s.isEmpty()) { + msg = s; + if (board->getPipCount(US) >= 0) { + QString tmp; + tmp.setNum(board->getPipCount(US )); + msg += " - " + tmp; + tmp.setNum(board->getPipCount(THEM)); + msg += "-" + tmp; + } + } + setCaption(msg, false); +} + +/* + * Take the string from the commandline, give it to the engine, append + * to the history and clear the buffer. + */ +void KBg::handleCmd(const QString &s) +{ + if (!s.stripWhiteSpace().isEmpty()) { + engine[currEngine]->handleCommand(s); + cmdLine->completionObject()->addItem(s); + } + cmdLine->clear(); + cmdLine->completionBox()->close(); +} + +/* + * Reflect the availability of commands in the button bar. + */ +void KBg::allowCommand(int cmd, bool f) +{ + switch (cmd) { + case KBgEngine::Undo: + undoAction->setEnabled(f); + break; + case KBgEngine::Redo: + redoAction->setEnabled(f); + break; + case KBgEngine::Roll: + rollAction->setEnabled(f); + break; + case KBgEngine::Cube: + cubeAction->setEnabled(f); + break; + case KBgEngine::Done: + endAction->setEnabled(f); + break; + case KBgEngine::Load: + loadAction->setEnabled(f); + break; + } +} + +/* + * Catch the hide envents. That way, the current engine can close its + * child windows. + */ +void KBg::hideEvent(QHideEvent *e) +{ + KMainWindow::hideEvent(e); + engine[currEngine]->hideEvent(); +} + +/* + * Catch the show envents. That way, the current engine can open any + * previously hidden windows. + */ +void KBg::showEvent(QShowEvent *e) +{ + KMainWindow::showEvent(e); + engine[currEngine]->showEvent(); +} + +// EOF + diff --git a/kbackgammon/kbg.h b/kbackgammon/kbg.h new file mode 100644 index 00000000..d191c8a3 --- /dev/null +++ b/kbackgammon/kbg.h @@ -0,0 +1,228 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + hoefkens@pilot.msu.edu + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KBG_H +#define __KBG_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +class QSplitter; +class QCheckBox; +class QPopupMenu; +class QLabel; +class KAction; +class KSelectAction; +class KLineEdit; +class KDialogBase; +class KDoubleNumInput; + +class KBgEngine; +class KBgTextView; +class KBgBoardSetup; + + +class KBg : public KMainWindow +{ + Q_OBJECT + +public: + + /** + * Constructor creates the full main window + */ + KBg(); + + /** + * Destructor + */ + virtual ~KBg(); + + /** + * Read various settings from the configuration files or + * set some reasonable defaults + */ + void readConfig(); + +public slots: + + /** + * Set the caption to KFIBS_NAME + string + pipcount (if requested by + * the user) + */ + void updateCaption(const QString &s); + + /** + * Slot to be called by the engine - it enables/disables buttons + * in the button bar + */ + void allowCommand(int cmd, bool f); + + /** + * Sets the backgammon engine to type + */ + void setupEngine(); + + void startKCM(const QString &); + +signals: + + /** + * Tell all listeners to write their settings to disk + */ + void saveSettings(); + + /** + * Tell all listeners to restore their settings or use reasonable + * defaults + */ + void readSettings(); + +protected: + + /* + * Windows are to be hidden + */ + virtual void hideEvent(QHideEvent *); + + /* + * Redisplay the windows + */ + virtual void showEvent(QShowEvent *); + + /* + * Called before the window is closed. Check with the engine + * if that is okay. + */ + virtual bool queryClose(); + +protected slots: + + /** + * Show the button bar - or not - depending on the corresponding action + */ + void toggleMenubar(); + + void configureToolbars(); + void newToolbarConfig(); + + /** + * Starts the print dialog and asks the board to print itself + */ + void print(); + + void openNew(); + + /** + * Takes text from the commandline and hands it over to the + * current engine + */ + void handleCmd(const QString &); + + /** + * Saves the user settings to disk + */ + void saveConfig(); + + /** + * Slots for the respective actions - called by the button bar + * and some global key shortcuts + */ + void undo(); + void redo(); + void roll(); + void cube(); + void load(); + void done(); + + /** + * Opens and displays the respective home pages + */ + void showWWW(int t); + + void wwwFIBS(); + void wwwRule(); + + /** + * Show the big setup dialog + */ + void setupDlg(); + + /** + * Save the settings + */ + void setupOk(); + + /** + * Delete the setup dialog + */ + void setupDone(); + + /** + * Load default values for user settings + */ + void setupDefault(); + + /** + * Undo the settings + */ + void setupCancel(); + +private: + KAction *newAction, *undoAction, *redoAction, *rollAction, *cubeAction, *endAction, *loadAction; + + /* + * Each engine has its own identifier. + */ + enum Engines {None = -1, Offline, FIBS, GNUbg, NextGen, MaxEngine}; + QString engineString[MaxEngine]; + KBgEngine *engine[MaxEngine]; + int currEngine; + + QPopupMenu *dummyPopup; + enum HelpTopics {FIBSHome, RuleHome, MaxHelpTopic}; + QString helpTopic[MaxHelpTopic][2]; + KSelectAction *engineSet; + + /** + * Notebook for the setup + */ + KDialogBase *nb; + KDoubleNumInput *sbt; + QCheckBox *cbt, *cbs, *cbm; + + /* + * UI elements + */ + QSplitter *panner; + KBgBoardSetup *board; + KBgTextView *status; + KLineEdit *cmdLine; + QLabel *cmdLabel; + QString baseCaption; // for user friendly printing, we keep it around +}; + +#endif // __KBG_H diff --git a/kbackgammon/kbgboard.cpp b/kbackgammon/kbgboard.cpp new file mode 100644 index 00000000..8b961a45 --- /dev/null +++ b/kbackgammon/kbgboard.cpp @@ -0,0 +1,2918 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +/* + + This file contains the implementation of the KBgBoard class and + all related utility classes. + + Effort has been made to keep this class general. Please comment on that + if you want to use it in your own project. + +*/ + +#include + +#include "kbgboard.h" +#include "kbgboard.moc" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "version.h" + + +const int CUBE_UPPER = 3; +const int CUBE_LOWER = 4; + +static const int MINIMUM_CHECKER_SIZE = 10; + +/* + * Set the default settings in all user configurations + */ +void KBgBoardSetup::setupDefault() +{ + // default background color + setBackgroundColor(QColor(200, 200, 166)); + pbc_1->setPalette(QPalette(backgroundColor())); + + // checker colors + baseColors[0] = black; + baseColors[1] = white; + pbc_2->setPalette(QPalette(baseColors[0])); + pbc_3->setPalette(QPalette(baseColors[1])); + + // default font + setFont(QFont("Serif", 18, QFont::Normal)); + kf->setFont(getFont()); + + // short moves + setShortMoveMode(SHORT_MOVE_DOUBLE); + for (int i = 0; i < 3; i++) + rbMove[i]->setChecked(i == SHORT_MOVE_DOUBLE); + + // pip count + cbp->setChecked(computePipCount = true); +} + +/* + * User committed the changes. Save them. + */ +void KBgBoardSetup::setupOk() +{ + // font selection + setFont(kf->font()); + + // move strategy + for (int i = 0; i < 3; i++) + if (rbMove[i]->isChecked()) setShortMoveMode(i); + + // pipcount + computePipCount = cbp->isChecked(); +} + +/* + * User cancelled the changes. Undo the color changes that become + * visible right away. + */ +void KBgBoardSetup::setupCancel() +{ + // undo background color change + setBackgroundColor(saveBackgroundColor); + + // undo checker color changes + baseColors[0] = saveBaseColors[0]; + baseColors[1] = saveBaseColors[1]; + + for (int i = 0; i < 30; i++) + cells[i]->update(); +} + +/* + * Fills configuration page in the dialog nb + */ +void KBgBoardSetup::getSetupPages(KDialogBase *nb) +{ + /* + * Main Widget + * =========== + */ + QVBox *vbp = nb->addVBoxPage(i18n("Board"), i18n("Here you can configure the backgammon board"), + kapp->iconLoader()->loadIcon(PROG_NAME, KIcon::Desktop)); + + /* + * Need more than one page + */ + KTabCtl *tc = new KTabCtl(vbp, "board tabs"); + + QWidget *w = new QWidget(tc); + QGridLayout *gl = new QGridLayout(w, 3, 1, nb->spacingHint()); + + /* + * Group boxes + * =========== + */ + QGroupBox *ga = new QGroupBox(w); + QButtonGroup *gm = new QButtonGroup(w); + QGroupBox *go = new QGroupBox(w); + + ga->setTitle(i18n("Colors")); + gm->setTitle(i18n("Short Moves")); + go->setTitle(i18n("Options")); + + gl->addWidget(ga, 0, 0); + gl->addWidget(gm, 1, 0); + gl->addWidget(go, 2, 0); + + /* + * Appearance group + * ---------------- + */ + QGridLayout *blc = new QGridLayout(ga, 2, 2, 20); + + pbc_1 = new QPushButton(i18n("Background"), ga); + pbc_1->setPalette(QPalette(backgroundColor())); + + pbc_2 = new QPushButton(i18n("Color 1"), ga); + pbc_2->setPalette(QPalette(baseColors[0])); + + pbc_3 = new QPushButton(i18n("Color 2"), ga); + pbc_3->setPalette(QPalette(baseColors[1])); + + blc->addWidget(pbc_2, 0, 0); + blc->addWidget(pbc_3, 0, 1); + blc->addMultiCellWidget(pbc_1, 1, 1, 0, 1); + + connect(pbc_1, SIGNAL(clicked()), this, SLOT(selectBackgroundColor())); + connect(pbc_2, SIGNAL(clicked()), this, SLOT(selectBaseColorOne())); + connect(pbc_3, SIGNAL(clicked()), this, SLOT(selectBaseColorTwo())); + + /* + * Moving style + * ------------ + */ + QBoxLayout *blm = new QVBoxLayout(gm, nb->spacingHint()); + + blm->addSpacing(gm->fontMetrics().height()); + + for (int i = 0; i < 3; i++) + rbMove[i] = new QRadioButton(gm); + + rbMove[SHORT_MOVE_NONE]->setText(i18n("&Disable short moves. Only drag and drop will move.")); + rbMove[SHORT_MOVE_SINGLE]->setText(i18n("&Single clicks with the left mouse button will\n" + "move a checker the shortest possible distance.")); + rbMove[SHORT_MOVE_DOUBLE]->setText(i18n("D&ouble clicks with the left mouse button will\n" + "move a checker the shortest possible distance.")); + + for (int i = 0; i < 3; i++) { + rbMove[i]->setMinimumSize(rbMove[i]->sizeHint()); + blm->addWidget(rbMove[i]); + rbMove[i]->setChecked(i == getShortMoveMode()); + } + + /* + * Other options + * ------------- + */ + QGridLayout *glo = new QGridLayout(go, 1, 1, 20); + + cbp = new QCheckBox(i18n("Show pip count in title bar"), go); + cbp->setChecked(computePipCount); + cbp->adjustSize(); + cbp->setMinimumSize(cbp->size()); + + glo->addRowSpacing(0, cbp->height()); + glo->addWidget(cbp, 0, 0); + + gl->activate(); + + w->adjustSize(); + w->setMinimumSize(w->size()); + + tc->addTab(w, i18n("&Board")); + + /* + * Save current settings + * --------------------- + */ + saveBackgroundColor = backgroundColor(); + saveBaseColors[0] = baseColors[0]; + saveBaseColors[1] = baseColors[1]; + + /* + * Font selection page + * =================== + */ + w = new QWidget(tc); + kf = new KFontChooser(w); + kf->setFont(getFont()); + gl = new QGridLayout(w, 1, 1, nb->spacingHint()); + gl->addWidget(kf, 0, 0); + gl->activate(); + w->adjustSize(); + w->setMinimumSize(w->size()); + tc->addTab(w, i18n("&Font")); +} + +/* + * Empty constructor calls the board constructor + */ +KBgBoardSetup::KBgBoardSetup(QWidget *parent, const char *name, QPopupMenu *menu) + : KBgBoard(parent, name, menu) +{ + // empty +} + +/* + * User changed first checker color + */ +void KBgBoardSetup::selectBaseColorOne() +{ + KColorDialog *c = new KColorDialog(this, "base-col-1", true); + c->setColor(baseColors[0]); + if (c->exec()) { + baseColors[0] = c->color(); + pbc_2->setPalette(QPalette(baseColors[0])); + for (int i = 0; i < 30; i++) + cells[i]->update(); + } + delete c; +} + +/* + * User changed second checker color + */ +void KBgBoardSetup::selectBaseColorTwo() +{ + KColorDialog *c = new KColorDialog(this, "base-col-2", true); + c->setColor(baseColors[1]); + if (c->exec()) { + baseColors[1] = c->color(); + pbc_3->setPalette(QPalette(baseColors[1])); + for (int i = 0; i < 30; i++) + cells[i]->update(); + } + delete c; +} + +/* + * User changed background color + */ +void KBgBoardSetup::selectBackgroundColor() +{ + KColorDialog *c = new KColorDialog(this, "bg-col", true); + c->setColor(backgroundColor()); + if (c->exec()) { + setBackgroundColor(c->color()); + pbc_1->setPalette(QPalette(backgroundColor())); + for (int i = 0; i < 30; i++) + cells[i]->update(); + } + delete c; +} + +/* + * Saves the persistent settings of the board + */ +void KBgBoard::saveConfig() +{ + KConfig* config = kapp->config(); + config->setGroup(name()); + + config->writeEntry("bgcolor", backgroundColor()); + config->writeEntry("color-1", baseColors[0]); + config->writeEntry("color-2", baseColors[1]); + config->writeEntry("font", getFont()); + config->writeEntry("move", getShortMoveMode()); + config->writeEntry("pip", computePipCount); +} + +/* + * Restore the settings or use reasonable defaults + */ +void KBgBoard::readConfig() +{ + QColor col(200, 200, 166); + QFont fon("Serif", 18, QFont::Normal); + + KConfig* config = kapp->config(); + config->setGroup(name()); + + setBackgroundColor(config->readColorEntry("bgcolor", &col)); + baseColors[0] = config->readColorEntry("color-1", &black); + baseColors[1] = config->readColorEntry("color-2", &white); + setFont(config->readFontEntry("font", &fon)); + setShortMoveMode(config->readNumEntry("move", SHORT_MOVE_DOUBLE)); + computePipCount = config->readBoolEntry("pip", true); +} + +/* + * Get the font the board cells should use for the display of + * numbers and cube value. + */ +QFont KBgBoard::getFont() const +{ + return boardFont; +} + +/* + * Allows the users of the board classe to set the font to be used + * on the board. Note that the fontsize is dynamically set + */ +void KBgBoard::setFont(const QFont& f) +{ + boardFont = f; +} + +/* + * Ask the user for an updated cube value + */ +void KBgBoard::queryCube() +{ + KBgStatus *st = new KBgStatus(); + getState(st); + KBgBoardQCube *dlg = + new KBgBoardQCube(abs(st->cube()), (st->cube(US) > 0), (st->cube(THEM) > 0)); + if (dlg->exec()) { + bool u = ((dlg->getCubeValue() == 0) || (dlg->getCubeOwner() == US )); + bool t = ((dlg->getCubeValue() == 0) || (dlg->getCubeOwner() == THEM)); + st->setCube((int)rint(pow(2.0, dlg->getCubeValue())), u, t); + setState(*st); // JENS + } + delete dlg; + delete st; +} + +/* + * Constructor, creates the dialog but does not show nor execute it. + */ +KBgBoardQCube::KBgBoardQCube(int val, bool us, bool them) + : QDialog(0, 0, true) +{ + setCaption(i18n("Set Cube Values")); + + QBoxLayout *vbox = new QVBoxLayout(this, 17); + + QLabel *info = new QLabel(this); + + cb[0] = new QComboBox(this, "first sb"); + cb[1] = new QComboBox(this, "second sb"); + ok = new KPushButton(KStdGuiItem::ok(), this); + cancel = new KPushButton(KStdGuiItem::cancel(), this); + + info->setText(i18n("Set the face value of the cube and select who should be able to\n" + "double. Note that a face value of 1 automatically allows both\n" + "players to double.")); + + info->setMinimumSize(info->sizeHint()); + + vbox->addWidget(info, 0); + + QBoxLayout *hbox_1 = new QHBoxLayout(); + QBoxLayout *hbox_2 = new QHBoxLayout(); + + vbox->addLayout(hbox_1); + vbox->addLayout(hbox_2); + + hbox_1->addWidget(cb[1]); + hbox_1->addWidget(cb[0]); + + hbox_2->addWidget(ok); + hbox_2->addWidget(cancel); + + cb[0]->insertItem(" 1", 0); + cb[0]->insertItem(" 2", 1); + cb[0]->insertItem(" 4", 2); + cb[0]->insertItem(" 8", 3); + cb[0]->insertItem("16", 4); + cb[0]->insertItem("32", 5); + cb[0]->insertItem("64", 6); + + switch(val) { + case 1: + cb[0]->setCurrentItem(0); + break; + case 2: + cb[0]->setCurrentItem(1); + break; + case 4: + cb[0]->setCurrentItem(2); + break; + case 8: + cb[0]->setCurrentItem(3); + break; + case 16: + cb[0]->setCurrentItem(4); + break; + case 32: + cb[0]->setCurrentItem(5); + break; + case 64: + cb[0]->setCurrentItem(6); + break; + } + + cb[1]->insertItem(i18n("Lower Player"), US); + cb[1]->insertItem(i18n("Upper Player"), THEM); + cb[1]->insertItem(i18n("Open Cube"), BOTH); + + if (us && them) + cb[1]->setCurrentItem(BOTH); + else if (us) + cb[1]->setCurrentItem(US); + else if (them) + cb[1]->setCurrentItem(THEM); + + cb[0]->setMinimumSize(cb[0]->sizeHint()); + cb[1]->setMinimumSize(cb[1]->sizeHint()); + + ok->setMinimumSize(ok->sizeHint()); + cancel->setMinimumSize(cancel->sizeHint()); + + setMinimumSize(childrenRect().size()); + + vbox->activate(); + + resize(minimumSize()); + + ok->setAutoDefault (true); + ok->setDefault(true); + + cb[0]->setFocus(); + + connect(ok, SIGNAL(clicked()), SLOT(accept())); + connect(cancel, SIGNAL(clicked()), SLOT(reject())); + + connect(cb[0], SIGNAL(activated(int)), SLOT(changePlayer(int))); + connect(cb[1], SIGNAL(activated(int)), SLOT(changeValue (int))); +} + +/* + * Deconstructor, empty. + */ +KBgBoardQCube::~KBgBoardQCube() +{ + // nothing +} + +/* + * Get the face value of the cube + */ +int KBgBoardQCube::getCubeValue() +{ + return cb[0]->currentItem(); +} + +/* + * Get the owner of the cube + */ +int KBgBoardQCube::getCubeOwner() +{ + return cb[1]->currentItem(); +} + +/* + * If the cube is open, the value can only be 1 + */ +void KBgBoardQCube::changeValue(int player) +{ + if (player == BOTH) + cb[0]->setCurrentItem(0); + +} + +/* + * If the value is 1, the cube has to be open; and if the value + * becomes bigger than 1, the player cannot stay open. + */ +void KBgBoardQCube::changePlayer(int val) +{ + if (val == 0) + cb[1]->setCurrentItem(BOTH); + else if (cb[1]->currentItem() == BOTH) + cb[1]->setCurrentItem(US); +} + +/* + * Constructor, creates the dialog but does not show nor execute it. + */ +KBgBoardQDice::KBgBoardQDice(const char *name) + : QDialog(0, name, true) +{ + setCaption(i18n("Set Dice Values")); + + QBoxLayout *vbox = new QVBoxLayout(this, 17); + + QLabel *info = new QLabel(this); + + sb[0] = new QSpinBox(this, "first sb"); + sb[1] = new QSpinBox(this, "second sb"); + ok = new KPushButton(KStdGuiItem::ok(), this); + cancel = new KPushButton(KStdGuiItem::cancel(), this); + + info->setText(i18n("Set the face values of the selected dice. The other player's\n" + "dice will be cleared and it will be the dice's owner's turn.")); + + info->setMinimumSize(info->sizeHint()); + + vbox->addWidget(info, 0); + + QBoxLayout *hbox_1 = new QHBoxLayout(); + QBoxLayout *hbox_2 = new QHBoxLayout(); + + vbox->addLayout(hbox_1); + vbox->addLayout(hbox_2); + + hbox_1->addWidget(sb[0]); + hbox_1->addWidget(sb[1]); + + hbox_2->addWidget(ok); + hbox_2->addWidget(cancel); + + sb[0]->setMinimumSize(sb[0]->sizeHint()); + sb[1]->setMinimumSize(sb[1]->sizeHint()); + + ok->setMinimumSize(ok->sizeHint()); + cancel->setMinimumSize(cancel->sizeHint()); + + setMinimumSize(childrenRect().size()); + + vbox->activate(); + + resize(minimumSize()); + + ok->setAutoDefault (true); + ok->setDefault(true); + + sb[0]->setFocus(); + + connect(ok, SIGNAL(clicked()), SLOT(accept())); + connect(cancel, SIGNAL(clicked()), SLOT(reject())); + + sb[0]->setValue(1); + sb[1]->setValue(1); + + sb[0]->setRange(1, 6); + sb[1]->setRange(1, 6); +} + +/* + * Deconstructor, empty. + */ +KBgBoardQDice::~KBgBoardQDice() +{ + // nothing +} + +/* + * Get the face value of the dice + */ +int KBgBoardQDice::getDice(int n) +{ + return sb[n]->value(); +} + +/* + * Allows for overriding the current turn color in edit mode. + */ +void KBgBoard::storeTurn(const int pcs) +{ + storedTurn = ((pcs > 0) ? +1 : -1); +} + +/* + * Switch edit mode on/off + */ +void KBgBoard::setEditMode(const bool m) +{ + editMode = m; +} + +/* + * Retrurns the current edit mode status. + */ +bool KBgBoard::getEditMode() const +{ + return editMode; +} + +/* + * This function takes a KBgStatus object and fills it with the current + * board status. + */ +KBgStatus* KBgBoard::getState(KBgStatus *st) const +{ + st->setColor(color); + st->setDirection(direction); + + st->setCube(cube, maydouble[US], maydouble[THEM]); + + st->setBar(US, onbar[US]); st->setBar(THEM, onbar[THEM]); + st->setHome(US, onhome[US]); st->setHome(THEM, onhome[THEM]); + + st->setDice(US, 0, dice[US][0]); + st->setDice(US, 1, dice[US][1]); + + st->setDice(THEM, 0, dice[THEM][0]); + st->setDice(THEM, 1, dice[THEM][1]); + + for (int i = 1; i < 25; ++i) + st->setBoard(i, ((color*board[i] < 0) ? THEM : US), abs(board[i])); + + return st; +} + +/* + * This function lets external users change the context menu + */ +void KBgBoard::setContextMenu(QPopupMenu *menu) +{ + contextMenu = menu; +} + +/* + * This function prints all moves up to now in the extended FIBS command + * notation (that is moves that involved kicking have a "+" instead of "-". + */ +void KBgBoard::sendMove() +{ + if (getEditMode()) + return; + + QString s, t; + + s.setNum(moveHistory.count()); + s += " "; + + QPtrListIterator it(moveHistory); + for (; it.current(); ++it) { + KBgBoardMove *move = it.current(); + if (move->source() == BAR_US || move->source() == BAR_THEM ) { + s += "bar"; + } else { + t.setNum(move->source()); + s += t; + } + if (move->wasKicked()) + s += "+"; + else + s += "-"; + + if ((move->destination() != HOME_THEM_LEFT) && (move->destination() != HOME_THEM_RIGHT) && + (move->destination() != HOME_US_LEFT ) && (move->destination() != HOME_US_RIGHT )) { + t.setNum(move->destination()); + s += t; + } else { + s += "off"; + } + s += " "; + } + emit currentMove(&s); +} + +/* + * This is overloaded from QWidget, since it has to pass the new + * background color to the child widgets (the cells). + */ +void KBgBoard::setBackgroundColor(const QColor &col) +{ + if (col != backgroundColor()) { + QWidget::setBackgroundColor(col); + for( int i = 0; i < 30; ++i) + cells[i]->setBackgroundColor(col); + } +} + +/* + * Overloaded from QWidget since we have to resize all cells + */ +void KBgBoard::resizeEvent(QResizeEvent *) +{ + int xo0 = 0; + int xo1, w; + int hu = height()/2; + int hl = height() - hu; + + checkerDiam = (int)((width()/15-2)<(height()/10.0-2) ? + (width()/15-2) : (height()/10.0-2)); + + if (checkerDiam < MINIMUM_CHECKER_SIZE) + checkerDiam = MINIMUM_CHECKER_SIZE; + + for (int i = 0; i < 14; ++i) { + xo1 = int((i+1)*width()/15.0); + w = xo1 - xo0; + cells[i ]->setGeometry(xo0, 0, w, hu); + cells[i+15]->setGeometry(xo0, hu, w, hl); + xo0 = xo1; + } + cells[14]->setGeometry(xo0, 0, width() - xo0, hu); + cells[29]->setGeometry(xo0, hu, width() - xo0, hl); +} + +/* + * This function draws the whole board in black and white on the + * painter *p. It is very well suited for printing on paper. + * It scales the output according to the width of the widget. + * I.e. if the widget is insanely long (y-direction) this will look + * shitty. The upper 20% of the painter are not used. So the caller + * can print whatever she/he wants above the 0.2*p->viewport().height() + * margin (like game status information). + */ +void KBgBoard::print(QPainter *p) +{ + double sf = 0.8*p->viewport().width()/width(); + int xo = int((p->viewport().width() - sf*width())/2); + int yo = int(0.2*p->viewport().height()); + int hu = height()/2; + + int xo0 = 0; + for (int i = 0; i < 15; ++i) { + cells[i ]->paintCell(p, xo+sf*xo0, yo , sf); + cells[i+15]->paintCell(p, xo+sf*xo0, yo+sf*(hu-1), sf); + xo0 = int((i+1)*width()/15.0); + } +} + +/* + * This function returns the selected drawing color for a checker + * of the given sign(!). I.e. we distinguish checkers by whether + * they are negative or positive. + */ +QColor KBgBoard::getCheckerColor(int p) const +{ + return ((p < 0) ? baseColors[0] : baseColors[1]); +} + +/* + * This small utility function returns the y-coordinate base + * of a checker. This is the offset in the y-coordinate at + * which we have toposition the upper corner of the first + * checker so that it is fully in the cell. + */ +int KBgBoardField::numberBase() const +{ + return (cellID < 13) ? 0 : height()-20; +} + +/* + * This function computes the proper diameter for checkers on this cell. + * It tries to stay within the horizontal boundaries and adjusts the + * diameter in such a way that 5 checkers fit on top of each other and + * there is still some room for stacked checkers. + */ +int KBgBoardCell::getCheckerDiameter() const +{ + return board->checkerDiam; +} + +/* + * Draws the cells content using the painter p. + * Reimplemented from QLabel. + */ +void KBgBoardCell::drawContents(QPainter *) +{ + QRect cr(0, 0, width(), height()); + cr.moveBottomLeft(rect().bottomLeft()); + QPixmap pix(cr.size()); + QPainter tmp; + pix.fill(this, cr.topLeft()); + tmp.begin(&pix); + paintCell(&tmp); + tmp.end(); + bitBlt(this, 0, 0, &pix); + /* + * New state is now current. + * This avoids unnecessary redrawings. + */ + stateChanged = false; +} + +/* + * This does the absolute bare minimum of painting a cell. It draws a small + * horizontal black line that marks the outer boundary of the cell and all + * overloaded paintCell() member are supposed to call this one after(!) they + * have painted themselves. + */ +void KBgBoardCell::paintCell(QPainter *p, int xo, int yo, double sf) const +{ + int x1 = xo; int x2 = xo; + int y1 = yo; int y2 = yo; + + if ((cellID==HOME_THEM_LEFT || cellID==BAR_THEM) || + (cellID<13 && cellID>0)) { + x2 += int(sf*width()); + } else if ((cellID==HOME_US_LEFT || cellID==BAR_US) || + (cellID<25 && cellID>12)) { + x2 += int(sf*width()); + y1 = y2 += int(sf*(height()-1)); + } else if (cellID == HOME_THEM_RIGHT) { + x2 += int(sf*(width()-1)); + } else if (cellID == HOME_US_RIGHT) { + x2 += int(sf*(width()-1)); + y1 = y2 += int(sf*(height()-1)); + } else { + return; // do nothing if the cellID is wrong + } + + // draw line in black + p->setBrush( black ); + p->setPen( black ); + p->drawLine(x1, y1, x2, y2); +} + +/* + * This function draws vertical boundaries around a cell. This is used + * for bars and homes to get them separated from the rest of the board. + */ +void +KBgBoardCell::drawVertBorder(QPainter *p, int xo, int yo, double sf) const +{ + p->setBrush(black); + p->setPen(black); + p->drawLine(xo, yo, xo, yo+sf*(height()-1)); + p->drawLine(xo+sf*(width()-1), yo, xo+sf*(width()-1), yo+sf*(height()-1)); +} + +/* + * This function draws the content of the homes on the painter *p. It + * starts at the upper left corner (xo, yo) and uses the scaling factor + * sf. + */ +void KBgBoardHome::paintCell(QPainter *p, int xo, int yo, double sf) const +{ + /* + * Only these homes contain checkers. The other ones contains dice and cube. + */ + if (((cellID == HOME_THEM_LEFT ) && (direction > 0)) || + ((cellID == HOME_THEM_RIGHT) && (direction < 0)) || + ((cellID == HOME_US_LEFT ) && (direction > 0)) || + ((cellID == HOME_US_RIGHT ) && (direction < 0))) { + + drawOverlappingCheckers(p, xo, yo, sf); + + } else { + + drawDiceAndCube(p, ((cellID == HOME_THEM_LEFT || + cellID == HOME_THEM_RIGHT) ? + THEM : US), xo, yo, sf); + + } + + /* + * Finally draw the boundaries + */ + drawVertBorder(p, xo, yo, sf); + KBgBoardCell::paintCell(p, xo, yo, sf); +} + +/* + * This function draws the content of the bar cells. Bars may contain + * checkers and the cube. Please read the comments in the code on how + * and why the checkers and (especially) the cube is printed. + */ +void KBgBoardBar::paintCell(QPainter *p, int xo, int yo, double sf) const +{ + /* + * Put in the checkers. + */ + drawOverlappingCheckers(p, xo, yo, sf); + + /* + * Now comes a slightly tricky part: the cube belongs in the center + * of the board if nobody has doubled yet. In the way we do the board + * the center belongs to two(!) fields - both bars. + * + * If we are not printing on paper we use the fact that + * Qt will clip the drawing for us. So we print the upper + * half of the cube and the lower half on different cells. + * + * Since there is no such thing as clipping when we print + * on paper we can only print one cube. It turns out that + * the lower one is sufficiently centered. + */ + if (board->canDouble(US) && + board->canDouble(THEM) && + !(abs(xo)+abs(yo) > 0 && cellID == BAR_THEM)) { + + drawCube(p, cellID == BAR_THEM ? CUBE_UPPER : + CUBE_LOWER, xo, yo, sf); + + } + + /* + * Finally draw the boundaries + */ + drawVertBorder(p, xo, yo, sf); + KBgBoardCell::paintCell(p, xo, yo, sf); +} + + +/* + * This function draws a cube on the painetr p. The cube will be drawn in + * the coundaries given by cubeRect(...). The other parameters are like + * in the other functions. + */ +void KBgBoardCell::drawCube(QPainter *p, int who, int xo, int yo, + double sf) const +{ + QRect r = cubeRect(who, true, sf); + r.moveTopLeft(QPoint(xo+r.left(), yo+r.top())); + + p->setBrush(black); + p->setPen(black); + p->drawRoundRect(r, 20, 20); + + r = cubeRect(who, false, sf); + r.moveTopLeft(QPoint(xo+r.left(), yo+r.top())); + + p->setBrush(white); + p->setPen(white); + p->drawRoundRect(r, 20, 20); + + p->setBrush(black); + p->setPen(black); + + QString cubeNum; + int v = board->getCube(); + /* + * Ensure that the cube shows 64 initially + */ + if (v == 1) v = 64; + cubeNum.setNum(v); + + /* + * Adjust the font size + */ + QFont f = board->getFont(); + f.setPointSizeFloat(0.75*f.pointSizeFloat()); + p->setFont(f); + p->drawText(r, AlignCenter, cubeNum); +} + +/* + * This function returns a boundary rectangle for the dice. It does so for both + * dice (i is either 0 or 1). It can return big and small rectangles and everything + * is scaled with a default value of 1.0. The scale parameter determines the the + * size of the dice relative to the checker diameter. + */ +QRect KBgBoardCell::diceRect(int i, bool big, double sf, double scale) const +{ + int d = int(scale*getCheckerDiameter()); + int l = (1+width())%2; + int k = (big ? 0 : 1); + return(QRect(sf*(width()/2-d+k), + sf*(height()/2-2*d-3+2*i*(d+3)-1+k), + sf*(2*(d-k)+1-l), + sf*(2*(d-k)+1-l))); +} + +/* + * This function returns a bounding rectangle for the cube. This rectangle + * is moved to the correct place and scaled correctly. The cube is slightly + * smaller than the dice. + */ +QRect KBgBoardCell::cubeRect(int who, bool big, double sf) const +{ + QRect r = diceRect(0, big, sf, 0.40); + + int d = int(0.40*getCheckerDiameter()); + int h = r.height(); + int k = (big ? 1 : 0); + + switch (who) { + case US: + r.setTop(sf*(height() - 3*d) - k); + break; + case THEM: + r.setTop(sf*d - k); + break; + case CUBE_UPPER: + r.setTop(height()-d*sf - k); + break; + case CUBE_LOWER: + r.setTop( -d*sf - k); + break; + default: + return(QRect(0,0,0,0)); + } + r.setHeight(h); + return r; +} + +/* + * This function draws the face value on a given dice painter. + * If the painting of dice should be saved this is the place + * to modify. + */ +void KBgBoardHome::drawDiceFace(QPainter *p, int col, int num, int who, + int xo, int yo, double sf) const +{ + p->setBrush(board->getCheckerColor(col)); + p->setPen(board->getCheckerColor(col)); + + QRect r = diceRect(num, false, sf); + r.moveTopLeft(QPoint(xo+r.left(), yo+r.top())); + + int cx = r.width() /2; + int cy = r.height()/2; + int cx2 = cx/2; + int cy2 = cy/2; + int cx7 = int(0.7*cx); + int cy7 = int(0.7*cy); + + switch (board->getDice(who, num)) { + case 5: + p->drawEllipse(r.x()+cx-cx7 , r.y()+cy+cy7-1, 2, 2); + p->drawEllipse(r.x()+cx+cx7-1, r.y()+cy-cy7 , 2, 2); + case 3: // fall through + p->drawEllipse(r.x()+cx-cx7 , r.y()+cy-cy7 , 2, 2); + p->drawEllipse(r.x()+cx+cx7-1, r.y()+cy+cy7-1, 2, 2); + case 1: // fall through + p->drawEllipse(r.x()+cx , r.y()+cy , 2, 2); + break; + case 4: + p->drawEllipse(r.x()+cx-cx2, r.y()+cy+cy2-1, 2, 2); + p->drawEllipse(r.x()+cx+cx2-1, r.y()+cy-cy2, 2, 2); + case 2: // fall through + p->drawEllipse(r.x()+cx-cx2, r.y()+cy-cy2, 2, 2); + p->drawEllipse(r.x()+cx+cx2-1, r.y()+cy+cy2-1, 2, 2); + break; + case 6: + p->drawEllipse(r.x()+cx-cx2, r.y()+cy-cy7, 2, 2); + p->drawEllipse(r.x()+cx-cx2, r.y()+cy, 2, 2); + p->drawEllipse(r.x()+cx-cx2, r.y()+cy+cy7, 2, 2); + p->drawEllipse(r.x()+cx+cx2-1, r.y()+cy-cy7, 2, 2); + p->drawEllipse(r.x()+cx+cx2-1, r.y()+cy, 2, 2); + p->drawEllipse(r.x()+cx+cx2-1, r.y()+cy+cy7, 2, 2); + break; + default: // nothing + break; + } +} + +/* + * This function draws a nice little square on the painter p. + * The square is suited to contain a a face value as printed + * by drawDiceFace(...). + */ +void KBgBoardHome::drawDiceFrame(QPainter *p, int col, int num, + int xo, int yo, bool big, double sf) const +{ + p->setBrush(board->getCheckerColor(col)); + p->setPen(board->getCheckerColor(col)); + QRect r = diceRect(num, big, sf); + r.moveTopLeft(QPoint(xo+r.left(), yo+r.top())); + p->drawRoundRect(r, 20, 20); +} + +/* + * If the event is left button we just store that. If the event is right + * button we ask the board to possibly display the popup menu. + */ +void KBgBoardCell::mousePressEvent(QMouseEvent *e) +{ + if (e->button() == RightButton) + board->showContextMenu(); + else + mouseButton = e->button(); +} + +/* + * This function sets the short move mode of the board. + */ +void KBgBoard::setShortMoveMode(int m) +{ + switch (m) { + case SHORT_MOVE_NONE: + case SHORT_MOVE_SINGLE: + shortMoveMode = m; + break; + case SHORT_MOVE_DOUBLE: + default: + shortMoveMode = SHORT_MOVE_DOUBLE; + } +} + +/* + * This function returns the currently selected short move mode. + */ +int KBgBoard::getShortMoveMode() +{ + return shortMoveMode; +} + +/* + * This function checks if (a) the mouse event was a left button, + * (b) the parameter m equals the currently selected short move + * mode and (c) t a short move from this field is possible. If all + * tests are ok, the shortest possible move away from here is + * made. + */ +void KBgBoardCell::checkAndMakeShortMove(QMouseEvent *e, int m) +{ + if ((e->button() == LeftButton) && + (board->getShortMoveMode() == m) && + (dragPossible()) && + (!board->getEditMode())) + makeShortMove(); +} + +/* + * This functions reacts on a double click. + */ +void KBgBoardCell::mouseDoubleClickEvent(QMouseEvent *e) +{ + checkAndMakeShortMove(e, SHORT_MOVE_DOUBLE); +} + +/* + * This function reacts on a double click. Note that the bar knows + * about two different double clicks: double the cube and make a + * short move. + */ +void KBgBoardBar::mouseDoubleClickEvent(QMouseEvent *e) +{ + QRect r = cubeRect(cellID == BAR_THEM ? CUBE_UPPER : CUBE_LOWER, true); + if (board->canDouble(US) && + board->canDouble(THEM) && r.contains(e->pos())) { + if (board->getEditMode()) + board->queryCube(); + else + board->getDoubleCube(US); + return; + } + checkAndMakeShortMove(e, SHORT_MOVE_DOUBLE); +} + +/* + * This is the destructor of the backgammon board. It frees + * all resources previously allocated. + */ +KBgBoard::~KBgBoard() +{ + restoreCursor(); +} + +/* + * This function draws dice and cube on the painter for a home cell. + * who may be either US or THEM. + */ +void KBgBoardHome::drawDiceAndCube(QPainter *p, int who, int xo, int yo, + double sf) const +{ + int col = ((who == THEM) ? -color : color); + + /* + * draw the empty squares and then put the face value in there + */ + for (int i = 0; i < 2; i++) { + drawDiceFrame(p,-col, i, xo, yo, true, sf); + drawDiceFrame(p, col, i, xo, yo, false, sf); + drawDiceFace(p,-col, i, who, xo, yo, sf); + } + /* + * if necessary draw the cube + */ + if (board->canDouble(who) && + !(board->canDouble(US) && board->canDouble(THEM))) + drawCube(p, who, xo, yo, sf); +} + +/* + * This function determines whether a drag off this home is possible. + * This is only possible if there are checkers and edit mode is on. + */ +bool KBgBoardHome::dragPossible() const +{ + if (board->getEditMode()) + return (pcs != 0); + return false; +} + +/* + * This function determines whether a drag off this bar is possible. + * It checks in the follwoing order: (1) owner of this bar is the one + * whose turn it is now, (2) does the board allow moving right now is + * it in read-only mode? + */ +bool KBgBoardBar::dragPossible() const +{ + if (board->getEditMode()) + return (pcs != 0); + + switch(board->getTurn()) { + case US: + if (pcs*color <= 0) return false; + break; + case THEM: + if (pcs*color >= 0) return false; + break; + default: + return false; + } + return board->movingAllowed(); +} + +/* + * This function checks whether a checker can be moved away from + * this field. It first checks whether the owner of this field is + * the one whose turn to move it it, then it is checked whether + * the players bar is empty and finally it is checked if the board + * is in read-only mode. + */ +bool KBgBoardField::dragPossible() const +{ + if (board->getEditMode()) + return (pcs != 0); + + switch(board->getTurn()) { + case US: + if (pcs*color <= 0) return false; + break; + case THEM: + if (pcs*color >= 0) return false; + break; + default: + return false; + } + if (board->getOnBar(board->getTurn())) + return false; + return board->movingAllowed(); +} + +/* + * This function returns the current read-write flag of the board. + * If this returns true the board doesn't accept user input. If + * allowmoving is true we will accept user events. + */ +bool KBgBoard::movingAllowed() const +{ + return allowmoving; +} + +/* + * This function sets the read-write or read-only flag of the + * board. See also movingAllowed(). + */ +void KBgBoard::allowMoving(const bool fl) +{ + allowmoving = fl; +} + +/* + * This function returns the current pip count of the player w. + */ +int KBgBoard::getPipCount(const int& w) const +{ + if (!computePipCount || (w != US && w != THEM)) + return -1; + int pip = 25*abs(onbar[w]); + int d = ((w == US) ? 1 : -1); + for (int i = 1; i < 25; i++) { + if (d*board[i]*color > 0) + pip += ((d*direction < 0) ? + i*abs(board[i]) : + (25 - i)*abs(board[i])); + } + return pip; +} + +/* + * This function handles double clicks on homes. It will ignore + * double clicks on the real home and only handle the ones on the + * "other" home - the one with the dice. It will propagate the event + * only if the the click happened within the boundaries of a + * dice or the cube. + */ +void KBgBoardHome::mouseDoubleClickEvent(QMouseEvent * e) +{ + if (e->button() != LeftButton) + return; + /* + * Check whether this is the bookkeeping home... + */ + if ((cellID == HOME_US_LEFT && direction < 0) || + (cellID == HOME_US_RIGHT && direction > 0) || + (cellID == HOME_THEM_LEFT && direction < 0) || + (cellID == HOME_THEM_RIGHT && direction > 0)) { + + int w = ((cellID == HOME_US_LEFT || cellID == HOME_US_RIGHT) ? + US : THEM); + for (int i = 0; i < 2; ++i) { + QRect r = diceRect(i, true); + if (r.contains(e->pos())) { + if (board->getEditMode()) { + + KBgBoardQDice *dlg = new KBgBoardQDice(); + if (dlg->exec()) { + KBgStatus *st = new KBgStatus(); + board->getState(st); + st->setDice(w, 0, dlg->getDice(0)); + st->setDice(w, 1, dlg->getDice(1)); + st->setDice(((w == US) ? THEM : US), 0, 0); + st->setDice(((w == US) ? THEM : US), 1, 0); + board->setState(*st); // JENS + delete st; + } + delete dlg; + + } else + board->getRollDice(w); + return; + } + } + if (board->canDouble(w) && + !(board->canDouble(US) && board->canDouble(THEM))) { + QRect r = cubeRect(w, true); + if (r.contains(e->pos())) + if (board->getEditMode()) + board->queryCube(); + else + board->getDoubleCube(w); + } + } +} + +/* + * This function determines if a checker can be dropped on this field. + * It checks whether the field is already owned, empty or contains + * only one opponents piece. Then the dice are checked. + */ +bool KBgBoardField::dropPossible(int fromCellID, int newColor) +{ + if ((newColor*pcs > 0) || (pcs == 0) || (abs(pcs) == 1)) + // editMode is checked in diceAllowMove(...) + return board->diceAllowMove(fromCellID, cellID); + return false; +} + +/* + * This function determines if a checker can be dropped on this field. + * Drops on the bar are never possible. + */ +bool KBgBoardBar::dropPossible(int fromCellID, int newColor) +{ + if (!board->getEditMode()) + return false; + + if (newColor*pcs > 0) + return true; + if ((cellID == BAR_US) && (board->getTurn() == US)) + return true; + if ((cellID == BAR_THEM) && (board->getTurn() == THEM)) + return true; + + return (fromCellID == -12345); // always false +} + +/* + * This function checks if the current player can move a checker off. + * Check if we can move a piece off. This obviously only works if there + * are no pieces on the bar and all remaining pieces are in the home + * board. This does not check the dice and it doesn't work for multiple + * moves that start outside the home. + */ +bool KBgBoard::moveOffPossible() const +{ + if (getEditMode()) + return true; + + int w = getTurn(); + int d = ((w == THEM) ? -1 : 1); + if (onbar[w] == 0 && d*direction > 0) { + for (int i = 1; i < 19; ++i) { + if (d*color*board[i] > 0) return false; + } + return true; + + } else if (onbar[w] == 0 && d*direction < 0) { + for (int i = 24; i > 6; --i) { + if (d*color*board[i] > 0) return false; + } + return true; + } + return false; +} + +/* + * This function tries to determine the field cell under the point p. + * The point needs to be in board coordinates and the function returns + * a pointer to the cell or NULL if there is no cell under the point. + */ + +KBgBoardCell* KBgBoard::getCellByPos(const QPoint& p) const +{ + for (int i = 0; i < 30; ++i) { + if (cells[i]->rect().contains(cells[i]->mapFromParent(p))) + return cells[i]; + } + return NULL; +} + +/* + * This function takes a board number (1 to 24 and 0 or 25 depending on the + * direction) or a cell ID and returns a pointer to the corresponding cell. + * If the cell cannot be found it returns NULL. + */ +KBgBoardCell* KBgBoard::getCell(int num) +{ + switch (num) { + case BAR_US: + return (KBgBoardCell *)cells[22]; + case BAR_THEM: + return (KBgBoardCell *)cells[ 7]; + case HOME_THEM_LEFT: + return (KBgBoardCell *)cells[ 0]; + case HOME_THEM_RIGHT: + return (KBgBoardCell *)cells[14]; + case HOME_US_LEFT: + return (KBgBoardCell *)cells[15]; + case HOME_US_RIGHT: + return (KBgBoardCell *)cells[29]; + default: + int cell; + if (num < 0 || num > 25) + return NULL; + else if (num < 7) + cell = ((direction > 0) ? num : 29 - num); + else if (num < 13) + cell = ((direction > 0) ? num + 1 : 28 - num); + else if (num < 19) + cell = ((direction > 0) ? 41 - num : num - 12); + else + cell = ((direction > 0) ? 40 - num : num - 11); + return (KBgBoardCell *)cells[cell]; + } +} + +/* + * This function translates a field ID to the field number or just + * returns the ID for bars and homes. + */ +int KBgBoard::IDtoNum(const int ID) const +{ + if (ID > 0 && ID < 25) { + if (ID < 13) + return ((direction > 0) ? ID : 12 + ID); + else + return ((direction > 0) ? 37 - ID : 25 - ID); + } + return ID; +} + +/* + * This function takes a checker from the cell if possible. It also + * updates the bookkeeping of the board and redraws itself. + */ +bool KBgBoardCell::getPiece() +{ + if (pcs != 0) { + ((pcs > 0) ? --pcs : ++pcs); + stateChanged = true; + refresh(); + board->updateField(getNumber(), pcs); + return true; + } + return false; +} + +/* + * This function stores the current cursor and replaces it with the + * supplied one c. + */ +void KBgBoard::replaceCursor(const QCursor& c) +{ + if (savedCursor) + delete savedCursor; + savedCursor = new QCursor(cursor()); + setCursor(c); +} + +/* + * This function restores the previously set cursor to the stored one. + */ +void KBgBoard::restoreCursor() +{ + if (savedCursor) { + setCursor(*savedCursor); + delete savedCursor; + savedCursor = NULL; + } +} + +/* + * This function puts a checker of color newColor on the cell. It handles + * all necessary updates including the kicking. It will however not properly + * handle illegal moves! + */ +void KBgBoardCell::putPiece(int newColor) +{ + if (newColor*pcs > 0) { + pcs > 0 ? ++pcs : --pcs; + } else if (pcs == 0) { + newColor > 0 ? pcs = 1 : pcs = -1; + } else if (newColor*pcs < 0) { + board->kickedPiece(); + newColor > 0 ? pcs = 1 : pcs = -1; + } + stateChanged = true; + refresh(); + board->updateField(getNumber(), pcs); + board->sendMove(); +} + +/* + * This function handles mouse release events. It is important to know that + * the cell where the first mousePressEvent occurred receives the release event. + * The release event marks the end of a drag or a single click short move. + */ +void KBgBoardCell::mouseReleaseEvent(QMouseEvent *e) +{ + if (dragInProgress) { + + KBgBoardCell *dest = board->getCellByPos + (mapToParent(e->pos())); + board->restoreCursor(); + if ((dest != NULL) && (dest->dropPossible(cellID, ((board->getTurn() == US) ? + color : -color)))) { + if (!board->getEditMode()) + board->makeMove(getNumber(), dest->getNumber()); + dest->putPiece(((board->getTurn() == US) ? color : -color)); + } else { + putPiece(((board->getTurn() == US) ? color : -color)); + } + dragInProgress = false; + + } else { + + checkAndMakeShortMove(e, SHORT_MOVE_SINGLE); + + } +} + +/* + * This is the destructor of the home cells. It doesn't do anything. + */ +KBgBoardHome::~KBgBoardHome() +{ + // nothing +} + +/* + * This is the destructor of the bar cells. It doesn't do anything. + */ +KBgBoardBar::~KBgBoardBar() +{ + // nothing +} + +/* + * This is the destructor of regular fields. It doesn't do anything. + */ +KBgBoardField::~KBgBoardField() +{ + // nothing +} + +/* + * This is the constructor of the bars. It calls the base class' constructor + * and defines the QWhatsThis string. + */ +KBgBoardBar::KBgBoardBar(QWidget * parent, int numID) + : KBgBoardCell(parent, numID) +{ + QWhatsThis::add(this, i18n("This is the bar of the backgammon board.\n\n" + "Checkers that have been kicked from the board are put " + "on the bar and remain there until they can be put back " + "on the board. Checkers can be moved by dragging them to " + "their destination or by using the 'short move' feature.\n\n" + "If the cube hasn't been doubled yet and if it can be used, " + "its face shows 64 and if the cube can be doubled, double " + "clicking it will do so.")); +} + +/* + * This is the constructor of regular fields. It calls the base class' constructor + * and defines the QWhatsThis string. + */ +KBgBoardField::KBgBoardField(QWidget * parent, int numID) + : KBgBoardCell(parent, numID) +{ + QWhatsThis::add(this, i18n("This is a regular field of the backgammon board.\n\n" + "Checkers can be placed on this field and if the current state " + "of the game and the dice permit this, they can be moved by " + "dragging them to their destination or by using the 'short " + "move' feature.")); +} + +/* + * This is the constructor of the homes. It calls the base class' constructor + * and defines the QWhatsThis string. + */ +KBgBoardHome::KBgBoardHome(QWidget * parent, int numID) + : KBgBoardCell(parent, numID) +{ + QWhatsThis::add(this, i18n("This part of the backgammon board is the home.\n\n" + "Depending on the direction of the game, one of the homes " + "contains the dice and the other one contains checkers that " + "have been moved off the board. Checkers can never be moved " + "away from the home. If this home contains the dice and the " + "current state of the game permits this, double clicking on " + "the dice will roll them. Moreover, the cube might be placed " + "on the home bar and if it can be doubled, double clicking it " + "will do so.")); + savedDice[0] = -1; + savedDice[1] = -1; +} + +/* + * This function updates the number of checkers on the bar and also updates + * the cell if the cube has changed (this is more often than necessary...) + */ +void KBgBoardBar::cellUpdate(const int p, const bool cubechanged) +{ + stateChanged = (cubechanged || colorChanged); + if (pcs != p) { + stateChanged = true; + pcs = p; + } +} + +/* + * This function updates the number of checkers on the field. + */ +void KBgBoardField::cellUpdate(const int p, const bool cubechanged) +{ + if (p != pcs) { + pcs = p; + stateChanged = true; + } + bool f = stateChanged; // useless, avoids compiler warning + stateChanged = cubechanged; + stateChanged = (f || colorChanged); +} + +/* + * This function updates the number of checkers on the home if it + * actually contains checkers. It will also redraw if the cube or dice + * have changed. + */ +void KBgBoardHome::cellUpdate(const int p, const bool cubechanged) +{ + if ((cellID == HOME_THEM_LEFT && direction > 0) || + (cellID == HOME_THEM_RIGHT && direction < 0) || + (cellID == HOME_US_LEFT && direction > 0) || + (cellID == HOME_US_RIGHT && direction < 0)) { + + if (pcs != p) { + pcs = p; + stateChanged = true; + } + + } else { + + int who = ((cellID == HOME_THEM_LEFT || cellID == HOME_THEM_RIGHT) ? THEM : US); + + stateChanged = ((savedDice[0] != board->getDice(who, 0)) || + (savedDice[1] != board->getDice(who, 1))); + + savedDice[0] = board->getDice(who, 0); + savedDice[1] = board->getDice(who, 1); + + stateChanged = (stateChanged || cubechanged || colorChanged || directionChanged); + } +} + +/* + * This function returns whose players turn it is. + */ +int KBgBoard::getTurn() const +{ + if (getEditMode()) + return ((storedTurn*color > 0) ? US : THEM); + + if (getDice(US , 0) != 0 && getDice(US , 1) != 0) + return US; + if (getDice(THEM, 0) != 0 && getDice(THEM, 1) != 0) + return THEM; + return -1; +} + +/* + * This is the constructor of the basic cells. It initializes the cell + * to a sane state and connects a signal to the board. + */ +KBgBoardCell::KBgBoardCell(QWidget * parent, int numID) + : QLabel(parent) +{ + board = (KBgBoard *)parent; + + direction = +1; + color = -1; + pcs = 0; + cellID = numID; + stateChanged = false; + colorChanged = false; + directionChanged = false; + mouseButton = NoButton; + dragInProgress = false; + + connect(parent, SIGNAL(finishedUpdate()), this, SLOT(refresh())); +} + +/* + * This is the destructor of the cells. It doesn't do anything. + */ +KBgBoardCell::~KBgBoardCell() +{ + // nothing +} + +/* + * This function returns the color of the checkers on this cell. + */ +int KBgBoardCell::getCellColor() +{ + return ((pcs < 0) ? -1 : +1); +} + +/* + * This function updates the basic board settings color and direction + * and signals a redraw if necessary. + */ +void KBgBoardCell::statusUpdate(int dir, int col) +{ + if (direction != dir || color != col) { + colorChanged = (color != col); + directionChanged = (direction != dir); + color = col; + direction = dir; + stateChanged = true; + } +} + +/* + * This function refreshes the content of the cell if necessary. + */ +void KBgBoardCell::refresh() +{ + if (stateChanged) { + update(); + stateChanged = false; + colorChanged = false; + directionChanged = false; + } +} + +/* + * This function returns the board number of this cell as given by the board. + */ +int KBgBoardCell::getNumber() const +{ + return board->IDtoNum(cellID); +} + +/* + * This function returns the number of checkers of player who on the bar. + */ +int KBgBoard::getOnBar(int who) const +{ + return ((who == US || who == THEM) ? onbar[who] : 0); +} + +/* + * This function returns the face value of the n-th dice of player w + */ +int KBgBoard::getDice( int w, int n ) const +{ + return (((w == US || w == THEM) && (n == 0 || n == 1)) ? dice[w][n] : 0); +} + +/* + * This function returns the current cube value. + */ +int KBgBoard::getCube() const +{ + return cube; +} + +/* + * This function updates the stored number of pieces on field f to v. + */ +void KBgBoard::updateField(int f, int v) +{ + switch (f) { + case BAR_US: + case BAR_THEM: + onbar[((f == BAR_US) ? US : THEM)] = v; + break; + case HOME_US_RIGHT: + case HOME_US_LEFT: + onhome[US] = v; + break; + case HOME_THEM_RIGHT: + case HOME_THEM_LEFT: + onhome[THEM] = v; + break; + default: + if (0 < f && f < 25) + board[f] = v; + break; + } +} + +/* + * This function displays the context menu our parent may have given us + */ +void KBgBoard::showContextMenu() +{ + if (contextMenu) contextMenu->popup(QCursor::pos()); +} + +/* + * This function determines if the player who can double. + */ +bool KBgBoard::canDouble(int who) const +{ + return ((who == US || who == THEM) ? maydouble[who] : false); +} + +/* + * This function is a simple utility for makeMove. It takes care + * of all the bookeeeping needed for a move. + */ +int KBgBoard::makeMoveHelper(int si, int sf, int delta) +{ + moveHistory.append(new KBgBoardMove(si, sf, abs(delta))); + --possMoves[abs(delta)]; + return delta; +} + +/* + * This function makes a move from src to dest for the current player. + * It can handle illegal moves but the move should have been checked. + */ +void KBgBoard::makeMove(int src, int dest) +{ + int m[4]; + int l; + + int d = direction*((getTurn() == US) ? +1 : -1); + + if (src == BAR_US || src == BAR_THEM ) { + + int start = ((d > 0) ? 0 : 25); + l = checkMultiMove(start, dest, m); + moveHistory.append(new KBgBoardMove(src, start+d*m[0], m[0])); + src = start+d*m[0]; + --possMoves[m[0]]; + for (int i = 1; i < l; i++) + src += makeMoveHelper(src, src+d*m[i], d*m[i]); + + } else if (0 < src && src < 25 && 0 < dest && dest < 25) { + + l = checkMultiMove(src, dest, m); + for (int i = 0; i < l; i++) + src += makeMoveHelper(src, src+d*m[i], d*m[i]); + + } else { + + int s = src; + int final = ((d > 0) ? 25 : 0); + while (((l = checkMultiMove(s, final, m)) == 0) && (0 < s && s < 25)) + s -= d; + + for (int i = 0; i < l-1; i++) + src += makeMoveHelper(src, src+d*m[i], d*m[i]); + + moveHistory.append(new KBgBoardMove(src, dest, ((d > 0) ? 25 - src : src))); + --possMoves[m[l-1]]; + + } +} + +/* + * This function checks if there is any possibility (based on the dice) + * to move from src to dest. It takes the ownership of the intermediate + * fields into account. The function returns the number of steps necessary + * to perform the move (or 0 if the move is not possible) and the actual + * dice values used for the steps are returned in the array m. + * + * The values src and dest are expected to be in board coordinates and the + * homes and/or bars should already be mapped to the corresponding values + * 0 and 25 (based onb direction and whose turn it is). + */ +int KBgBoard::checkMultiMove(int src, int dest, int m[4]) +{ + m[0] = 0; m[1] = 0; m[2] = 0; m[3] = 0; + + int mcolor = ((getTurn() == US) ? color : -color); + int d = ((src > dest) ? -1 : 1); + + /* + * These are very easy special cases: move length is 0 or + * player cannot move to the destination field. + */ + if ((src == dest) || (mcolor*board[dest] < -1)) return 0; + + int diceToUse[4]; + int dice = 0; + /* + * Get the available step sizes for this move + */ + for (int i = 1; i < 7; i++) { + for (int j = 0; j < possMoves[i]; j++) { + diceToUse[dice++] = i; + /* + * If this happens there is something wrong + */ + if (dice > 4) return 0; + } + } + /* + * And start all possible combination of dices. + */ + switch (dice) { + case 4: if (src+4*d*diceToUse[0] == dest) { + if ((mcolor*board[src+1*d*diceToUse[0]] >= 0) && + (mcolor*board[src+2*d*diceToUse[0]] >= 0) && + (mcolor*board[src+3*d*diceToUse[0]] >= 0)) { + m[0] = m[1] = m[2] = m[3] = diceToUse[0]; + return 4; + } + } + case 3: if (src+3*d*diceToUse[0] == dest) { + if ((mcolor*board[src+1*d*diceToUse[0]] >= 0) && + (mcolor*board[src+2*d*diceToUse[0]] >= 0)) { + m[0] = m[1] = m[2] = diceToUse[0]; + return 3; + } + } + case 2: if ((src+d*(diceToUse[0]+diceToUse[1])) == dest) { + if (mcolor*board[src+d*diceToUse[0]] >= 0) { + m[0] = diceToUse[0]; + m[1] = diceToUse[1]; + return 2; + } + if (mcolor*board[src+d*diceToUse[1]] >= 0) { + m[0] = diceToUse[1]; + m[1] = diceToUse[0]; + return 2; + } + } + case 1: if (abs(src-dest) < 7 && possMoves[abs(src-dest)] > 0) { + m[0] = abs(src-dest); + return 1; + } + default: return 0; + } +} + +/* + * This function determines if a checker can be dropped on this home field. + * It first checks whether this is the proper of the four home fields (belongs + * to the player and not the one with dice and cube). Then we check if the move + * itself is possible. + */ +bool KBgBoardHome::dropPossible(int fromCellID, int newColor) +{ + if ((cellID==HOME_US_LEFT && board->getTurn() == US && direction > 0) || + (cellID==HOME_THEM_LEFT && board->getTurn() == THEM && direction > 0) || + (cellID==HOME_US_RIGHT && board->getTurn() == US && direction < 0) || + (cellID==HOME_THEM_RIGHT && board->getTurn() == THEM && direction < 0)) + return (board->moveOffPossible() && + board->diceAllowMove(fromCellID, cellID)); + return (newColor == -12345); // always false +} + +/* + * This function is a simple boolean interface to checkMultiMove. + * It takes car of directions and bar/home mappings. If necessary + * it also handles the case of bearing off. + */ +bool KBgBoard::diceAllowMove(int src, int dest) +{ + int m[4]; + int w = getTurn(); + int k = ((w == US) ? +1 : -1); + int t = ((k*direction > 0) ? 25 : 0); + int d = ((k*direction > 0) ? +1 : -1); + + if (getEditMode()) + return true; + + if ((w == US && src == BAR_US) || (w == THEM && src == BAR_THEM)) { + /* + * Move comes from a bar. Hence it has to end on a field + * and not on bars or homes. If there are checkers left + * on the bar we don't accept multi moves. + */ + if (0 < dest && dest < 25) { + int r = checkMultiMove((k*direction > 0) ? 0 : 25, + IDtoNum(dest), m); + return((abs(onbar[w]) == 0) ? (r != 0) : (r == 1)); + } else { + return false; + } + } else if (0 < dest && dest < 25 && 0 < src && src < 25) { + /* + * Move from a field to a field + */ + if (direction*k*(IDtoNum(dest)-IDtoNum(src)) > 0) { + return(checkMultiMove(IDtoNum(src), IDtoNum(dest), m)); + } else { + return false; + } + } else { + /* + * Move from a field on the home. First we try exact dice. + */ + if (checkMultiMove(IDtoNum(src), t, m) > 0) return true; + + /* + * Then maybe we could bear the checker off ? + */ + int i = IDtoNum(src); + while (0 < i && i < 25) { + i -= d; + if (k*color*board[i] > 0) return false; + } + + /* + * Indeed we are bearing off. So find the highest dice and use it. + * Start from all the way back to catch double 6 from the start. + */ + int j = 24; + while (checkMultiMove(t-d*j, t, m) == 0 && j > 0) {--j;} + return (j >= t-d*IDtoNum(src)); + } + return false; +} + +/* + * This is the most important of all members of the board class. It takes + * a single board status object and initializes the internal status. + */ +void KBgBoard::setState(const KBgStatus &st) +{ + color = st.color(); + direction = st.direction(); + + cubechanged = (cube != abs(st.cube())); + cube = abs(st.cube()); + maydouble[US ] = (st.cube(US ) > 0); + maydouble[THEM] = (st.cube(THEM) > 0); + + for (int i = 0; i < 30; i++) + cells[i]->statusUpdate(direction, color); + + for (int i = 1; i < 25; ++i) + board[i] = st.board(i); + + onbar[US ] = st.bar(US ); + onbar[THEM] = st.bar(THEM); + + onhome[US] = st.home(US ); + onhome[THEM] = st.home(THEM); + + dice[US ][0] = st.dice(US , 0); + dice[US ][1] = st.dice(US , 1); + dice[THEM][0] = st.dice(THEM, 0); + dice[THEM][1] = st.dice(THEM, 1); + + for (int i = 0; i < 7; ++i) + possMoves[i] = 0; + + int w = getTurn(); + if (getEditMode()) + w = ((dice[US][0] && dice[US][1]) ? US : THEM); + + if (w == US || w == THEM) { + ++possMoves[dice[w][0]]; + ++possMoves[dice[w][1]]; + if (dice[w][0] == dice[w][1]) + possMoves[dice[w][0]] *= 2; + } + + board[ 0] = 0; + board[25] = 0; + for (int i=1; i<25; ++i) + (getCell(i))->cellUpdate(board[i]); + + (getCell(BAR_US ))->cellUpdate(st.bar(US ), cubechanged); + (getCell(BAR_THEM))->cellUpdate(st.bar(THEM), cubechanged); + + (getCell(HOME_US_LEFT ))->cellUpdate(st.home(US ), cubechanged); + (getCell(HOME_US_RIGHT ))->cellUpdate(st.home(US ), cubechanged); + (getCell(HOME_THEM_LEFT ))->cellUpdate(st.home(THEM), cubechanged); + (getCell(HOME_THEM_RIGHT))->cellUpdate(st.home(THEM), cubechanged); + + moveHistory.clear(); + redoHistory.clear(); + + emit finishedUpdate(); +} + +/* + * This function starts a drag from this cell if possible. It asks the board to + * change the mouse pointer and takes a checker away from this cell. + */ +void KBgBoardCell::mouseMoveEvent(QMouseEvent *) +{ + if ((mouseButton == LeftButton) && dragPossible()) { + dragInProgress = true; + QRect cr(0, 0, 1+getCheckerDiameter(), 1+getCheckerDiameter()); + cr.moveBottomLeft(rect().bottomLeft()); + QPixmap pix(cr.size()); + QPainter tmp; + pix.fill(this, cr.topLeft()); + tmp.begin(&pix); + board->drawSimpleChecker(&tmp, 0, 0, pcs, getCheckerDiameter()); + tmp.end(); + pix.setMask(pix.createHeuristicMask()); + QBitmap mask = *(pix.mask()); + QBitmap newCursor; + newCursor = pix; + board->replaceCursor(QCursor(newCursor, mask)); + if (board->getEditMode()) + board->storeTurn(pcs); + getPiece(); + } + mouseButton = NoButton; +} + +/* + * This function draws a checker on the painter p. It is painted + * in the ractangle with the upper left corner (x,y) and has a + * maximum diameter of diam. This checker has only two colors and + * as such it is suited for the mouse cursor and printing. + */ +void KBgBoard::drawSimpleChecker(QPainter *p, int x, int y, int pcs, + int diam) const +{ + p->setBrush(getCheckerColor(pcs)); + p->setPen(getCheckerColor(pcs)); + p->drawEllipse(x+1, y+0, diam-0, diam-0); + p->setBrush(getCheckerColor(-pcs)); + p->setPen(getCheckerColor(-pcs)); + p->drawEllipse(x+2, y+1, diam-2, diam-2); + p->setBrush(getCheckerColor(pcs)); + p->setPen(getCheckerColor(pcs)); + p->drawEllipse(x+3, y+2, diam-4, diam-4); +} + +/* + * This function draws an anti-aliased checker on the painter p. It + * is painted in the ractangle with the upper left corner (x,y) and + * has a diameter of diam. col indicates the color of the cell this + * checker is painted on. Special values for col are 0 and 100 that + * indicate that the checker is stacked (bars and homes) or stacked + * on a field respectively. upper indicates whether the checker is + * in the upper half of the board or not. + */ +void KBgBoard::drawChecker(QPainter *p, int x, int y, int pcs, int diam, + int col, bool upper) const +{ + drawCircle(p, x, y, pcs, diam , col, upper, true ); + drawCircle(p, x+1, y+1,-pcs, diam-2, col, upper, false); + drawCircle(p, x+2, y+2, pcs, diam-4, col, upper, false); +} + +/* + * This function draws checkers on the painter *p. They overlap so that + * up to fifteen checkers fit on the cell. This is used by homes and + * bars. + */ +void KBgBoardCell::drawOverlappingCheckers(QPainter *p, int xo, int yo, + double sf) const +{ + int d = getCheckerDiameter(); + bool upper = + cellID == HOME_THEM_LEFT || + cellID == HOME_THEM_RIGHT || + cellID == BAR_THEM; + double xp = xo + sf*((width()-d-1)/2); + double ra = sf*d; + for (int i = 0; i < abs(pcs); ++i) { + double yp = yo + (upper ? 1+i*sf*height()/25.0 : + sf*(height()-d-i*height()/25.0)); + board->drawChecker(p, xp, yp, pcs, ra, 0, upper); + } +} + +/* + * This function paints the content of a regular cell on the painter p. + * It does so by first drawing a triangle (depending on whether we draw + * on the screen or not this will be antialiased). Then on top of that + * we draw the field number in inverse color. Finally we draw all the + * checkers in such a way that always five are in one level and the next + * level is slightly shifted. + */ +void KBgBoardField::paintCell(QPainter *p, int xo, int yo, double sf) const +{ + QColor color, alphaColor, background = backgroundColor(); + bool printing = abs(xo)+abs(yo) > 0; + + if (printing) { + /* + * This is the code for black and white printing on + * paper. This justs draws a triangle and surrounds + * it by a black triangle. Easy but works. + */ + QPointArray pa(3); + + color = (getNumber()%2 ? white : black); + + if (cellID < 13) { + pa.setPoint( 0, xo , yo ); + pa.setPoint( 1, xo + sf*width()/2, yo + 0.9*sf*height()); + pa.setPoint( 2, xo + sf*width() , yo ); + } else { + pa.setPoint( 0, xo , yo + sf*(height()-1)); + pa.setPoint( 1, xo + sf*width()/2, yo + 0.1*sf*height()); + pa.setPoint( 2, xo + sf*width() , yo + sf*(height()-1)); + } + + p->setBrush(color); + p->setPen(color); + p->drawPolygon(pa); + + p->setBrush(black); + p->setPen(black); + p->drawPolyline(pa); + + } else { + /* + * This is the code for antialiased triangles. This code has + * been written by Bo Thorsen. + */ + color = board->getCheckerColor(getNumber()%2-1); + + int topX, topY, bottomX1, bottomX2, bottomY, incrY; + topX = xo + (int)(sf*width()/2.0); + bottomX1 = xo; + bottomX2 = xo + (int)(sf*width()); + if (cellID < 13) { + topY = yo + (int)(0.9*sf*height()); + bottomY = yo; + incrY = 1; + } else { + topY = yo + (int)(0.1*sf*height()); + bottomY = yo + (int)(sf*height()); + incrY = -1; + } + + float x1 = bottomX1, x2 = bottomX2; + float dx1 = (float)(topX-bottomX1) / (topY-bottomY); + float dx2 = (float)(topX-bottomX2) / (topY-bottomY); + if (dx1 < 0) dx1 = -dx1; + if (dx2 < 0) dx2 = -dx2; + + p->setPen( color ); + p->drawLine(bottomX1, bottomY, bottomX2, bottomY); + x1 += dx1; + x2 -= dx2; + + /* + * The scaling factor (0.99) cuts off the top op the points + */ + for (int y=bottomY; x1 < x2*0.99; y+=incrY) { + int ix1 = (int)x1, ix2 = (int)x2; + float a1 = x1 - ix1, a2 = x2 - ix2; + + /* + * This is a simple linear interpolation between + * the two colors + */ + int red1 = (int) + ((1-a1)*color.red() + a1*background.red()); + int green1 = (int) + ((1-a1)*color.green() + a1*background.green()); + int blue1 = (int) + ((1-a1)*color.blue() + a1*background.blue()); + int red2 = (int) + (a2*color.red() + (1-a2)*background.red()); + int green2 = (int) + (a2*color.green() + (1-a2)*background.green()); + int blue2 = (int) + (a2*color.blue() + (1-a2)*background.blue()); + + /* + * Draw the antialiasing pixels + */ + alphaColor.setRgb(red1, green1, blue1); + p->setPen(alphaColor); + p->drawPoint(ix1, y); + alphaColor.setRgb(red2, green2, blue2); + p->setPen(alphaColor); + p->drawPoint(ix2, y); + + ix1++; + ix2--; + x1 += dx1; + x2 -= dx2; + + if (ix1 <= ix2 && x1 < x2*0.99) { + /* + * Draw the line + */ + p->setPen(color); + p->drawLine(ix1, y, ix2, y); + } + } + } + + /* + * Print the field number in inverted color + */ + color = board->getCheckerColor((1+getNumber())%2-1); + + p->setBrush(color); + p->setPen(color); + + QString t; + t.setNum(getNumber()); + + p->setFont(board->getFont()); + int textHeight = QFontMetrics(p->font()).height(); + p->drawText(xo, yo+((cellID < 13) ? 5 : height()-5-textHeight), + width()*sf, textHeight, AlignCenter, t); + + /* + * Put the checkers on the field. + */ + int d = getCheckerDiameter(); + double yp, xp = xo + sf*((width()-d-1)/2); + double ra = sf*d; + bool upper = cellID < 13; + int col = (getNumber()%2) ? 1 : -1; + + for (int i = 0; i < abs(pcs); ++i) { + /* + * There is hard work in these formulas. Unless you have + * tried _ALL_ possible windowsizes: don't touch! + */ + yp = yo + (upper ? sf*((i%5)+(i/5)/4.0)*(d-1) : + sf*(height()-((1+i%5)*d)-int(i/5)*0.25*d)-1); + if (printing) { + board->drawSimpleChecker(p, xp, yp, pcs, ra); + } else { + board->drawChecker(p, xp, yp, pcs, ra, + ((i < 5) ? col : 100), upper); + } + } + + /* + * Finally draw the horizontal boundaries + */ + KBgBoardCell::paintCell(p, xo, yo, sf); +} + +/* + * This function draws an anti-aliased circle on the painter p. It is painted + * in the ractangle with the upper left corner (x,y) and has a maximum diameter + * of diam. col and upper are as in drawChecker(). outer indicates if this + * circle blends with the background. Note that this function needs knowledge + * about the triangles on the cells. This is long but it is just a big if + * construct. + */ +void KBgBoard::drawCircle(QPainter *p, int x, int y, int pcs, int diam, + int col, bool upper, bool outer) const +{ + QColor fColor = getCheckerColor(pcs); + QColor alphaColor; + QColor bColor; + + int red, green, blue; + int rad = diam/2; + int xoff = 0; + + float sn = 4; + float rs = 0.25*diam*diam; + float cf, a; + + for (int ys = rad; ys >= 0; ys--) { + for (int xs = xoff; cf = 0, xs < rad; xs++) { + + /* + * perform super-sample this pixel + */ + for (int s1 = 0; s1 < sn; s1++) + for (int s2 = 0; s2 < sn; s2++) + if ((rad-xs+s1/sn)*(rad-xs+s1/sn)+ + (rad-ys+s2/sn)*(rad-ys+s2/sn) < rs) + cf += 1; + a = cf/sn/sn; + + if (outer && (col == 0 || col == 100)) { + + if (col == 0) + bColor = backgroundColor(); + else + bColor = fColor; + + red = (int) + ((1-a)*bColor.red()+a*fColor.red()); + green = (int) + ((1-a)*bColor.green()+a*fColor.green()); + blue = (int) + ((1-a)*bColor.blue()+a*fColor.blue()); + + alphaColor.setRgb(red, green, blue); + + p->setBrush(alphaColor); + p->setPen(alphaColor); + + if (upper) { + + p->drawPoint(x+xs, y+diam-ys); + p->drawPoint(x+diam-xs, y+diam-ys); + + p->setBrush(fColor); + p->setPen(fColor); + + p->drawPoint(x+xs, y+ys); + p->drawPoint(x+diam-xs, y+ys); + + } else { + + p->drawPoint(x+xs, y+ys); + p->drawPoint(x+diam-xs, y+ys); + + p->setBrush(fColor); + p->setPen(fColor); + + p->drawPoint(x+xs, y+diam-ys); + p->drawPoint(x+diam-xs, y+diam-ys); + + } + + } else if (outer) { + + if (upper) { + + bColor = getCheckerColor(col); + + red = (int)((1-a)*bColor.red()+ + a*fColor.red()); + green = (int)((1-a)*bColor.green()+ + a*fColor.green()); + blue = (int)((1-a)*bColor.blue()+ + a*fColor.blue()); + + alphaColor.setRgb(red, green, blue); + + p->setBrush(alphaColor); + p->setPen(alphaColor); + + p->drawPoint(x+xs, y+ys); + p->drawPoint(x+diam-xs, y+ys); + p->drawPoint(x+xs, y+diam-ys); + p->drawPoint(x+diam-xs, y+diam-ys); + + bColor = backgroundColor(); + + red = (int)((1-a)*bColor.red()+ + a*fColor.red()); + green = (int)((1-a)*bColor.green()+ + a*fColor.green()); + blue = (int)((1-a)*bColor.blue()+ + a*fColor.blue()); + + alphaColor.setRgb(red, green, blue); + p->setBrush(alphaColor); + p->setPen(alphaColor); + + if (x+xs < rad*(y+ys)/(0.45*height())) { + p->drawPoint(x+xs, y+ys); + p->drawPoint(x+diam-xs, y+ys); + } + if (x+xsdrawPoint(x+xs, y+diam-ys); + p->drawPoint(x+diam-xs, y+diam-ys); + } + + } else { + + bColor = getCheckerColor(col); + + red = (int)((1-a)*bColor.red()+ + a*fColor.red()); + green = (int)((1-a)*bColor.green()+ + a*fColor.green()); + blue = (int)((1-a)*bColor.blue()+ + a*fColor.blue()); + + alphaColor.setRgb(red, green, blue); + p->setBrush(alphaColor); + p->setPen(alphaColor); + + p->drawPoint(x+xs, y+ys); + p->drawPoint(x+diam-xs, y+ys); + p->drawPoint(x+xs, y+diam-ys); + p->drawPoint(x+diam-xs, y+diam-ys); + + bColor = backgroundColor(); + + red = (int)((1-a)*bColor.red()+ + a*fColor.red()); + green = (int)((1-a)*bColor.green()+ + a*fColor.green()); + blue = (int)((1-a)*bColor.blue()+ + a*fColor.blue()); + + alphaColor.setRgb(red, green, blue); + p->setBrush(alphaColor); + p->setPen(alphaColor); + + if (x+xsdrawPoint(x+xs, y+ys); + p->drawPoint(x+diam-xs, y+ys); + } + if (x+xs < rad*(0.5-(y+diam-ys)/ + (1.0*height()))/0.45) { + p->drawPoint(x+xs, y+diam-ys); + p->drawPoint(x+diam-xs, y+diam-ys); + } + } + + } else { + + bColor = getCheckerColor(-pcs); + + red = (int)((1-a)*bColor.red()+ + a*fColor.red()); + green = (int)((1-a)*bColor.green()+ + a*fColor.green()); + blue = (int)((1-a)*bColor.blue()+ + a*fColor.blue()); + + alphaColor.setRgb(red, green, blue); + p->setBrush(alphaColor); + p->setPen(alphaColor); + + p->drawPoint(x+xs, y+ys); + p->drawPoint(x+diam-xs, y+ys); + p->drawPoint(x+xs, y+diam-ys); + p->drawPoint(x+diam-xs, y+diam-ys); + + } + + if (fabs(cf-sn*sn) < 0.0001) { + + p->moveTo(x+xs, y+ys); + p->lineTo(x+diam-xs, y+ys); + p->moveTo(x+xs, y+diam-ys); + p->lineTo(x+diam-xs, y+diam-ys); + + xoff = xs; + break; + + } + } + } +} + +/* + * This function redoes a previously undone move + */ +void KBgBoard::redoMove() +{ + if (getEditMode()) + return; + + int w = getTurn(); + int mcolor = ((w == US) ? color : -color); + KBgBoardMove *move = redoHistory.last(); + if (move && (w == US || w == THEM)) { + /* + * Make changes at source + */ + if (move->source() == BAR_US || move->source() == BAR_THEM) { + onbar[w] -= mcolor; + (getCell(move->source()))->cellUpdate(onbar[w], false); + } else { + board[move->source()] -= mcolor; + (getCell(move->source()))->cellUpdate(board[move->source()]); + } + /* + * Make changes at the destination + */ + if ((move->destination() == HOME_THEM_LEFT ) || (move->destination() == HOME_THEM_RIGHT) || + (move->destination() == HOME_US_LEFT ) || (move->destination() == HOME_US_RIGHT )) { + onhome[w] += mcolor; + (getCell(move->destination()))->cellUpdate(onhome[w], false); + } else { + board[move->destination()] += mcolor; + if (move->wasKicked()) { + board[move->destination()] = mcolor; + onbar[((w == US) ? THEM : US)] -= mcolor; + (getCell(((w == US) ? BAR_THEM : BAR_US)))->cellUpdate + (onbar[((w == US) ? THEM : US)], false); + } + (getCell(move->destination()))->cellUpdate(board[move->destination()]); + } + makeMove(move->source(), move->destination()); + redoHistory.remove(); + emit finishedUpdate(); + } + sendMove(); +} + +/* + * This function performs and undo for the last move and updates the parent + * of the board by calling sendMove() after the undo. + */ +void KBgBoard::undoMove() +{ + if (getEditMode()) + return; + + int w = getTurn(); + int mcolor = ((w == US) ? color : -color); + KBgBoardMove *move = moveHistory.last(); + if (move && (w == US || w == THEM)) { + /* + * Undo changes at source + */ + if (move->source() == BAR_US || move->source() == BAR_THEM) { + onbar[w] += mcolor; + (getCell(move->source()))->cellUpdate(onbar[w], false); + } else { + board[move->source()] += mcolor; + (getCell(move->source()))->cellUpdate + (board[move->source()]); + } + /* + * Undo changes at the destination + */ + if ( (move->destination() == HOME_THEM_LEFT ) || + (move->destination() == HOME_THEM_RIGHT) || + (move->destination() == HOME_US_LEFT ) || + (move->destination() == HOME_US_RIGHT )) { + onhome[w] -= mcolor; + (getCell(move->destination()))->cellUpdate + (onhome[w], false); + } else { + board[move->destination()] -= mcolor; + if (move->wasKicked()) { + board[move->destination()] = -mcolor; + onbar[((w == US) ? THEM : US)] += mcolor; + (getCell(((w == US) ? + BAR_THEM : BAR_US)))->cellUpdate + (onbar[((w == US) ? THEM : US)], false); + } + (getCell(move->destination()))->cellUpdate + (board[move->destination()]); + } + ++possMoves[move->length()]; + redoHistory.append(new KBgBoardMove(*move)); + moveHistory.remove(); + emit finishedUpdate(); + } + sendMove(); +} + +/* + * While putting a piece on a cell the cell has noticed that it changed + * ownership and hence needs a piece to be kicked. Since cells don't + * know where the opponents bar is we handle this here. + */ +void KBgBoard::kickedPiece() +{ + int w = ((getTurn()) == US ? THEM : US); + + if (w == US) { + onbar[w] += color; + (getCell(BAR_US ))->cellUpdate(onbar[w], false); + } else { + onbar[w] -= color; + (getCell(BAR_THEM))->cellUpdate(onbar[w], false); + } + if (!getEditMode()) { + KBgBoardMove *move = moveHistory.last(); + move->setKicked(true); + } + emit finishedUpdate(); +} + +/* + * This is a very short utility function for makeShortMove(). + */ +void KBgBoardCell::makeShortMoveHelper(int s, int d) +{ + if (getPiece()) { + board->makeMove(s, d); + KBgBoardCell *dest = board->getCell(d); + dest->putPiece(((board->getTurn() == US) ? color : -color)); + } +} + +/* + * This function makes the shortes possible move from this cell. It + * uses only one dice and and it will kick opponent checkers. + */ +void KBgBoardCell::makeShortMove() +{ + int m[4]; + + int dir = ((board->getTurn() == US) ? direction : -direction); + int src = board->IDtoNum(cellID); + + if (src == BAR_US || src == BAR_THEM) { + + int s = (dir > 0) ? 0 : 25; + for (int i = 1; i < 7; i++) { + int d = (dir > 0) ? i : 25 - i; + if (board->checkMultiMove(s, d, m) == 1) { + makeShortMoveHelper(src, d); + break; + } + } + + } else { + + for (int i = 1; i < 7; i++) { + int d = src + dir*i; + if (d > 25) d = 25; + if (d < 0) d = 0; + if (0 < d && d < 25) { + + if (board->checkMultiMove(src, d, m) == 1) { + makeShortMoveHelper(src, d); + break; + } + + } else { + + if (board->moveOffPossible()) { + int whichHome; + if (board->getTurn() == US) + whichHome = ((direction > 0) ? + HOME_US_LEFT : + HOME_US_RIGHT); + else + whichHome = ((direction > 0) ? + HOME_THEM_LEFT : + HOME_THEM_RIGHT); + + if (board->diceAllowMove + (cellID, whichHome)) { + makeShortMoveHelper(src, whichHome); + break; + } + } + } + } + } +} + +/* + * Ask the current backgammon engine for a doubled cube. + */ +void KBgBoard::getDoubleCube(const int w) +{ + emit doubleCube(w); +} + +/* + * Ask the current backgammon engine rolling the dice. + */ +void KBgBoard::getRollDice(const int w) +{ + emit rollDice(w); +} + +/* + * This is the constructor of the KBgBoard class. It creates + * a backgammon board with an initial distribution of checkers, empty + * dice and a cube with face value 1. The initial board is not usable! + * You have to change the status by passing a KBgStatus + * object to setState(...) before you can play! + */ +KBgBoard::KBgBoard(QWidget *parent, const char *name, QPopupMenu *menu) + : QWidget(parent, name) +{ + /* + * The following lines set up internal bookkeeping data. + */ + moveHistory.setAutoDelete(true); + redoHistory.setAutoDelete(true); + cube = 1; + allowMoving(true); + setEditMode(false); + savedCursor = NULL; + checkerDiam = MINIMUM_CHECKER_SIZE; + + /* + * We may be initialized with a popup menu by our parent. + */ + contextMenu = menu; + + baseColors[0] = black; + baseColors[1] = white; + + /* + * Get the 30 cells that constitute the board and initialize + * them properly. + */ + cells[ 0] = new KBgBoardHome(this, HOME_THEM_LEFT); + cells[14] = new KBgBoardHome(this, HOME_THEM_RIGHT); + cells[15] = new KBgBoardHome(this, HOME_US_LEFT); + cells[29] = new KBgBoardHome(this, HOME_US_RIGHT); + + cells[ 7] = new KBgBoardBar(this, BAR_THEM); + cells[22] = new KBgBoardBar(this, BAR_US); + + for (int i=1; i<7; ++i) { + cells[ i] = new KBgBoardField(this, i); + cells[ 7+i] = new KBgBoardField(this, 6+i); + cells[15+i] = new KBgBoardField(this, 12+i); + cells[22+i] = new KBgBoardField(this, 18+i); + } + + /* + * Get the default seeting of the board and initialize the + * state of it. + */ + KBgStatus *st = new KBgStatus(); + + st->setCube(1, true, true); + st->setDirection(+1); + st->setColor(+1); + + st->setBoard( 1, US, 2); st->setBoard( 6, THEM, 5); + st->setBoard( 8, THEM, 3); st->setBoard(12, US, 5); + st->setBoard(13, THEM, 5); st->setBoard(17, US, 3); + st->setBoard(19, US, 5); st->setBoard(24, THEM, 2); + + st->setHome(US, 0); + + st->setDice(US , 0, 0); st->setDice(US , 1, 0); + st->setDice(THEM, 0, 0); st->setDice(THEM, 1, 0); + + setState(*st); + + delete st; + + /* + * This line simplifies the checkMultiMove(...) function a lot. + */ + board[0] = board[25] = 0; + + /* + * User interface design settings come here. These may be + * overwritten by the user. + */ + shortMoveMode = SHORT_MOVE_DOUBLE; + setBackgroundColor(QColor(200, 200, 166)); + computePipCount = true; + + /* + * Set initial font + */ + setFont(QApplication::font()); +} + +QSize KBgBoard::minimumSizeHint() const +{ + return QSize(MINIMUM_CHECKER_SIZE * 15, MINIMUM_CHECKER_SIZE * 11); +} + +QSize KBgBoard::sizeHint() const { + return QSize(MINIMUM_CHECKER_SIZE *15*4,MINIMUM_CHECKER_SIZE*11*2); +} + diff --git a/kbackgammon/kbgboard.h b/kbackgammon/kbgboard.h new file mode 100644 index 00000000..e2f35f68 --- /dev/null +++ b/kbackgammon/kbgboard.h @@ -0,0 +1,967 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + + +/* + + This file contains the declaration and definition of a couple of + classes related to the KBgBoard class. + + Effort has been made to keep this class general. Please comment on that + if you want to use it in your own project. Most of the stuff is private + and/or in utility classes that shouldn't be used directly. All public + interfaces are in teh beginning of the file. + +*/ + +#ifndef KBGBOARD_H +#define KBGBOARD_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kbgstatus.h" + +/* + * Just some internal constants and classes + */ +const int US = KBgStatus::US; +const int THEM = KBgStatus::THEM; +const int BOTH = KBgStatus::BOTH; + +const int HOME_US_LEFT = 101; +const int HOME_US_RIGHT = 102; +const int HOME_THEM_LEFT = 103; +const int HOME_THEM_RIGHT = 104; +const int BAR_US = 105; +const int BAR_THEM = 106; +const int HOME_US = 107; +const int HOME_THEM = 108; + +const int SHORT_MOVE_NONE = 0; +const int SHORT_MOVE_SINGLE = 1; +const int SHORT_MOVE_DOUBLE = 2; + +class KBgBoard; +class KBgBoardCell; +class KBgBoardMove; +class KBgBoardHome; +class KBgBoardBar; +class KBgBoardField; +class KBgBoardQDice; + + +/** + * The KBgBoard class. + * + * This class handles all game operations of the client. + */ +class KBgBoard : public QWidget +{ + Q_OBJECT + + friend class KBgBoardCell; + friend class KBgBoardHome; + friend class KBgBoardBar; + friend class KBgBoardField; + + public: + + /** + * Constructor and destructor. Parameter as usual. + */ + KBgBoard(QWidget *parent = 0, const char *name = 0, + QPopupMenu *menu = 0); + virtual ~KBgBoard(); + + /** + * Returns the current read/write status of the board. + */ + bool movingAllowed() const; + + /** + * Returns the current up-to-the-second pip count (including + * the moves that have been done. + */ + int getPipCount(const int& w) const; + + /** + * Sets the momentary short move mode. The parameter should + * be one of the constanst SHORT_MOVE_NONE, SHORT_MOVE_SINGLE + * or SHORT_MOVE_DOUBLE. This the automatic moving of a checker + * with the shortest possible move away from the current field. + */ + void setShortMoveMode(int m); + + /** + * Returns the current short move mode. + */ + int getShortMoveMode(); + + /** + * Sets the background color and passes the info to the + * child widgets + */ + virtual void setBackgroundColor(const QColor &col); + + /** + * Prints the baord along with some basic info onto the + * painetr p. It is assumed that this painter is a postscript + * printer. Hence the plot is black/white only. + */ + void print(QPainter *p); + + /** + * Get whose turn it is - US, THEM or 0 + */ + int getTurn() const; + + /** + * Allows for overriding the current turn color in edit mode. + */ + void storeTurn(const int pcs); + + /** + * Retrurns the current edit mode status. + */ + bool getEditMode() const; + + /** + * Get a new value for the cube from the user - this opens a dialog + */ + void queryCube(); + + /** + * Get the font the board cells should use for the display of + * numbers and cube value. + */ + QFont getFont() const; + + /** + * This function has to be reimplemented to provide a minimum size for + * the playing area. + */ + QSize minimumSizeHint() const; +public slots: + + /** + * This allows the users of this widget to dis- and enable + * mouse events. In effect this triggers the read/write + * flag of the board. + */ + void allowMoving(const bool fl); + + /** + * Overwritten resize event handler. + * + * We overwrite the handler to make sure that all the cells are + * repainted as well. + */ + virtual void resizeEvent(QResizeEvent *); + + /** + * Undo the last move. + */ + void undoMove(); + + /** + * Redo a previously undone move + */ + void redoMove(); + + /** + * This is the most important public member. It takes + * a board status - s. the KBgBoardStatus class - + * and sets the board accordingly. + */ + void setState(const KBgStatus &); + + /** + * Set the context menu + */ + void setContextMenu(QPopupMenu *menu); + + /** + * Get the current state of the board. + */ + KBgStatus *getState(KBgStatus *st) const; + + /** + * Sets the edit mode of the board. In that mode the board can be + * modified arbitrarily. + */ + void setEditMode(const bool m); + + /** + * Allows the users of the board classe to set the font to be used + * on the board. Note that the fontsize is dynamically set + */ + void setFont(const QFont& f); + + /** + * Write the current configuration to the application's data base + */ + void saveConfig(); + + /** + * Restore the stored configuration or start with reasonable defaults + */ + void readConfig(); + + signals: + + /** + * The text identifies the current game status - could be put + * into the main window caption + */ + void statText(const QString &msg); + + /** + * The cells connect to this signal and it tells them that it is + * time to update their content now if necessary. + */ + void finishedUpdate(); + + /** + * The user has requested the dice to be rolled. Emit the + * request to somebody who knows how to do that. + */ + void rollDice(const int w); + + /** + * Ask the server to double + */ + void doubleCube(const int w); + + /** + * Once the moves are all made, build a server command and send + * them out. + */ + void currentMove(QString *s); + + /* ************************************************** */ + /* ************************************************** */ + + /* Everything below this line is private and it */ + /* shouldn't be used by users of this widget. */ + + /* This means the whole file! All following */ + /* classes and members are private. */ + + /* ************************************************** */ + /* ************************************************** */ + +protected: + virtual QSize sizeHint() const; + + QColor baseColors[2]; + QFont boardFont; + KBgBoardCell* cells[30]; + bool computePipCount; + + private: + + /** + * Emits a currentMove string to whomever cares. + */ + void sendMove(); + + /** + * Emit a request for doubling. + */ + void getDoubleCube(const int w); + + /** + * Get pieces on who's bar - US or THEM + */ + int getOnBar( int who ) const; + + /** + * Get who's dice num - who = US or THEM, num = 0 or 1 + */ + int getDice( int w, int n ) const; + + /** + * Get the number on the cube + */ + int getCube() const; + + /** + * Ask the server for rolling + */ + void getRollDice(const int w); + + /** + * Tell the board that we kicked a piece off and store + * the information + */ + void kickedPiece(); + + /** + * Check whether a move off is possible by checking that all pieces + * are either in the home board or already off + */ + bool moveOffPossible() const; + + /** + * Check whether who (US or THEM) can double + */ + bool canDouble( int who ) const; + + /** + * The cells have to tell us if the change the number of pieces, since + * we check that sometimes + */ + void updateField( int f, int v ); + + /** + * Convert an ID from to the board numbers + */ + int IDtoNum(const int ID ) const; + + /** + * Checks if there is a possibility to get from src to dest. + */ + int checkMultiMove( int src, int dest, int m[4] ); + + /** + * Checks whether the dice allow a move from src to dest (ID's) + */ + bool diceAllowMove( int src, int dest ); + + /** + * Make a move from src to dest. The numbers are cellID's. + */ + void makeMove( int src, int dest ); + + /** + * Translates a field number to a pointer to the cell. + */ + KBgBoardCell* getCell(int num); + + /** + * Draws a piece on the painter p, with the upper left corner + * of the enclosing rectangle being (x,y) + */ + void drawCircle(QPainter *p, int x, int y, int pcs, int diam, + int col, bool upper, bool outer) const; + + /** + * Draws an anti-aliased checker on the painter p. + */ + void drawChecker(QPainter *p, int x, int y, int pcs, int diam, + int col, bool upper) const; + + /** + * Draws a simple 2-color checker on the painter p. This is intended + * for printing. + */ + void drawSimpleChecker(QPainter *p, int x, int y, int pcs, + int diam) const; + + /** + * Given a position on the board, return the cell under the mouse pointer + */ + KBgBoardCell* getCellByPos(const QPoint& p) const; + + /** + * Name says it all, doesn't it? + */ + void showContextMenu(); + + /** + * Temporary replace the cursor, saves the old one + */ + void replaceCursor(const QCursor& c); + + /** + * Restore the previously stored cursor. + */ + void restoreCursor(); + + /** + * Given the sign of p, return the current base color + */ + QColor getCheckerColor(int p) const; + + /** + * Small utility function for makeMove - just for readability + */ + int makeMoveHelper(int si, int sf, int delta); + + /** + * Private data members - no description needed + */ + QPopupMenu *contextMenu; + QPtrList moveHistory; + QPtrList redoHistory; + int direction, color; + int hasmoved; + bool allowmoving, editMode; + int storedTurn; + int onbar[2]; + int onhome[2]; + int board[26]; + int dice[2][2]; + int possMoves[7]; + int cube; + int checkerDiam; + bool cubechanged; + bool maydouble[2]; + int shortMoveMode; + QCursor *savedCursor; +}; + +/** + * Base class for the cells on the board + * + * This base class provides all the necessary functions of a cell + * on a backgammon board. It has a bunch of virtual functions that + * are overloaded in the derived classes. + */ +class KBgBoardCell : public QLabel +{ + Q_OBJECT + + public: + + /** + * Constructor and destructor + */ + KBgBoardCell(QWidget * parent, int numID); + virtual ~KBgBoardCell(); + + /** + * sets the number and color of checkers on this cell + * takes care of repainting + */ + virtual void cellUpdate(const int p, const bool cubechanged = false) = 0; + + /** + * Draws the content of the cell on the painter *p + */ + virtual void paintCell(QPainter *p, int xo = 0, int yo = 0, + double sf = 1.0) const; + + /** + * Updates all the status variables at once + */ + virtual void statusUpdate(int dir, int col); + +protected: + + /** + * Draw vertical lines around the board. + */ + void drawVertBorder(QPainter *p, int xo, int yo, double sf = 1.0) const; + void drawOverlappingCheckers(QPainter *p, int xo, int yo, + double sf = 1.0) const; + void drawCube(QPainter *p, int who, int xo, int yo, double sf = 1.0) const; + + /** + * Puts a piece of color on a field + */ + void putPiece( int newColor ); + + /** + * Removes a piece from a field. Returns true if success or false else + * (i.e. there is no piece on this field. + */ + bool getPiece(); + + /** + * Return the number of this cell + */ + virtual int getNumber() const; + + /** + * Return the suggested diameter of a piece + */ + int getCheckerDiameter() const; + int getCellColor(); + + /** + * Do we allow a drop of the DragEvent ? This checks the payload and + * reacts on it. + */ + virtual bool dropPossible(int fromCellID, int newColor) = 0; + + protected: + + /** + * Overwrite how a cell draws itself + */ + virtual void drawContents(QPainter *); + + /** + * Status numbers that store the current board status. + */ + int mouseButton; + int direction; + int color; + + /** + * How many pieces are we currently holding ? + */ + int pcs; + + /** + * Our own ID + */ + int cellID; + + /** + * Indicates whether this cell needs to repaint itself after + * the board has been processed. + */ + bool stateChanged; + bool colorChanged, directionChanged; + + /** + * the board and the pieces are one unit (none makes + * sense without the other). So the pieces know and access their parent. + */ + KBgBoard *board; + void checkAndMakeShortMove(QMouseEvent *e, int m); + + /** + * Returns the bounding rectangle of the cube on this cell + */ + QRect cubeRect( int who, bool big, double sf = 1.0 ) const; + + /** + * Returns the bounding rectangle of the dice i on this cell + */ + QRect diceRect(int i, bool big, double sf = 1.0, double scale = 0.45) const; + bool dragInProgress; + + protected slots: + /** + * Refreshes the widget. This is essentially a call to update(). + */ + virtual void refresh(); + + /** + * Can we currently drag from this field ? + */ + virtual bool dragPossible() const = 0; + + /** + * Possibly initiate a drag. + */ + virtual void mouseMoveEvent( QMouseEvent * ); + virtual void mousePressEvent(QMouseEvent *e); + + /** + * Make the shortes possible move away from this cell + */ + void makeShortMove(); + void makeShortMoveHelper(int s, int d); + + /** + * Catch a single left click and perhapes make a move. + */ + virtual void mouseReleaseEvent( QMouseEvent *e ); + + /** + * Catch a double left click and perhapes make a move. + */ + virtual void mouseDoubleClickEvent( QMouseEvent *e ); +}; + +/** + * The homes are derived from the regular cells. They just overwrite + * some members. + */ +class KBgBoardHome : public KBgBoardCell +{ + Q_OBJECT + + public: + /** + * sets the number and color of checkers on this cell + * takes care of repainting + */ + virtual void cellUpdate(const int p, const bool cubechanged = false); + + /* + * Draws the content of the cell on the painter *p + */ + virtual void paintCell(QPainter *p, int xo = 0, int yo = 0, + double sf = 1.0) const; + + /** + * Constructor and destructor + */ + KBgBoardHome( QWidget * parent, int numID); + virtual ~KBgBoardHome(); + + /** + * Check whether a drop on the home cell is possible. + */ + virtual bool dropPossible(int fromCellID, int newColor); + + protected: + /** + * Determine whether a drag from the home is possible. + */ + virtual bool dragPossible() const; + + /** + * Get the double clicks + */ + virtual void mouseDoubleClickEvent( QMouseEvent *e ); + + /** + * The homes contain dice and cube. This draws them. + */ + void drawDiceAndCube(QPainter *p, int who, int xo, int yo, + double sf) const; + + void drawDiceFrame(QPainter *p, int col, int num, int xo, int yo, + bool big, double sf) const; + void drawDiceFace(QPainter *p, int col, int num, int who, int xo, + int yo, double sf) const; + + private: + /** + * Save old dice to avoid repainting + */ + int savedDice[2]; + +}; + +/** + * The bars are derived from the regular cells. They just overwrite + * some members. + */ +class KBgBoardBar : public KBgBoardCell +{ + Q_OBJECT + + public: + /** + * sets the number and color of checkers on this cell + * takes care of repainting + */ + virtual void cellUpdate(const int p, const bool cubechanged = false); + + /** + * Draws the content of the cell on the painter *p + */ + virtual void paintCell(QPainter *p, int xo = 0, int yo = 0, + double sf = 1.0) const; + + /** + * Constructor + */ + KBgBoardBar( QWidget * parent, int numID ); + + /** + * Destructor + */ + virtual ~KBgBoardBar(); + + /** + * Check whether a drop on the bar cell is possible. + */ + virtual bool dropPossible(int fromCellID, int newColor); + + protected: + /** + * Determine whether a drag from the bar is possible. + */ + virtual bool dragPossible() const; + /** + * Get the double clicks + */ + virtual void mouseDoubleClickEvent(QMouseEvent *e); +}; + +/** + * The fields are derived from the regular cells. They just overwrite + * some members. + */ +class KBgBoardField : public KBgBoardCell +{ + Q_OBJECT + + public: + /** + * Constructor and destructor + */ + KBgBoardField( QWidget * parent, int numID); + virtual ~KBgBoardField(); + + /** + * sets the number and color of checkers on this cell + * takes care of repainting + */ + virtual void cellUpdate(const int p, const bool cubechanged = false); + + /** + * Draws the content of the cell on the painter *p + */ + virtual void paintCell(QPainter *p, int xo = 0, int yo = 0, + double sf = 1.0) const; + + /** + * Check whether a drop on the field cell is possible. + */ + virtual bool dropPossible(int fromCellID, int newColor); + + protected: + /** + * Determine whether a drag from the field is possible. + */ + virtual bool dragPossible() const; + + /** + * Return the y-coordinate of the number of the field. + */ + int numberBase() const; +}; + +/** + * Internal class for storing a move in the undo history buffer. + */ +class KBgBoardMove +{ + public: + /** + * Accepts source, destination and the dice that made this move + * possible. Set the kicked flag to false. + */ + KBgBoardMove(int src, int dest, int delta) + {s = src; d = dest; l = delta; k = false;} + + /** + * Set this move to be a kick + */ + void setKicked(bool kicked) {k = kicked;} + + /** + * Look up the source + */ + int source() const {return s;} + + /** + * Look up the destination + */ + int destination() const {return d;} + + /** + * Look up the dice that made this move + */ + int length() const {return l;} + + /** + * Check whether the move kicked a piece + */ + bool wasKicked() const {return k;} + + private: + /** + * Source, destination, dice + */ + int s, d, l; + + /** + * Kicked move ? + */ + bool k; +}; + +/** + * Simple dialog that allows to query the user for dice values. + * + * A very simple dialog with two SpinBoxes and two buttons. + */ +class KBgBoardQDice : public QDialog +{ + Q_OBJECT + +public: + + /** + * Constructor and destructor + */ + KBgBoardQDice(const char *name = 0); + virtual ~KBgBoardQDice(); + +protected: + + /** + * Spin boxes and buttons are children + */ + QSpinBox *sb[2]; + QPushButton *ok; + QPushButton *cancel; + +public slots: + + /** + * Get the face values. + */ + int getDice(int n); +}; + + +/** + * Simple dialog that allows to query the user for the cube value. + */ +class KBgBoardQCube : public QDialog +{ + Q_OBJECT + +public: + + /** + * Constructor and destructor + */ + KBgBoardQCube(int val, bool us, bool them); + virtual ~KBgBoardQCube(); + +protected: + + /** + * Spin boxes and buttons are children + */ + QComboBox *cb[2]; + QPushButton *ok; + QPushButton *cancel; + +public slots: + + /** + * Get the face values. + */ + int getCubeValue(); + int getCubeOwner(); + +protected slots: + + /** + * These slots are needed to get consistent relations + * between the two combo boxes. + */ + void changePlayer(int val); + void changeValue(int player); + +}; + + +/** + * Extension of the KBgBoard class that can add itself + * to a QTabDialog for configuration. + */ +class KBgBoardSetup : public KBgBoard +{ + Q_OBJECT + +public: + + /** + * Constructor + */ + KBgBoardSetup(QWidget *parent = 0, const char *name = 0, + QPopupMenu *menu = 0); + + /** + * Lets the board put its setup pages into the notebook nb + */ + void getSetupPages(KDialogBase *nb); + +public slots: + + /** + * Setup changes are confirmed. Store them. + */ + void setupOk(); + + /** + * Setup has been cancelled. Undo the color changes + */ + void setupCancel(); + + /** + * Load default values for user settings + */ + void setupDefault(); + +protected slots: + + /** + * Open a color dialog for the background color + */ + void selectBackgroundColor(); + + /** + * Open a color dialog for the first checker color + */ + void selectBaseColorOne(); + + /** + * Open a color dialog for the second checker color + */ + void selectBaseColorTwo(); + +private: + + /** + * Save settings before the user changed them + */ + KFontChooser *kf; + + QRadioButton *rbMove[3]; + + QColor saveBackgroundColor; + QColor saveBaseColors[2]; + + /** + * Need these to change their colors + */ + QPushButton *pbc_1, *pbc_2, *pbc_3; + QCheckBox *cbp; +}; + +#endif // KBGBOARD_H diff --git a/kbackgammon/kbgstatus.cpp b/kbackgammon/kbgstatus.cpp new file mode 100644 index 00000000..1215324e --- /dev/null +++ b/kbackgammon/kbgstatus.cpp @@ -0,0 +1,544 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include "kbgstatus.h" +#include "kbgstatus.moc" + +#include +#include + + +/* + * Parse a rawboard description from FIBS and initialize members. + */ +KBgStatus::KBgStatus(const QString &rawboard) +{ + /* + * This is the format string from hell... + */ + const char *format = ("%*[^:]%*[:]%99[^:]%*[:]%99[^:]%*[:]%i%*[:]%i%*[:]%i%*[:]" + "%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]" + "%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]" + "%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]" + "%i%*[:]" + "%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]" + "%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]" + "%i%*[:]%i%*[:]"); + + char opponent[100], player[100]; + + QString cap; + + int board[26], ldice[2][2], maydouble[2], scratch[4], onhome[2], onbar[2]; + int points[2]; + int tomove, lturn, color, cube, direction, redoubles, bar, home, length; + + // split the incoming line at colons - latin1() is fine, since the string comes from FIBS. + sscanf (rawboard.latin1(), format, + player, opponent, + &length, + &points[0], &points[1], + &board[ 0], &board[ 1], &board[ 2], &board[ 3], &board[ 4], &board[ 5], + &board[ 6], &board[ 7], &board[ 8], &board[ 9], &board[10], &board[11], + &board[12], &board[13], &board[14], &board[15], &board[16], &board[17], + &board[18], &board[19], &board[20], &board[21], &board[22], &board[23], + &board[24], &board[25], + <urn, + &ldice[US ][0], &ldice[US ][1], &ldice[THEM][0], &ldice[THEM][1], + &cube, + &maydouble[US], &maydouble[THEM], + &doubled_, + &color, + &direction, + &home, &bar, + &onhome[US], &onhome[THEM], // on home + &onbar[US], &onbar[THEM], // on bar + &tomove, + &scratch[2], &scratch[3], // forced move & did crawford + &redoubles); + + player_[US] = player; + player_[THEM] = opponent; + + setCube(cube, maydouble[US], maydouble[THEM]); + setDirection(direction); + setColor(color); + for (int i = 1; i < 25; i++) { + if (board[i] == 0 || color == board[i]/abs(board[i])) + setBoard(i, US, abs(board[i])); + else + setBoard(i, THEM, abs(board[i])); + } + setDice(US , 0, ldice[US ][0]); + setDice(US , 1, ldice[US ][1]); + setDice(THEM, 0, ldice[THEM][0]); + setDice(THEM, 1, ldice[THEM][1]); + + setHome(US, onhome[US ]); + setHome(THEM, onhome[THEM]); + + setBar(US, board[ bar]); + setBar(THEM, board[25-bar]); + + setPoints(US, points[0]); + setPoints(THEM, points[1]); + + if (lturn == 0) + setLength(-1); + else + setLength(length); + + int t = lturn*color; + if (t > 0) setTurn(US); + if (t < 0) setTurn(THEM); + if (t == 0) setTurn(NONE); +} + +/* + * Constructor initializes the status to an empty board with cube one + * and empty dice. + */ +KBgStatus::KBgStatus() + : QObject() +{ + /* + * Initialize members + */ + for (int i = 0; i < 26; ++i) + setBoard(i, US, 0); + + for (int i = US; i <= THEM; i++) { + setDice (i, 0, 0); + setDice (i, 1, 0); + setHome (i, 0); + setBar (i, 0); + setPoints(i, -1); + setPlayer(i, QString::null); + } + setColor(White, US); + setCube(1, BOTH); // also initializes maydouble + setDirection(1); + setLength(-1); + setTurn(NONE); + + // initialize members without assignment functions + doubled_ = 0; +} + +/* + * Copy constructor calls private utility function. + */ +KBgStatus::KBgStatus(const KBgStatus &rhs) + : QObject() +{ + copy(rhs); +} + +/* + * Destructor + */ +KBgStatus::~KBgStatus() +{ + // nothing to do +} + +/* + * Assignment operator shares a lot of code with the copy + * constructor. + */ +KBgStatus& KBgStatus::operator=(const KBgStatus &rhs) +{ + if (this == &rhs) return *this; + copy(rhs); + return *this; +} + +void KBgStatus::copy(const KBgStatus &rhs) +{ + for (int i = 0; i < 26; i++) + board_[i] = rhs.board_[i]; + + for (int i = US; i <= THEM; i++) { + + home_[i] = rhs.home_[i]; + bar_ [i] = rhs.bar_ [i]; + dice_[i][0] = rhs.dice_[i][0]; + dice_[i][1] = rhs.dice_[i][1]; + + maydouble_[i] = rhs.maydouble_[i]; + player_ [i] = rhs.player_ [i]; + points_ [i] = rhs.points_ [i]; + } + + cube_ = rhs.cube_; + direction_ = rhs.direction_; + color_ = rhs.color_; + turn_ = rhs.turn_; + doubled_ = rhs.doubled_; +} + + +/* + * Access functions + */ +int KBgStatus::board(const int &i) const +{ + return ((0 < i && i < 25) ? color_*board_[i] : 0); +} + +int KBgStatus::home(const int &w) const +{ + return ((w == US || w == THEM) ? color_*home_[w] : 0); +} + +int KBgStatus::bar(const int &w) const +{ + return ((w == US || w == THEM) ? color_*bar_[w] : 0); +} + +int KBgStatus::color(const int &w) const +{ + return ((w == THEM) ? -color_ : color_); +} + +int KBgStatus::direction() const +{ + return direction_; +} + +int KBgStatus::dice(const int &w, const int &n) const +{ + if ((w == US || w == THEM) && (n == 0 || n == 1)) + return dice_[w][n]; + else + return 0; +} + +int KBgStatus::cube(const int &w) const +{ + if (w == US || w == THEM) + return ((maydouble_[w]) ? cube_ : -cube_); + return 0; +} + +int KBgStatus::points(const int& w) const +{ + return ((w == US || w == THEM) ? points_[w] : -1); +} + +QString KBgStatus::player(const int &w) const +{ + return ((w == US || w == THEM) ? player_[w] : QString::null); +} + +int KBgStatus::length() const +{ + return length_; +} + +int KBgStatus::turn() const +{ + return turn_; +} + +bool KBgStatus::doubled() const +{ + return doubled_; +} + + +/* + * Assignment functions + */ +void KBgStatus::setBoard(const int &i, const int &w, const int &v) +{ + if (0 < i && i < 25) { + if (w == US) + board_[i] = abs(v); + else if (w == THEM) + board_[i] = -abs(v); + } +} + +void KBgStatus::setHome(const int &w, const int &v) +{ + if (w == US) + home_[w] = abs(v); + else if (w == THEM) + home_[w] = -abs(v); +} + +void KBgStatus::setBar(const int& w, const int& v) +{ + if (w == US) + bar_[w] = abs(v); + else if (w == THEM) + bar_[w] = -abs(v); +} + +void KBgStatus::setColor(const int &c, const int &w) +{ + if (w == US) + color_ = ((c < 0) ? Black : White); + else if (w == THEM) + color_ = ((c < 0) ? White : Black); +} + +void KBgStatus::setDirection(const int &dir) +{ + direction_ = ((dir < 0) ? -1 : +1); +} + +void KBgStatus::setDice(const int &w, const int &n, const int &v) +{ + if ((w == US || w == THEM) && (n == 0 || n == 1)) { + if (0 <= v && v <= 6) + dice_[w][n] = v; + else + dice_[w][n] = 0; + } +} + +void KBgStatus::setCube(const int &c, const bool &us, const bool &them) +{ + int w = NONE; + if (us) w = US; + if (them) w = THEM; + if (us && them) w = BOTH; + setCube(c, w); +} + +void KBgStatus::setCube(const int &c, const int &w) +{ + // assume that int has at least 32 bits... + for (int i = 0; i < 31; i++) { + if (1< 0)) { + start = 25; + dir = -1; + } else { + start = 0; + dir = 1; + } + + /* + * Get the current dice transferred into the move[] array. The + * final zero is a marker + */ + int move[5] = {0, 0, 0, 0, 0}; + move[0] = dice(turn(), 0); + move[1] = dice(turn(), 1); + if (move[0] == move[1]) { + move[3] = move[2] = move[0]; + // saves some work further down + if (move[0] == 0) + return 0; + + } + + bool doubledice = (move[3] != 0); + int count = 4; + + /* + * Get a copy of ourselves. That way we can mess around with + * the internals of the game. + */ + KBgStatus sc(*this); + + /* + * Start with getting all checkers off the bar + */ + while (count > 0 && sc.bar(turn()) != 0) { + if (move[--count] != 0) { + if (color(turn())*sc.board(start+dir*move[count]) >= -1) { + sc.setBar(turn(), abs(sc.bar(turn()))-1); + sc.setBoard(start + dir*move[count], turn(), 1); + move[count] = 0; + } + } + } + + /* + * Collect remaining moves in the beginning of the move array + */ + int j = 0; + for (int i = 0; i < 4; i++) { + if ((move[j] = move[i])) + ++j; + if (i > j) move[i] = 0; + } + + + /* + * Find number of remaining moves + */ + int moves = 0; + move[4] = 0; + while (move[moves]) + ++moves; + + /* + * Done or no more moves because the bar is not empty + */ + if (sc.bar(turn()) != 0 || move[0] == 0) + return (move[3] ? 4 - moves : 2 - moves); + + /* + * Try to find possible moves on the board + */ + if (moves == 1 || move[0] == move[1]) { + + /* + * Order doesn't matter, all moves are equal + */ + while (--moves >= 0 && movePossible(sc, move[moves], start, dir)); + moves++; + return (doubledice ? 4 - moves : 2 - moves); + + } else { + + /* + * Order does matter; try both ways. + */ + moves = 0; + for (int i = 0; i < 25; i++) { + if (movePossible(sc, move[0], start + i*dir, dir)) { + moves = 1; + if (movePossible(sc, move[1], start, dir)) { + return 2; + } + } + // Restore scratch copy... + sc = *this; + } + for (int i = 0; i < 25; i++) { + + if (movePossible(sc, move[1], start + i*dir, dir)) { + moves = 1; + if (movePossible(sc, move[0], start, dir)) { + return 2; + } + } + // Restore scratch copy... + sc = *this; + } + return moves; + } +} + +bool KBgStatus::movePossible(KBgStatus &sc, int a, int start, int dir) const +{ + /* + * Determine where the first checker in moving direction is + * located + */ + int first = (dir > 0) ? 1 : 24; + int last = (dir > 0) ? 25 : 0; + while (first != last && color(turn())*sc.board(first) <= 0) + first += dir; + + /* + * Are we moving off ? + */ + bool off = false; + if ((dir > 0 && first > 18) || (dir < 0 && first < 7)) + off = true; + + /* + * Find a move by exhaustive search. + */ + while (true) { + + start += dir; + int final = start+dir*a; + + /* + * Make absolutely sure that the loop terminates eventually + */ + if (start <= 0 || start >= 25) + return false; + + if (color(turn())*sc.board(start) > 0) { + + if (0 < final && final < 25 && color(turn())*sc.board(final) >= -1) { + sc.setBoard(start, turn(), abs(sc.board(start)) - 1); + sc.setBoard(final, turn(), abs(sc.board(final)) + 1); + return true; + } else if (off && (final == 0 || final == 25)) { + sc.setBoard(start, turn(), abs(sc.board(start)) - 1); + sc.setHome(turn(), abs(sc.home(turn())) + 1); + return true; + } else if (off && first == start && (final > 24 || final < 1)) { + sc.setBoard(start, turn(), abs(sc.board(start)) - 1); + sc.setHome(turn(), abs(sc.home(turn())) + 1); + return true; + } + } + } +} + +// EOF diff --git a/kbackgammon/kbgstatus.h b/kbackgammon/kbgstatus.h new file mode 100644 index 00000000..5543e1ca --- /dev/null +++ b/kbackgammon/kbgstatus.h @@ -0,0 +1,310 @@ +/* + Copyright (C) 2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef KBGSTATUS_H +#define KBGSTATUS_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + + +/** + * This is a the fundamental class of the KBg* hierarchy. It represents + * the state of backgammon games. + * + * The game states can be initialized in a variety of information and + * are meant to be passed to the board. In addition to that, the class + * has several utility functions that are helpful for engines that + * maintain a local state. + * + * @short The backgammon status object + * @author Jens Hoefkens + * @version $Id$ + */ +class KBgStatus : public QObject +{ + + Q_OBJECT + + public: + + /** + * These numbers are used to distinguish the two players. The + * notion of US and THEM is a leftover from ancient times. + */ + enum {NONE = -1, US = 0, THEM = 1, BOTH = 2}; + + /** + * The names are just to distinguish the two different colors. + */ + enum {Black = -1, White = +1}; + + /** + * The default constructor initializes the status to an "empty" + * state. The board and dice are empty and the cube shows 1. + */ + KBgStatus(); + + /** + * Constructor from a FIBS rawboard string + */ + KBgStatus(const QString &rawboard); + + /** + * Copy constructor + */ + KBgStatus(const KBgStatus &rhs); + + /** + * Assignment operator + */ + KBgStatus& operator=(const KBgStatus &rhs); + + /** + * Destructor + */ + virtual ~KBgStatus(); + + + /* + * The absolute value of the return value is the number of + * checkers on the i-th field (or zero if i is out of + * bounds). If the return value has the same sign as the + * current color of US, it belongs to US, otherwise it belongs + * to THEM. + */ + int board(const int &i) const; + + /* + * The absolute value of the return value is the number of + * checkers on the home of player w (or zero if i is out of + * bounds). If the return value has the same sign as the + * current color of US, it belongs to US, otherwise it belongs + * to THEM. + * + * The encoding of the color is slighly redundant. See also board(...). + */ + int home(const int &w = US) const; + + /* + * The absolute value of the return value is the number of + * checkers on the bar of player w (or zero if i is out of + * bounds). If the return value has the same sign as the + * current color of US, it belongs to US, otherwise it belongs + * to THEM. + * + * The encoding of the color is slighly redundant. See also board(...). + */ + int bar(const int &w = US) const; + + /* + * Return the current color of player w. If w is invalid, the + * color of US is returned. The return value will be either + * Black or White. + */ + int color(const int& w = US) const; + + /* + * Returns the current direction of the game. It is -1 if US + * plays from 24 to 1 and +1 if US plays from 1 to 24. + */ + int direction() const; + + /* + * Returns the n-th dice of player w. If w is invalid or if n + * is out of bounds, return zero. Also, if the dice haven't + * been set, zero is returned. + */ + int dice(const int &w, const int &n) const; + + /* + * Returns the value of the cube. If w can double, the return + * value is positive, if w may not double, the negative value + * of the cube is returned. If w is not legal, zero is + * returned. + */ + int cube(const int &w = US) const; + + /* + * Return the points of w in th ecurrent game. Negative values + * indicate that either w was not a legal player ID or that + * the engine doesn't have any information on points. + */ + int points(const int &w) const; + + /* + * Return the name of player w. If w is out of bounds or if + * the player names have not been set, QString::null is + * returned. + */ + QString player(const int &w = US) const; + + /* + * Return the length of the game. Negative values should be used to + * indicate that the game is over. Zero indicates that the game is + * unlimited. + */ + int length() const; + + /* + * Return whose turn it is. The possible return values are US, + * THEM, and NONE. + */ + int turn() const; + + /* + * Return true if the cube has just been offered. If this + * information is not available or if this is not the case, + * return false. + */ + bool doubled() const; + + /* + * Set the number of checkers of player w on the i-th field to + * the absolute value of v. If either i or w are out of bound, + * nothing is done. + * + * Internally, positive numbers are stored for US and negative + * ones for THEM. While this coding is redundant, it is + * consistent with the storing of board positions. + */ + void setBoard(const int &i, const int &w, const int &v); + + /* + * Set the number of checkers on the home of player w to the + * absolute value of v. If w is out of bound, nothing is done. + * + * Internally, positive numbers are stored for US and negative + * ones for THEM. While this coding is redundant, it is + * consistent with the storing of board positions. See also + * setBoard(...). + */ + void setHome(const int &w = US, const int &v = 0); + + /* + * Set the number of checkers on the bar of player w to the + * absolute value of v. If w is out of bound, nothing is done. + * + * Internally, positive numbers are stored for US and negative + * ones for THEM. While this coding is redundant, it is + * consistent with the storing of board positions. See also + * setBoard(...). + */ + void setBar(const int &w = US, const int &v = 0); + + /* + * This function sets the color of player w to c. Negative + * values of c translate to Black for US (and White for + * THEM). Non-negative values for c translate to White for US + * and Black for THEM. + */ + void setColor(const int& col, const int& w = US); + + /* + * Set the direction of the game. If dir is negative, US plays + * from 24 to 1. If dir is positive, US plays from 1 to 24. + */ + void setDirection(const int &dir); + + /* + * Set the n-th dice of player w to v. Nothing is done if w is + * invalid or if n is out of bounds. If v is invalid, the + * value zero is assigned (i.e., the dice is invalidated). + */ + void setDice(const int &w, const int &n, const int &v); + + /* + * Set the cube to c us indicates if US can double, them + * indicates if THEM can double. + * + * This function is depreciated. + */ + void setCube(const int &c, const bool &us, const bool &them); + + /* + * Set the cube to c, which must be a legal value (i.e., a + * power of 2). w indicates who can double. Legal values are + * NONE, US, THEM, and BOTH. + */ + void setCube(const int &c, const int &w); + + /* + * Set the points of w in the current game to p. Nothing is + * done if w is illegal. + */ + void setPoints(const int &w, const int &p); + + /* + * Set the name of player w to name. If w is out of bound, + * nothing is done. + */ + void setPlayer(const int &w, const QString &name); + + /* + * Set the length of the game. Negative values should be used to + * indicate that the game is over. Zero indicates that the game + * is unlimited. + */ + void setLength(const int &l); + + /* + * Set the turn to w. Legal values for w are US, THEM, and + * NONE (which should indicate that the game is over). + */ + void setTurn(const int &w); + + /* + * Return the number of possible moves basesd on the current + * dice, checkers, etc. + */ + int moves() const; + + private: + + /* + * Determine if there is any possibility to move a steps + * anywhere starting from start or later into direction + * dir in the game given by sc. + */ + bool movePossible(KBgStatus &sc, int a, int start, int dir) const; + + /* + * Copy constr. and assignment share a lot of code. + */ + void copy(const KBgStatus &rhs); + + /* + * Private variables with self-expalanatory names. + */ + QString player_[2]; + + int board_[26], home_[2], bar_[2], dice_[2][2], points_[2]; + int color_, direction_, cube_, length_, turn_; + int doubled_; + + bool maydouble_[2]; +}; + +#endif // KBGSTATUS_H diff --git a/kbackgammon/kbgtextview.cpp b/kbackgammon/kbgtextview.cpp new file mode 100644 index 00000000..b99a2f27 --- /dev/null +++ b/kbackgammon/kbgtextview.cpp @@ -0,0 +1,104 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999,2000 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include "kbgtextview.moc" +#include "kbgtextview.h" + +#include +#include +#include +#include +#include +#include + +#include + +// == advanced text control ==================================================== + +/* + * Constructor + */ +KBgTextView::KBgTextView(QWidget *parent, const char *name) + : KTextBrowser(parent, name) +{ + clear(); + setLinkUnderline(true); +} + +/* + * Destructor + */ +KBgTextView::~KBgTextView() +{ + // empty +} + +/* + * Write the string l to the TextView and put the cursor at the end of + * the current text + */ +void KBgTextView::write(const QString &l) +{ + append("" + l + "
\n"); + scrollToBottom(); +} + +/* + * Clears the view by overwriting the text with an empty string. + */ +void KBgTextView::clear() +{ + setText(""); +} + +/* + * Open a font-selection dialog. + */ +void KBgTextView::selectFont() +{ + QFont f = font(); + KFontDialog::getFont(f, false, this, true); + setFont(f); +} + +/* + * Restore the previously stored settings + */ +void KBgTextView::readConfig() +{ + KConfig* config = kapp->config(); + config->setGroup(name()); + + // nothing to restore +} + +/* + * Save the current settings to disk + */ +void KBgTextView::saveConfig() +{ + KConfig* config = kapp->config(); + config->setGroup(name()); + + // nothing to save +} + +// EOF diff --git a/kbackgammon/kbgtextview.h b/kbackgammon/kbgtextview.h new file mode 100644 index 00000000..887136f4 --- /dev/null +++ b/kbackgammon/kbgtextview.h @@ -0,0 +1,81 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999,2000 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#ifndef __KBGTEXTVIEW_H +#define __KBGTEXTVIEW_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + + +/** + * A small extension to the QTextView control. + */ +class KBgTextView : public KTextBrowser +{ + Q_OBJECT + +public: + + /** + * Constructor + */ + KBgTextView(QWidget *parent = 0, const char *name = 0); + + /** + * Destructor + */ + virtual ~KBgTextView(); + +public slots: + + /** + * Restore previously saved setting or provides defaults + */ + void readConfig(); + + /** + * Save current settings + */ + void saveConfig(); + + /** + * Simple interface to the non-slot function selectFont() + */ + void selectFont(); + + /** + * Clears the view by setting the text to "" + */ + void clear(); + + /** + * Write the string at the end of the buffer and scroll to + * the end + */ + void write(const QString &); +}; + +#endif // __KBGTEXTVIEW_H diff --git a/kbackgammon/main.cpp b/kbackgammon/main.cpp new file mode 100644 index 00000000..fe20cc7c --- /dev/null +++ b/kbackgammon/main.cpp @@ -0,0 +1,67 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include +#include +#include +#include +#include + +#include "kbg.h" +#include "version.h" + +static const char description[] = I18N_NOOP("A Backgammon program for KDE"); +static const char notice[] = I18N_NOOP("This is a graphical backgammon program. It supports " + "backgammon games\nwith other players, games against " + "computer engines like GNU bg and even\n" + "on-line games on the 'First Internet Backgammon Server'."); + +/* + * Main program doesn't do much - more or less standard stuff. Right + * after creating a KBg object, control is passed to it. + */ +int main(int argc, char *argv[]) +{ + KAboutData about(PROG_NAME, I18N_NOOP("KBackgammon"), PROG_VERSION, description, + KAboutData::License_GPL, "(C) 1999-2001 Jens Hoefkens", notice); + + about.addAuthor ("Jens Hoefkens", I18N_NOOP("Author & maintainer"), + "jens@hoefkens.com"); + + about.addCredit ("Bo Thorsen", I18N_NOOP("Initial anti-aliasing of the board"), + "gobo@imada.sdu.dk"); + + KCmdLineArgs::init(argc, argv, &about); + + KApplication app; + KGlobal::locale()->insertCatalogue("libkdegames"); + + if (app.isRestored()) + RESTORE(KBg) + else { + KBg* widget = new KBg; + app.setMainWidget(widget); + widget->readConfig(); + widget->show(); + } + return app.exec(); +} + diff --git a/kbackgammon/pics/Makefile.am b/kbackgammon/pics/Makefile.am new file mode 100644 index 00000000..faf4aad5 --- /dev/null +++ b/kbackgammon/pics/Makefile.am @@ -0,0 +1,6 @@ +pics_DATA = kbackgammon-double.xpm kbackgammon-chat.png + +picsdir = $(kde_datadir)/kbackgammon/pics + +EXTRA_DIST = $(pics_DATA) + diff --git a/kbackgammon/pics/kbackgammon-chat.png b/kbackgammon/pics/kbackgammon-chat.png new file mode 100644 index 00000000..65369f6d Binary files /dev/null and b/kbackgammon/pics/kbackgammon-chat.png differ diff --git a/kbackgammon/pics/kbackgammon-double.xpm b/kbackgammon/pics/kbackgammon-double.xpm new file mode 100644 index 00000000..c8ef69b0 --- /dev/null +++ b/kbackgammon/pics/kbackgammon-double.xpm @@ -0,0 +1,29 @@ +/* XPM */ +static char *kbackgammon[]={ +"22 22 4 1", +". c None", +"a c #000000", +"b c #a0a0a0", +"# c #c3c3c3", +"......................", +"......................", +"......................", +"......................", +"......................", +".............###......", +"............aaaaa.....", +"...........aab..aa....", +"...........a....aa....", +"...............aaa....", +"...aa..aa.....aaa.....", +"...aabaab....aaa......", +"....aaa.....aab.......", +"....aaa....aab........", +"...aa.aa...aa.........", +"..aa...aa..aaaaaaa....", +"...........baaaaab....", +"......................", +"......................", +"......................", +"......................", +"......................"}; diff --git a/kbackgammon/sounds/Makefile.am b/kbackgammon/sounds/Makefile.am new file mode 100644 index 00000000..ae7f255f --- /dev/null +++ b/kbackgammon/sounds/Makefile.am @@ -0,0 +1,8 @@ + +sounds_DATA = kbackgammon-lost.wav kbackgammon-won.wav kbackgammon-roll.wav kbackgammon-move.wav + +soundsdir = $(kde_datadir)/kbackgammon/sounds + +EXTRA_DIST = $(sounds_DATA) + + diff --git a/kbackgammon/sounds/kbackgammon-lost.wav b/kbackgammon/sounds/kbackgammon-lost.wav new file mode 100644 index 00000000..a27c8e15 Binary files /dev/null and b/kbackgammon/sounds/kbackgammon-lost.wav differ diff --git a/kbackgammon/sounds/kbackgammon-move.wav b/kbackgammon/sounds/kbackgammon-move.wav new file mode 100644 index 00000000..e867f675 Binary files /dev/null and b/kbackgammon/sounds/kbackgammon-move.wav differ diff --git a/kbackgammon/sounds/kbackgammon-roll.wav b/kbackgammon/sounds/kbackgammon-roll.wav new file mode 100644 index 00000000..29744c92 Binary files /dev/null and b/kbackgammon/sounds/kbackgammon-roll.wav differ diff --git a/kbackgammon/sounds/kbackgammon-won.wav b/kbackgammon/sounds/kbackgammon-won.wav new file mode 100644 index 00000000..7fd28abf Binary files /dev/null and b/kbackgammon/sounds/kbackgammon-won.wav differ diff --git a/kbackgammon/version.h b/kbackgammon/version.h new file mode 100644 index 00000000..11d5dbec --- /dev/null +++ b/kbackgammon/version.h @@ -0,0 +1,32 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + + +#ifndef __KBG_VERSION_H +#define __KBG_VERSION_H + + +#define PROG_NAME "kbackgammon" +#define PROG_VERSION "2.6.0" + +#define PROG_COOKIE 10500 // see also kdebug.areas + +#endif // __KBG_VERSION_H diff --git a/kbattleship/AUTHORS b/kbattleship/AUTHORS new file mode 100644 index 00000000..cfa61d99 --- /dev/null +++ b/kbattleship/AUTHORS @@ -0,0 +1,4 @@ +Nikolas Zimmermann +Daniel Molkentin +Kevin Krammer +Albert Astals Cid diff --git a/kbattleship/CLIENTS b/kbattleship/CLIENTS new file mode 100644 index 00000000..393dda40 --- /dev/null +++ b/kbattleship/CLIENTS @@ -0,0 +1,13 @@ +Information about other KBattleship-compatible clients: + +Client OS Status Homepage/Screenshots + +KBattleship Linux/KDE 100% http://games.kde.org/kbattleship +Mac KBattleship Mac OS + Mac OS X 100% http://www.sebek.de/ + +Wishes: +gBattleship +WinBattleship + +Nikolas Zimmermann + diff --git a/kbattleship/COPYING b/kbattleship/COPYING new file mode 100644 index 00000000..54754ab4 --- /dev/null +++ b/kbattleship/COPYING @@ -0,0 +1,341 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/kbattleship/ChangeLog b/kbattleship/ChangeLog new file mode 100644 index 00000000..39adbf93 --- /dev/null +++ b/kbattleship/ChangeLog @@ -0,0 +1 @@ +See NEWS diff --git a/kbattleship/INSTALL b/kbattleship/INSTALL new file mode 100644 index 00000000..02a4a074 --- /dev/null +++ b/kbattleship/INSTALL @@ -0,0 +1,167 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes a while. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Type `make install' to install the programs and any data files and + documentation. + + 4. You can remove the program binaries and object files from the + source code directory by typing `make clean'. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff --git a/kbattleship/Makefile.am b/kbattleship/Makefile.am new file mode 100644 index 00000000..409a1b7d --- /dev/null +++ b/kbattleship/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = kbattleship diff --git a/kbattleship/NEWS b/kbattleship/NEWS new file mode 100644 index 00000000..579e06db --- /dev/null +++ b/kbattleship/NEWS @@ -0,0 +1,45 @@ +Sun Jul 29 14:17:55 CEST 2001 + o 0.9 -> 1.0 version (WildFox) + (AI enhancements) + +Mon Apr 30 10:09:31 CEST 2001 + o 0.8 -> 0.9 version (WildFox) + (AlphaBlending added + bigger battlefield) + +Tue Apr 17 23:34:45 CEST 2001 + o 0.7 -> 0.8 version (WildFox) + (Rewrote network handling) + +Sat Apr 14 20:41:42 CEST 2001 + o 0.6 -> 0.7 version (WildFox) + (AI working + added) + +Sat Apr 14 10:56:20 CEST 2001 + o 0.5 -> 0.6 version (WildFox) + (ship-placing preview working) + +Tue Mar 26 22:57:08 CEST 2001 + o Moved into kdegames (WildFox) + (paaarty :) + +Sat Mar 24 01:57:31 CET 2001 + o 0.4 -> 0.5 version (WildFox) + (much has been changed) + +Fri Mar 16 23:55:16 CET 2001 + o Beta2 -> 0.4 version (WildFox) + (yiippie...beta state is over) + +Wed Dec 27 00:22:55 CET 2000 + o Beta -> Beta2 version (WildFox) + (only 2 really important TODO things left) + +Sun Dec 24 01:50:47 CET 2000 + o Alpha -> Beta version (WildFox) + +Sat Nov 4 18:23:27 UTC 2000 + o Checked in into KDE CVS (WildFox) + (module: kdenonbeta) + +Fri Nov 01 16:06:52 CET 2000 + o Initial creation (WildFox) diff --git a/kbattleship/README b/kbattleship/README new file mode 100644 index 00000000..e2e0ad30 --- /dev/null +++ b/kbattleship/README @@ -0,0 +1,23 @@ +KBattleship 1.0 +----------------------------------- + +KBatteship is a KDE implentation of the +popular game "Battleship". + +In the current version, you can play it +with two computers in a TCP/IP based +network and (as of version 0.7) with the +computer. + +KBattleship uses an XML based protocol, +meaning you can write a compatible application +in almost every programming or scripting +language (Would be nice to hear of a +GNOME port) + +If you have ideas or want to get involved, +simple contanct the authors (see AUTHORS). + +Have a lot of fun! + +The KBattleship Team diff --git a/kbattleship/TODO b/kbattleship/TODO new file mode 100644 index 00000000..6b14a6b4 --- /dev/null +++ b/kbattleship/TODO @@ -0,0 +1,22 @@ +KBattleship TODO List: + +p = partially +x = ready +a = assigned task +? = really necessary + +KB Team: + READY :) + +User ideas: + READY :) + +Competiton: + http://batnav.sourceforge.net/batnav-en.html (tronical) + +as discussed with danimo: +- there should be at least 2 sets of ships. Like 2 different navys +- one should be able to choose between different sets of maps: + - maps with or without land/island or whatever + - maps of differnt size (bigger maps with more ships) + diff --git a/kbattleship/VERSION b/kbattleship/VERSION new file mode 100644 index 00000000..66c8f8d2 --- /dev/null +++ b/kbattleship/VERSION @@ -0,0 +1 @@ +KBattleship 1.0 \ No newline at end of file diff --git a/kbattleship/configure.in.in b/kbattleship/configure.in.in new file mode 100644 index 00000000..35f58a6e --- /dev/null +++ b/kbattleship/configure.in.in @@ -0,0 +1,2 @@ + +AC_CHECK_HEADERS(sys/filio.h stropts.h) diff --git a/kbattleship/kbattleship/Makefile.am b/kbattleship/kbattleship/Makefile.am new file mode 100644 index 00000000..6c59ddd5 --- /dev/null +++ b/kbattleship/kbattleship/Makefile.am @@ -0,0 +1,39 @@ +SUBDIRS = dialogs . pictures sounds + +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) +METASOURCES = AUTO + +bin_PROGRAMS = kbattleship +kbattleship_SOURCES = kbaiplayer.cpp kbstrategy.cpp kbverticalstepstrategy.cpp\ + kbdestroyshipstrategy.cpp kbhorizontalstepstrategy.cpp \ + kbrandomshotstrategy.cpp kbdiagonalwrapstrategy.cpp \ + kmessage.cpp kbattleshipserver.cpp kbattleshipclient.cpp \ + kbattleshipview.cpp kgridwidget.cpp kbattlefield.cpp \ + kchatwidget.cpp kserverdialog.cpp kclientdialog.cpp \ + kstatdialog.cpp kbdiagonalshotstrategy.cpp \ + konnectionhandling.cpp kship.cpp kshiplist.cpp \ + kbchooserstrategy.cpp kbattleship.cpp main.cpp +kbattleship_LDADD = $(LIB_KDEGAMES) ./dialogs/libkbattleshipdialogs.la $(LIB_KFILE) $(LIB_KDNSSD) +kbattleship_COMPILE_FIRST = dialogs/infoDlg.h dialogs/chatDlg.h dialogs/connectDlg.h dialogs/serverDlg.h dialogs/statDlg.h +kbattleship_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kbattleship_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +services_DATA = _kbattleship._tcp +servicesdir = $(kde_datadir)/zeroconf + +xdg_apps_DATA = kbattleship.desktop + +rcdir = $(kde_datadir)/kbattleship +rc_DATA = kbattleshipui.rc eventsrc + +messages: rc.cpp + $(EXTRACTRC) */*.ui >> rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kbattleship.pot + +kbattleship.o: dialogs/infoDlg.h dialogs/chatDlg.h dialogs/connectDlg.h dialogs/serverDlg.h dialogs/statDlg.h +kbattleshipview.o: dialogs/infoDlg.h dialogs/chatDlg.h dialogs/connectDlg.h dialogs/serverDlg.h dialogs/statDlg.h +kchatwidget.o: dialogs/chatDlg.h +kclientdialog.o: dialogs/connectDlg.h +kserverdialog.o: dialogs/serverDlg.h +kstatdialog.o: dialogs/statDlg.h + diff --git a/kbattleship/kbattleship/_kbattleship._tcp b/kbattleship/kbattleship/_kbattleship._tcp new file mode 100644 index 00000000..7044f950 --- /dev/null +++ b/kbattleship/kbattleship/_kbattleship._tcp @@ -0,0 +1,4 @@ +Name=KBattleship games +Type=_kbattleship._tcp +Exec=kbattleship %u +Icon=kbattleship diff --git a/kbattleship/kbattleship/dialogs/Makefile.am b/kbattleship/kbattleship/dialogs/Makefile.am new file mode 100644 index 00000000..6f7fa1ab --- /dev/null +++ b/kbattleship/kbattleship/dialogs/Makefile.am @@ -0,0 +1,14 @@ +INCLUDES = $(all_includes) +METASOURCES = AUTO + +noinst_LTLIBRARIES = libkbattleshipdialogs.la +libkbattleshipdialogs_la_SOURCES = dummy.cpp connectDlg.ui serverDlg.ui \ + chatDlg.ui statDlg.ui infoDlg.ui +libkbattleshipdialogs_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) +libkbattleshipdialogs_la_LIBADD = $(LIB_KDEUI) + +dummy.cpp: + echo > dummy.cpp + +DISTCLEANFILES = dummy.cpp + diff --git a/kbattleship/kbattleship/dialogs/chatDlg.ui b/kbattleship/kbattleship/dialogs/chatDlg.ui new file mode 100644 index 00000000..40ffdbf3 --- /dev/null +++ b/kbattleship/kbattleship/dialogs/chatDlg.ui @@ -0,0 +1,91 @@ + +chatDlg + + + chatDlg + + + + 0 + 0 + 252 + 236 + + + + Chat Widget + + + + unnamed + + + 5 + + + 3 + + + + layouter + + + + unnamed + + + 0 + + + 6 + + + + commentEdit + + + Enter a message here + + + + + sendBtn + + + &Send + + + true + + + Press here to send the message + + + + + + + + chatView + + + WidgetWidth + + + true + + + + + chatLabel + + + Chat dialog: + + + + + + + + diff --git a/kbattleship/kbattleship/dialogs/connectDlg.ui b/kbattleship/kbattleship/dialogs/connectDlg.ui new file mode 100644 index 00000000..1a5b53de --- /dev/null +++ b/kbattleship/kbattleship/dialogs/connectDlg.ui @@ -0,0 +1,183 @@ + +clientConnectDlg + + + clientConnectDlg + + + + 0 + 0 + 332 + 148 + + + + Connect to Server + + + + + + + + unnamed + + + + nicknameLabel + + + + 5 + 0 + 0 + 0 + + + + &Nick name: + + + AlignVCenter|AlignLeft + + + nicknameEdit + + + + + + + nicknameEdit + + + 10 + + + Enter a name that identifies you in the game + + + + + serverLabel + + + + 5 + 0 + 0 + 0 + + + + &Server: + + + AlignVCenter|AlignLeft + + + serverEdit + + + + + + + lanLabel + + + LAN games: + + + lanBox + + + + + Layout4 + + + + unnamed + + + 0 + + + 6 + + + + serverEdit + + + + 3 + 5 + 0 + 0 + + + + + + portLabel + + + + 0 + 0 + 0 + 0 + + + + &Port: + + + AlignVCenter|AlignLeft + + + portEdit + + + + + + + + + portEdit + + + 65000 + + + 54321 + + + Choose a port to connect to + + + + + + + lanBox + + + + + + + + kcombobox.h + + + + kcombobox.h + klineedit.h + kcombobox.h + + diff --git a/kbattleship/kbattleship/dialogs/infoDlg.ui b/kbattleship/kbattleship/dialogs/infoDlg.ui new file mode 100644 index 00000000..3c66d854 --- /dev/null +++ b/kbattleship/kbattleship/dialogs/infoDlg.ui @@ -0,0 +1,248 @@ + +KInfoDialog + + + KInfoDialog + + + + 0 + 0 + 254 + 197 + + + + Enemy Client Information + + + + unnamed + + + 11 + + + 6 + + + + Frame8 + + + StyledPanel + + + Raised + + + + unnamed + + + 11 + + + 6 + + + + Layout6 + + + + unnamed + + + 0 + + + 6 + + + + lbl_clientIdentfierLabel + + + + 1 + + + + Client identifier: + + + + + lbl_ClientInformationLabel + + + + 1 + + + + MShape + + + MShadow + + + Client information: + + + + + lbl_clientVersion + + + + 3 + 1 + 0 + 0 + + + + CV + + + + + lbl_clientVersionLabel + + + + 1 + + + + Client version: + + + + + lbl_ProtocolVersionLabel + + + + 1 + + + + Protocol version: + + + + + lbl_ProtocolVersion + + + + 3 + 1 + 0 + 0 + + + + PV + + + + + lbl_ClientInformation + + + + 3 + 1 + 0 + 0 + + + + CI + + + + + lbl_clientIdentfier + + + + 3 + 1 + 0 + 0 + + + + CI + + + + + + + + + Layout7 + + + + unnamed + + + 0 + + + 6 + + + + Spacer13_2 + + + Horizontal + + + Expanding + + + + + PushButton7 + + + &OK + + + true + + + + + Spacer13 + + + Horizontal + + + Expanding + + + + + + + + + PushButton7 + clicked() + KInfoDialog + accept() + + + + diff --git a/kbattleship/kbattleship/dialogs/serverDlg.ui b/kbattleship/kbattleship/dialogs/serverDlg.ui new file mode 100644 index 00000000..f16216b5 --- /dev/null +++ b/kbattleship/kbattleship/dialogs/serverDlg.ui @@ -0,0 +1,132 @@ + +serverStartDlg + + + serverStartDlg + + + + 0 + 0 + 247 + 138 + + + + Start Server + + + + + + + + unnamed + + + + gamenameLabel + + + &Game name: + + + gamenameEdit + + + + + portEdit + + + 65000 + + + 54321 + + + Choose a port where the server listens on + + + + + portLabel_2 + + + + 5 + 0 + 0 + 0 + + + + &Port: + + + AlignVCenter|AlignLeft + + + portEdit + + + + + + + + + nicknameLabel + + + + 5 + 0 + 0 + 0 + + + + MShape + + + MShadow + + + &Nick name: + + + AlignVCenter|AlignLeft + + + nicknameEdit + + + + + + + gamenameEdit + + + 100 + + + Enter a name that identifies you in the game + + + + + nicknameEdit + + + 100 + + + Enter a name that identifies you in the game + + + + + + diff --git a/kbattleship/kbattleship/dialogs/statDlg.ui b/kbattleship/kbattleship/dialogs/statDlg.ui new file mode 100644 index 00000000..7ff2c202 --- /dev/null +++ b/kbattleship/kbattleship/dialogs/statDlg.ui @@ -0,0 +1,465 @@ + +statDlg + + + statDlg + + + + 0 + 0 + 144 + 338 + + + + + unnamed + + + 5 + + + 3 + + + + Spacer20_3 + + + Vertical + + + Expanding + + + + 20 + 20 + + + + + + Layout8 + + + + unnamed + + + 0 + + + 6 + + + + Spacer18_2 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + OwnLabel + + + + 19 + 1 + + + + 0 + + + + + TextLabel1 + + + + 19 + 1 + + + + : + + + AlignCenter + + + + + + + EnemyLabel + + + + 19 + 1 + + + + 0 + + + AlignVCenter|AlignRight + + + + + + + Spacer18 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + + + Spacer20 + + + Vertical + + + Expanding + + + + 20 + 20 + + + + + + gbShots + + + Box + + + Sunken + + + Shots + + + + unnamed + + + 11 + + + 6 + + + + Layout17 + + + + unnamed + + + 0 + + + 6 + + + + pShots_2 + + + + 0 + 0 + 0 + 0 + + + + image0 + + + true + + + + + Spacer9_3 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + ShotLCD + + + NoFrame + + + false + + + 3 + + + Flat + + + Shows all shots + + + + + + + + + gbHits + + + Hits + + + + unnamed + + + 11 + + + 6 + + + + Layout15 + + + + unnamed + + + 0 + + + 6 + + + + pHits_2 + + + + 0 + 0 + 0 + 0 + + + + image1 + + + true + + + + + Spacer9_2 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + HitLCD + + + NoFrame + + + 3 + + + Flat + + + Shows all hit ships + + + + + + + + + gbWater + + + Water + + + + unnamed + + + 11 + + + 6 + + + + Layout18 + + + + unnamed + + + 0 + + + 6 + + + + pWater_2 + + + + 0 + 0 + 0 + 0 + + + + image2 + + + true + + + + + Spacer9 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + WaterLCD + + + NoFrame + + + 3 + + + Flat + + + Shows all water shots + + + + + + + + + Spacer20_2 + + + Vertical + + + Expanding + + + + 20 + 20 + + + + + + + + 89504e470d0a1a0a0000000d4948445200000020000000200806000000737a7af40000030649444154789ced9531681c5710863f8535ccc209de820dbb2081b67071450a77c1b8ba2a3e3b019d9322526564bb31a9845358c685e154048e5422453029020a84dc059c7855283e55569360a7307663d840047b60831edcc10e64e1523ce94987e390a45173af5916dece7cf3cf3fb3338f771f7392e79d13cd3e0598024c01a60040f06f2eb5efb6c7c76f96554924115ae9e4bb2a6bf7d666fe0bc0ccf155dc59ef8cb55287551d254be753ecc82281608716131a08404b855360c460d562c49017396110faf2d66eff339007e8ac77c62631e848d152915382a2482000482034de6f604243efc71e5460871609052a10910970dc67d8c2a2958bb37a7bf50d180fd05e6f8fcdac61e993251f48878a558b20c8acd05cfa88ecbbefa184ece70c0055a758e34203803889e9fdd0f3eda1c2c7b3430b15acdd3d52c59b5070156c7ebbc9d35f9e32f863407a36e5dcbbe7a8d7ebf477faf47b3f615f596456687dd072df89d0b8d0209d4f8993983008b9f9e94da79c82d4c4ab129f89899398f67a7bfc0680a2fe62fe7b8e56cac6571b68a544a79de1545d558362e0ed7bdc88d76e5cc3c40680d6620b630c12887b8a38a8c081b4ef3908ef6d4126fabd902ef0e4b727d417eabe872272d4bc03d8e474e22b7f983de4ca8757e83ee892cea584b5100984642ea1ac4a4242f647fb586b7d984905c499a779b1891d5aeedcbae393fbe900e2f91802884c84d4846c3bc3c40641e83ee872a97989ce971d072c4e1d1da94b3eb2e479cecaf59599490f0482202c2f2e43004994f0fce5735e3c7bc1eeafbb2c2f2e73f9e265d2f9142ad8d9def1b28612b2f9f526f95e8e20b43e6ed1dfea3b238f14fbdaa223a5d82b28f60a563f3b9a06df02ad94b016d2ddeabaa572e8890307975af28847945549b69511d522440455259d4b290605d976e6a56d2e36b9ffcd7dcebf77de8df49f3a91f8f04c2ea2cf3b631339c378e9554917d2c9193f50ec703911b84ab552f65fef3bc396ce9c7f97f4ad006f3b1b5f6c8ca5e6608e8f1538a8c384e68c61e5eacaff5fc527714efc6f380598024c01a6007f0170086155964fc7580000000049454e44ae426082 + + + 789c4dd9577332bb9606e0fbfd2bbedaba3b35a5436ce89a9a0b8213c9c6018ca7e6425a92b04d70001c989aff3e42eb5d3aa7becdc68f5ba99752abfdcf7ffc79bc19fff9c73fffdaedcdfe85fed0b3f9fcf30f77d86c7efffb7ffeeb7ffffabb5efb13ff6bd66a7f6a7fffc75f7f87a73ff447d5ab756ad59307c965a3021fb26df2add834922bd994dc871bc8dfc9e6fc4331d2dfc16d5cef9ddca8e6fcdd6cbede12a3feaf6c2eaf80db9cdff7b239ff4ecce9cd354c4d73b2ab8839bdcbd7dba93c7b64372b458adfd2271b297f19c45cbefb81519ebf1773796e22b6e97edc96dd44bc692846fa1b31a73753b8c9eda19698d3878f6cbe9f8d98f387f76c97daafe092d3077372b326fde1155bdae367b0d4f799cdf9dfc45c3e6de112f15c64f3fd8ec4dc3edfcf4ef9cd0dec8ad45ff6498cfacec49cdea7f1d53cc59febfb654b7acbf7638a2af26fc488c7524ccd54ff1c6e72fd612b46fe5f71c9fdf30ae37ebd3eb9c8f7474db6d46fbb30fad35e65737d5e8cf4468cf65dc2258f6723e5957cff6e2de6fc6622e6f6aa05ecd09f293e8591f16e1cbb28e16636d7f70d3baecfccb2b9bd5a8cfa3762c4eb458cfe2676abcaf9555dcced33a36c2ebf23e6f8aa463697b71553b26dc34d943f15a3fc75365faf88d1de9598e31f72798e7d0f97f0f8e4563daf577b18ebc5d288313f576c99efae849b886f9f2de3c33fc125e23d80d11ff682ddaa36aa29ff39dce4f1a2e662ce6f0a18e3d7b4c4656aafaa89793d26b4af85fe3646ccf9d5319bafe7f41c4f3ab0dbe85ffacce6f985f6b69b885f1a2f2d2bf10bcf62ac1f1b9878bc869118ebf3af18f391efd74abcad65cbf8f40f62f44f939dc7570d96f1f403a33fd4af18f15cc2aec1f17f10235e3d31c7dba27d121fa3b2b9bfbfc598cfefd9293f4d608c6fda67737fdc89915fca6f72ffaa2d2cebc78398d3878518fd81fa4aec372e5d6fd7637ff07af62a463c976c89bfafc0586fc2373bc77f9dcdfbcb869dfbe30946fc4d1596f568c89678aa7d365f6fc0129f67b8e4fed22b31ae1fc5581feec5bc3e2823763ec5a30b633ed0bb98f3eb7c9df37b6297e8df7001973e95e7f9feac3c0f84c09678f82d8cf5caf561c487be6089cf588cfd9ac43c9ec28f98cb23cd9678299427f1501f625e7fd5272ce5bf66a7fcfa458c7834c43c1e75919ddaa33bec1c1f9bcdeb5536a7571b31dab3869b9cdebf89f9ba47fb4bac7f24d7713f01ed8bdd9bfa233cb08dc43bc5b36c349bcdd43fbe0e3b5e7fed0fbb25fb1fd2b7f1fcb1b4d9dcde6b18e3cfae60895f9b5dcafcd5d91cafb118eb9d83f1bca05662c42f5f47fc8762ceaf6bd9a97d7a0663bca89198f3fb39db54d92ecdd7385970bef0255c72bcec1bbbc07e45b7b03c0fb6d8323ecd153baf971d18f76f2b62dcff038c78aa4731ce0b6f623c5fa0bd6df457388a79bf27d45fcaf34280cb7a99aebf8ab9bdba22e6f1a3af6194afd11e89971a6673fe252cf737870dd6e34b76dcc0783d49cf3be6341ef9f9a1c52e106fb563b7509e5f8811af4b18cf7fdec3e88f70c56e5779bdb31f30f61fdd8425be3d18f3393831d6cf6e363fbf6dd832df6d15c6f80b62e78b94bf604bfc6805637ed8001bb48fc4886f4f8ce78506ec71dd8b313e70ffb686f39a860bf692c421a4f6d660c3a6b4df19cacfdb962dfde1de615cb777629ebfc4fd17c73ff6df4e36af2f6d31c793c438af995b31d6f70b589edfced825ca3703318f2f7b80d15e7523467caa621e1f3485e57c7493cded7b8631bf692cc6fec6e325ce078c97168cfa0dda23fda32fb3793ca17c5be3fd4fefc548bf1373f9fa072e38bf0ab081cf611fd2f85b72ff13d530ffd278b68d461bfbb566cbfeed9beca2caf39114ecf8bafa664b7fd2078cfe536f62c4a32346ffec60399f7e8ad1ff0d31f6af2a8cf9e87ad929bd437dd21f74cd36689f6d8a715e1d8ad19e1296f68fc548ff9ecdd72f61999f67ecdc3f688f2d38bdbe12737dea0e36bc1eab6731d6db0f31ea7b813dda83f8510de9bfc48877339bdf273818f3dda5f1695ddedf385eae85fe371531d6f32d5b9ee74d1d96e7438e8f2b25be8f30e2e58eb03c7fddc298cf6a26c67a3165e7fefa81d11ffe1c46bcec9718eb33da97e7574dccd7cdaf18fddf675b5f4bfde1d2f8a6661eff812deb9dabb3db553c3f8d608c5f7560cb7aa408c67aa38fb0c4e315c6fe6d2ed806e76d752fe6fdc27c89110f03e3fef4488cfd1ce55b8c073511737d7a91cde9f375debff4209bafcf618c6fd517e37d036573fa3b36a17fc20ddb798cc7b4fe9393f3968765bdf1b76c79dfe2348cf70f96dbebe4f9df217d89fdd69ec34d9e6fc6c3384fd947b6ac076691cded2f61192f2f6c5bc3fcaf8a119fba98d75f5ac3888f6e8911dfa76c3e4f59d820fdbfccd7cf600fa7f5d235653efb6b7651e5fbf37b18f7eb6aec16c697dfc178fea13376bb8af8dec3d81fed275bf64bd5661b8c6f8bfae27ce47879b695e7956731daffc8268c3f3510e3f9c967737c038cf891a437ec308531bec29cedf17ce3d3f398f3395e1dd8f1f8b09cdf174dc4eb15c6fb12ba64cbfe6567ec529e57ea6c996f760de37eed924d059e0ff6628c0707e37ef46f36c7a7643b8caf700dcbfda5fef54581f39e1bb25b309db3e5fc402fecbc3e9cb149d68f4f31d7a7fbd9a93e75c176e81fb51423fd7b764aaf0f7081ebdfb0c17ce1f6fb16c69f53b0f40f97e7e57c4f5c9fcfe795299ba47c939deab77c7f317ef5f43e529f8bd1be0b58d2f760c3fb835e8b91fe06463c96cfec80eb36ad47a190f7e36ec596f5cbdfc0f2fef2832df1359f6c295fa33c8ff686193b884b589ee7d3fa11421ecf23b8e4f7b1ee0cc6f8b6dc9ed0c2fee7d762fc3de908233ffd8831de5e6087f176c76e233f1931febe24d7b13ed12f2cf3cd89f1f7262fc6fbc43ebb94f1f22846f97358ce9b1762de2f7c80e57e2a62b4b72ac6fb81158cfb73cf62ec5713b6c1fb58ba873de2d1cbe6f69eb36d0dfe12637c2ce102fd31860dde473c89393d9118f737803de2331363bd477f12ea73056cf0f748f41779dcdf6536bf8f7c60cbfc719f7081fd6524c6fedc800de2f721c679e0578c781f60d4e7afc4188f3f628c870edbd7709ede8ad1be265ce0feac18e3e5418cf74d4a8cf984f879c3eb8fbbcfe6f3f18518e7fd6e365f1f88b15e6dc458bfd1ff7185e3f1f328c6fb1aa90feb81c77c0b35d4f790cdef4f5ec578bedec3781fe09662fc3de9518c7821de41da1fc4382f2fc4389f1dc4d89fead9dcde211c50bfcbe6f3f4b318e5edb2f93c4162bcef9a67f3f3c4b798f74fe7b3f9fdf7a598db37dd6707b656f8186d3569a7bd0e7aa99ff58b7ed52bbdd69b7f4bf3ef9fede93bf5d19b7ed71ffa53efe267af0ffa4b7feb1ffd1b4b39ea8eeeea9eeeebb378ed439fc7b417fa32faf4f355f2792a63a0877aa4c77aa2aff58d9eea5b7da7eff543fc79a6e7fa512fe2f5275dd1555dd375ddd04d5d440f754bb775a974da8b9532ca2a524e7915d4523deb837a89edf951af6aa58f6aad366aabded4bbfa509f6aa7f6eaa0bed4b7fa51bfeaa83aa98caeeaa9be2675a6ced585ba8c255ca9811a6aab466aac576aa2aed58d9aaa5b75a7eed5839aa9b97a540bf5a42aaaaaf89d415d35e2bd7faba65eab22b6a3a5dab194d2e818dd9551f12c1d8fbbaa6ee253b60966699ecd8b79352bb38edf1bb34dfbe09b9a9a77f3613ecdceeccdc17c996ff3a397e6d71c4dc7744dcff4cd99daeba6393717e6d25cc5dc03f56e866664c66692cab83637666a6ecd9db9370ff1be8f6666e6e6518dccc23c998aa99a9a9a9b7aece96e2cf5f48eae302dd336a58db76b797fb46a61e9745eb4c12eedb37db1afe6ccaeecda6eecd6bed977fb613fedceee63845fd5c81ef48b9ae88efdb2dff6c7fea6328eb663bbb667fbf62c7eceed85bdb4577660877664c77662afed8d9dda5b7b67efed839dd9798cd7ca3eea9969d8857d4a6554d4d15663b9b5f8a9db868d272edbb26d5b92563352f1bc6589c891a7404b7aa6177a55135ad13a2e541b4a31556b7aa377fa88390bfaa45dfcece34f3b3ad0177dd30ffdd2913ad4a51ef5e98ccee9822ee98a062af60c0d6994e6df9826f1b3a7ebf87d63148dd5034d632937744b77744f0f34a3393dd2829ea812cbac528dead4a0753c1316d44afd62629d13f5aa67d48e43a1a09276a7f79d7470da2967e2a31d39179f71835bba67f7e25edd4a3f9a7bb7761bb7a5616ac7944af716e35d4b751fa227b14def3476efeec37dba9ddb4ff7ee904ad9b92fba73df7109fe71bff14e0a2e23c561e28e31578c83ebd044af5cd7f55cdf9db973b77717b1043a7ddca5bb7203377423377613774d43b77537a98c669c51e3d85bb53837c66a6b9bb18c69fcdcba3b771fefc44ff7fffae71edccccddda35bb827f74385aba4b9df7155dac77562651a3449ad6ad397abb9ba6bc493cddceda67b9ae3b388ad896771d7726d577aed15c7d49b382e9a6a150f1ee4de62ff8ce309d3c587fae097319697ff6a837f7697fec5bffa955ffb0d1576e1b73cc674c7bfe9957ff71f3e96e03fd536c6a5e1777eef0ffe2bc6c0f96f174ebdeb76fec7ffc6128ebee3bbbe1763daf2fd148fafd3d857237f16579bb1ebd2752cf1dcdef90b7fe92efc55cc35f0c358dec88ffdc45ffb1b3ff5b7fe2e9e87eefd839ff13855af7eee1fe3aa77f414e7a6f10bffe42bbeea6bbeee1bbee90bdff26d5f061d94bf0b26f6498586fededdc4ffcfb85f820d71258ceb45195c88bb27b54efd1e96ae129ee3eef4e27ec2abbb8ebe0e2b77ed1fe2b5754cb3f133dad845da3bb66eeb676113b67641adf046f13b46ab1f530d63eaf7f8f9b08b106797fd4dbf1f86cf98eefd942bd6b24bfbe13e1cc257fcec7d2fec7c4fc65ff8c6671d6bf8b64fa7391a7ec26f389e72864eccd73da54f65c49f422fe63f7df7e3b54e2cb1ef4fbf49bf4fdffd78b51fcec279b80897e12ade5d2f0cc2f0f4fb54c6574c358a9faf980e6d09e358d2e97b7ffa8ea94f759ed21dc3245c879b30b54fe1f6d4f67097ca18a43abed2f780db70726a413f95d065876e8cc37d7808b3300f8f6111cb7f0a69acc73a631da7fb43abf97b80b645a756e3fe634caaf1a4530ff5e93e3442330c5219456a4327a66ec5cf21b76590ee8ebf0b6e5bfcf9f4af1de20c08e574bfd4cbb467a7bac72945d7fd22b2e353ce531fc53286a92da7b6759626d6510983a55d12cfa1a5433c3a5cf7d2fbde32a43e487d13bac912af4e90b6c4b62d97a7b62c9fa7fbbfffef3ffffa7fb85958cf + + + 789c3d98d9522b460e86efcf53504777a7a614e3dd959a0bc060035e31669b9a0bb51730c678df989a77cfef96d421247ca8b5fc92ba7d92bffe9cbd749a677ffefab5dec86632381b7cc8eaeccf703b9b1dfff3df7fffefd7ef5ce60c7f670bd9b3ecef7ffdfa1d32678333cae0af522ef29d71def8d2b86cfc9038447e331e987d9d58ed7de7b2dadbcae719b5d3dcd9ecf7c679655a189795799f38faf393f140996e94b319b31f8df3957c8cff9c7810b96a3c50e60be55cc6ec1abfec7ab8a79cea6767ab6f636cf5f3dad8ea0fb789355e30b6fef020b1da0fcaae275c39ab1e1e1bbbbe4262d533342eab9dae13abfdc3d8f487aeb2ebe76767d1f87963ef5fd6d9ece7c665657e3cf1799a273f18fb3cb789a3beeec6d8f5af945dbf8871deec7d67ebc7c2d8f4f22cb1eaf9311e987d9258ed2367ab7fa99cfaf1eeac7609c6a69fbb89b53f7563ef4727b1da73c69eef32b1da5f94f31965a6c8691f588c4dbfd48c7dbf76ce66a7c4311e358dad1f524dacf637e59ced1bb5136b3d5367ad9f5ac6b61f5475b6fd38248e76c9185b7fe822b1eaef197b7f3e136b7cd3effd09afce41cf5f19fb3c5ace6aa7983f9bf6452ac6de8f5162cdf7a29cde8799b3d5ff6cecf7efdbd9ec2563dfc7b9b3f587126bbeacb1e9a75e62ed9fc71fa81ea92be7bd9e65628d977336fd0563eb0f579dad7f2de3b29d7f30f67c711fb215d7136ace763fae8cbd7f4767d37b9358ed3fca5e3f7f2556bd0767cb3f36f67d1a385bfd569fd72f6f8987d1ee6c7ac23e71b493cea352f07d7a491ced12fb933bcff9fbfa6a6c7a65e66cf36828bbbeb04dacfd5b199b1eba74b6fedf1bfb7e1c9cadfea5b1f7b3e36cfef9c4aaef56b9e0f98fcea6b796389e0f4de382d96f126b3fe27ee5d2fce4d6d8eb3d37f6fdbd76b6fa878935de93b2d7270fcea6a79858cf4f8dbdbe4662d53b741e14e2f90be38a9dbf4eacfb5171b6f31de3a1e5ff4eacf9e3e7511ef3b2f96f8c5d6f4139f5bbe16cfbb932b6fa696fecf5349db59e3032f67a2689f57cddd9ce4f958be776be9658ebbf71b6f38fc6de2fcd2f5e5fb830b6fae4ced8ea916c62dd9f0f678b1f94bd1ef94aacf3da3a5bfefbc46a777fab8fc689d5de35f6f9c5fd2f640b05dbcf86b2e70b3fce16af6c6c7ae53db19eff74363d8bc4da0f67eb0fad9cf53ce7126bbcb9b1efd3d1d9cebf26567b4fb9e4fd89f32a88d71b06ce66ff34f6793d3b5bfdef89d5be36f67a76ce564f39b1da4bce43eddf9d72c9f76be26cfeb789d53e7436ff91b1f733be87c5acdbb9686c76ce38ab5d96c6a68f7689751e5fce966f6d6c7ac2d859ed21def762aa3fec8cbdbfe7c63edfbbc45affd6d9f23d195b3fe923b19eef3adbf94fe5b2efffccd9f4c679947229de5439d98bc6deaf92b3d95f138fa2ffc0d8ea0df1fd2b8574fec7d8f55c258efef2616c7ae8d1d9e2c5fee38f7b43cbf7ae5cf1f91612477bb8761e15238bb1d5132689357ffcefb372a8b8de7b67b3bf386b3cda185bfdd47756bbec8dadde10cf57f26ea78ab1e9e37367b57336b1fab3b2b8def83e550662fa649e58eba91b8bdabb1b26160e3ce0218f78cceffcc113fee429fef9c5337c7ff39c17bc846582af39af78cd1bde763731df8677bce7031ff9872ff892afb8cad77cc335aef32dbeeef89e1bdce4167ed386adc35d9c7ee01e3f44ff47eec3ff899f91b3c52ffcca6f9ce173ce728ef35ce02297f05319bf7be50ae2bee0dc1bef88cd7f83cafba8eb8a88845b14b844031ad288c6f44e1f34e137fa44ee6b6ed094be6846df88d2e715cdd59f167ca4254edcd08ad6b4a12ded28d09e0e74a41fe86dd305f7e91267eafc4a57885425a12a8fa3ff135dd30db886c8759aa1ca3addd21ddd53951ad4a4166a5b529bdac8dfa13d7297a94b0fd4e369f43ff57dc22fa8f791fa34e25b7aa2677aa1577aa30c9df39cb298d152274039cad3150dd1c9776a47ff211f68498553975073910aa0364fa94465aa0843fb2b1d857823c27d0950b34505797451f52ff92003be95217ed7a4aa8c507755c6f22e1ff42113f99429ede44b66f48c492fe59b6b72ea89f02afaa343b482e62f78f7a014ded496057dd2a32c29c80adf4b59d34a3694e505f5309b2ff4d0f2a3331df438275bc4cc4125b64b76b2e73b643dc8024adb3493a3fcc8858ce15fe51bb9c446d4e839eef79554e51a14d09b3636b3cf5f31f717a69fc50cdb98213443c10dcfa52675e8bcc5e97b5e46ff5bb9433fa6728fcaaad8a6be4c658f0dda4a439ad2a21769c3a3814daed1443ab8050dd94a97afa51af52fe8809343587bf07d40b706d2931d8d642d8fd297963c207f40bc23b7e409353c23ff0a3bacf39fa3b217799537c9a0febe9c43f71e9dcf626a39c11f74a881c91631a1a21cb14fd82129a19b2b91e83f96326ac3ecf95b3252917a60da7109678b98c4919e0261b773320912023ab20c03dc8a3cf2af63ff73d4912e5d85217fa303cf61848c35dc8470ea12badf44e426faf1888a3e70977a50b285da0bf5c75635b077537c8454b14b1bec721ffbbfc2366fd1f11aedc3587ab486f7845af09a8577dcc9a2d47911ebefe35e35691d3ee880fdeaf3166fd14c1ec204a74af0df05fc1bb3dc63b3dbb8b53dd83ee50691ecff4fe1a56ae34e8c30a34fd4b0a6676cef17b47e4a8f9bf2c1af612a0ff406db842e90ad717a63907f1ffd2788dae67af8a229269b0933d480b72f7ccb2eccd1c53db41c51f951821cb81d16e8de90af4fbb12fd0f889943059f88104e5b80aeb730d53195b16b3dec47239ceeffe95435ac30bd4beae2055986b5ea87822a5ebe06ee652e6c4eb700b358e06b0eef1dfcaaa77b2b6fd4099f611b7698247a110eda7fbc9c630c768f19d730f52ee2bf862366dec6bd20285923f712513be107fe1738d5c01b7d7a95a27fb80c57f0afe2b737785f6fa1af18f05606dc16fe0eb530c44f5fb0e28e40770df716d5867ab8d5fe87bb704f0bbc8105bc22d7786df0e663f7bba7379f0ea181cc43dc864e68483634d1834bbe0c2da87e228efeb7618daf76b8c21b56c31d29a136dde82ddefc1c174307113ba78d42f62a7ad8c50eddd33ce8fbbf0e0fb408f7bc878a1eb6fa0dd3bcc6e7c07778c4cf3778d1d7127f4fe8c9e9f303afd03ef4a13bfa87a7987f8d78cfdcc1fb38c73754e27daba2ceabd00a2fd0fb125e4f1cead073dddd40f53af463fe157e7a0b99b0c64b7efaf43cbde647be4435e7211bb2e8d765c885163e217a218fd8b81fe13eb45173e1f7fffffef50feb776dab + + + + diff --git a/kbattleship/kbattleship/eventsrc b/kbattleship/kbattleship/eventsrc new file mode 100644 index 00000000..c2c8fe7e --- /dev/null +++ b/kbattleship/kbattleship/eventsrc @@ -0,0 +1,475 @@ +[!Global!] +IconName=kbattleship +Comment=KBattleship +Comment[ar]=لعبة سÙينة الحرب (KBattleship) +Comment[be]=МарÑкі бой +Comment[bn]=কে-বà§à¦¯à¦¾à¦Ÿà§‡à¦²à¦¶à§€à¦ª +Comment[cs]=Souboj lodí +Comment[de]=Schiffe versenken (KBattleship) +Comment[eo]=BatalÅipo +Comment[hi]=के-बैटलशिप +Comment[hr]=KPotapanje brodova +Comment[is]=KSjóorrusta +Comment[nds]=Scheep versenken (KBattleship) +Comment[ne]=केडीई बà¥à¤¯à¤¾à¤Ÿà¤²à¤¸à¥€à¤ª +Comment[pa]=ਕੇ-ਜੰਗੀ ਜਹਾਜ਼ +Comment[ro]=Bătălie navală +Comment[sl]=KBojnaLadja +Comment[sv]=Sänka fartyg +Comment[ta]=கேபோரà¯à®•à¯à®•à®ªà¯à®ªà®²à¯ +Comment[tg]=KҶанги Киштиҳо +Comment[tr]=Amiral Battı +Comment[zh_TW]=KBattleship 戰艦 + +[shoot_water] +Name=Shot at water +Name[ar]=إصابة ÙÙŠ الماء +Name[be]=СтрÑл у ваду +Name[bg]=ПропуÑк (Ñтрелба във водата) +Name[bn]=পানিতে কামান চালান +Name[bs]=Pucanj u vodu +Name[ca]=Tir a l'aigua +Name[cs]=StÅ™ela do vody +Name[cy]=Saethu i'r dŵr +Name[da]=Skudt pÃ¥ vand +Name[de]=Schuss ins Wasser +Name[el]=ΠυÏοβολισμός στο νεÏÏŒ +Name[eo]=Pafo al akvo +Name[es]=Disparo al agua +Name[et]=Lask vette +Name[eu]=Tiroa uretara +Name[fa]=شلیک کردن در آب +Name[fi]=Ammus veteen +Name[fr]=Tirer dans l'eau +Name[gl]=Disparo á auga +Name[he]=ירייה ×‘×ž×™× +Name[hi]=पानी पर गोली चलाà¤à¤ +Name[hr]=Pucanj u vodu +Name[hu]=Mellé ment lövés +Name[is]=Skot í sjóinn +Name[it]=Colpo in acqua +Name[ja]=æ°´é¢ã‚’撃㤠+Name[km]=បាញ់​ážáŸ’រូវ​ទឹក +Name[lt]=Å Å«vis į vandenį +Name[lv]=TrÄpÄ«ts Å«denÄ« +Name[mk]=ИÑтрел во вода +Name[nb]=Skutt til sjøs +Name[nds]=Schööt in't Water +Name[ne]=पानीमा गोली +Name[nl]=Schot in het water +Name[nn]=Skot i sjøen +Name[pa]=ਪਾਣੀ ਉੱਤੇ ਗੋਲਾਬਾਰੀ +Name[pl]=PudÅ‚o +Name[pt]=Disparo na água +Name[pt_BR]=Tiro na água +Name[ro]=A tras în apă +Name[ru]=Промах +Name[se]=Bávkkáhus mearas +Name[sk]=Zásah vody +Name[sl]=Ustreli proti vodi +Name[sr]=Пуцањ у воду +Name[sr@Latn]=Pucanj u vodu +Name[sv]=Skott i vattnet +Name[ta]=நீரில௠எரி +Name[tg]=Тирпарронӣ дар Об +Name[tr]=Yara aldı +Name[uk]=ПоÑтріл у воду +Name[wa]=Côp dins l' aiwe +Name[zh_CN]=å‘水中射击 +Name[zh_TW]=æ°´é¢å°„æ“Š +Comment=Someone has shot at the water +Comment[ar]=لقد رمى أحدهم على الماء +Comment[be]=ХтоÑьці зрабіў ÑÑ‚Ñ€Ñл у ваду +Comment[bg]=ПропуÑк (Ñтрелба във водата) +Comment[bn]=কোনো à¦à¦•à¦œà¦¨ পানিতে গোলা ছà§à§œà§‡à¦›à§‡ +Comment[bs]=Neko je pucao u vodu +Comment[ca]=Algú dispara a l'aigua +Comment[cs]=NÄ›kdo vystÅ™elil do vody +Comment[cy]=Mae rhywun wedi saethu i'r dŵr +Comment[da]=Nogen har skudt pÃ¥ vandet +Comment[de]=Da hat jemand ins Wasser geschossen +Comment[el]=Κάποιος πυÏοβόλησε στο νεÏÏŒ +Comment[eo]=Iu pafis al la akvo +Comment[es]=Alguien ha disparado al agua +Comment[et]=Keegi tulistas vette +Comment[eu]=Norbaitek urari tiro egin dio +Comment[fa]=شخصی در آب شلیک کرده است +Comment[fi]=Joku on ampunut veteen +Comment[fr]=Quelqu'un a tiré dans l'eau +Comment[gl]=Alguén disparou á auga +Comment[he]=מישהו ירה ×‘×ž×™× +Comment[hi]=किसी ने पानी पर गोली चलाई +Comment[hr]=Neko je pucao u vodu +Comment[hu]=Az egyik játékos lövése nem talált +Comment[is]=Einhver skaut í vatnið +Comment[it]=Qualcuno ha sparato nell'acqua +Comment[ja]=誰ã‹ãŒæ°´é¢ã‚’æ’ƒã£ãŸ +Comment[km]=មាន​មនុស្ស​បាន​បាញ់​ážáŸ’រូវ​ទឹក +Comment[lt]=Kažkas Å¡ovÄ— į vandenį +Comment[lv]=KÄds trÄpija Å«denÄ« +Comment[mk]=Ðекој Ñтрелаше во вода +Comment[nb]=Noen har skutt pÃ¥ vannet +Comment[nds]=Een hett in't Water schaten +Comment[ne]=केसैले पानीमा गोली हानेको छ +Comment[nl]=Iemand heeft in het water geschoten +Comment[nn]=Nokon har skote pÃ¥ vatnet +Comment[pa]=ਕਿਸੇ ਨੇ ਪਾਣੀ ਉੱਤੇ ਗੋਲਾਬਾਰੀ ਕੀਤੀ +Comment[pl]=KtoÅ› spudÅ‚owaÅ‚ +Comment[pt]=Alguém disparou para a água +Comment[pt_BR]=Alguém atirou na água +Comment[ro]=Cineva a tras în apă +Comment[ru]=Кто-то промазал +Comment[se]=Giinu lea bávkkihan bombba +Comment[sk]=Niekto trafil vodu +Comment[sl]=Nekdo je ustrelil v vodo +Comment[sr]=Ðеко је пуцао у воду +Comment[sr@Latn]=Neko je pucao u vodu +Comment[sv]=NÃ¥gon har skjutit i vattnet +Comment[ta]=யாரோ தணà¯à®£à¯€à®°à¯ˆ நோகà¯à®•à®¿ சà¯à®Ÿà¯à®Ÿà¯à®µà®¿à®Ÿà¯à®Ÿà®¾à®°à¯ +Comment[tg]=КаÑе дар об тир паронд +Comment[tr]=Birileri biri suya ateÅŸ etti +Comment[uk]=ХтоÑÑŒ попав у воду +Comment[zh_CN]=有人å‘水中射击 +Comment[zh_TW]=æŸäººå·²é€²è¡Œæ°´é¢å°„æ“Š +default_sound=ship-player-shoot-water.ogg +default_presentation=1 + +[shoot_hit_1] +Name=Player 1 shot +Name[ar]=رمية اللاعب 1 +Name[be]=СтрÑл першага гульнёўцы +Name[bg]=ИзÑтрел на играч 1 +Name[bn]=পà§à¦°à¦¥à¦® খেলোয়াড় গোলা ছà§à§œà§‡à¦›à§‡ +Name[bs]=Pucanj igraÄa 1 +Name[ca]=Tir del jugador 1 +Name[cs]=HrÃ¡Ä 1 vystÅ™elil +Name[cy]=Saethu Chwaraewr 1 +Name[da]=1. spillers skud +Name[de]=Schuss von Spieler 1 +Name[el]=Παίκτης 1 πυÏοβόλησε +Name[eo]=Ludanto 1 pafis +Name[es]=Disparo del jugador 1 +Name[et]=Mängija 1 lask +Name[eu]=1. jokalariak tiro egin du +Name[fa]=شلیک بازیکن Û± +Name[fi]=Pelaaja 1 ampui +Name[fr]=Le joueur 1 a tiré +Name[gl]=Disparo do xogador 1 +Name[he]=יריית שחקן 1 +Name[hi]=खिलाड़ी 1 ने गोली चलाई +Name[hr]=Puca igraÄ 1 +Name[hu]=Az 1. játékos egyik hajója találatot kapott +Name[is]=Leikmaður 1 skaut +Name[it]=Spara il giocatore n. 1 +Name[ja]=プレイヤー 1 発射 +Name[km]=អ្នក​លáŸáž„ ១ បាន​បាញ់ +Name[lt]=I ŽaidÄ—jo Å¡Å«vis +Name[lv]=PirmÄ spÄ“lÄ“tÄja Å¡Äviens +Name[mk]=ИÑтрел на играчот 1 +Name[nb]=Spiller 1 skutt +Name[nds]=Schööt vun Speler 1 +Name[ne]=खेलाडी १ गोली +Name[nl]=Schot van speler 1 +Name[nn]=Spelar 1 skote +Name[pa]=ਖਿਡਾਰੀ 1 ਗੋਲੀ ਚਲਾਈ +Name[pl]=Strzela Gracz 1 +Name[pt]=Jogador 1 disparou +Name[pt_BR]=Jogador 1 atira +Name[ro]=Jucătorul 1 a tras +Name[ru]=Ход первого игрока +Name[se]=Speallár 1 lea bávkkihan bombba +Name[sk]=HrÃ¡Ä 1 vystrelil +Name[sl]=Strel igralca 1 +Name[sr]=Пуца играч 1 +Name[sr@Latn]=Puca igraÄ 1 +Name[sv]=Skott frÃ¥n första spelaren +Name[ta]=விளையாடà¯à®ªà®µà®°à¯ 1 எரிதல௠+Name[tg]=Тирпарронии бозингари 1 +Name[tr]=Oyunu 1 atış +Name[uk]=ПоÑтріл першого Ð³Ñ€Ð°Ð²Ñ†Ñ +Name[zh_CN]=Player 1 射击 +Name[zh_TW]=玩家 1 å°„æ“Š +Comment=Player 1 takes a shot +Comment[ar]=اللاعب 1 ÙŠ رم +Comment[be]=Першы гульнёўца ÑтралÑе +Comment[bg]=ИзÑтрел на играч 1 +Comment[bn]=পà§à¦°à¦¥à¦® খেলোয়াড় গোলা ছà§à§œà¦›à§‡ +Comment[bs]=IgraÄ 1 je na potezu +Comment[ca]=El jugador 1 dispara +Comment[cs]=HrÃ¡Ä 1 vystÅ™elil +Comment[cy]=Chwaraewr 1 yn saethu +Comment[da]=Spiller 1 skyder +Comment[de]=Spieler 1 schießt +Comment[el]=Ο παίκτης 1 πυÏοβολεί +Comment[eo]=Ludanto 1 pafas +Comment[es]=El jugador 1 hace un disparo +Comment[et]=Mängija 1 tulistas +Comment[eu]=1. jokalariak tiro egin du +Comment[fa]=بازیکن ۱، یک گلوله می‌خورد +Comment[fi]=Pelaaja 1 ampuu +Comment[fr]=Le joueur 1 a subi un tir +Comment[gl]=O xogador 1 fai un disparo +Comment[he]=שחקן 1 יורה +Comment[hi]=खिलाड़ी 1 ने गोली चलाना लिया +Comment[hr]=IgraÄ 1 upravo puca +Comment[hu]=Az 1. játékos lÅ‘ +Comment[is]=Leikmaður 1 skýtur +Comment[it]=Il giocatore 1 prende un colpo +Comment[ja]=プレイヤー 1 ãŒç™ºå°„ +Comment[km]=អ្នក​លáŸáž„​១ បាន​បាញ់ +Comment[lt]=I ŽaidÄ—jas Å¡auna +Comment[lv]=Å auj pirmais spÄ“lÄ“tÄjs +Comment[mk]=Играчот 1 иÑтрела +Comment[nb]=Spiller 1 er truffet +Comment[nds]=Speler 1 schütt +Comment[ne]=खेलाडी १ ले गोली लियो +Comment[nl]=Speler 1 schiet raak +Comment[nn]=Spelar 1 er truffen +Comment[pl]=Gracz 1 zostaÅ‚ trafiony +Comment[pt]=Jogador 1 dispara +Comment[pt_BR]=Jogador 1 leva um tiro +Comment[ro]=Jucătorul 1 a primit o lovitură +Comment[ru]=Ход первого игрока +Comment[se]=Speallár 1 lea deaivvahallan +Comment[sk]=HrÃ¡Ä 1 zasiahnutý +Comment[sl]=Igralec 1 je dobil strel +Comment[sr]=Играч 1 управо пуца +Comment[sr@Latn]=IgraÄ 1 upravo puca +Comment[sv]=Första spelaren skjuter +Comment[ta]=விளையாடà¯à®Ÿà®¾à®³à®°à¯ 1 விளையாட தà¯à®µà®™à¯à®•à¯à®•à®¿à®±à®¾à®°à¯ +Comment[tg]=Бозингари 1 тир паронд +Comment[tr]=Oyuncu 1 atış attı +Comment[uk]=Перший гравець ÑтрілÑÑ” +Comment[zh_CN]=Player 1 射击 +Comment[zh_TW]=玩家 1 進行了一次射擊 +default_sound=ship-player1-shoot.ogg +default_presentation=1 + +[shoot_hit_2] +Name=Player 2 shot +Name[ar]=رمية اللاعب 2 +Name[be]=СтрÑл другога гульнёўцы +Name[bg]=ИзÑтрел на играч 2 +Name[bn]=দà§à¦¬à¦¿à¦¤à§€à§Ÿ খেলোয়াড় গোলা ছà§à§œà§‡à¦›à§‡ +Name[bs]=Pucanj igraÄa 2 +Name[ca]=Tir del jugador 2 +Name[cs]=HrÃ¡Ä 2 vystÅ™elil +Name[cy]=Saethu Chwaraewr 2 +Name[da]=2. spillers skud +Name[de]=Schuss von Spieler 2 +Name[el]=Παίκτης 2 πυÏοβόλησε +Name[eo]=Ludanto 2 pafis +Name[es]=Disparo del jugador 2 +Name[et]=Mängija 2 lask +Name[eu]=2. jokalariak tiro egin du +Name[fa]=شلیک بازیکن Û² +Name[fi]=Pelaaja 2 ampui +Name[fr]=Le joueur 2 a tiré +Name[gl]=Disparo do xogador 2 +Name[he]=יריית שחקן 2 +Name[hi]=खिलाड़ी 2 ने गोली चलाई +Name[hr]=Puca igraÄ 2 +Name[hu]=A 2. játékos egyik hajója találatot kapott +Name[is]=Leikmaður 2 skaut +Name[it]=Spara il giocatore n. 2 +Name[ja]=プレイヤー 2 発射 +Name[km]=អ្នក​លáŸáž„ ២ បាន​បាញ់ +Name[lt]=II ŽaidÄ—jo Å¡Å«vis +Name[lv]=OtrÄ spÄ“lÄ“tÄja Å¡Äviens +Name[mk]=ИÑтрел на играчот 2 +Name[nb]=Spiller 2 skutt +Name[nds]=Schööt vun Speler 2 +Name[ne]=खेलाडी २ गोली +Name[nl]=Schot van speler 2 +Name[nn]=Spelar 2 skote +Name[pa]=ਖਿਡਾਰੀ 2 ਨੇ ਗੋਲੀ ਚਲਾਈ +Name[pl]=Strzela Gracz 2 +Name[pt]=Jogador 2 disparou +Name[pt_BR]=Jogador 2 atira +Name[ro]=Jucătorul 2 a tras +Name[ru]=Ход второго игрока +Name[se]=Speallár 2 lea deaivvahallan +Name[sk]=HrÃ¡Ä 2 vystrelil +Name[sl]=Strel igralca 2 +Name[sr]=Пуца играч 2 +Name[sr@Latn]=Puca igraÄ 2 +Name[sv]=Skott frÃ¥n andra spelaren +Name[ta]=விளையாடà¯à®ªà®µà®°à¯ 2 எரிதல௠+Name[tg]=Тирпарронии бозингари 2 +Name[tr]=Oyuncu 2 atış +Name[uk]=ПоÑтріл другого Ð³Ñ€Ð°Ð²Ñ†Ñ +Name[zh_CN]=Player 2 射击 +Name[zh_TW]=玩家 2 å°„æ“Š +Comment=Player 2 takes a shot +Comment[ar]=اللاعب 2 يرمى +Comment[be]=Другі гульнёўца ÑтарлÑе +Comment[bg]=ИзÑтрел на играч 2 +Comment[bn]=দà§à¦¬à¦¿à¦¤à§€à§Ÿ খেলোয়াড় গোলা ছà§à§œà¦›à§‡ +Comment[bs]=IgraÄ 2 je na potezu +Comment[ca]=El jugador 2 dispara +Comment[cs]=HrÃ¡Ä 2 vystÅ™elil +Comment[cy]=Chwaraewr 2 yn saethu +Comment[da]=Spiller 2 skyder +Comment[de]=Spieler 2 schießt +Comment[el]=Ο παίκτης 2 πυÏοβολεί +Comment[eo]=Ludanto 2 pafas +Comment[es]=El jugador 2 hace un disparo +Comment[et]=Mängija 2 tulistas +Comment[eu]=2. jokalariak tiro egin du +Comment[fa]=بازیکن ۲، یک گلوله می‌خورد +Comment[fi]=Pelaaja 2 ampuu +Comment[fr]=Le joueur 2 a subi un tir +Comment[gl]=O xogador 2 fai un disparo +Comment[he]=שחקן 2 יורה +Comment[hi]=खिलाड़ी 2 ने गोली चलाना लिया +Comment[hr]=IgraÄ 2 upravo puca +Comment[hu]=A 2. játékos lÅ‘ +Comment[is]=Leikmaður 2 skýtur +Comment[it]=Il giocatore 2 prende un colpo +Comment[ja]=プレイヤー 2 ãŒç™ºå°„ +Comment[km]=អ្នក​លáŸáž„ ២ បាន​បាញ់ +Comment[lt]=II ŽaidÄ—jas Å¡auna +Comment[lv]=Å auj otrais spÄ“lÄ“tÄjs +Comment[mk]=Играчот 2 иÑтрела +Comment[nb]=Spiller 2 er truffet +Comment[nds]=Speler 2 schütt +Comment[ne]=खेलाडी २ ले गोली लियो +Comment[nl]=Speler 2 schiet raak +Comment[nn]=Spelar 2 er truffen +Comment[pl]=Gracz 2 zostaÅ‚ trafiony +Comment[pt]=Jogador 2 dispara +Comment[pt_BR]=Jogador 2 leva um tiro +Comment[ro]=Jucătorul 2 a primit o lovitură +Comment[ru]=Ход второго игрока +Comment[se]=Speallár 2 lea deaivvahallan +Comment[sk]=HrÃ¡Ä 2 zasiahnutý +Comment[sl]=Igralec 2 je dobil strel +Comment[sr]=Играч 2 управо пуца +Comment[sr@Latn]=IgraÄ 2 upravo puca +Comment[sv]=Andra spelaren skjuter +Comment[ta]=விளையாடà¯à®Ÿà®¾à®³à®°à¯ 2 விளையாட தà¯à®µà®™à¯à®•à¯à®•à®¿à®±à®¾à®°à¯ +Comment[tg]=Бозингари 2 тир паронд +Comment[tr]=Oyuncu 2 atış attı +Comment[uk]=Другий гравець ÑтрілÑÑ” +Comment[zh_CN]=Player 2 射击 +Comment[zh_TW]=玩家 2 進行了一次射擊 +default_sound=ship-player2-shoot.ogg +default_presentation=1 + +[shoot_sink] +Name=Sunk ship +Name[ar]=سÙينة مغرقة +Name[be]=Карабель пайшоў на дно +Name[bg]=Потопен е кораб +Name[bn]=ডà§à¦¬à§‡ যাওয়া জাহাজ +Name[br]=Lestr kaset d'ar strad +Name[bs]=Potopljen brod +Name[ca]=Vaixell enfonsat +Name[cs]=LoÄ potopena +Name[cy]=Suddo llong +Name[da]=Sunket skib +Name[de]=Schiff versenkt +Name[el]=Βουλιαγμένο πλοίο +Name[eo]=Sinkita Åipo +Name[es]=Barco hundido +Name[et]=Laev uputatud +Name[eu]=Urperatutako ontzia +Name[fa]=کشتی غرق‌‌‌شده +Name[fi]=Laiva upposi +Name[fr]=Bateau coulé +Name[ga]=Long curtha chun tóin farraige +Name[gl]=Barco afundido +Name[he]=ספינה טבועה +Name[hi]=जहाज डà¥à¤¬à¤¾à¤à¤ +Name[hr]=Brod je potopljen +Name[hu]=Elsüllyedt egy hajó +Name[is]=Sökkti skipi +Name[it]=Nave affondata +Name[ja]=沈んã èˆ¹ +Name[km]=នាវា​លិច +Name[lt]=Nuskendes laivas +Name[lv]=Nogrimis kuÄ£is +Name[mk]=Потопи брод +Name[nb]=Senket skip +Name[nds]=Schipp afbuddelt +Name[ne]=पानीजहाज डà¥à¤¬à¥à¤¯à¥‹ +Name[nl]=Gezonken schip +Name[nn]=Senka skip +Name[pa]=ਡà©à©±à¨¬à¨¦à¨¾ ਜਹਾਜ਼ +Name[pl]=Zatopiony okrÄ™t +Name[pt]=Navio afundado +Name[pt_BR]=Navio afundado +Name[ro]=Nava se scunfundă +Name[ru]=Корабль потоплен +Name[se]=Skiipa vuojui +Name[sk]=Potopenie lode +Name[sl]=Potopljena ladja +Name[sr]=Брод је потопљен +Name[sr@Latn]=Brod je potopljen +Name[sv]=Sänkt skepp +Name[ta]=மூழà¯à®•à¯à®®à¯ கபà¯à®ªà®²à¯ +Name[tg]=Киштии ғарқшуда +Name[tr]=Batık gemi +Name[uk]=Потопив корабель +Name[wa]=Batea coulé +Name[zh_CN]=击沉军舰 +Name[zh_TW]= 沉船 +Comment=A ship has been sunk +Comment[ar]=لقد أغرقت سÙينة +Comment[be]=Карабель пайшоў на дно +Comment[bg]=Потопен е кораб +Comment[bn]=à¦à¦•à¦Ÿà¦¿ জাহাজ ডà§à¦¬à§‡ গিয়েছে +Comment[br]=Kaset d'ar strad eo ul lestr +Comment[bs]=Brod je potopljen +Comment[ca]=S'ha enfonsat un vaixell +Comment[cs]=LoÄ byla potopena +Comment[cy]=Mae llong wedi ei suddo +Comment[da]=Et skib er blevet sænket +Comment[de]=Ein Schiff wurde versenkt +Comment[el]=Ένα πλοίο βυθίστηκε +Comment[es]=Se ha hundido un barco +Comment[et]=Laev uputati +Comment[eu]=Ontzi bat urperatu dute +Comment[fa]=یک کشتی غرق شده است +Comment[fi]=Laiva on upotettu +Comment[fr]=Un bateau a été coulé +Comment[gl]=Afundiuse un barco +Comment[he]=ספינה הוטבעה +Comment[hi]=à¤à¤• जहाज डूबा +Comment[hr]=Brod je upravo potopljen +Comment[hu]=Elsüllyedt egy hajó +Comment[is]=Skipi hefur verið sökkt +Comment[it]=Una nave è stata affondata +Comment[ja]=船ã¯æ²ˆæ²¡ã—ã¾ã—㟠+Comment[km]=នាវា​ážáŸ’រូវ​បាន​លិច +Comment[lt]=Laivas nuskandintas +Comment[lv]=Kugis ir nogrimis +Comment[mk]=Потопен е брод +Comment[nb]=Et skip er senket +Comment[nds]=En Schipp is afbuddelt +Comment[ne]=पानीजहाज डà¥à¤¬à¥‡à¤•à¥‹ छ +Comment[nl]=Er is een schip gezonken +Comment[nn]=Eit skip er senka +Comment[pa]=ਇੱਕ ਜਹਾਜ਼ ਡà©à©±à¨¬ ਗਿਆ +Comment[pl]=OkrÄ™t zostaÅ‚ zatopiony +Comment[pt]=Foi afundado um navio +Comment[pt_BR]=Um navio acabou de afundar +Comment[ro]=O navă s-a scufundat +Comment[ru]=Корабль потоплен +Comment[se]=Skiipa vuojui +Comment[sk]=Potopenie lode +Comment[sl]=Ladja je bila potopljena +Comment[sr]=Брод је управо потопљен +Comment[sr@Latn]=Brod je upravo potopljen +Comment[sv]=Ett skepp har sänkts +Comment[ta]=கபà¯à®ªà®²à¯ மூழà¯à®•à®¿à®µà®¿à®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=Киштӣ ғарқ гардид +Comment[tr]=Bir gemi batırıldı +Comment[uk]=Корабель було потоплено +Comment[uz]=Kema choÊ»ktirildi +Comment[uz@cyrillic]=Кема чўктирилди +Comment[wa]=On batea a stî coulé +Comment[zh_CN]=击沉了一艘军舰 +Comment[zh_TW]= 有一艘傳沉沒了 +default_sound=ship-sink.ogg +default_presentation=1 diff --git a/kbattleship/kbattleship/kbaiplayer.cpp b/kbattleship/kbattleship/kbaiplayer.cpp new file mode 100644 index 00000000..ca95c2da --- /dev/null +++ b/kbattleship/kbattleship/kbaiplayer.cpp @@ -0,0 +1,107 @@ +/*************************************************************************** + kbaiplayer.cpp + ---------- + Developers: (c) 2001 Kevin Krammer + (c) 2001 Nikolas Zimmermann + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include + +#include "kbchooserstrategy.h" + +#include "kbaiplayer.moc" + +#define MAX_SHIP_LEN 4 + +KBAIPlayer::KBAIPlayer() +{ + m_ownShipList = 0; + m_battleField = 0; + m_masterStrategy = 0; + m_randomSeq = new KRandomSequence(KApplication::random()); +} + +KBAIPlayer::~KBAIPlayer() +{ + delete m_masterStrategy; + delete m_randomSeq; +} + +void KBAIPlayer::init(KBattleField *battle_field, KShipList *ai_shiplist) +{ + m_battleField = battle_field; + m_ownShipList = ai_shiplist; + + if(m_battleField != 0) + { + QRect rect = m_battleField->enemyRect(); + int grid = m_battleField->gridSize(); + m_fieldRect = QRect(0, 0, (rect.width() / grid), (rect.height() / grid)); + } +} + +void KBAIPlayer::slotRestart() +{ + if(m_randomSeq == 0 || m_ownShipList == 0 || m_battleField == 0) + return; + + addShips(); + chooseStrategy(); + emit sigReady(); +} + +void KBAIPlayer::addShips() +{ + m_ownShipList->clear(); + + for(int shiplen = MAX_SHIP_LEN; shiplen >= 1; shiplen--) + { + int x, y; + bool vertical; + + do + { + x = (int) m_randomSeq->getLong(m_fieldRect.width()); + y = (int) m_randomSeq->getLong(m_fieldRect.height()); + vertical = m_randomSeq->getBool(); + } + while(!shipPlaced(shiplen, x, y, vertical)); + } +} + +void KBAIPlayer::chooseStrategy() +{ + delete m_masterStrategy; + + m_masterStrategy = new KBChooserStrategy(); + m_masterStrategy->init(m_battleField, m_fieldRect); +} + +bool KBAIPlayer::slotRequestShot() +{ + if(m_masterStrategy != 0 && m_masterStrategy->hasMoreShots()) + { + QPoint pos = m_masterStrategy->nextShot(); + emit sigShootAt(pos); + m_masterStrategy->shotAt(pos); + return true; + } + return false; +} + +bool KBAIPlayer::shipPlaced(int shiplen, int x, int y, bool vertical) +{ + QRect ship = vertical ? QRect(x, y, 1, shiplen) : QRect(x, y, shiplen, 1); + return m_ownShipList->addNewShip(vertical, ship.x(), ship.y()); +} diff --git a/kbattleship/kbattleship/kbaiplayer.h b/kbattleship/kbattleship/kbaiplayer.h new file mode 100644 index 00000000..aebb149d --- /dev/null +++ b/kbattleship/kbattleship/kbaiplayer.h @@ -0,0 +1,59 @@ +/*************************************************************************** + kbaiplayer.h + ---------- + Developers: (c) 2001 Kevin Krammer + (c) 2001 Nikolas Zimmermann + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBAIPLAYER_H +#define KBAIPLAYER_H + +#include + +#include + +#include "kbstrategy.h" +#include "kbattlefield.h" +#include "kshiplist.h" + +class KBAIPlayer : public QObject +{ + Q_OBJECT +public: + KBAIPlayer(); + ~KBAIPlayer(); + + void init(KBattleField *battle_field, KShipList *ai_shiplist); + +public slots: + void slotRestart(); + bool slotRequestShot(); + bool shipPlaced(int shiplen, int x, int y, bool vertical); + +signals: + void sigShootAt(const QPoint pos); + void sigReady(); + +private: + void chooseStrategy(); + void addShips(); + + KBStrategy *m_masterStrategy; + KShipList *m_ownShipList; + KBattleField *m_battleField; + KRandomSequence *m_randomSeq; + + QRect m_fieldRect; +}; + +#endif diff --git a/kbattleship/kbattleship/kbattlefield.cpp b/kbattleship/kbattleship/kbattlefield.cpp new file mode 100644 index 00000000..0467ae28 --- /dev/null +++ b/kbattleship/kbattleship/kbattlefield.cpp @@ -0,0 +1,233 @@ +/*************************************************************************** + kbattlefield.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kbattleship.h" +#include "kship.h" + +#include "kbattlefield.h" + +KBattleField::KBattleField(QWidget *parent, bool grid) : KGridWidget(parent, grid) +{ + m_parent = parent; + m_width = parent->width(); + m_canDraw = true; + + m_ownfieldx = 10; + m_ownfieldy = 10; + m_enemyfieldx = 10; + m_enemyfieldy = 10; + + clearOwnField(); + clearEnemyField(); + clearPreviewField(); + drawField(); +} + +void KBattleField::clearOwnField() +{ + for(int i = 0; i != m_ownfieldx; i++) + { + for(int j = 0; j != m_ownfieldy; j++) + { + m_ownfield[i][j] = KBattleField::FREE; + } + } +} + +void KBattleField::clearEnemyField() +{ + for(int i = 0; i != m_enemyfieldx; i++) + { + for(int j = 0; j != m_enemyfieldy; j++) + { + m_enemyfield[i][j] = KBattleField::FREE; + } + } +} + +void KBattleField::clearPreviewField() +{ + for(int i = 0; i != m_ownfieldx; i++) + { + for(int j = 0; j != m_ownfieldy; j++) + { + m_newfield[i][j] = KBattleField::FREE; + m_newdata[i][j] = false; + } + } +} + +void KBattleField::setPreviewState(int fieldx, int fieldy, int type, bool rotate) +{ + m_newfield[fieldx][fieldy] = type; + m_newdata[fieldx][fieldy] = true; + m_rotatedata[fieldx][fieldy] = rotate; +} + +void KBattleField::drawField() +{ + drawOwnField(); + drawEnemyField(); + clearPreviewField(); + finished(); +} + +void KBattleField::drawOwnField() +{ + if(!m_canDraw) + return; + + KBattleshipWindow *window = static_cast(m_parent->parent()->parent()); + KShip *ship = 0; + int data; + + for(int i = 0; i != m_ownfieldx; i++) + { + for(int j = 0; j != m_ownfieldy; j++) + { + setValues(((i * gridSize()) + ownXPosition()), ((j * gridSize()) + ownYPosition()), gridSize()); + if(!m_newdata[i][j]) + data = m_ownfield[i][j]; + else + data = m_newfield[i][j]; + switch(data) + { + case KBattleField::FREE: + drawSquare(); + break; + + case KBattleField::WATER: + drawSquare(); + drawWaterIcon(); + break; + + case KBattleField::HIT: + drawSquare(); + ship = window->shipAt(i, j); + if(ship->placedLeft()) + drawShipIcon((ship->shiptype() + 1), (ship->shipxstop() - i + 1), true, true); + else + drawShipIcon((ship->shiptype() + 1), (j - ship->shipystart() + 1), false, true); + break; + + case KBattleField::DEATH: + drawSquare(); + drawDeathIcon(); + break; + + default: + ship = window->shipAt(i, j); + if(ship) + { + drawSquare(); + if(m_newdata[i][j]) + data = m_ownfield[i][j]; + drawShipIcon(data, ship->placedLeft()); + } + else if(!ship) + drawShipIcon(data, !m_rotatedata[i][j], false, true); + break; + } + } + } +} + +void KBattleField::drawEnemyField() +{ + if(!m_canDraw) + return; + + KBattleshipWindow *window = static_cast(m_parent->parent()->parent()); + + for(int i = 0; i != m_enemyfieldx; i++) + { + for(int j = 0; j != m_enemyfieldy; j++) + { + setValues(((i * gridSize()) + enemyXPosition()), ((j * gridSize()) + enemyYPosition()), gridSize()); + switch(m_enemyfield[i][j]) + { + case KBattleField::FREE: + drawSquare(); + break; + + case KBattleField::WATER: + drawSquare(); + drawWaterIcon(); + break; + + case KBattleField::HIT: + drawSquare(); + drawHitIcon(); + break; + + case KBattleField::BORDER: + drawSquare(); + drawDeathBorder(); + break; + + case KBattleField::DEATH: + drawSquare(); + drawDeathIcon(); + break; + + default: + drawSquare(); + KShip *ship = window->enemyShipAt(i, j); + if(ship->placedLeft()) + drawShipIcon(m_enemyfield[i][j], true); + else + drawShipIcon(m_enemyfield[i][j]); + break; + } + } + } +} + +int KBattleField::ownXPosition() +{ + return 10; +} + +int KBattleField::ownYPosition() +{ + return 10; +} + +int KBattleField::enemyXPosition() +{ + return (m_width / 2) + 10; +} + +int KBattleField::enemyYPosition() +{ + return 10; +} + +int KBattleField::rectX() +{ + return 10; +} + +QRect KBattleField::ownRect() +{ + return QRect(ownXPosition(), ownYPosition(), m_ownfieldx * gridSize(), m_ownfieldy * gridSize()); +} + +QRect KBattleField::enemyRect() +{ + return QRect(enemyXPosition(), enemyYPosition(), m_enemyfieldx * gridSize(), m_enemyfieldy * gridSize()); +} diff --git a/kbattleship/kbattleship/kbattlefield.h b/kbattleship/kbattleship/kbattlefield.h new file mode 100644 index 00000000..d1e62b3f --- /dev/null +++ b/kbattleship/kbattleship/kbattlefield.h @@ -0,0 +1,82 @@ +/*************************************************************************** + kbattlefield.h + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBATTLEFIELD_H +#define KBATTLEFIELD_H + +#include +#include + +#include "kgridwidget.h" + +class KBattleField : public KGridWidget +{ +public: + enum{FREE, WATER, HIT, DEATH, BORDER, SHIP1P1, SHIP2P1, SHIP2P2, SHIP3P1, SHIP3P2, SHIP3P3, SHIP4P1, SHIP4P2, SHIP4P3, SHIP4P4}; + KBattleField(QWidget *parent, bool grid); + + void clearOwnField(); + void clearEnemyField(); + void clearPreviewField(); + + void drawField(); + void setDrawField(bool draw) { m_canDraw = draw; } + + void setOwnState(int fieldx, int fieldy, int type) { m_ownfield[fieldx][fieldy] = type; } + int ownState(int fieldx, int fieldy) { return m_ownfield[fieldx][fieldy]; } + + void setEnemyState(int fieldx, int fieldy, int type) { m_enemyfield[fieldx][fieldy] = type; } + int enemyState(int fieldx, int fieldy) { return m_enemyfield[fieldx][fieldy]; } + + void setPreviewState(int fieldx, int fieldy, int type, bool rotate); + + QRect ownRect(); + QRect enemyRect(); + + int gridSize() { return 32; } + +private: + void drawOwnField(); + void drawEnemyField(); + + int ownXPosition(); + int ownYPosition(); + int enemyXPosition(); + int enemyYPosition(); + + int rectX(); + + int m_ownfield[15][15]; + int m_enemyfield[15][15]; + + int m_newfield[15][15]; + bool m_newdata[15][15]; + bool m_rotatedata[15][15]; + + int m_ownfieldx; + int m_ownfieldy; + int m_enemyfieldx; + int m_enemyfieldy; + + int m_width; + + bool m_canDraw; + + QWidget *m_parent; +}; + +#endif diff --git a/kbattleship/kbattleship/kbattleship.cpp b/kbattleship/kbattleship/kbattleship.cpp new file mode 100644 index 00000000..b7d38891 --- /dev/null +++ b/kbattleship/kbattleship/kbattleship.cpp @@ -0,0 +1,1352 @@ +/*************************************************************************** + kbattleship.cpp + ----------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#include "kbattleship.moc" + +extern const char *clientVersion; + +KBattleshipWindow::KBattleshipWindow() : KMainWindow() +{ + shift = false; + m_connection = 0; + m_lost = 0; + m_config = 0; + m_client = 0; + m_server = 0; + m_aiPlayer = 0; + m_aiHits = 0; + + init(); +} + +KBattleshipWindow::~KBattleshipWindow() +{ + if(m_config != 0) + saveOptions(); + delete m_aiPlayer; + delete m_ownshiplist; + delete m_enemyshiplist; +} + +void KBattleshipWindow::init() +{ + m_aiPlaying = false; + m_placeable = false; + m_shootable = false; + m_serverHasClient = false; + m_config = kapp->config(); + initStatusBar(); + initActions(); + readOptions(); + initView(); + initChat(); + initShipPlacing(); + parseCommandLine(); +} + +void KBattleshipWindow::slotConfigureNotifications() +{ + KNotifyDialog::configure(this); +} + +void KBattleshipWindow::initStatusBar() +{ + m_ownNickname = "-"; + m_enemyNickname = "-"; + statusBar()->insertItem(i18n(" Player 1: %1 ").arg(m_ownNickname), ID_PLAYER_OWN, 0, true); + statusBar()->insertItem(i18n(" Player 2: %1 ").arg(m_enemyNickname), ID_PLAYER_ENEMY, 0, true); + statusBar()->insertItem(i18n("Ready"), ID_STATUS_MSG, 1); + statusBar()->setItemAlignment(ID_STATUS_MSG, AlignLeft); +} + +void KBattleshipWindow::initActions() +{ + KStdAction::configureNotifications(this, SLOT(slotConfigureNotifications()), actionCollection()); + m_gameServerConnect = new KAction(i18n("&Connect to Server..."), "connect_no", Key_F2, this, SLOT(slotServerConnect()), actionCollection(), "game_serverconnect"); + m_gameNewServer = new KAction(i18n("&Start Server..."), "network", Key_F3, this, SLOT(slotNewServer()), actionCollection(), "game_newserver"); + m_gameSingle = new KAction(i18n("S&ingle Player..."), "gear", Key_F4, this, SLOT(slotSinglePlayer()), actionCollection(), "game_singleplayer"); + m_gameQuit = KStdGameAction::quit(this, SLOT(close()), actionCollection()); + KStdGameAction::highscores(this, SLOT(slotHighscore()), actionCollection()); + m_gameEnemyInfo = new KAction(i18n("&Enemy Info"), "view_text", Key_F11, this, SLOT(slotEnemyClientInfo()), actionCollection(), "game_enemyinfo"); + + m_configSound = new KToggleAction(i18n("&Play Sounds"), 0, actionCollection(), "options_configure_sound"); + m_configGrid = new KToggleAction(i18n("&Show Grid"), 0, this, SLOT(slotShowGrid()), actionCollection(), "options_show_grid"); + m_configGrid->setCheckedState(i18n("Hide Grid")); + + m_gameEnemyInfo->setEnabled(false); + + setupGUI( KMainWindow::Save | StatusBar | Keys | Create ); +} + +void KBattleshipWindow::initChat() +{ + connect(m_chat, SIGNAL(sigSendMessage(const QString &)), this, SLOT(slotSendChatMessage(const QString &))); + connect(m_chat, SIGNAL(sigChangeEnemyNickname(const QString &)), this, SLOT(slotChangeEnemyPlayer(const QString &))); + connect(m_chat, SIGNAL(sigChangeOwnNickname(const QString &)), this, SLOT(slotChangedNickCommand(const QString &))); +} + +void KBattleshipWindow::changeShipPlacementDirection(){ + shift = !shift; +} + +void KBattleshipWindow::initShipPlacing() +{ + connect(m_ownshiplist, SIGNAL(sigOwnFieldDataChanged(int, int, int)), this, SLOT(slotChangeOwnFieldData(int, int, int))); + connect(m_ownshiplist, SIGNAL(sigLastShipAdded()), this, SLOT(slotShipsReady())); +} + +void KBattleshipWindow::initView() +{ + QWidget *dummy = new QWidget(this, "dummy"); + setCentralWidget(dummy); + + QGridLayout *topLayout = new QGridLayout(dummy, 2, 2, 0, -1, "topLayout"); + + m_chat = new KChatWidget(dummy); + m_view = new KBattleshipView(dummy, "", m_configGrid->isChecked()); + m_stat = new KStatDialog(dummy); + topLayout->setColStretch(1, 10); + topLayout->setRowStretch(1, 10); + topLayout->addWidget(m_view, 0, 0); + topLayout->addWidget(m_stat, 0, 1); + topLayout->addMultiCellWidget(m_chat, 1, 1, 0, 1); + + m_ownshiplist = new KShipList(); + m_enemyshiplist = new KShipList(); + + m_view->startDrawing(); + setFocusProxy(m_view); + + connect(m_view, SIGNAL(sigEnemyFieldClicked(int, int)), this, SLOT(slotEnemyFieldClick(int, int))); + connect(m_view, SIGNAL(sigOwnFieldClicked(int, int)), this, SLOT(slotPlaceShip(int, int))); + connect(m_view, SIGNAL(sigMouseOverField(int, int)), this, SLOT(slotPlaceShipPreview(int, int))); + connect(m_view, SIGNAL(changeShipPlacementDirection()), this, SLOT(changeShipPlacementDirection())); +} + +void KBattleshipWindow::slotDeleteAI() +{ + m_aiHits = 0; + delete m_aiPlayer; + m_aiPlayer = 0; +} + +void KBattleshipWindow::slotRestartAI() +{ + m_aiHits = 0; + slotStartBattleshipGame(false); +} + +void KBattleshipWindow::slotEnemyFieldClick(int fieldx, int fieldy) +{ + if(m_connection != 0 || m_aiPlaying) + { + if(!m_aiPlaying && m_connection == 0) + return; + + if(!m_serverHasClient && m_connection != 0) + return; + + if(!m_shootable) + return; + + if(m_view->enemyFieldState(fieldx, fieldy) == KBattleField::FREE) + { + if(!m_aiPlaying && !m_lost) + { + slotStatusMsg(i18n("Sending Message...")); + KMessage *msg = new KMessage(KMessage::SHOOT); + msg->addField("fieldx", QString::number(fieldx)); + msg->addField("fieldy", QString::number(fieldy)); + slotSendMessage(msg); + } + + if(m_stat->hit() != 10 && m_aiPlaying) + { + m_stat->setShot(); + + int showstate; + + if(m_enemyshiplist->shipTypeAt(fieldx, fieldy) == 99) + { + m_stat->setWater(); + showstate = KBattleField::WATER; + } + else + { + m_stat->setHit(); + showstate = KBattleField::HIT; + } + + slotChangeEnemyFieldData(fieldx, fieldy, showstate); + + if(showstate == KBattleField::HIT) + { + KShip *ship = m_enemyshiplist->shipAt(fieldx, fieldy); + typedef QValueList DeathValueList; + DeathValueList deathList; + bool xokay = true, yokay = true; + int tempy = 0, tempx = 0; + + if(ship->placedLeft()) + { + for(tempx = ship->shipxstart(); tempx <= ship->shipxstop(); tempx++) + { + if(m_view->enemyFieldState(tempx, fieldy) == KBattleField::HIT) + { + deathList.append(tempx); + xokay = true; + yokay = false; + } + else + { + xokay = false; + yokay = false; + break; + } + } + } + else + { + for(tempy = ship->shipystart(); tempy <= ship->shipystop(); tempy++) + { + if(m_view->enemyFieldState(fieldx, tempy) == KBattleField::HIT) + { + deathList.append(tempy); + xokay = false; + yokay = true; + } + else + { + xokay = false; + yokay = false; + break; + } + } + } + + if(xokay) + { + DeathValueList::Iterator it; + for(it = deathList.begin(); it != deathList.end(); ++it) + { + if(fieldy+1 < m_enemyshiplist->m_fieldy) m_view->changeEnemyFieldData(*it, fieldy+1, KBattleField::BORDER); + m_view->changeEnemyFieldData(*it, fieldy, KBattleField::DEATH); + if(fieldy > 0) m_view->changeEnemyFieldData(*it, fieldy-1, KBattleField::BORDER); + } + if(ship->shipxstart() > 0) + { + if (fieldy > 0) m_view->changeEnemyFieldData(ship->shipxstart()-1, fieldy-1, KBattleField::BORDER); + m_view->changeEnemyFieldData(ship->shipxstart()-1, fieldy, KBattleField::BORDER); + if (fieldy < m_enemyshiplist->m_fieldy) m_view->changeEnemyFieldData(ship->shipxstart()-1, fieldy+1, KBattleField::BORDER); + } + if(ship->shipxstop() < m_enemyshiplist->m_fieldx) + { + if (fieldy>0) m_view->changeEnemyFieldData(ship->shipxstop()+1, fieldy-1, KBattleField::BORDER); + m_view->changeEnemyFieldData(ship->shipxstop()+1, fieldy, KBattleField::BORDER); + if (fieldy < m_enemyshiplist->m_fieldy)m_view->changeEnemyFieldData(ship->shipxstop()+1,fieldy+1, KBattleField::BORDER); + } + } + else if(yokay) + { + DeathValueList::Iterator it; + for(it = deathList.begin(); it != deathList.end(); ++it) + { + if (fieldx>0) m_view->changeEnemyFieldData(fieldx-1, *it, KBattleField::BORDER); + m_view->changeEnemyFieldData(fieldx, *it, KBattleField::DEATH); + if(fieldxm_fieldx) m_view->changeEnemyFieldData(fieldx+1, *it, KBattleField::BORDER); + } + if(ship->shipystart()>0) + { + if (fieldx>0)m_view->changeEnemyFieldData(fieldx-1, ship->shipystart()-1, KBattleField::BORDER); + m_view->changeEnemyFieldData(fieldx, ship->shipystart()-1, KBattleField::BORDER); + if (fieldxm_fieldx)m_view->changeEnemyFieldData(fieldx+1, ship->shipystart()-1, KBattleField::BORDER); + } + if(ship->shipystop()m_fieldy) + { + if (fieldx>0)m_view->changeEnemyFieldData(fieldx-1, ship->shipystop()+1, KBattleField::BORDER); + m_view->changeEnemyFieldData(fieldx, ship->shipystop()+1, KBattleField::BORDER); + if (fieldxm_fieldx)m_view->changeEnemyFieldData(fieldx+1, ship->shipystop()+1, KBattleField::BORDER); + } + } + } + } + + if(m_stat->hit() == 10 && m_aiPlaying) + { + m_aiPlaying = false; + m_shootable = false; + slotChangeOwnPlayer("-"); + slotChangeEnemyPlayer("-"); + m_gameSingle->setText(i18n("S&ingle Player")); + m_gameNewServer->setEnabled(true); + m_gameServerConnect->setEnabled(true); + slotStatusMsg(i18n("You won the game :)")); + m_stat->slotAddOwnWon(); + slotUpdateHighscore(); + switch(KMessageBox::questionYesNo(this, i18n("Do you want to restart the game?"),QString::null,i18n("Restart"),i18n("Do Not Restart"))) + { + case KMessageBox::Yes: + QTimer::singleShot(0, this, SLOT(slotRestartAI())); + break; + + case KMessageBox::No: + QTimer::singleShot(0, this, SLOT(slotDeleteAI())); + break; + } + return; + } + else if(m_aiPlayer != 0 && m_aiPlaying) + m_aiPlayer->slotRequestShot(); + } + } +} + +void KBattleshipWindow::slotReceivedEnemyFieldData(int fieldx, int fieldy, int enemystate, int xstart, int xstop, int ystart, int ystop, bool death) +{ + m_stat->setShot(); + + int showstate; + + if(enemystate == 99) + { + m_stat->setWater(); + showstate = KBattleField::WATER; + } + else + { + m_stat->setHit(); + showstate = KBattleField::HIT; + } + + slotChangeEnemyFieldData(fieldx, fieldy, showstate); + + if(death) + { + if(xstart == xstop) + { + for(int i = ystart; i <= ystop; i++) + { + if (fieldx>0) m_view->changeEnemyFieldData(fieldx-1, i, KBattleField::BORDER); + m_view->changeEnemyFieldData(fieldx, i, KBattleField::DEATH); + if(fieldxm_fieldx) m_view->changeEnemyFieldData(fieldx+1, i, KBattleField::BORDER); + } + if(ystart>0) + { + if (fieldx>0)m_view->changeEnemyFieldData(fieldx-1, ystart-1, KBattleField::BORDER); + m_view->changeEnemyFieldData(fieldx, ystart-1, KBattleField::BORDER); + if (fieldxm_fieldx)m_view->changeEnemyFieldData(fieldx+1, ystart-1, KBattleField::BORDER); + } + if(ystopm_fieldy) + { + if (fieldx>0)m_view->changeEnemyFieldData(fieldx-1, ystop+1, KBattleField::BORDER); + m_view->changeEnemyFieldData(fieldx, ystop+1, KBattleField::BORDER); + if (fieldxm_fieldx)m_view->changeEnemyFieldData(fieldx+1, ystop+1, KBattleField::BORDER); + } + } + else if(ystart == ystop) + { + for(int i = xstart; i <= xstop; i++) + { + if(fieldy+1 < m_enemyshiplist->m_fieldy) m_view->changeEnemyFieldData(i, fieldy+1, KBattleField::BORDER); + m_view->changeEnemyFieldData(i, fieldy, KBattleField::DEATH); + if(fieldy > 0) m_view->changeEnemyFieldData(i, fieldy-1, KBattleField::BORDER); + } + if(xstart > 0) + { + if (fieldy > 0) m_view->changeEnemyFieldData(xstart-1, fieldy-1, KBattleField::BORDER); + m_view->changeEnemyFieldData(xstart-1, fieldy, KBattleField::BORDER); + if (fieldy < m_enemyshiplist->m_fieldy) m_view->changeEnemyFieldData(xstart-1, fieldy+1, KBattleField::BORDER); + } + if(xstop < m_enemyshiplist->m_fieldx) + { + if (fieldy>0) m_view->changeEnemyFieldData(xstop+1, fieldy-1, KBattleField::BORDER); + m_view->changeEnemyFieldData(xstop+1, fieldy, KBattleField::BORDER); + if (fieldy < m_enemyshiplist->m_fieldy)m_view->changeEnemyFieldData(xstop+1,fieldy+1, KBattleField::BORDER); + } + } + } + + if(m_stat->hit() != 10) + slotStatusMsg(i18n("Waiting for enemy to shoot..")); + else + { + KMessage *msg = m_view->getAliveShips(m_ownshiplist); // let's show ai player ships + slotSendMessage(msg); + slotStatusMsg(i18n("You won the game :)")); + m_stat->slotAddOwnWon(); + slotUpdateHighscore(); + if(m_connection->type() == KonnectionHandling::SERVER) + slotServerReplay(); + else + slotClientReplay(); + } +} + +void KBattleshipWindow::slotClientLost() +{ + slotAbortNetworkGame(); + slotStatusMsg(i18n("Enemy disconnected.")); +} + +void KBattleshipWindow::slotServerLost() +{ + slotAbortNetworkGame(); + slotStatusMsg(i18n("Enemy disconnected.")); +} + +void KBattleshipWindow::slotAbortNetworkGame() +{ + slotStatusMsg(i18n("Ready")); + slotChangeOwnPlayer("-"); + slotChangeEnemyPlayer("-"); + + m_gameServerConnect->setText(i18n("&Connect to server")); + m_gameNewServer->setText(i18n("&Start server")); + m_gameSingle->setText(i18n("S&ingle game")); + m_gameServerConnect->setEnabled(true); + m_gameNewServer->setEnabled(true); + m_gameSingle->setEnabled(true); + m_gameEnemyInfo->setEnabled(false); + + m_chat->clear(); + + m_aiPlaying = false; + m_shootable = false; + m_placeable = false; + m_serverHasClient = false; + + if (m_connection) + { + if(m_connection->type() == KonnectionHandling::SERVER) + { + delete m_kbserver; + m_kbserver = 0; + } + else + { + delete m_kbclient; + m_kbclient = 0; + } + delete m_connection; + m_connection = 0; + } +} + +void KBattleshipWindow::slotReplay() +{ + cleanup(true); + m_aiPlaying = false; + m_shootable = false; + m_lost = false; + if(m_connection->type() == KonnectionHandling::SERVER) + m_placeable = true; + else + m_placeable = false; + m_stat->clear(); +} + +void KBattleshipWindow::slotPlaceShipPreview(int fieldx, int fieldy) +{ + int xadd = 0, yadd = 0; + + if(m_connection != 0 || m_aiPlaying) + { + if(!m_aiPlaying && m_connection == 0) + return; + + if(m_connection != 0 && !m_aiPlaying && !m_serverHasClient) + return; + + if((m_placeable && m_ownshiplist->canAddShips()) || m_aiPlaying) + { + switch(m_ownshiplist->shipCount()) + { + case 4: + for(int i = 0; i <= 3; i++) + { + if(!shift) + xadd = i; + else + yadd = i; + m_view->previewShip(fieldx + xadd, fieldy + yadd, KBattleField::SHIP4P1 + i, shift); + } + break; + + case 3: + for(int i = 0; i <= 2; i++) + { + if(!shift) + xadd = i; + else + yadd = i; + m_view->previewShip(fieldx + xadd, fieldy + yadd, KBattleField::SHIP3P1 + i, shift); + } + break; + + case 2: + for(int i = 0; i <= 1; i++) + { + if(!shift) + xadd = i; + else + yadd = i; + m_view->previewShip(fieldx + xadd, fieldy + yadd, KBattleField::SHIP2P1 + i, shift); + } + break; + + case 1: + m_view->previewShip(fieldx, fieldy, KBattleField::SHIP1P1, shift); + break; + } + + m_view->field()->drawField(); + } + } +} + +void KBattleshipWindow::slotPlaceShip(int fieldx, int fieldy) +{ + if(m_connection != 0 || m_aiPlaying) + { + if(!m_aiPlaying && m_connection == 0) + return; + + if(m_connection != 0 && !m_aiPlaying && !m_serverHasClient) + return; + + if(m_placeable && m_ownshiplist->canAddShips()) + m_ownshiplist->addNewShip(shift, fieldx, fieldy); + } +} + +void KBattleshipWindow::slotShipsReady() +{ + if(m_aiPlaying) + { + slotStatusMsg(i18n("Waiting for computer player to start the match...")); + m_placeable = false; + m_aiPlayer->slotRequestShot(); + return; + } + + KMessage *msg = new KMessage(KMessage::SHIPSREADY); + slotSendMessage(msg); + + if(m_connection->type() == KonnectionHandling::SERVER) + slotStatusMsg(i18n("Waiting for other player to place their ships...")); + else + slotStatusMsg(i18n("Waiting for other player to start the match...")); + + m_placeable = false; +} + +void KBattleshipWindow::slotSendMessage(int fieldx, int fieldy, int state) +{ + if(m_connection != 0) + { + KMessage *msg = new KMessage(KMessage::ANSWER_SHOOT); + msg->addField(QString("fieldx"), QString::number(fieldx)); + msg->addField(QString("fieldy"), QString::number(fieldy)); + msg->addField(QString("fieldstate"), QString::number(state)); + + if(m_connection->type() == KonnectionHandling::SERVER) + m_kbserver->sendMessage(msg); + else + m_kbclient->sendMessage(msg); + } +} + +void KBattleshipWindow::slotSendMessage(KMessage *msg) +{ + if(m_connection != 0) + { + if(m_connection->type() == KonnectionHandling::SERVER) + m_kbserver->sendMessage(msg); + else + m_kbclient->sendMessage(msg); + } +} + +void KBattleshipWindow::slotSendChatMessage(const QString &text) +{ + if(m_connection != 0 && m_serverHasClient) + { + KMessage *msg = new KMessage(KMessage::CHAT); + msg->chatMessage(m_ownNickname, text); + slotSendMessage(msg); + } +} + +void KBattleshipWindow::slotChangedNickCommand(const QString &text) +{ + m_ownNickname = text; + slotChangeOwnPlayer(m_ownNickname); + m_chat->setNickname(m_ownNickname); +} + +KShip *KBattleshipWindow::shipAt(int fieldx, int fieldy) +{ + return m_ownshiplist->shipAt(fieldx, fieldy); +} + +KShip *KBattleshipWindow::enemyShipAt(int fieldx, int fieldy) +{ + return m_enemyshiplist->shipAt(fieldx, fieldy); +} + +void KBattleshipWindow::slotUpdateHighscore() +{ + // Balancing factors + // a = shot-balance + // b = water-balance + double a = 3; + double b = 0.5; + double score = (a * m_stat->hit() - b * m_stat->water()) / (m_stat->shot() + m_stat->water()) * 1000; + if(score == 0) score = 1; + + KScoreDialog *scoreDialog = new KScoreDialog(KScoreDialog::Name | KScoreDialog::Score | KScoreDialog::Custom1 | KScoreDialog::Custom2 | KScoreDialog::Custom3, this); + scoreDialog->addField(KScoreDialog::Custom1, i18n("Shots"), "shots"); + scoreDialog->addField(KScoreDialog::Custom2, i18n("Hits"), "hits"); + scoreDialog->addField(KScoreDialog::Custom3, i18n("Water"), "water"); + + KScoreDialog::FieldInfo info; + info[KScoreDialog::Name] = m_ownNickname; + info[KScoreDialog::Custom1] = QString::number(m_stat->shot()); + info[KScoreDialog::Custom2] = QString::number(m_stat->hit()); + info[KScoreDialog::Custom3] = QString::number(m_stat->water()); + + scoreDialog->addScore((int)score, info, false, false); +} + +void KBattleshipWindow::saveOptions() +{ + m_config->setGroup("General"); + m_config->writeEntry("PlaySounds", m_configSound->isChecked()); + m_config->writeEntry("ShowGrid", m_configGrid->isChecked()); + m_config->sync(); +} + +void KBattleshipWindow::readOptions() +{ + m_config->setGroup("General"); + m_configSound->setChecked(m_config->readBoolEntry("PlaySounds", true)); + m_configGrid->setChecked(m_config->readBoolEntry("ShowGrid", false)); +} + +void KBattleshipWindow::slotHighscore() +{ + KScoreDialog *scoreDialog = new KScoreDialog(KScoreDialog::Name | KScoreDialog::Score | KScoreDialog::Custom1 | KScoreDialog::Custom2 | KScoreDialog::Custom3, this); + scoreDialog->addField(KScoreDialog::Custom1, i18n("Shots"), "shots"); + scoreDialog->addField(KScoreDialog::Custom2, i18n("Hits"), "hits"); + scoreDialog->addField(KScoreDialog::Custom3, i18n("Water"), "water"); + scoreDialog->show(); +} + +void KBattleshipWindow::slotEnemyClientInfo() +{ + KInfoDialog *m_info = new KInfoDialog(this); + + m_info->lbl_clientIdentfier->setText(m_enemyClient); + m_info->lbl_clientVersion->setText(m_enemyClientVersion); + m_info->lbl_ClientInformation->setText(m_enemyClientDescription); + m_info->lbl_ProtocolVersion->setText(m_enemyProtocolVersion); + + m_info->show(); +} + +void KBattleshipWindow::slotServerConnect() +{ + if(m_connection == 0) + { + if(m_client != 0) { + m_client->show(); + return; + } + + slotStatusMsg(i18n("Loading Connect-Server dialog...")); + + m_client = new KClientDialog(this); + connect(m_client, SIGNAL(sigConnectServer()), this, SLOT(slotConnectToBattleshipServer())); + connect(m_client, SIGNAL(sigCancelConnect()), this, SLOT(slotDeleteConnectDialog())); + m_client->show(); + + slotStatusMsg(i18n("Ready")); + } + else + slotAbortNetworkGame(); +} + +void KBattleshipWindow::slotDeleteConnectDialog() +{ + delete m_client; + m_client = 0; +} + +void KBattleshipWindow::slotReplayRequest() +{ + switch(KMessageBox::questionYesNo(this, i18n("The client is asking to restart the game. Do you accept?"),QString::null,i18n("Accept Restart"), i18n("Deny Restart"))) + { + case KMessageBox::Yes: + if (m_connection) + { // the client could have closed while the user was thinking if he wanted to replay + slotReplay(); + slotStatusMsg(i18n("Please place your ships. Use the \"Shift\" key to place the ships vertically.")); + } + else slotAbortNetworkGame(); + break; + + case KMessageBox::No: + slotAbortNetworkGame(); + break; + } +} + +void KBattleshipWindow::slotServerReplay() +{ + KMessage *msg = new KMessage(KMessage::REPLAY); + switch(KMessageBox::questionYesNo(this, i18n("Do you want to restart the game?"), QString::null, i18n("Restart"), i18n("Do Not Restart"))) + { + case KMessageBox::Yes: + if (m_connection) + { // the client could have closed while the user was thinking if he wanted to replay + slotReplay(); + slotStatusMsg(i18n("Please place your ships. Use the \"Shift\" key to place the ships vertically.")); + slotSendMessage(msg); + } + else + { + delete msg; + slotAbortNetworkGame(); + } + break; + + case KMessageBox::No: + delete msg; + slotAbortNetworkGame(); + break; + } +} + +void KBattleshipWindow::slotClientReplay() +{ + KMessage *msg = new KMessage(KMessage::REPLAY); + switch(KMessageBox::questionYesNo(this, i18n("Do you want to ask the server restarting the game?"), QString::null, i18n("Ask to Restart"), i18n("Do Not Ask"))) + { + case KMessageBox::Yes: + if (m_connection) + { // the server could have closed while the user was thinking if he wanted to replay + slotReplay(); + slotStatusMsg(i18n("Waiting for an answer...")); + slotSendMessage(msg); + } + else + { + delete msg; + slotAbortNetworkGame(); + } + break; + + case KMessageBox::No: + delete msg; + slotAbortNetworkGame(); + break; + } +} + +void KBattleshipWindow::cleanup(bool placechange) +{ + if(placechange) + m_placeable = false; + m_view->field()->setDrawField(false); + m_ownshiplist->clear(); + m_enemyshiplist->clear(); + m_view->clearField(); + m_view->field()->setDrawField(true); + m_view->field()->drawField(); +} + +void KBattleshipWindow::slotNewServer() +{ + if(m_connection == 0) + { + if(m_server != 0) + return; + + slotStatusMsg(i18n("Loading Start-Server dialog...")); + + m_server = new KServerDialog(this); + connect(m_server, SIGNAL(okClicked()), this, SLOT(slotStartBattleshipServer())); + connect(m_server, SIGNAL(cancelClicked()), this, SLOT(slotDeleteServerDialog())); + m_server->show(); + + slotStatusMsg(i18n("Ready")); + } + else + slotAbortNetworkGame(); +} + +void KBattleshipWindow::slotDeleteServerDialog() +{ + delete m_server; + m_server = 0; +} + +void KBattleshipWindow::slotSendVersion() +{ + KMessage *msg = new KMessage(KMessage::GETVERSION); + msg->versionMessage(); + slotSendMessage(msg); + + QTimer::singleShot(150, this, SLOT(slotSendGreet())); +} + +void KBattleshipWindow::slotSendGreet() +{ + m_serverHasClient = true; + m_chat->slotAcceptMsg(true); + + KMessage *msg = new KMessage(KMessage::GREET); + msg->addField(QString("nickname"), m_ownNickname); + slotSendMessage(msg); +} + +void KBattleshipWindow::slotStartBattleshipServer() +{ + m_gameNewServer->setText(i18n("&Stop server")); + m_gameServerConnect->setEnabled(false); + m_gameSingle->setEnabled(false); + slotStatusMsg(i18n("Waiting for a player...")); + m_kbserver = new KBattleshipServer((m_server->port()).toInt(),m_server->gamename()); + m_ownNickname = m_server->nickname(); + m_chat->setNickname(m_ownNickname); + slotChangeOwnPlayer(m_ownNickname); + delete m_server; + m_server = 0; + cleanup(true); + m_aiPlaying = false; + m_shootable = false; + m_placeable = true; + m_stat->clear(); + m_stat->clearWon(); + if(m_connection == 0) + { + m_connection = new KonnectionHandling(this, m_kbserver); + connect(m_connection, SIGNAL(sigStatusBar(const QString &)), this, SLOT(slotStatusMsg(const QString &))); + connect(m_connection, SIGNAL(sigEnemyNickname(const QString &)), this, SLOT(slotChangeEnemyPlayer(const QString &))); + connect(m_connection, SIGNAL(sigSendNickname()), this, SLOT(slotSendGreet())); + connect(m_connection, SIGNAL(sigPlaceShips(bool)), this, SLOT(slotSetPlaceable(bool))); + connect(m_connection, SIGNAL(sigShootable(bool)), this, SLOT(slotSetShootable(bool))); + connect(m_connection, SIGNAL(sigSendFieldState(int, int)), this, SLOT(slotSendEnemyFieldState(int, int))); + connect(m_connection, SIGNAL(sigEnemyFieldData(int, int, int, int, int, int, int, bool)), this, SLOT(slotReceivedEnemyFieldData(int, int, int, int, int, int, int, bool))); + connect(m_connection, SIGNAL(sigClientLost()), this, SLOT(slotClientLost())); + connect(m_connection, SIGNAL(sigAbortNetworkGame()), this, SLOT(slotAbortNetworkGame())); + connect(m_connection, SIGNAL(sigReplay()), this, SLOT(slotReplayRequest())); + connect(m_connection, SIGNAL(sigChatMessage(const QString &, const QString &, bool)), m_chat, SLOT(slotReceivedMessage(const QString &, const QString &, bool))); + connect(m_connection, SIGNAL(sigClientInformation(const QString &, const QString &, const QString &, const QString &)), this, SLOT(slotReceivedClientInformation(const QString &, const QString &, const QString &, const QString &))); + connect(m_connection, SIGNAL(sigLost(KMessage *)), this, SLOT(slotLost(KMessage *))); + } + else + { + if(m_connection->type() == KonnectionHandling::CLIENT) + { + disconnect(m_kbclient, SIGNAL(sigConnected()), this, SLOT(slotSendVersion())); + disconnect(m_connection, SIGNAL(sigAbortNetworkGame()), this, SLOT(slotAbortNetworkGame())); + disconnect(m_connection, SIGNAL(sigStatusBar(const QString &)), this, SLOT(slotStatusMsg(const QString &))); + disconnect(m_connection, SIGNAL(sigEnemyNickname(const QString &)), this, SLOT(slotChangeEnemyPlayer(const QString &))); + disconnect(m_connection, SIGNAL(sigSendFieldState(int, int)), this, SLOT(slotSendEnemyFieldState(int, int))); + disconnect(m_connection, SIGNAL(sigEnemyFieldData(int, int, int, int, int, int, int, bool)), this, SLOT(slotReceivedEnemyFieldData(int, int, int, int, int, int, int, bool))); + disconnect(m_connection, SIGNAL(sigShootable(bool)), this, SLOT(slotSetShootable(bool))); + disconnect(m_connection, SIGNAL(sigPlaceShips(bool)), this, SLOT(slotSetPlaceable(bool))); + disconnect(m_connection, SIGNAL(sigServerLost()), this, SLOT(slotServerLost())); + disconnect(m_connection, SIGNAL(sigReplay()), this, SLOT(slotReplay())); + disconnect(m_connection, SIGNAL(sigChatMessage(const QString &, const QString &, bool)), m_chat, SLOT(slotReceivedMessage(const QString &, const QString &, bool))); + disconnect(m_connection, SIGNAL(sigLost(KMessage *)), this, SLOT(slotLost(KMessage *))); + m_connection->updateInternal(m_kbserver); + connect(m_connection, SIGNAL(sigStatusBar(const QString &)), this, SLOT(slotStatusMsg(const QString &))); + connect(m_connection, SIGNAL(sigEnemyNickname(const QString &)), this, SLOT(slotChangeEnemyPlayer(const QString &))); + connect(m_connection, SIGNAL(sigSendNickname()), this, SLOT(slotSendGreet())); + connect(m_connection, SIGNAL(sigPlaceShips(bool)), this, SLOT(slotSetPlaceable(bool))); + connect(m_connection, SIGNAL(sigShootable(bool)), this, SLOT(slotSetShootable(bool))); + connect(m_connection, SIGNAL(sigSendFieldState(int, int)), this, SLOT(slotSendEnemyFieldState(int, int))); + connect(m_connection, SIGNAL(sigEnemyFieldData(int, int, int, int, int, int, int, bool)), this, SLOT(slotReceivedEnemyFieldData(int, int, int, int, int, int, int, bool))); + connect(m_connection, SIGNAL(sigClientLost()), this, SLOT(slotClientLost())); + connect(m_connection, SIGNAL(sigAbortNetworkGame()), this, SLOT(slotAbortNetworkGame())); + connect(m_connection, SIGNAL(sigReplay()), this, SLOT(slotReplayRequest())); + connect(m_connection, SIGNAL(sigChatMessage(const QString &, const QString &, bool)), m_chat, SLOT(slotReceivedMessage(const QString &, const QString &, bool))); + connect(m_connection, SIGNAL(sigLost(KMessage *)), this, SLOT(slotLost(KMessage *))); + } + else + m_connection->updateInternal(m_kbserver); + } + m_kbserver->init(); +} + +void KBattleshipWindow::slotLost(KMessage *msg) +{ + m_stat->slotAddEnemyWon(); + if (!msg->field("ship0").isNull()) m_view->drawEnemyShipsHuman(msg, m_enemyshiplist); + m_lost = true; +} + +void KBattleshipWindow::slotSendEnemyFieldState(int fieldx, int fieldy) +{ + int data, showstate; + bool xokay = false, yokay = false, is_kill = false; + typedef QValueList DeathValueList; + DeathValueList deathList; + + data = m_ownshiplist->shipTypeAt(fieldx, fieldy); + if(data == 99) + showstate = KBattleField::WATER; + else + showstate = KBattleField::HIT; + + slotChangeOwnFieldData(fieldx, fieldy, showstate); + + KMessage *msg = new KMessage(KMessage::ANSWER_SHOOT); + + if(showstate == KBattleField::HIT) + { + if(m_ownshiplist->shipTypeAt(fieldx, fieldy) != 0 && m_ownshiplist->shipTypeAt(fieldx, fieldy) != 99) + { + KShip *ship = m_ownshiplist->shipAt(fieldx, fieldy); + int tempy = 0, tempx = 0; + + if(ship->shipystart() == ship->shipystop() && ship->shipxstart() != ship->shipxstop()) + { + for(tempx = ship->shipxstart(); tempx <= ship->shipxstop(); tempx++) + { + if(m_view->ownFieldState(tempx, fieldy) == KBattleField::HIT) + { + deathList.append(tempx); + xokay = true; + yokay = false; + } + else + { + xokay = false; + yokay = false; + break; + } + } + } + else if(ship->shipystart() != ship->shipystop() && ship->shipxstart() == ship->shipxstop()) + { + for(tempy = ship->shipystart(); tempy <= ship->shipystop(); tempy++) + { + if(m_view->ownFieldState(fieldx, tempy) == KBattleField::HIT) + { + deathList.append(tempy); + xokay = false; + yokay = true; + } + else + { + xokay = false; + yokay = false; + break; + } + } + } + } + else if(m_ownshiplist->shipTypeAt(fieldx, fieldy) == 0) + { + msg->addField(QString("xstart"), QString::number(fieldx)); + msg->addField(QString("xstop"), QString::number(fieldx)); + msg->addField(QString("ystart"), QString::number(fieldy)); + msg->addField(QString("ystop"), QString::number(fieldy)); + msg->addField(QString("death"), QString("true")); + is_kill = true; + } + } + + msg->addField(QString("fieldx"), QString::number(fieldx)); + msg->addField(QString("fieldy"), QString::number(fieldy)); + + if(xokay) + { + msg->addField(QString("xstart"), QString::number(deathList.first())); + msg->addField(QString("xstop"), QString::number(deathList.last())); + msg->addField(QString("ystart"), QString::number(fieldy)); + msg->addField(QString("ystop"), QString::number(fieldy)); + msg->addField(QString("death"), QString("true")); + is_kill = true; + } + else if(yokay) + { + msg->addField(QString("xstart"), QString::number(fieldx)); + msg->addField(QString("xstop"), QString::number(fieldx)); + msg->addField(QString("ystart"), QString::number(deathList.first())); + msg->addField(QString("ystop"), QString::number(deathList.last())); + msg->addField(QString("death"), QString("true")); + is_kill = true; + } + + if(is_kill) + // If sunk, reveal ship type + msg->addField(QString("fieldstate"), QString::number(data)); + else if(showstate == KBattleField::HIT) + // On non-fatal hit, keep ship type secret + msg->addField(QString("fieldstate"), QString::number(1)); + else /* showstate == KBattleField::WATER */ + // Miss + msg->addField(QString("fieldstate"), QString::number(99)); + + if(m_connection->type() == KonnectionHandling::SERVER) + m_kbserver->sendMessage(msg); + else + m_kbclient->sendMessage(msg); +} + +void KBattleshipWindow::slotChangeOwnFieldData(int fieldx, int fieldy, int type) +{ + m_view->changeOwnFieldData(fieldx, fieldy, type); + playSound(true, type); +} + +void KBattleshipWindow::playSound(bool enemy, int fieldstate) +{ + if (m_configSound->isChecked()) + { + switch(fieldstate) + { + case KBattleField::WATER: + KNotifyClient::event(winId(), "shoot_water"); + break; + + case KBattleField::HIT: + if(enemy) + KNotifyClient::event(winId(), "shoot_hit_1"); + else + KNotifyClient::event(winId(), "shoot_hit_2"); + break; + + case KBattleField::DEATH: + KNotifyClient::event(winId(), "shoot_sink"); + break; + } + } +} + +void KBattleshipWindow::slotChangeEnemyFieldData(int fieldx, int fieldy, int type) +{ + m_view->changeEnemyFieldData(fieldx, fieldy, type); + playSound(false, type); +} + +void KBattleshipWindow::parseCommandLine() { + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + if ( args->count() > 0 ) + { + KURL u( args->url(0)); + if(u.protocol().isEmpty()) + u.setProtocol("kbattleship"); + if( !u.isValid()) { + KMessageBox::sorry(this, + i18n("The URL passed to KDE Battleship '%1' is not a valid url") + .arg(args->arg(0))); + return; + } + if( u.protocol() != "kbattleship" ) { + KMessageBox::sorry(this, + i18n("The URL passed to KDE Battleship '%1' is not recognised " + "as a Battleship game.") + .arg(args->arg(0))); + return; + } + + slotConnectToBattleshipServer(u.host(), u.port(), u.user()); + + } + +} + +void KBattleshipWindow::slotConnectToBattleshipServer() +{ + QString host = m_client->host(); + int port = m_client->port().toInt(); + QString nickname = m_client->nickname(); + delete m_client; + m_client = 0; + slotConnectToBattleshipServer(host, port, nickname); +} +void KBattleshipWindow::slotConnectToBattleshipServer(const QString &host, int port, const QString &nickname) +{ + m_kbclient = new KBattleshipClient(host, port); + nickname.isEmpty() ? m_ownNickname = "TestUser" : m_ownNickname = nickname; + m_chat->setNickname(m_ownNickname); + slotChangeOwnPlayer(m_ownNickname); + cleanup(true); + m_aiPlaying = false; + m_shootable = false; + m_placeable = false; + m_stat->clear(); + m_stat->clearWon(); + m_gameServerConnect->setText(i18n("Dis&connect from server")); + m_gameNewServer->setEnabled(false); + m_gameSingle->setEnabled(false); + if(m_connection == 0) + { + m_connection = new KonnectionHandling(this, m_kbclient); + connect(m_kbclient, SIGNAL(sigConnected()), this, SLOT(slotSendVersion())); + connect(m_connection, SIGNAL(sigAbortNetworkGame()), this, SLOT(slotAbortNetworkGame())); + connect(m_connection, SIGNAL(sigStatusBar(const QString &)), this, SLOT(slotStatusMsg(const QString &))); + connect(m_connection, SIGNAL(sigEnemyNickname(const QString &)), this, SLOT(slotChangeEnemyPlayer(const QString &))); + connect(m_connection, SIGNAL(sigSendFieldState(int, int)), this, SLOT(slotSendEnemyFieldState(int, int))); + connect(m_connection, SIGNAL(sigEnemyFieldData(int, int, int, int, int, int, int, bool)), this, SLOT(slotReceivedEnemyFieldData(int, int, int, int, int, int, int, bool))); + connect(m_connection, SIGNAL(sigShootable(bool)), this, SLOT(slotSetShootable(bool))); + connect(m_connection, SIGNAL(sigPlaceShips(bool)), this, SLOT(slotSetPlaceable(bool))); + connect(m_connection, SIGNAL(sigServerLost()), this, SLOT(slotServerLost())); + connect(m_connection, SIGNAL(sigReplay()), this, SLOT(slotReplay())); + connect(m_connection, SIGNAL(sigChatMessage(const QString &, const QString &, bool)), m_chat, SLOT(slotReceivedMessage(const QString &, const QString &, bool))); + connect(m_connection, SIGNAL(sigClientInformation(const QString &, const QString &, const QString &, const QString &)), this, SLOT(slotReceivedClientInformation(const QString &, const QString &, const QString &, const QString &))); + connect(m_connection, SIGNAL(sigLost(KMessage *)), this, SLOT(slotLost(KMessage *))); + } + else + { + if(m_connection->type() == KonnectionHandling::SERVER) + { + disconnect(m_connection, SIGNAL(sigStatusBar(const QString &)), this, SLOT(slotStatusMsg(const QString &))); + disconnect(m_connection, SIGNAL(sigEnemyNickname(const QString &)), this, SLOT(slotChangeEnemyPlayer(const QString &))); + disconnect(m_connection, SIGNAL(sigSendNickname()), this, SLOT(slotSendGreet())); + disconnect(m_connection, SIGNAL(sigPlaceShips(bool)), this, SLOT(slotSetPlaceable(bool))); + disconnect(m_connection, SIGNAL(sigShootable(bool)), this, SLOT(slotSetShootable(bool))); + disconnect(m_connection, SIGNAL(sigSendFieldState(int, int)), this, SLOT(slotSendEnemyFieldState(int, int))); + disconnect(m_connection, SIGNAL(sigEnemyFieldData(int, int, int, int, int, int, int, bool)), this, SLOT(slotReceivedEnemyFieldData(int, int, int, int, int, int, int, bool))); + disconnect(m_connection, SIGNAL(sigClientLost()), this, SLOT(slotClientLost())); + disconnect(m_connection, SIGNAL(sigAbortNetworkGame()), this, SLOT(slotAbortNetworkGame())); + disconnect(m_connection, SIGNAL(sigReplay()), this, SLOT(slotReplayRequest())); + disconnect(m_connection, SIGNAL(sigChatMessage(const QString &, const QString &, bool)), m_chat, SLOT(slotReceivedMessage(const QString &, const QString &, bool))); + disconnect(m_connection, SIGNAL(sigLost(KMessage *)), this, SLOT(slotLost(KMessage *))); + m_connection->updateInternal(m_kbclient); + connect(m_kbclient, SIGNAL(sigConnected()), this, SLOT(slotSendVersion())); + connect(m_connection, SIGNAL(sigAbortNetworkGame()), this, SLOT(slotAbortNetworkGame())); + connect(m_connection, SIGNAL(sigStatusBar(const QString &)), this, SLOT(slotStatusMsg(const QString &))); + connect(m_connection, SIGNAL(sigEnemyNickname(const QString &)), this, SLOT(slotChangeEnemyPlayer(const QString &))); + connect(m_connection, SIGNAL(sigSendFieldState(int, int)), this, SLOT(slotSendEnemyFieldState(int, int))); + connect(m_connection, SIGNAL(sigEnemyFieldData(int, int, int, int, int, int, int, bool)), this, SLOT(slotReceivedEnemyFieldData(int, int, int, int, int, int, int, bool))); + connect(m_connection, SIGNAL(sigShootable(bool)), this, SLOT(slotSetShootable(bool))); + connect(m_connection, SIGNAL(sigPlaceShips(bool)), this, SLOT(slotSetPlaceable(bool))); + connect(m_connection, SIGNAL(sigServerLost()), this, SLOT(slotServerLost())); + connect(m_connection, SIGNAL(sigReplay()), this, SLOT(slotReplay())); + connect(m_connection, SIGNAL(sigChatMessage(const QString &, const QString &, bool)), m_chat, SLOT(slotReceivedMessage(const QString &, const QString &, bool))); + m_kbclient->init(); + connect(m_connection, SIGNAL(sigClientInformation(const QString &, const QString &, const QString &, const QString &)), this, SLOT(slotReceivedClientInformation(const QString &, const QString &, const QString &, const QString &))); + connect(m_connection, SIGNAL(sigLost(KMessage *)), this, SLOT(slotLost(KMessage *))); + } + else + m_connection->updateInternal(m_kbclient); + } + m_kbclient->init(); +} + +void KBattleshipWindow::slotSetPlaceable(bool place) +{ + m_placeable = place; +} + +void KBattleshipWindow::slotSetShootable(bool shoot) +{ + m_shootable = shoot; +} + +void KBattleshipWindow::slotShowGrid() +{ + if(!m_configGrid->isChecked()) + m_view->field()->disableGrid(); + else + m_view->field()->enableGrid(); +} + +void KBattleshipWindow::slotStatusMsg(const QString &text) +{ + statusBar()->clear(); + statusBar()->changeItem(text, ID_STATUS_MSG); +} + +void KBattleshipWindow::slotChangeOwnPlayer(const QString &text) +{ + statusBar()->clear(); + statusBar()->changeItem(i18n(" Player 1: %1 ").arg(text), ID_PLAYER_OWN); +} + +void KBattleshipWindow::slotChangeEnemyPlayer(const QString &text) +{ + statusBar()->clear(); + statusBar()->changeItem(i18n(" Player 2: %1 ").arg(text), ID_PLAYER_ENEMY); +} + +void KBattleshipWindow::slotSinglePlayer() +{ + bool ok; + if(!m_aiPlaying) + { + KUser u; + m_ownNickname = KInputDialog::getText(i18n("Start Game"), i18n("Nick name:"), + u.loginName(), &ok, this); + if (ok) + { + slotStatusMsg(i18n("Ready")); + slotStartBattleshipGame(); + } + } + else + { + if(m_aiPlayer != 0) + { + m_aiPlaying = false; + slotChangeOwnPlayer("-"); + slotChangeEnemyPlayer("-"); + m_gameSingle->setText(i18n("S&ingle Player")); + m_gameNewServer->setEnabled(true); + m_gameServerConnect->setEnabled(true); + slotStatusMsg(i18n("Ready")); + m_stat->clear(); + m_chat->clear(); + QTimer::singleShot(0, this, SLOT(slotDeleteAI())); + cleanup(false); + } + } +} + +void KBattleshipWindow::slotStartBattleshipGame() +{ + slotStartBattleshipGame(true); +} + +void KBattleshipWindow::slotStartBattleshipGame(bool clearstat) +{ + m_gameSingle->setText(i18n("&Stop game")); + m_gameNewServer->setEnabled(false); + m_gameServerConnect->setEnabled(false); + slotStatusMsg(i18n("Waiting for the AI player to place the ships...")); + slotChangeOwnPlayer(m_ownNickname); + slotChangeEnemyPlayer(KGameMisc::randomName()); + cleanup(true); + if(m_connection != 0) + { + delete m_connection; + m_connection = 0; + } + m_aiPlaying = true; + m_shootable = false; + m_stat->clear(); + if(clearstat) + m_stat->clearWon(); + + if(m_aiPlayer == 0) + { + m_aiPlayer = new KBAIPlayer(); + m_aiPlayer->init(m_view->field(), m_enemyshiplist); + connect(m_aiPlayer, SIGNAL(sigReady()), this, SLOT(slotAIReady())); + connect(m_aiPlayer, SIGNAL(sigShootAt(const QPoint)), this, SLOT(slotAIShootsAt(const QPoint))); + } + m_aiPlayer->slotRestart(); +} + +void KBattleshipWindow::slotAIReady() +{ + slotStatusMsg(i18n("Please place your ships. Use the \"Shift\" key to place the ships vertically.")); + m_placeable = true; +} + +void KBattleshipWindow::slotAIShootsAt(const QPoint pos) +{ + if(!m_shootable) + m_shootable = true; + + int showstate = m_view->ownFieldState(pos.x(), pos.y()); + + if(showstate == KBattleField::HIT) + { + m_aiPlayer->slotRequestShot(); + return; + } + else if(showstate == KBattleField::FREE) + showstate = KBattleField::WATER; + else if(showstate >= KBattleField::SHIP1P1) + { + showstate = KBattleField::HIT; + m_aiHits++; + } + + slotStatusMsg(i18n("Enemy has shot. Shoot now.")); + slotChangeOwnFieldData(pos.x(), pos.y(), showstate); + + if(m_aiHits == 10 && m_stat->hit() != 10) + { + m_aiPlaying = false; + m_shootable = false; + slotChangeOwnPlayer("-"); + slotChangeEnemyPlayer("-"); + m_gameSingle->setText(i18n("S&ingle Player")); + m_gameNewServer->setEnabled(true); + m_gameServerConnect->setEnabled(true); + slotStatusMsg(i18n("You lost the game. :(")); + m_stat->slotAddEnemyWon(); + slotUpdateHighscore(); + m_view->drawEnemyShipsAI(m_enemyshiplist); // let's show ai player ships + switch(KMessageBox::questionYesNo(this, i18n("Do you want to restart the game?"), QString::null, i18n("Restart"), i18n("Do Not Restart"))) + { + case KMessageBox::Yes: + QTimer::singleShot(0, this, SLOT(slotRestartAI())); + break; + + case KMessageBox::No: + QTimer::singleShot(0, this, SLOT(slotDeleteAI())); + break; + } + } + else + { + if(!m_shootable) + m_shootable = true; + } +} + +void KBattleshipWindow::slotReceivedClientInformation(const QString &clientName, const QString &clientVersion, const QString &clientDescription, const QString &protocolVersion) +{ + m_enemyClient = clientName; + m_enemyClientVersion = clientVersion; + m_enemyClientDescription = clientDescription; + m_enemyProtocolVersion = protocolVersion; + m_gameEnemyInfo->setEnabled(true); + + if(m_connection->type() == KonnectionHandling::SERVER) + { + KMessage *msg = new KMessage(KMessage::GETVERSION); + msg->versionMessage(); + slotSendMessage(msg); + } +} diff --git a/kbattleship/kbattleship/kbattleship.desktop b/kbattleship/kbattleship/kbattleship.desktop new file mode 100644 index 00000000..48cd8dd1 --- /dev/null +++ b/kbattleship/kbattleship/kbattleship.desktop @@ -0,0 +1,76 @@ +[Desktop Entry] +Name=KBattleship +Name[af]=Kbattleship +Name[ar]=لعبة سÙينة الحرب (KBattleship) +Name[be]=МарÑкі бой +Name[bn]=কে-বà§à¦¯à¦¾à¦Ÿà§‡à¦²à¦¶à§€à¦ª +Name[cs]=LodÄ› +Name[de]=Schiffe versenken +Name[hi]=के-बैटलशिप +Name[hr]=KPotapanje brodova +Name[is]=KSjóorrusta +Name[nds]=Scheep versenken +Name[ne]=केडीई बà¥à¤¯à¤¾à¤Ÿà¤²à¤¸à¥€à¤ª +Name[pa]=ਕੇ-ਜੰਗੀ ਜਹਾਜ਼ +Name[ro]=Bătălie navală +Name[sl]=KBojnaLadja +Name[sv]=Kbattleship +Name[ta]=கேபாடà¯à®Ÿà®¿à®²à¯à®·à®¿à®ªà¯ +Name[tg]=KҶанги Киштиҳо +Name[th]=เรือรบ - K +Name[tr]=Amiral Battı +Name[zh_TW]=KBattleship 戰艦 +Exec=kbattleship %i %m -caption "%c" +Icon=kbattleship +Type=Application +DocPath=kbattleship/index.html +GenericName=Battleship Game +GenericName[be]=Ð“ÑƒÐ»ÑŒÐ½Ñ Ñž марÑкі бой +GenericName[bg]=МорÑки шах +GenericName[bn]=বà§à¦¯à¦¾à¦Ÿà§‡à¦²à¦¶à§€à¦ª খেলা +GenericName[bs]=Igra podmornica +GenericName[ca]=Joc d'enfonsar la flota +GenericName[cs]=Bitva lodí +GenericName[cy]=Gêm Longau Rhyfel +GenericName[da]=Krigsskibe-spil +GenericName[de]=Schiffe versenken +GenericName[el]=Παιχνίδι Battleship +GenericName[eo]=BatalÅipa ludo +GenericName[es]=Juego de la batalla de naves +GenericName[et]=Laevade pommitamise mäng +GenericName[eu]=Ontzi-guda jokoa +GenericName[fa]=بازی Battleship +GenericName[fi]=Meritaistelupeli +GenericName[fr]=Jeu de bataille navale +GenericName[he]=משחק צוללות +GenericName[hr]=Igra potapanja brodova +GenericName[hu]=Torpedós +GenericName[is]=Sjóorustuleikur +GenericName[it]=Gioco a battaglia navale +GenericName[ja]=戦艦ゲーム +GenericName[km]=ល្បែង​នាវា​ចម្បាំង +GenericName[ko]=전함 게임 +GenericName[lt]=Laivų mÅ«Å¡io žaidimas +GenericName[lv]=JÅ«ras kaujas spÄ“le +GenericName[mk]=Игра Ñо потопување бродови +GenericName[nb]=Slagskip +GenericName[nds]=Scheep versenken +GenericName[ne]=बà¥à¤¯à¤¾à¤Ÿà¤²à¤¸à¥€à¤ª खेल +GenericName[nl]=Zeeslagspel +GenericName[nn]=Slagskip +GenericName[pa]=ਜੰਗੀ ਜਹਾਜ਼ ਖੇਡ +GenericName[pl]=Gra w statki +GenericName[pt]=Jogo de Batalha Naval +GenericName[pt_BR]=Jogo de Batalha Naval +GenericName[ru]=МорÑкой бой +GenericName[sk]=Bojová loÄ hra +GenericName[sl]=Igra potapljanja ladjic +GenericName[sr]=Игра подморница +GenericName[sr@Latn]=Igra podmornica +GenericName[sv]=Sänka fartyg spel +GenericName[ta]=போரà¯à®¤à¯à®¤à®³ விளையாடà¯à®Ÿà¯ +GenericName[uk]=Гра в морÑький бій +GenericName[zh_TW]=戰艦éŠæˆ² +Terminal=false +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;StrategyGame; diff --git a/kbattleship/kbattleship/kbattleship.h b/kbattleship/kbattleship/kbattleship.h new file mode 100644 index 00000000..7e606140 --- /dev/null +++ b/kbattleship/kbattleship/kbattleship.h @@ -0,0 +1,162 @@ +/*************************************************************************** + kbattleship.h + ----------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBATTLESHIP_H +#define KBATTLESHIP_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dialogs/infoDlg.h" + +#include "kbaiplayer.h" +#include "kbattleshipclient.h" +#include "kbattleshipserver.h" +#include "kbattleshipview.h" +#include "kchatwidget.h" +#include "kclientdialog.h" +#include "konnectionhandling.h" +#include "kserverdialog.h" +#include "kship.h" +#include "kshiplist.h" +#include "kstatdialog.h" + +class KBattleshipWindow : public KMainWindow +{ + Q_OBJECT +public: + enum{ID_STATUS_MSG, ID_PLAYER_OWN, ID_PLAYER_ENEMY}; + KBattleshipWindow(); + ~KBattleshipWindow(); + + void init(); + + KShip *shipAt(int fieldx, int fieldy); + KShip *enemyShipAt(int fieldx, int fieldy); + +private slots: + void changeShipPlacementDirection(); + void slotConfigureNotifications(); + void slotLost(KMessage *msg); + void slotStatusMsg(const QString &text); + void slotReceivedEnemyFieldData(int fieldx, int fieldx1, int enemystate, int xstart, int xstop, int ystart, int ystop, bool death); + void slotSendEnemyFieldState(int, int); + void slotChangeOwnPlayer(const QString &text); + void slotChangeEnemyPlayer(const QString &text); + void slotSendVersion(); + void slotSendGreet(); + void slotShipsReady(); + void slotSetPlaceable(bool place); + void slotSetShootable(bool shoot); + void slotEnemyFieldClick(int fieldx, int fieldy); + void slotSendMessage(int fieldx, int fieldy, int state); + void slotSendMessage(KMessage *msg); + void slotClientLost(); + void slotServerLost(); + void slotServerReplay(); + void slotClientReplay(); + void slotAIReady(); + void slotAIShootsAt(const QPoint pos); + void slotDeleteAI(); + void slotRestartAI(); + void slotSinglePlayer(); + void slotServerConnect(); + void slotDeleteConnectDialog(); + void slotNewServer(); + void slotDeleteServerDialog(); + void slotHighscore(); + void slotShowGrid(); + void slotStartBattleshipGame(); + void slotStartBattleshipGame(bool clearstat); + void slotStartBattleshipServer(); + /** + * Get server to connect to from "Connect to server" dialog. + */ + void slotConnectToBattleshipServer(); + void slotConnectToBattleshipServer(const QString &host, int port, const QString &nickname); + void slotPlaceShipPreview(int fieldx, int fieldy); + void slotPlaceShip(int fieldx, int fieldy); + void slotChangeOwnFieldData(int fieldx, int fieldy, int type); + void slotChangeEnemyFieldData(int fieldx, int fieldy, int type); + void slotUpdateHighscore(); + void slotAbortNetworkGame(); + void slotReplay(); + void slotReplayRequest(); + void slotChangedNickCommand(const QString &text); + void slotSendChatMessage(const QString &text); + void slotEnemyClientInfo(); + void slotReceivedClientInformation(const QString &client, const QString &clientVersion, const QString &clientInformation, const QString &protocolVersion); + +private: + bool shift; + void initActions(); + void initStatusBar(); + void initView(); + void initChat(); + void initShipPlacing(); + void saveOptions(); + void readOptions(); + + void cleanup(bool placechange = true); + void playSound(bool enemy, int fieldstate); + void parseCommandLine(); + + bool m_placeable; + bool m_shootable; + bool m_aiPlaying; + bool m_serverHasClient; + bool m_lost; + int m_aiHits; + + QString m_enemyClient; + QString m_enemyClientVersion; + QString m_enemyClientDescription; + QString m_enemyProtocolVersion; + + KConfig *m_config; + KBAIPlayer *m_aiPlayer; + KonnectionHandling *m_connection; + KBattleshipServer *m_kbserver; + KBattleshipClient *m_kbclient; + KChatWidget *m_chat; + KStatDialog *m_stat; + KBattleshipView *m_view; + KAction *m_gameServerConnect; + KAction *m_gameNewServer; + KAction *m_gameQuit; + KAction *m_gameEnemyInfo; + KAction *m_gameSingle; + KToggleAction *m_configSound; + KToggleAction *m_configGrid; + KClientDialog *m_client; + KServerDialog *m_server; + KShipList *m_ownshiplist; + KShipList *m_enemyshiplist; + QString m_ownNickname; + QString m_enemyNickname; + +}; +#endif diff --git a/kbattleship/kbattleship/kbattleshipclient.cpp b/kbattleship/kbattleship/kbattleshipclient.cpp new file mode 100644 index 00000000..d8ae2a75 --- /dev/null +++ b/kbattleship/kbattleship/kbattleshipclient.cpp @@ -0,0 +1,84 @@ +/*************************************************************************** + kbattleshipclient.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#ifdef HAVE_STROPTS_H +#include +#endif +#ifdef HAVE_SYS_FILIO_H +#include +#endif +#include +#include +#include "kmessage.h" +#include "kbattleshipclient.moc" + +KBattleshipClient::KBattleshipClient(const QString &host, int port) : KExtendedSocket(host, port, inetSocket) +{ +} + +void KBattleshipClient::init() +{ + if(connect()) + { + emit sigSocketFailure(status()); + return; + } + + m_readNotifier = new QSocketNotifier(fd(), QSocketNotifier::Read, this); + QObject::connect(m_readNotifier, SIGNAL(activated(int)), SLOT(slotReadData())); + emit sigConnected(); +} + +void KBattleshipClient::sendMessage(KMessage *msg) +{ + QCString post = msg->sendStream().utf8(); + writeBlock(post.data(), post.length()); + emit sigMessageSent(msg); +} + +void KBattleshipClient::slotReadData() +{ + int len; + ioctl(fd(), FIONREAD, &len); + if(!len) + { + delete m_readNotifier; + m_readNotifier = 0; + emit sigEndConnect(); + return; + } + + char *buf = new char[len + 1]; + readBlock(buf, len); + buf[len] = 0; + m_readBuffer += QString::fromUtf8(buf); + delete []buf; + int pos; + while ((pos = m_readBuffer.find("")) >= 0) + { + pos += 11; // Length of "" + KMessage *msg = new KMessage(); + msg->setDataStream(m_readBuffer.left(pos)); + m_readBuffer.remove(0, pos); + emit sigNewMessage(msg); + } +} diff --git a/kbattleship/kbattleship/kbattleshipclient.h b/kbattleship/kbattleship/kbattleshipclient.h new file mode 100644 index 00000000..d69ec7f9 --- /dev/null +++ b/kbattleship/kbattleship/kbattleshipclient.h @@ -0,0 +1,49 @@ +/*************************************************************************** + kbattleshipclient.h + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBATTLESHIPCLIENT_H +#define KBATTLESHIPCLIENT_H + +#include +#include +#include "kmessage.h" + +class KBattleshipClient : public KExtendedSocket +{ + Q_OBJECT +public: + KBattleshipClient(const QString &host, int port); + + void init(); + void sendMessage(KMessage *msg); + +private slots: + void slotReadData(); + +signals: + void sigConnected(); + void sigEndConnect(); + void sigSocketFailure(int); + void sigNewMessage(KMessage *); + void sigMessageSent(KMessage *); + +private: + QSocketNotifier *m_readNotifier; + QString m_readBuffer; +}; + +#endif diff --git a/kbattleship/kbattleship/kbattleshipserver.cpp b/kbattleship/kbattleship/kbattleshipserver.cpp new file mode 100644 index 00000000..d03bd213 --- /dev/null +++ b/kbattleship/kbattleship/kbattleshipserver.cpp @@ -0,0 +1,124 @@ +/*************************************************************************** + kbattleshipserver.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include + +#include +#ifdef HAVE_STROPTS_H +#include +#endif +#ifdef HAVE_SYS_FILIO_H +#include +#endif +#include +#include +#include +#include +#include "kbattleshipserver.moc" + +KBattleshipServer::KBattleshipServer(int port, const QString& name) + : KExtendedSocket(QString::null, port, inetSocket | passiveSocket), m_name(name) +{ + m_port = port; + m_serverSocket = 0; +} + +void KBattleshipServer::init() +{ + if(listen()) + { + KMessageBox::error(0L, i18n("Failed to bind to local port \"%1\"\n\nPlease check if another KBattleship server instance\nis running or another application uses this port.").arg(m_port)); + emit sigServerFailure(); + return; + } + m_service.setServiceName(m_name); + m_service.setType(BATTLESHIP_SERVICE); + m_service.setPort(m_port); + m_service.publishAsync(); + m_connectNotifier = new QSocketNotifier(fd(), QSocketNotifier::Read, this); + QObject::connect(m_connectNotifier, SIGNAL(activated(int)), SLOT(slotNewConnection())); +} + +void KBattleshipServer::slotNewConnection() +{ + KExtendedSocket *sock; + accept(sock); + if(sock && m_serverSocket == 0) + { + m_service.stop(); + m_serverSocket = sock; + m_readNotifier = new QSocketNotifier(sock->fd(), QSocketNotifier::Read, this); + QObject::connect(m_readNotifier, SIGNAL(activated(int)), this, SLOT(slotReadClient())); + emit sigNewConnect(); + } + else + delete sock; +} + +void KBattleshipServer::slotReadClient() +{ + int len; + ioctl(m_serverSocket->fd(), FIONREAD, &len); + if(!len) + { + slotDiscardClient(i18n("The connection broke down!"), false, true); + return; + } + + char *buf = new char[len + 1]; + m_serverSocket->readBlock(buf, len); + buf[len] = 0; + m_readBuffer += QString::fromUtf8(buf); + delete []buf; + int pos; + while ((pos = m_readBuffer.find("")) >= 0) + { + pos += 11; // Length of "" + KMessage *msg = new KMessage(); + msg->setDataStream(m_readBuffer.left(pos)); + m_readBuffer.remove(0, pos); + emit sigNewMessage(msg); + } +} + +void KBattleshipServer::sendMessage(KMessage *msg) +{ + QCString post = msg->sendStream().utf8(); + m_serverSocket->writeBlock(post.data(), post.length()); + emit sigMessageSent(msg); +} + +void KBattleshipServer::slotDiscardClient(const QString &reason, bool kmversion, bool bemit) +{ + KMessage *msg = new KMessage(KMessage::DISCARD); + msg->addField("reason", reason); + if(kmversion) + msg->addField("kmversion", "true"); + else + msg->addField("kmversion", "false"); + QCString post = msg->sendStream().utf8(); + m_serverSocket->writeBlock(post.data(), post.length()); + delete msg; + + delete m_readNotifier; + m_readNotifier = 0; + delete m_serverSocket; + m_serverSocket = 0; + + if(bemit) + emit sigEndConnect(); +} diff --git a/kbattleship/kbattleship/kbattleshipserver.h b/kbattleship/kbattleship/kbattleshipserver.h new file mode 100644 index 00000000..46f815e0 --- /dev/null +++ b/kbattleship/kbattleship/kbattleshipserver.h @@ -0,0 +1,60 @@ +/*************************************************************************** + kbattleshipserver.h + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBATTLESHIPSERVER_H +#define KBATTLESHIPSERVER_H + +#include +#include +#include +#include "kmessage.h" + +class KBattleshipServer : public KExtendedSocket +{ + Q_OBJECT +public: + KBattleshipServer(int port, const QString& name); + void init(); + void sendMessage(KMessage *msg); + +public slots: + void slotDiscardClient(const QString &reason, bool kmversion, bool bemit); + +private slots: + void slotNewConnection(); + void slotReadClient(); + +signals: + void sigServerFailure(); + void sigNewConnect(); + void sigEndConnect(); + void sigNewMessage(KMessage *); + void sigMessageSent(KMessage *); + +private: + QSocketNotifier *m_connectNotifier; + QSocketNotifier *m_readNotifier; + KExtendedSocket *m_serverSocket; + QString m_readBuffer; + DNSSD::PublicService m_service; + int m_port; + QString m_name; +}; + +#define BATTLESHIP_SERVICE "_kbattleship._tcp" + +#endif diff --git a/kbattleship/kbattleship/kbattleshipui.rc b/kbattleship/kbattleship/kbattleshipui.rc new file mode 100644 index 00000000..491fbb03 --- /dev/null +++ b/kbattleship/kbattleship/kbattleshipui.rc @@ -0,0 +1,17 @@ + + + + + &Game + + + + + + &Settings + + + + + + diff --git a/kbattleship/kbattleship/kbattleshipview.cpp b/kbattleship/kbattleship/kbattleshipview.cpp new file mode 100644 index 00000000..86e8cbf6 --- /dev/null +++ b/kbattleship/kbattleship/kbattleshipview.cpp @@ -0,0 +1,295 @@ +/*************************************************************************** + kbattleshipview.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include + +#include +#include +#include "kbattleship.h" +#include "kbattleshipview.moc" + +KBattleshipView::KBattleshipView(QWidget *parent, const char *name, bool draw) + : QWidget(parent, name, WResizeNoErase), m_drawGrid(draw) +{ + setFixedSize(20 * 32 + 30, 10 * 32 + 20); + setBackgroundMode(NoBackground); + setMouseTracking(true); + installEventFilter(this); + + m_decide = false; + m_lastX = 0; + m_lastY = 0; + m_battlefield = 0; +} + +KBattleshipView::~KBattleshipView() +{ + delete m_battlefield; +} + +void KBattleshipView::startDrawing() +{ + m_battlefield = new KBattleField(this, m_drawGrid); +} + +void KBattleshipView::clearField() +{ + m_battlefield->clearOwnField(); + m_battlefield->clearEnemyField(); +} + +int KBattleshipView::ownFieldState(int fieldx, int fieldy) +{ + return m_battlefield->ownState(fieldx, fieldy); +} + +int KBattleshipView::enemyFieldState(int &fieldx, int &fieldy) +{ + return m_battlefield->enemyState(fieldx, fieldy); +} + +void KBattleshipView::previewShip(int fieldx, int fieldy, int type, bool rotate) +{ + m_battlefield->setPreviewState(fieldx, fieldy, type, rotate); +} + +void KBattleshipView::changeOwnFieldData(int fieldx, int fieldy, int type) +{ + m_battlefield->setOwnState(fieldx, fieldy, type); + m_battlefield->drawField(); +} + +void KBattleshipView::changeEnemyFieldData(int fieldx, int fieldy, int type) +{ + m_battlefield->setEnemyState(fieldx, fieldy, type); + m_battlefield->drawField(); +} + +void KBattleshipView::drawEnemyShipsAI(KShipList *list) +{ + KShip *ship; + int state; + int grid = m_battlefield->gridSize(); + int width = m_battlefield->enemyRect().width() / grid; + int height = m_battlefield->enemyRect().height() / grid; + + for(int i = 0; i < width; i++) + { + for(int j = 0; j < height; j++) + { + ship = list->shipAt(i, j); + state = enemyFieldState(i, j); + if (ship && state != KBattleField::HIT && state != KBattleField::DEATH) + { + changeEnemyFieldData(i, j, ship->shipTypeEnum(i, j)); + } + } + } +} + +void KBattleshipView::drawEnemyShipsHuman(KMessage *msg, KShipList *list) +{ + int posx, posy, placedLeft; + bool left; + int i = 3; + while (!msg->field(QString("ship%1").arg(i)).isNull()) + { + posx = msg->field(QString("ship%1").arg(i)).section(" ", 0, 0).toInt(); + posy = msg->field(QString("ship%1").arg(i)).section(" ", 1, 1).toInt(); + placedLeft = msg->field(QString("ship%1").arg(i)).section(" ", 2, 2).toInt(); + if (placedLeft == 0) left = false; + else left = true; + list->addNewShip(!left, posx, posy); + i--; + } + drawEnemyShipsAI(list); +} + +KMessage *KBattleshipView::getAliveShips(KShipList *list) +{ + KShip *ship; + QString shipPos, shipNum; + int shipType; + int grid = m_battlefield->gridSize(); + int width = m_battlefield->enemyRect().width() / grid; + int height = m_battlefield->enemyRect().height() / grid; + KMessage *msg = new KMessage(KMessage::WON); + bool shipsFound[4] = {false, false, false, false}; + + for(int i = 0; i < width; i++) + { + for(int j = 0; j < height; j++) + { + ship = list->shipAt(i, j); + shipType = list->shipTypeAt(i, j); + if (ship && !shipsFound[shipType]) + { + shipPos.sprintf("%d %d %d", i, j, ship->placedLeft()); + shipsFound[shipType] = true; + shipNum.sprintf("ship%d",shipType); + msg->addField(shipNum, shipPos); + } + } + } + return msg; +} + +bool KBattleshipView::eventFilter(QObject *object, QEvent *event) +{ + if(event->type() == QEvent::KeyPress && m_decide) + { + QKeyEvent *keyEvent = static_cast(event); + if(keyEvent->key() == Key_Shift){ + emit sigMouseOverField(m_lastX, m_lastY); + emit changeShipPlacementDirection(); + } + } + else if(event->type() == QEvent::KeyRelease && m_decide) + { + QKeyEvent *keyEvent = static_cast(event); + if(keyEvent->key() == Key_Shift){ + emit sigMouseOverField(m_lastX, m_lastY); + emit changeShipPlacementDirection(); + } + } + else if(event->type() == QEvent::MouseButtonRelease) + { + m_decide = false; + + QMouseEvent *mouseEvent = static_cast(event); + + if(mouseEvent->button() == RightButton){ + emit sigMouseOverField(m_lastX, m_lastY); + emit changeShipPlacementDirection(); + return true; + } + + if(mouseEvent->button() != LeftButton) + return false; + + QPoint point(mouseEvent->x(), mouseEvent->y()); + QRect ownRect = m_battlefield->ownRect(); + QRect enemyRect = m_battlefield->enemyRect(); + + QRect newRect; + + int fieldx = 0; + int fieldy = 0; + + if(ownRect.contains(point)) + newRect = ownRect; + else if(enemyRect.contains(point)) + newRect = enemyRect; + else + return false; + + int j = -1; + + for(int i = newRect.left(); i <= newRect.right(); i += m_battlefield->gridSize()) + { + j++; + QRect tempRect(i, newRect.top(), m_battlefield->gridSize(), newRect.bottom() - newRect.top()); + + if(tempRect.contains(point)) + { + fieldx = j; + break; + } + } + + j = -1; + + for(int i = newRect.top(); i <= newRect.bottom(); i += m_battlefield->gridSize()) + { + j++; + QRect tempRect(newRect.left(), i, newRect.right() - newRect.left(), m_battlefield->gridSize()); + + if(tempRect.contains(point)) + { + fieldy = j; + break; + } + } + + if( newRect == ownRect) + emit sigOwnFieldClicked(fieldx, fieldy); + else if(newRect == enemyRect) + emit sigEnemyFieldClicked(fieldx, fieldy); + + return true; + } + else if(event->type() == QEvent::MouseMove) + { + setFocus(); + m_decide = true; + + QMouseEvent *mouseEvent = static_cast(event); + + QPoint point(mouseEvent->x(), mouseEvent->y()); + QRect ownRect = m_battlefield->ownRect(); + + int fieldx = 0; + int fieldy = 0; + + if(ownRect.contains(point)) + { + int j = -1; + + for(int i = ownRect.left(); i <= ownRect.right(); i += m_battlefield->gridSize()) + { + j++; + QRect tempRect(i, ownRect.top(), m_battlefield->gridSize(), ownRect.bottom() - ownRect.top()); + + if(tempRect.contains(point)) + { + fieldx = j; + break; + } + } + + j = -1; + + for(int i = ownRect.top(); i <= ownRect.bottom(); i += m_battlefield->gridSize()) + { + j++; + QRect tempRect(ownRect.left(), i, ownRect.right() - ownRect.left(), m_battlefield->gridSize()); + + if(tempRect.contains(point)) + { + fieldy = j; + break; + } + } + + m_lastX = fieldx; + m_lastY = fieldy; + + emit sigMouseOverField(fieldx, fieldy); + } + else + m_battlefield->drawField(); + + return true; + } + else if(event->type() == QEvent::Paint) + { + m_battlefield->drawField(); + return true; + } + + return QWidget::eventFilter(object, event); +} diff --git a/kbattleship/kbattleship/kbattleshipview.h b/kbattleship/kbattleship/kbattleshipview.h new file mode 100644 index 00000000..f7f0a953 --- /dev/null +++ b/kbattleship/kbattleship/kbattleshipview.h @@ -0,0 +1,72 @@ +/*************************************************************************** + kbattleshipview.h + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBATTLESHIPVIEW_H +#define KBATTLESHIPVIEW_H + +#include + +#include +#include +#include +#include + +#include "kbattlefield.h" +#include "kmessage.h" +#include "kship.h" +#include "kshiplist.h" + +class KBattleshipView : public QWidget +{ + Q_OBJECT +public: + KBattleshipView(QWidget *parent = 0, const char *name = 0, bool draw = false); + ~KBattleshipView(); + + KBattleField *field() { return m_battlefield; } + + void startDrawing(); + void clearField(); + void changeOwnFieldData(int fieldx, int fieldy, int type); + void changeEnemyFieldData(int fieldx, int fieldy, int type); + + void previewShip(int fieldx, int fieldy, int type, bool rotate); + + int ownFieldState(int fieldx, int fieldy); + int enemyFieldState(int &fieldx, int &fieldy); + + void drawEnemyShipsAI(KShipList *list); + void drawEnemyShipsHuman(KMessage *msg, KShipList *list); + KMessage *getAliveShips(KShipList *list); + +signals: + void sigEnemyFieldClicked(int, int); + void sigOwnFieldClicked(int, int); + void sigMouseOverField(int, int); + void changeShipPlacementDirection(); + +private: + bool eventFilter(QObject *object, QEvent *event); + + KBattleField *m_battlefield; + bool m_drawGrid; + bool m_decide; + int m_lastX; + int m_lastY; +}; + +#endif diff --git a/kbattleship/kbattleship/kbchooserstrategy.cpp b/kbattleship/kbattleship/kbchooserstrategy.cpp new file mode 100644 index 00000000..f365c6d1 --- /dev/null +++ b/kbattleship/kbattleship/kbchooserstrategy.cpp @@ -0,0 +1,139 @@ +/*************************************************************************** + kbchooserstrategy.cpp + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kbchooserstrategy.h" + +#include + +#include "kbrandomshotstrategy.h" +#include "kbdiagonalwrapstrategy.h" +#include "kbhorizontalstepstrategy.h" +#include "kbverticalstepstrategy.h" + +#define MAX_CHILD_NUM 4 + +KBChooserStrategy::KBChooserStrategy(KBStrategy *parent) : KBStrategy(parent) +{ + m_destroyer = new KBDestroyShipStrategy(this); + m_destroying = false; + + m_child = 0; + + m_random = new KRandomSequence(KApplication::random()); +} + +KBChooserStrategy::~KBChooserStrategy() +{ + delete m_destroyer; + delete m_child; + delete m_random; +} + +void KBChooserStrategy::init(KBattleField *field, const QRect &field_rect) +{ + KBStrategy::init(field, field_rect); + + if(m_destroyer != 0) + m_destroyer->init(field, field_rect); + + advance(); +} + +const QPoint KBChooserStrategy::nextShot() +{ + if(hasMoreShots()) + { + if(m_destroying) + return m_destroyer->nextShot(); + else if(advance()) + return m_child->nextShot(); + } + + return QPoint(0, 0); +} + +bool KBChooserStrategy::advance() +{ + if(!m_destroying && m_prevShots.count() % 5 == 0) + { + delete m_child; + + switch(m_random->getLong(MAX_CHILD_NUM)) + { + case 0: + m_child = new KBVerticalStepStrategy(this); + break; + + case 1: + m_child = new KBHorizontalStepStrategy(this); + break; + + case 2: + m_child = new KBDiagonalWrapStrategy(this); + break; + + default: + m_child = new KBRandomShotStrategy(this); + break; + } + + m_child->init(m_battleField, m_fieldRect); + } + + return true; +} + +bool KBChooserStrategy::hasMoreShots() +{ + if(m_parent == 0) + { + if((!m_destroying) && m_prevShots.count() > 0) + { + QPoint pos = m_prevShots.last(); + int state = m_battleField->ownState(pos.x(), pos.y()); + if(state == KBattleField::HIT) + { + m_destroying = true; + m_destroyer->destroyShipAt(pos); + } + } + + if(m_destroying) + { + if(m_destroyer->hasMoreShots()) + return true; + else + m_destroying = false; + } + } + + for(int row = 0; row < m_fieldRect.height(); row++) + { + for(int col = 0; col < m_fieldRect.width(); col++) + { + if(enemyFieldStateAt(col, row) != KBStrategy::SHOT) + return true; + } + } + + return false; +} + +void KBChooserStrategy::shotAt(const QPoint &pos) +{ + m_prevShots.append(pos); + m_child->shotAt(pos); +} diff --git a/kbattleship/kbattleship/kbchooserstrategy.h b/kbattleship/kbattleship/kbchooserstrategy.h new file mode 100644 index 00000000..298cbe68 --- /dev/null +++ b/kbattleship/kbattleship/kbchooserstrategy.h @@ -0,0 +1,46 @@ +/*************************************************************************** + kbchooserstrategy.h + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBCHOOSERSTRATEGY_H +#define KBCHOOSERSTRATEGY_H + +#include + +#include "kbstrategy.h" +#include "kbdestroyshipstrategy.h" + +class KBChooserStrategy : public KBStrategy +{ +public: + KBChooserStrategy(KBStrategy *parent = 0); + virtual ~KBChooserStrategy(); + + virtual void init(KBattleField *field, const QRect &field_rect); + virtual const QPoint nextShot(); + virtual bool hasMoreShots(); + virtual void shotAt(const QPoint &pos); + +private: + bool advance(); + + KBStrategy *m_child; + KBDestroyShipStrategy *m_destroyer; + KRandomSequence *m_random; + + bool m_destroying; +}; + +#endif diff --git a/kbattleship/kbattleship/kbdestroyshipstrategy.cpp b/kbattleship/kbattleship/kbdestroyshipstrategy.cpp new file mode 100644 index 00000000..714bdd7c --- /dev/null +++ b/kbattleship/kbattleship/kbdestroyshipstrategy.cpp @@ -0,0 +1,390 @@ +/*************************************************************************** + kbdestroyshipstrategy.cpp + ---------- + Developers: (c) 2001 Kevin Krammer + (c) 2001 Nikolas Zimmermann + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kbdestroyshipstrategy.h" + +KBDestroyShipStrategy::KBDestroyShipStrategy(KBStrategy *parent) : KBStrategy(parent) +{ + m_working = false; +} + +void KBDestroyShipStrategy::init(KBattleField *field, const QRect &field_rect) +{ + KBStrategy::init(field, field_rect); + m_working = false; +} + +const QPoint KBDestroyShipStrategy::nextShot() +{ + if(hasMoreShots()) + return QPoint(m_column, m_row); + else + return m_start; +} + +bool KBDestroyShipStrategy::hasMoreShots() +{ + if(!m_working) + return false; + + if(shipDestroyed()) + { + m_working = false; + markBorderingFields(); + return false; + } + + if(enemyFieldStateAt(m_column, m_row) != KBStrategy::SHOT) + return true; + + // last shot was no success :( + if(m_battleField->ownState(m_column, m_row) == KBattleField::WATER) + { + m_column = m_start.x(); + m_row = m_start.y(); + } + + switch(m_direction) + { + case VERTICAL: + if(searchUp() || searchDown()) + return true; + else + { + //kdDebug << "KBDestroyShipStrategy: failed vertical search!" << endl; + m_working = false; + } + break; + + case HORIZONTAL: + if(searchLeft() || searchRight()) + return true; + else + { + //kdDebug << "KBDestroyShipStrategy: failed horizontal search!" << endl; + m_working = false; + } + break; + + default: + int up = m_row > 0 ? m_battleField->ownState(m_column, m_row - 1) : -1; + int down = m_row < (m_fieldRect.height() - 1) ? m_battleField->ownState(m_column, m_row + 1) : -1; + int left = m_column > 0 ? m_battleField->ownState(m_column - 1, m_row) : -1; + int right = m_column < (m_fieldRect.width() - 1) ? m_battleField->ownState(m_column + 1, m_row) : -1; + + if((up != -1 && up == KBattleField::HIT) || (down != -1 && down == KBattleField::HIT)) + { + m_direction = VERTICAL; + return hasMoreShots(); + } + + if((left != -1 && left == KBattleField::HIT) || (right != -1 && right == KBattleField::HIT)) + { + m_direction = HORIZONTAL; + return hasMoreShots(); + } + + if(searchUp() || searchDown() || searchLeft() || searchRight()) + return true; + else + { + //kdDebug << "KBDestroyStrategy: ship unsinkable?" << endl; + m_working = false; + } + break; + } + return false; +} + +void KBDestroyShipStrategy::shotAt(const QPoint &pos) +{ + m_prevShots.append(pos); +} + +void KBDestroyShipStrategy::destroyShipAt(const QPoint pos) +{ + if(enemyFieldStateAt(pos.x(), pos.y()) == FREE || m_battleField->ownState(pos.x(), pos.y()) == KBattleField::DEATH || m_battleField->ownState(pos.x(), pos.y()) == KBattleField::WATER) + m_working = false; + else + { + m_working = true; + m_start = pos; + m_column = pos.x(); + m_row = pos.y(); + m_direction = NODIR; + } +} + +bool KBDestroyShipStrategy::searchUp() +{ + int row = m_row; + int prevCol = m_column - 1; + int nextCol = m_column + 1; + + while(row >= 0 && (m_row - row) < 4 && enemyFieldStateAt(m_column, row) == KBStrategy::SHOT) + { + if(m_battleField->ownState(m_column, row) == KBattleField::WATER) + return false; + + row--; + + bool leftOK = true; + bool rightOK = true; + if(prevCol >= 0) + leftOK = (enemyFieldStateAt(prevCol, row) == FREE) || (m_battleField->ownState(prevCol, row) == KBattleField::WATER); + + if(nextCol < m_fieldRect.width()) + rightOK = (enemyFieldStateAt(nextCol, row) == FREE) || (m_battleField->ownState(nextCol, row) == KBattleField::WATER); + + if(!(rightOK && leftOK)) + return false; + } + + if(row < 0 || (m_row - row) >= 4) + return false; + + m_row = row; + return true; +} + +bool KBDestroyShipStrategy::searchDown() +{ + int row = m_row; + int prevCol = m_column - 1; + int nextCol = m_column + 1; + + while(row < m_fieldRect.height() && (row - m_row) < 4 && enemyFieldStateAt(m_column, row) == KBStrategy::SHOT) + { + if(m_battleField->ownState(m_column, row) == KBattleField::WATER) + return false; + + row++; + + bool leftOK = true; + bool rightOK = true; + if(prevCol >= 0) + leftOK = (enemyFieldStateAt(prevCol, row) == FREE) || (m_battleField->ownState(prevCol, row) == KBattleField::WATER); + + if(nextCol < m_fieldRect.width()) + rightOK = (enemyFieldStateAt(nextCol, row) == FREE) || (m_battleField->ownState(nextCol, row) == KBattleField::WATER); + + if(!(rightOK && leftOK)) + return false; + } + + if(row >= m_fieldRect.height() || (row - m_row) >= 4) + return false; + + m_row = row; + return true; +} + +bool KBDestroyShipStrategy::searchLeft() +{ + int col = m_column; + int prevRow = m_row - 1; + int nextRow = m_row + 1; + + while(col >= 0 && (m_column - col) < 4 && enemyFieldStateAt(col, m_row) == KBStrategy::SHOT) + { + if(m_battleField->ownState(col, m_row) == KBattleField::WATER) + return false; + + col--; + + bool upOK = true; + bool downOK = true; + if(prevRow >= 0) + upOK = (enemyFieldStateAt(col, prevRow) == FREE) || (m_battleField->ownState(col, prevRow) == KBattleField::WATER); + + if(nextRow < m_fieldRect.height()) + downOK = (enemyFieldStateAt(col, nextRow) == FREE) || (m_battleField->ownState(col, nextRow) == KBattleField::WATER); + + if(!(upOK && downOK)) + return false; + } + + if(col < 0 || (m_column - col) >= 4) + return false; + + m_column = col; + return true; +} + +bool KBDestroyShipStrategy::searchRight() +{ + int col = m_column; + int prevRow = m_row - 1; + int nextRow = m_row + 1; + + while(col < m_fieldRect.width() && (col - m_column) < 4 && enemyFieldStateAt(col, m_row) == KBStrategy::SHOT) + { + if(m_battleField->ownState(col, m_row) == KBattleField::WATER) + return false; + + col++; + + bool upOK = true; + bool downOK = true; + if(prevRow >= 0) + upOK = (enemyFieldStateAt(col, prevRow) == FREE) || (m_battleField->ownState(col, prevRow) == KBattleField::WATER); + + if(nextRow < m_fieldRect.height()) + downOK = (enemyFieldStateAt(col, nextRow) == FREE) || (m_battleField->ownState(col, nextRow) == KBattleField::WATER); + + if(!(upOK && downOK)) + return false; + } + + if(col >= m_fieldRect.width() || (col - m_column) >= 4) + return false; + + m_column = col; + return true; +} + +bool KBDestroyShipStrategy::shipDestroyed() +{ + int col = m_start.x(); + int row = m_start.y(); + int state = m_battleField->ownState(col, row); + + while(m_direction != HORIZONTAL && row >= 0 && state != KBattleField::FREE && state != KBattleField::WATER) + { + if(enemyFieldStateAt(col, row) == SHIP) + return false; + + row--; + if(row >= 0) + state = m_battleField->ownState(col, row); + } + + row = m_start.y(); + state = m_battleField->ownState(col, row); + while(m_direction != HORIZONTAL && row < m_fieldRect.height() && state != KBattleField::FREE && state != KBattleField::WATER) + { + if(enemyFieldStateAt(col, row) == SHIP) + return false; + + row++; + if(row < m_fieldRect.height()) + state = m_battleField->ownState(col, row); + } + + row = m_start.y(); + state = m_battleField->ownState(col, row); + while(m_direction != VERTICAL && col >= 0 && state != KBattleField::FREE && state != KBattleField::WATER) + { + if(enemyFieldStateAt(col, row) == SHIP) + return false; + + col--; + if(col >= 0) + state = m_battleField->ownState(col, row); + } + + col = m_start.x(); + state = m_battleField->ownState(col, row); + while(m_direction != VERTICAL && col < m_fieldRect.width() && state != KBattleField::FREE && state != KBattleField::WATER) + { + if(enemyFieldStateAt(col, row) == SHIP) + return false; + + col++; + if(col < m_fieldRect.width()) + state = m_battleField->ownState(col, row); + } + + return true; +} + +void KBDestroyShipStrategy::markBorderingFields() +{ + int col = m_start.x(); + int row = m_start.y(); + int i,j; + + if (m_direction == VERTICAL) + { + while (m_fieldRect.contains(col, row) && + m_battleField->ownState(col, row) == KBattleField::HIT) + { + row--; + } + if (row >= 0) + { // above the ship + setViablePos(col, row, false); + } + row++; + i = col+1; // right of the ship + j = col-1; // left of the ship + while (m_fieldRect.contains(col, row) && + m_battleField->ownState(col, row) == KBattleField::HIT) + { + if (m_fieldRect.contains(i, row)) + setViablePos(i, row, false); + if (m_fieldRect.contains(j, row)) + setViablePos(j, row, false); + setViablePos(col, row, false); + row++; + } + if (m_fieldRect.contains(col, row)) + { // below the ship + setViablePos(col, row, false); + } + } + else if (m_direction == HORIZONTAL) + { + while (m_fieldRect.contains(col, row) && + m_battleField->ownState(col, row) == KBattleField::HIT) + { + col--; + } + if (col >= 0) + { // left of the ship + setViablePos(col, row, false); + } + col++; + i = row+1; // below the ship + j = row-1; // above the ship + while (m_fieldRect.contains(col, row) && + m_battleField->ownState(col, row) == KBattleField::HIT) + { + if (m_fieldRect.contains(col, i)) + setViablePos(col, i, false); + if (m_fieldRect.contains(col, j)) + setViablePos(col, j, false); + setViablePos(col, row, false); + col++; + } + if (m_fieldRect.contains(col, row)) + { // right of the ship + setViablePos(col, row, false); + } + } + else + { + if (row > 0) + setViablePos(col, (row-1), false); + if (row < (m_fieldRect.height()-1)) + setViablePos(col, (row+1), false); + if (col > 0) + setViablePos((col-1), row, false); + if (col < (m_fieldRect.width()-1)) + setViablePos((col+1), row, false); + } +} diff --git a/kbattleship/kbattleship/kbdestroyshipstrategy.h b/kbattleship/kbattleship/kbdestroyshipstrategy.h new file mode 100644 index 00000000..0fb6ca92 --- /dev/null +++ b/kbattleship/kbattleship/kbdestroyshipstrategy.h @@ -0,0 +1,55 @@ +/*************************************************************************** + kbdestroyshipstratgey.h + ---------- + Developers: (c) 2001 Kevin Krammer + (c) 2001 Nikolas Zimmermann + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBDESTROYSHIPSTRATEGY_H +#define KBDESTROYSHIPSTRATEGY_H + +#include "kbstrategy.h" + +class KBDestroyShipStrategy : public KBStrategy +{ +public: + KBDestroyShipStrategy(KBStrategy *parent = 0); + + virtual void init(KBattleField *field, const QRect &field_rect); + virtual const QPoint nextShot(); + virtual bool hasMoreShots(); + virtual void shotAt(const QPoint &pos); + + virtual void destroyShipAt(const QPoint pos); + +private: + enum {NODIR, VERTICAL, HORIZONTAL}; + + bool m_working; + QPoint m_start; + + int m_column; + int m_row; + + int m_direction; + + virtual bool searchUp(); + virtual bool searchDown(); + virtual bool searchLeft(); + virtual bool searchRight(); + + virtual bool shipDestroyed(); + virtual void markBorderingFields(); +}; + +#endif diff --git a/kbattleship/kbattleship/kbdiagonalshotstrategy.cpp b/kbattleship/kbattleship/kbdiagonalshotstrategy.cpp new file mode 100644 index 00000000..f0e0d6c3 --- /dev/null +++ b/kbattleship/kbattleship/kbdiagonalshotstrategy.cpp @@ -0,0 +1,110 @@ +/*************************************************************************** + kbdiagonalshotstrategy.cpp + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kbdiagonalshotstrategy.h" + +KBDiagonalShotStrategy::KBDiagonalShotStrategy(KBStrategy *parent) : KBStrategy(parent) +{ + m_column = 0; + m_row = 0; + m_vertical = 0; + m_horizontal = 0; +} + +const QPoint KBDiagonalShotStrategy::nextShot() +{ + if(hasMoreShots()) + return QPoint(m_column, m_row); + + return QPoint(0,0); +} + +bool KBDiagonalShotStrategy::advance() +{ + while (m_fieldRect.contains(m_column, m_row)) + { + if(enemyFieldStateAt(m_column, m_row) != KBStrategy::SHOT) + return true; + m_column += m_horizontal; + m_row += m_vertical; + } + + return false; +} + +bool KBDiagonalShotStrategy::hasMoreShots() +{ + return advance(); +} + +void KBDiagonalShotStrategy::shotAt(const QPoint &pos) +{ + m_prevShots.append(pos); +} + +void KBDiagonalShotStrategy::startAt(int col, int row, Direction dir) +{ + m_column = col; + m_row = row; + + switch(dir) + { + case LEFTUP: + m_vertical = -1; + m_horizontal = -1; + break; + + case LEFTDOWN: + m_vertical = 1; + m_horizontal = -1; + break; + + case RIGHTUP: + m_vertical = -1; + m_horizontal = 1; + break; + + case RIGHTDOWN: + m_vertical = 1; + m_horizontal = 1; + break; + + default: + m_vertical = 0; + m_horizontal = 0; + break; + } +} + +QPoint KBDiagonalShotStrategy::endPoint() +{ + int row = m_row; + int col = m_column; + + if(m_vertical == 0 || m_horizontal == 0) + return QPoint(col, row); + + while(m_fieldRect.contains(col, row)) + { + row += m_vertical; + col += m_horizontal; + } + + row -= m_vertical; + col -= m_horizontal; + + return QPoint(col, row); +} diff --git a/kbattleship/kbattleship/kbdiagonalshotstrategy.h b/kbattleship/kbattleship/kbdiagonalshotstrategy.h new file mode 100644 index 00000000..b7c9b086 --- /dev/null +++ b/kbattleship/kbattleship/kbdiagonalshotstrategy.h @@ -0,0 +1,43 @@ +/*************************************************************************** + kbdiagonalshotstrategy.h + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBDIAGONALSHOTSTRATEGY_H +#define KBDIAGONALSHOTSTRATEGY_H + +#include "kbstrategy.h" + +class KBDiagonalShotStrategy : public KBStrategy +{ +public: + enum Direction {LEFTUP, LEFTDOWN, RIGHTUP, RIGHTDOWN}; + KBDiagonalShotStrategy(KBStrategy *parent = 0); + + virtual const QPoint nextShot(); + virtual bool hasMoreShots(); + virtual void shotAt(const QPoint &pos); + virtual void startAt(int col, int row, Direction dir); + virtual QPoint endPoint(); + +private: + bool advance(); + + int m_row; + int m_column; + int m_vertical; + int m_horizontal; +}; + +#endif diff --git a/kbattleship/kbattleship/kbdiagonalwrapstrategy.cpp b/kbattleship/kbattleship/kbdiagonalwrapstrategy.cpp new file mode 100644 index 00000000..7f88fb54 --- /dev/null +++ b/kbattleship/kbattleship/kbdiagonalwrapstrategy.cpp @@ -0,0 +1,320 @@ +/*************************************************************************** + kbdiagonalwrapstrategy.cpp + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include "kbdiagonalwrapstrategy.h" + +KBDiagonalWrapStrategy::KBDiagonalWrapStrategy(KBStrategy *parent) : KBStrategy(parent) +{ + m_child = new KBDiagonalShotStrategy(this); + + if(parent == 0) + { + m_destroyer = new KBDestroyShipStrategy(this); + m_destroying = false; + } + else + { + m_destroyer = 0; + m_destroying = false; + } +} + +KBDiagonalWrapStrategy::~KBDiagonalWrapStrategy() +{ + delete m_child; + delete m_destroyer; +} + +void KBDiagonalWrapStrategy::init(KBattleField *field, const QRect &field_rect) +{ + KBStrategy::init(field, field_rect); + KRandomSequence rand; + + m_column = (int) rand.getLong(m_fieldRect.width()); + m_row = (int) rand.getLong(m_fieldRect.height()); + + switch(rand.getLong(4)) + { + case 0: + m_direction = KBDiagonalShotStrategy::RIGHTDOWN; + m_row = 0; + break; + + case 1: + m_direction = KBDiagonalShotStrategy::RIGHTUP; + m_column = 0; + break; + + case 2: + m_direction = KBDiagonalShotStrategy::LEFTDOWN; + m_column = m_fieldRect.width() - 1; + break; + + default: + m_direction = KBDiagonalShotStrategy::LEFTUP; + m_row = m_fieldRect.height() - 1; + break; + } + + m_child->init(field, field_rect); + m_child->startAt(m_column, m_row, m_direction); + + m_start = QPoint(m_column, m_row); + + if(m_destroyer != 0) + m_destroyer->init(field, field_rect); +} + +const QPoint KBDiagonalWrapStrategy::nextShot() +{ + if(hasMoreShots()) + { + if(m_destroying) + return m_destroyer->nextShot(); + else if(m_child != 0) + return m_child->nextShot(); + } + + return m_start; +} + +bool KBDiagonalWrapStrategy::advance() +{ + switch(m_direction) + { + case KBDiagonalShotStrategy::RIGHTDOWN: + if(!advanceRightDown()) + return false; + break; + + case KBDiagonalShotStrategy::RIGHTUP: + if(!advanceRightUp()) + return false; + break; + + case KBDiagonalShotStrategy::LEFTDOWN: + if(!advanceLeftDown()) + return false; + break; + + case KBDiagonalShotStrategy::LEFTUP: + if(!advanceLeftUp()) + return false; + break; + + default: + break; + } + + return true; +} + +bool KBDiagonalWrapStrategy::hasMoreShots() +{ + if(m_parent == 0 && !m_destroying && m_prevShots.count() > 0) + { + QPoint pos = m_prevShots.last(); + int state = m_battleField->ownState(pos.x(), pos.y()); + if(state == KBattleField::HIT) + { + m_destroying = true; + m_destroyer->destroyShipAt(pos); + } + } + + if(m_destroying) + { + if(m_destroyer->hasMoreShots()) + return true; + else + m_destroying = false; + } + + if(!m_child->hasMoreShots()) + return advance(); + + return true; +} + +void KBDiagonalWrapStrategy::shotAt(const QPoint &pos) +{ + m_prevShots.append(pos); + + if(m_child != 0) + m_child->shotAt(pos); +} + +bool KBDiagonalWrapStrategy::advanceRightDown() +{ + int col; + + if(m_column == 0 && m_row != 0) + { + // start next + m_row = 0; + m_column = (m_start.x() + 3) % m_fieldRect.width(); + m_child->startAt(m_column, m_row, m_direction); + + if(!m_child->hasMoreShots()) + { + col = 0; + m_child->startAt(col, m_row, m_direction); + while(!m_child->hasMoreShots()) + { + col++; + if(col >= m_fieldRect.width()) + return false; + + m_child->startAt(col, m_row, m_direction); + } + m_column = col; + } + m_start = QPoint(m_column, m_row); + } + else + { + //wrap; + m_column = 0; + m_row = m_child->endPoint().y(); + + m_child->startAt(m_column, m_row, m_direction); + } + + return true; +} + +bool KBDiagonalWrapStrategy::advanceRightUp() +{ + int row; + + if(m_row == (m_fieldRect.height() - 1) && m_column != 0) + { + // start next + m_column = 0; + m_row = m_start.y() - 3; + m_row = m_row < 0 ? m_row + m_fieldRect.height() : m_row; + + m_child->startAt(m_column, m_row, m_direction); + + if(!m_child->hasMoreShots()) + { + row = m_fieldRect.height() - 1; + m_child->startAt(m_column, row, m_direction); + while(!m_child->hasMoreShots()) + { + row--; + if(row < 0) + return false; + + m_child->startAt(m_column, row, m_direction); + } + m_row = row; + } + m_start = QPoint(m_column, m_row); + } + else + { + //wrap; + m_row = m_fieldRect.height() - 1; + m_column = m_child->endPoint().x(); + + m_child->startAt(m_column, m_row, m_direction); + } + + return true; +} + +bool KBDiagonalWrapStrategy::advanceLeftDown() +{ + int row; + + if(m_row == 0 && m_column != (m_fieldRect.width()-1)) + { + // start next + m_column = m_fieldRect.width() - 1; + m_row = (m_start.y() + 3) % m_fieldRect.height(); + + m_child->startAt(m_column, m_row, m_direction); + + if(!m_child->hasMoreShots()) + { + row = 0; + m_child->startAt(m_column, row, m_direction); + while(!m_child->hasMoreShots()) + { + row++; + if(row >= m_fieldRect.height()) + return false; + + m_child->startAt(m_column, row, m_direction); + } + m_row = row; + } + m_start = QPoint(m_column, m_row); + } + else + { + //wrap; + m_row = 0; + m_column = m_child->endPoint().x(); + + m_child->startAt(m_column, m_row, m_direction); + } + + return true; +} + +bool KBDiagonalWrapStrategy::advanceLeftUp() +{ + int col; + + if(m_column == (m_fieldRect.width()-1) && m_row != (m_fieldRect.height()-1)) + { + // start next + m_row = m_fieldRect.height() - 1; + m_column = m_start.x() - 3; + m_column = m_column < 0 ? m_column + m_fieldRect.width() : m_column; + + m_child->startAt(m_column, m_row, m_direction); + + if(!m_child->hasMoreShots()) + { + col = m_fieldRect.width() - 1; + m_child->startAt(col, m_row, m_direction); + while(!m_child->hasMoreShots()) + { + col--; + if(col < 0) + return false; + + m_child->startAt(col, m_row, m_direction); + } + m_column = col; + } + m_start = QPoint(m_column, m_row); + } + else + { + //wrap; + m_column = m_fieldRect.width() - 1; + m_row = m_child->endPoint().y(); + + m_child->startAt(m_column, m_row, m_direction); + } + + return true; +} diff --git a/kbattleship/kbattleship/kbdiagonalwrapstrategy.h b/kbattleship/kbattleship/kbdiagonalwrapstrategy.h new file mode 100644 index 00000000..06ff8f6a --- /dev/null +++ b/kbattleship/kbattleship/kbdiagonalwrapstrategy.h @@ -0,0 +1,53 @@ +/*************************************************************************** + kbdiagonalwrapstrategy.h + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBDIAGONALWRAPSTRATEGY_H +#define KBDIAGONALWRAPSTRATEGY_H + +#include "kbstrategy.h" +#include "kbdestroyshipstrategy.h" +#include "kbdiagonalshotstrategy.h" + +class KBDiagonalWrapStrategy : public KBStrategy +{ +public: + KBDiagonalWrapStrategy(KBStrategy *parent = 0); + virtual ~KBDiagonalWrapStrategy(); + + virtual void init(KBattleField *field, const QRect &field_rect); + virtual const QPoint nextShot(); + virtual bool hasMoreShots(); + virtual void shotAt(const QPoint &pos); + +private: + bool advance(); + bool advanceRightDown(); + bool advanceRightUp(); + bool advanceLeftDown(); + bool advanceLeftUp(); + + int m_row; + int m_column; + + QPoint m_start; + KBDiagonalShotStrategy *m_child; + KBDiagonalShotStrategy::Direction m_direction; + + KBDestroyShipStrategy *m_destroyer; + bool m_destroying; +}; + +#endif diff --git a/kbattleship/kbattleship/kbhorizontalstepstrategy.cpp b/kbattleship/kbattleship/kbhorizontalstepstrategy.cpp new file mode 100644 index 00000000..080bc08e --- /dev/null +++ b/kbattleship/kbattleship/kbhorizontalstepstrategy.cpp @@ -0,0 +1,210 @@ +/*************************************************************************** + kbhorizontalstepstrategy.cpp + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include "kbhorizontalstepstrategy.h" + +KBHorizontalStepStrategy::KBHorizontalStepStrategy(KBStrategy *parent) : KBStrategy(parent) +{ + m_child = 0; + if(parent == 0) // only the master destroys ships + { + m_destroyer = new KBDestroyShipStrategy(this); + m_destroying = false; + } + else + { + m_destroyer = 0; + m_destroying = false; + } +} + +KBHorizontalStepStrategy::~KBHorizontalStepStrategy() +{ + delete m_child; + delete m_destroyer; +} + +void KBHorizontalStepStrategy::init(KBattleField *field, const QRect &field_rect) +{ + KBStrategy::init(field, field_rect); + KRandomSequence rand; + m_column = (int) rand.getLong(m_fieldRect.width()); + m_row = (int) rand.getLong(m_fieldRect.height()); + m_start = QPoint(m_column, m_row); + m_passes = 0; + + if(m_destroyer != 0) + m_destroyer->init(field, field_rect); +} + +const QPoint KBHorizontalStepStrategy::nextShot() +{ + if(hasMoreShots()) + { + if(m_destroying) + return m_destroyer->nextShot(); + else if(m_passes == 0) + return QPoint(m_column, m_row); + else if(m_parent == 0) + return m_child->nextShot(); + } + + return m_start; +} + +bool KBHorizontalStepStrategy::advance() +{ + int col, row; + + col = m_column; + row = m_row; + + while(enemyFieldStateAt(col, row) == KBStrategy::SHOT) + { + col += 2; + if(col >= m_fieldRect.width()) + { + col = m_column % 2; + row = (row + 2) % m_fieldRect.height(); + } + + if(col == m_start.x() && row == m_start.y()) + { + col = (col + 1) % m_fieldRect.width(); + row = (row + 1) % m_fieldRect.height(); + } + } + + if(enemyFieldStateAt(col, row) != KBStrategy::SHOT) + { + m_column = col; + m_row = row; + return true; + } + + return false; +} + +void KBHorizontalStepStrategy::setStart(int col, int row) +{ + m_start = QPoint(col, row); + m_column = col; + m_row = row; +} + +bool KBHorizontalStepStrategy::hasMoreShots() +{ + if(m_parent != 0) + { + // Child Strategy + if(m_passes == 0) + { + if(enemyFieldStateAt(m_column, m_row) != KBStrategy::SHOT) + return true; + else if(advance()) + return true; + else + { + m_passes++; + return false; + } + } + else + return false; + } + else + { + // Parent Strategy + if((!m_destroying) && m_prevShots.count() > 0) + { + QPoint pos = m_prevShots.last(); + int state = m_battleField->ownState(pos.x(), pos.y()); + if(state == KBattleField::HIT) + { + m_destroying = true; + m_destroyer->destroyShipAt(pos); + } + } + if(m_destroying) + { + if(m_destroyer->hasMoreShots()) + return true; + else + m_destroying = false; + } + } + + int x, y; + switch(m_passes) + { + case 0: + if(enemyFieldStateAt(m_column, m_row) != KBStrategy::SHOT || advance()) + return true; + + m_passes++; + m_child = new KBHorizontalStepStrategy(this); + m_child->init(m_battleField, m_fieldRect); + + x = (m_start.x() + 1) % m_fieldRect.width(); + y = (m_start.y() + 1) % m_fieldRect.height(); + m_child->setStart(x, y); + + case 1: + if(m_child->hasMoreShots()) + return true; + + m_passes++; + delete m_child; + + m_child = new KBHorizontalStepStrategy(this); + m_child->init(m_battleField, m_fieldRect); + + x = (m_start.x() + 1) % m_fieldRect.width(); + y = m_start.y(); + m_child->setStart(x, y); + + case 2: + if(m_child->hasMoreShots()) + return true; + + m_passes++; + delete m_child; + + m_child = new KBHorizontalStepStrategy(this); + m_child->init(m_battleField, m_fieldRect); + + x = (m_start.x() + 2) % m_fieldRect.width(); + y = (m_start.y() + 1) % m_fieldRect.height(); + m_child->setStart(x, y); + + case 3: + if(m_child->hasMoreShots()) + return true; + + m_passes++; + + default: + return false; + } +} + +void KBHorizontalStepStrategy::shotAt(const QPoint &pos) +{ + m_prevShots.append(pos); + if(m_child != 0) + m_child->shotAt(pos); +} diff --git a/kbattleship/kbattleship/kbhorizontalstepstrategy.h b/kbattleship/kbattleship/kbhorizontalstepstrategy.h new file mode 100644 index 00000000..4e68e4e7 --- /dev/null +++ b/kbattleship/kbattleship/kbhorizontalstepstrategy.h @@ -0,0 +1,48 @@ +/*************************************************************************** + kbhorizontalstepstrategy.h + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBHORIZONTALSTEPSTRATEGY_H +#define KBHORIZONTALSTEPSTRATEGY_H + +#include "kbstrategy.h" +#include "kbdestroyshipstrategy.h" + +class KBHorizontalStepStrategy : public KBStrategy +{ +public: + KBHorizontalStepStrategy(KBStrategy *parent = 0); + virtual ~KBHorizontalStepStrategy(); + + virtual void init(KBattleField *field, const QRect &field_rect); + virtual const QPoint nextShot(); + virtual bool hasMoreShots(); + virtual void shotAt(const QPoint &pos); + +private: + bool advance(); + void setStart(int col, int row); + + int m_row; + int m_column; + int m_passes; + + QPoint m_start; + KBHorizontalStepStrategy *m_child; + KBDestroyShipStrategy *m_destroyer; + bool m_destroying; +}; + +#endif diff --git a/kbattleship/kbattleship/kbrandomshotstrategy.cpp b/kbattleship/kbattleship/kbrandomshotstrategy.cpp new file mode 100644 index 00000000..a3748a69 --- /dev/null +++ b/kbattleship/kbattleship/kbrandomshotstrategy.cpp @@ -0,0 +1,102 @@ +/*************************************************************************** + kbrandomshotstrategy.cpp + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kbrandomshotstrategy.h" + +KBRandomShotStrategy::KBRandomShotStrategy(KBStrategy *parent) : KBStrategy(parent) +{ + m_destroyer = new KBDestroyShipStrategy(this); + m_destroying = false; +} + +KBRandomShotStrategy::~KBRandomShotStrategy() +{ + delete m_destroyer; +} + +void KBRandomShotStrategy::init(KBattleField *field, const QRect &field_rect) +{ + KBStrategy::init(field, field_rect); + KRandomSequence rand; + m_column = (int) rand.getLong(m_fieldRect.width()); + m_row = (int) rand.getLong(m_fieldRect.height()); + + if(m_destroyer != 0) + m_destroyer->init(field, field_rect); +} + +const QPoint KBRandomShotStrategy::nextShot() +{ + if(hasMoreShots()) + { + if(m_destroying) + return m_destroyer->nextShot(); + else if(advance()) + return QPoint(m_column, m_row); + } + + return QPoint(0, 0); +} + +bool KBRandomShotStrategy::advance() +{ + while(enemyFieldStateAt(m_column, m_row) == KBStrategy::SHOT) + { + m_column = m_randomSeq.getLong(m_fieldRect.width()); + m_row = m_randomSeq.getLong(m_fieldRect.height()); + } + return true; +} + +bool KBRandomShotStrategy::hasMoreShots() +{ + if(m_parent == 0) + { + if((!m_destroying) && m_prevShots.count() > 0) + { + QPoint pos = m_prevShots.last(); + int state = m_battleField->ownState(pos.x(), pos.y()); + if(state == KBattleField::HIT) + { + m_destroying = true; + m_destroyer->destroyShipAt(pos); + } + } + if(m_destroying) + { + if(m_destroyer->hasMoreShots()) + return true; + else + m_destroying = false; + } + } + + for(int row = 0; row < m_fieldRect.height(); row++) + { + for(int col = 0; col < m_fieldRect.width(); col++) + { + if(enemyFieldStateAt(col, row) != KBStrategy::SHOT) + return true; + } + } + + return false; +} + +void KBRandomShotStrategy::shotAt(const QPoint &pos) +{ + m_prevShots.append(pos); +} diff --git a/kbattleship/kbattleship/kbrandomshotstrategy.h b/kbattleship/kbattleship/kbrandomshotstrategy.h new file mode 100644 index 00000000..1e93cefa --- /dev/null +++ b/kbattleship/kbattleship/kbrandomshotstrategy.h @@ -0,0 +1,47 @@ +/*************************************************************************** + kbrandomshotstrategy.h + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBRANDOMSHOTSTRATEGY_H +#define KBRANDOMSHOTSTRATEGY_H + +#include + +#include "kbstrategy.h" +#include "kbdestroyshipstrategy.h" + +class KBRandomShotStrategy : public KBStrategy +{ +public: + KBRandomShotStrategy(KBStrategy *parent = 0); + virtual ~KBRandomShotStrategy(); + + virtual void init(KBattleField *field, const QRect &field_rect); + virtual const QPoint nextShot(); + virtual bool hasMoreShots(); + virtual void shotAt(const QPoint &pos); + +private: + bool advance(); + + int m_row; + int m_column; + + KRandomSequence m_randomSeq; + KBDestroyShipStrategy *m_destroyer; + bool m_destroying; +}; + +#endif diff --git a/kbattleship/kbattleship/kbstrategy.cpp b/kbattleship/kbattleship/kbstrategy.cpp new file mode 100644 index 00000000..f8183cfd --- /dev/null +++ b/kbattleship/kbattleship/kbstrategy.cpp @@ -0,0 +1,108 @@ +/*************************************************************************** + kbstrategy.cpp + ---------- + Developers: (c) 2001 Kevin Krammer + (c) 2001 Nikolas Zimmermann + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kbstrategy.h" + +KBStrategy::KBStrategy(KBStrategy *parent) +{ + m_parent = parent; + m_viableShots = 0; +} + +KBStrategy::~KBStrategy() +{ + while ( !m_prevShots.empty() ) + { + m_prevShots.remove( m_prevShots.last() ); + } + if (m_parent == 0 && m_viableShots != 0) + { + delete[] m_viableShots; + } +} + +/* Returns the master strategy's shot list. */ +QValueList KBStrategy::masterShotList() +{ + return (!m_parent) ? m_prevShots : m_parent->masterShotList(); +} + +/* the AI player decided to shoot at pos */ +void KBStrategy::shotAt(const QPoint &pos) +{ + m_prevShots.append(pos); +} + +void KBStrategy::init(KBattleField *field, const QRect &field_rect) +{ + m_battleField = field; + m_fieldRect = field_rect; + if (!m_parent) + { + if (m_viableShots == 0) + { + m_viableShots = new bool[(field_rect.width()*field_rect.height())]; + } + for (int x = 0; x < field_rect.width(); ++x) + { + for (int y = 0; y < field_rect.height(); ++y) + { + //m_viableShots[x, y] = true; + setViablePos(x, y, true); + } + } + } + else + { + m_viableShots = m_parent->getViableShots(); + } +} + +/* Returns the field type of position (x, y) on the user player's field */ +int KBStrategy::enemyFieldStateAt(int x, int y) +{ + if (!isViablePos(x, y)) + return SHOT; // faking SHOT if position is not possible ship position + + switch(m_battleField->ownState(x, y)) + { + case KBattleField::FREE: + return KBStrategy::FREE; + case KBattleField::WATER: + case KBattleField::HIT: + case KBattleField::DEATH: + return KBStrategy::SHOT; + default: + return KBStrategy::SHIP; + } +} + +bool* KBStrategy::getViableShots() +{ + return m_viableShots; +} + +bool KBStrategy::isViablePos(int x, int y) +{ + return m_viableShots[(m_fieldRect.width()*y + x)]; +} + +void KBStrategy::setViablePos(int x, int y, bool viable) +{ + m_viableShots[(m_fieldRect.width()*y + x)] = viable; +} + diff --git a/kbattleship/kbattleship/kbstrategy.h b/kbattleship/kbattleship/kbstrategy.h new file mode 100644 index 00000000..55707a17 --- /dev/null +++ b/kbattleship/kbattleship/kbstrategy.h @@ -0,0 +1,52 @@ +/*************************************************************************** + kbstrategy.h + ---------- + Developers: (c) 2001 Kevin Krammer + (c) 2001 Nikolas Zimmermann + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBSTRATEGY_H +#define KBSTRATEGY_H + +#include +#include +#include "kbattlefield.h" + +class KBStrategy +{ +public: + enum{FREE, SHOT, SHIP}; + KBStrategy(KBStrategy *parent = 0); + virtual ~KBStrategy(); + + virtual const QPoint nextShot() = 0; + virtual void shotAt(const QPoint &pos); + virtual void init(KBattleField *field, const QRect &field_rect); + virtual bool hasMoreShots() = 0; + +protected: + QValueList masterShotList(); + int enemyFieldStateAt(int x, int y); + bool* getViableShots(); + + QRect m_fieldRect; + bool* m_viableShots; + bool isViablePos(int x, int y); + void setViablePos(int x, int y, bool viable); + QValueList m_prevShots; + + KBattleField *m_battleField; + KBStrategy *m_parent; +}; + +#endif diff --git a/kbattleship/kbattleship/kbverticalstepstrategy.cpp b/kbattleship/kbattleship/kbverticalstepstrategy.cpp new file mode 100644 index 00000000..736e9ac8 --- /dev/null +++ b/kbattleship/kbattleship/kbverticalstepstrategy.cpp @@ -0,0 +1,214 @@ +/*************************************************************************** + kbverticalstepstrategy.cpp + ---------- + Developers: (c) 2001 Kevin Krammer + (c) 2001 Nikolas Zimmermann + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include "kbverticalstepstrategy.h" + +KBVerticalStepStrategy::KBVerticalStepStrategy(KBStrategy *parent) : KBStrategy(parent) +{ + m_child = 0; + + if(parent == 0) // only the master destroys ships + { + m_destroyer = new KBDestroyShipStrategy(this); + m_destroying = false; + } + else + { + m_destroyer = 0; + m_destroying = false; + } +} + +KBVerticalStepStrategy::~KBVerticalStepStrategy() +{ + delete m_child; + delete m_destroyer; +} + +void KBVerticalStepStrategy::init(KBattleField *field, const QRect &field_rect) +{ + KBStrategy::init(field, field_rect); + KRandomSequence rand; + m_column = (int) rand.getLong(m_fieldRect.width()); + m_row = (int) rand.getLong(m_fieldRect.height()); + m_start = QPoint(m_column, m_row); + m_passes = 0; + + if(m_destroyer != 0) + m_destroyer->init(field, field_rect); +} + +const QPoint KBVerticalStepStrategy::nextShot() +{ + if(hasMoreShots()) + { + if(m_destroying) + return m_destroyer->nextShot(); + else if(m_passes == 0) + return QPoint(m_column, m_row); + else if(m_parent == 0) + return m_child->nextShot(); + } + + return m_start; +} + +bool KBVerticalStepStrategy::advance() +{ + int col, row; + + col = m_column; + row = m_row; + + while(enemyFieldStateAt(col, row) == KBStrategy::SHOT) + { + row += 2; + if(row >= m_fieldRect.height()) + { + row = m_row % 2; + col = (col + 2) % m_fieldRect.width(); + } + + if(col == m_start.x() && row == m_start.y()) + { + col = (col + 1) % m_fieldRect.width(); + row = (row + 1) % m_fieldRect.height(); + } + } + + if(enemyFieldStateAt(col, row) != KBStrategy::SHOT) + { + m_column = col; + m_row = row; + return true; + } + + return false; +} + +void KBVerticalStepStrategy::setStart(int col, int row) +{ + m_start = QPoint(col, row); + m_column = col; + m_row = row; +} + +bool KBVerticalStepStrategy::hasMoreShots() +{ + if(m_parent != 0) + { + // Child Strategy + if(m_passes == 0) + { + if(enemyFieldStateAt(m_column, m_row) != KBStrategy::SHOT) + return true; + else if(advance()) + return true; + else + { + m_passes++; + return false; + } + } + else + return false; + } + else + { + // Parent Strategy + if((!m_destroying) && m_prevShots.count() > 0) + { + QPoint pos = m_prevShots.last(); + int state = m_battleField->ownState(pos.x(), pos.y()); + if(state == KBattleField::HIT) + { + m_destroying = true; + m_destroyer->destroyShipAt(pos); + } + } + if(m_destroying) + { + if(m_destroyer->hasMoreShots()) + return true; + else + m_destroying = false; + } + + int x, y; + switch(m_passes) + { + case 0: + if(enemyFieldStateAt(m_column, m_row) != KBStrategy::SHOT) + return true; + else if (advance()) + return true; + + m_passes++; + m_child = new KBVerticalStepStrategy(this); + m_child->init(m_battleField, m_fieldRect); + + x = (m_start.x() + 1) % m_fieldRect.width(); + y = (m_start.y() + 1) % m_fieldRect.height(); + m_child->setStart(x, y); + + case 1: + if(m_child->hasMoreShots()) + return true; + + m_passes++; + delete m_child; + + m_child = new KBVerticalStepStrategy(this); + m_child->init(m_battleField, m_fieldRect); + + x = m_start.x(); + y = (m_start.y() + 1) % m_fieldRect.height(); + m_child->setStart(x, y); + + case 2: + if(m_child->hasMoreShots()) + return true; + + m_passes++; + delete m_child; + + m_child = new KBVerticalStepStrategy(this); + m_child->init(m_battleField, m_fieldRect); + + x = (m_start.x() + 1) % m_fieldRect.width(); + y = (m_start.y() + 2) % m_fieldRect.height(); + m_child->setStart(x, y); + + case 3: + if(m_child->hasMoreShots()) + return true; + + m_passes++; + + default: + return false; + } + } +} + +void KBVerticalStepStrategy::shotAt(const QPoint &pos) +{ + m_prevShots.append(pos); + if(m_child != 0) + m_child->shotAt(pos); +} diff --git a/kbattleship/kbattleship/kbverticalstepstrategy.h b/kbattleship/kbattleship/kbverticalstepstrategy.h new file mode 100644 index 00000000..904fa68e --- /dev/null +++ b/kbattleship/kbattleship/kbverticalstepstrategy.h @@ -0,0 +1,49 @@ +/*************************************************************************** + kbverticalstepstrategy.h + ---------- + Developers: (c) 2001 Kevin Krammer + (c) 2001 Nikolas Zimmermann + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBVERTICALSTEPSTRATEGY_H +#define KBVERTICALSTEPSTRATEGY_H + +#include "kbstrategy.h" +#include "kbdestroyshipstrategy.h" + +class KBVerticalStepStrategy : public KBStrategy +{ +public: + KBVerticalStepStrategy(KBStrategy *parent = 0); + virtual ~KBVerticalStepStrategy(); + + virtual void init(KBattleField *field, const QRect &field_rect); + virtual const QPoint nextShot(); + virtual bool hasMoreShots(); + virtual void shotAt(const QPoint &pos); + +private: + bool advance(); + void setStart(int col, int row); + + int m_row; + int m_column; + int m_passes; + + QPoint m_start; + KBVerticalStepStrategy *m_child; + KBDestroyShipStrategy *m_destroyer; + bool m_destroying; +}; + +#endif diff --git a/kbattleship/kbattleship/kchatwidget.cpp b/kbattleship/kbattleship/kchatwidget.cpp new file mode 100644 index 00000000..6c4755c6 --- /dev/null +++ b/kbattleship/kbattleship/kchatwidget.cpp @@ -0,0 +1,83 @@ +/*************************************************************************** + kchatwidget.cpp + ----------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include "kchatwidget.moc" + +KChatWidget::KChatWidget(QWidget *parent, const char *name) : chatDlg(parent, name) +{ + connect(sendBtn, SIGNAL(clicked()), this, SLOT(slotComputeMessage())); + connect(commentEdit, SIGNAL(returnPressed()), this, SLOT(slotComputeMessage())); + chatView->setFocusProxy(commentEdit); + chatView->setMinimumSize(0, 50); + commentEdit->installEventFilter(this); + + m_currentNickname = QString::null; + slotAcceptMsg(false); +} + +void KChatWidget::clear() +{ + m_currentNickname = QString::null; + slotAcceptMsg(false); + chatView->clear(); + commentEdit->clear(); +} + +void KChatWidget::slotAcceptMsg(bool value) +{ + m_acceptMsgs = value; +} + +void KChatWidget::slotReceivedMessage(const QString &nickname, const QString &msg, bool fromenemy) +{ + // Niko Z: + // IRC roxxx :) + if(msg.startsWith("/me ")) + chatView->append(QString(" * ") + nickname + QString(" ") + msg.mid(4)); + else if(msg.startsWith("/nick ")) + if(fromenemy) + emit sigChangeEnemyNickname(msg.mid(6)); + else + emit sigChangeOwnNickname(msg.mid(6)); + else + chatView->append(nickname + QString(": ") + msg); + chatView->setCursorPosition(chatView->numLines(), 0); +} + +bool KChatWidget::eventFilter(QObject *obj, QEvent *e) +{ + if(obj == commentEdit && e->type() == QEvent::Wheel) + { + kapp->notify(chatView, e); + return true; + } + return chatDlg::eventFilter(obj, e); +} + +void KChatWidget::slotComputeMessage() +{ + if(!commentEdit->text().stripWhiteSpace().isEmpty() && m_acceptMsgs) + { + slotReceivedMessage(m_currentNickname, commentEdit->text(), false); + emit sigSendMessage(commentEdit->text()); + commentEdit->setText(""); + } + else if(commentEdit->text().stripWhiteSpace().isEmpty() && m_acceptMsgs) + commentEdit->setText(""); + commentEdit->setFocus(); +} diff --git a/kbattleship/kbattleship/kchatwidget.h b/kbattleship/kbattleship/kchatwidget.h new file mode 100644 index 00000000..e9a756a7 --- /dev/null +++ b/kbattleship/kbattleship/kchatwidget.h @@ -0,0 +1,53 @@ +/*************************************************************************** + kchatwidget.h + --------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KCHATWIDGET_H +#define KCHATWIDGET_H + +#include +#include +#include +#include "dialogs/chatDlg.h" +#include "kmessage.h" + +class KChatWidget : public chatDlg +{ + Q_OBJECT +public: + KChatWidget(QWidget *parent = 0, const char *name = 0); + + void clear(); + void setNickname(const QString &nickname) { m_currentNickname = nickname; } + +public slots: + void slotAcceptMsg(bool value); + void slotComputeMessage(); + void slotReceivedMessage(const QString &nickname, const QString &msg, bool fromenemy = true); + +signals: + void sigSendMessage(const QString &); + void sigChangeEnemyNickname(const QString &); + void sigChangeOwnNickname(const QString &); + +private: + virtual bool eventFilter(QObject *, QEvent *); + + QString m_currentNickname; + bool m_acceptMsgs; +}; + +#endif diff --git a/kbattleship/kbattleship/kclientdialog.cpp b/kbattleship/kbattleship/kclientdialog.cpp new file mode 100644 index 00000000..78fc04de --- /dev/null +++ b/kbattleship/kbattleship/kclientdialog.cpp @@ -0,0 +1,140 @@ +/*************************************************************************** + kclientdialog.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include "kbattleshipserver.h" // for BATTLESHIP_SERVICE +#include "kclientdialog.moc" + +KClientDialog::KClientDialog(QWidget *parent, const char *name) + : KDialogBase(Plain, i18n("Connect to Server"), Ok|Cancel, Ok, parent, name, true, false, KGuiItem(i18n("&Connect"))) +{ + QFrame* page = plainPage(); + QGridLayout* pageLayout = new QGridLayout(page, 1, 1, 0, 0); + m_mainWidget = new clientConnectDlg(page); + pageLayout->addWidget(m_mainWidget, 0, 0); + + enableButtonOK(false); + m_config = kapp->config(); + KUser u; + m_mainWidget->nicknameEdit->setText(u.loginName()); + + connect(m_mainWidget->serverEdit, SIGNAL(returnPressed(const QString &)), this, SLOT(slotReturnPressed(const QString &))); + connect(m_mainWidget->serverEdit, SIGNAL(textChanged(const QString &)), this, SLOT(slotCheckEnableOk())); + + m_config->setGroup("History"); + m_browser = new DNSSD::ServiceBrowser(QString::fromLatin1(BATTLESHIP_SERVICE)); + connect(m_browser,SIGNAL(finished()),SLOT(nextBatch())); + m_browser->startBrowse(); + connect(m_mainWidget->lanBox,SIGNAL(activated(int)),SLOT(gameSelected(int))); + m_mainWidget->serverEdit->completionObject()->setItems(m_config->readListEntry("CompletionList")); + + m_mainWidget->serverEdit->setMaxCount(5); + m_mainWidget->serverEdit->setHistoryItems(m_config->readListEntry("HistoryList")); + + m_mainWidget->serverEdit->setCurrentItem(m_config->readNumEntry("Index", -1)); +} + +KClientDialog::~KClientDialog() +{ + m_config->setGroup("History"); + m_config->writeEntry("CompletionList", m_mainWidget->serverEdit->completionObject()->items()); + m_config->writeEntry("HistoryList", m_mainWidget->serverEdit->historyItems()); + m_config->writeEntry("Index", m_mainWidget->serverEdit->currentItem()); + m_config->sync(); +} + +void KClientDialog::slotCheckEnableOk() +{ + enableButtonOK(!m_mainWidget->serverEdit->currentText().stripWhiteSpace().isEmpty()); +} + +void KClientDialog::slotOk() +{ + QString server = m_mainWidget->serverEdit->currentText().stripWhiteSpace(); + if(!server.isEmpty()) + { + hide(); + m_mainWidget->serverEdit->addToHistory(server); + emit sigConnectServer(); + } + else + m_mainWidget->serverEdit->clearEdit(); +} + +void KClientDialog::slotReturnPressed(const QString &hostname) +{ + if(!hostname.stripWhiteSpace().isEmpty()) + m_mainWidget->serverEdit->addToHistory(hostname); + else + m_mainWidget->serverEdit->clearEdit(); +} + +void KClientDialog::slotCancel() +{ + hide(); + emit sigCancelConnect(); +} + +QString KClientDialog::port() const +{ + return QString::number(m_mainWidget->portEdit->value()); +} + +QString KClientDialog::host() const +{ + return m_mainWidget->serverEdit->currentText(); +} + +QString KClientDialog::nickname() const +{ + return m_mainWidget->nicknameEdit->text(); +} + +void KClientDialog::nextBatch() +{ + bool autoselect=false; + if (!m_mainWidget->lanBox->count()) autoselect=true; + m_mainWidget->lanBox->clear(); + QStringList names; + QValueList::ConstIterator itEnd = m_browser->services().end(); + for (QValueList::ConstIterator it = m_browser->services().begin(); + it!=itEnd; ++it) names << (*it)->serviceName(); + m_mainWidget->lanBox->insertStringList(names); + if (autoselect && m_mainWidget->lanBox->count()) gameSelected(0); +} + +void KClientDialog::gameSelected(int i) +{ + Q_ASSERT(i < m_browser->services().count()); if( i >= m_browser->services().count()) { slotCheckEnableOk(); return; } + + DNSSD::RemoteService::Ptr srv = m_browser->services()[i]; + + Q_ASSERT(srv); if(!srv) { slotCheckEnableOk(); return; } + + if (!srv->isResolved() && !srv->resolve()) return; + m_mainWidget->serverEdit->setCurrentItem(srv->hostName(),true); + m_mainWidget->portEdit->setValue(srv->port()); + slotCheckEnableOk(); +} + + diff --git a/kbattleship/kbattleship/kclientdialog.h b/kbattleship/kbattleship/kclientdialog.h new file mode 100644 index 00000000..e3dbf341 --- /dev/null +++ b/kbattleship/kbattleship/kclientdialog.h @@ -0,0 +1,61 @@ +/*************************************************************************** + kclientdialog.h + ----------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KCLIENTDIALOG_H +#define KCLIENTDIALOG_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dialogs/connectDlg.h" + +class KClientDialog : public KDialogBase +{ + Q_OBJECT +public: + KClientDialog(QWidget *parent = 0, const char *name = 0); + ~KClientDialog(); + + QString port() const; + QString host() const; + QString nickname() const; + +public slots: + virtual void slotOk(); + virtual void slotCancel(); + void slotReturnPressed(const QString &hostname); + void nextBatch(); + void gameSelected(int); + void slotCheckEnableOk(); + +signals: + void sigConnectServer(); + void sigCancelConnect(); + +private: + KConfig *m_config; + DNSSD::ServiceBrowser *m_browser; + clientConnectDlg *m_mainWidget; +}; + +#endif diff --git a/kbattleship/kbattleship/kgridwidget.cpp b/kbattleship/kbattleship/kgridwidget.cpp new file mode 100644 index 00000000..49a6d106 --- /dev/null +++ b/kbattleship/kbattleship/kgridwidget.cpp @@ -0,0 +1,416 @@ +/*************************************************************************** + kgridwidget.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include + +#include +#include +#include + +#include "kbattlefield.h" +#include "kgridwidget.h" + +KGridWidget::KGridWidget(QWidget *parent, bool draw) : m_drawGrid(draw) +{ + m_doubleBuffer = new QPixmap(parent->width(), parent->height()); + m_parent = parent; + + cleanBuffer(); + cacheImages(); +} + +KGridWidget::~KGridWidget() +{ + delete m_doubleBuffer; +} + +void KGridWidget::cacheImages() +{ + seaPng = QPixmap(findIcon("sea.png")); + waterPng = QPixmap(findIcon("water.png")); + hitPng = QPixmap(findIcon("hit.png")); + borderPng = QPixmap(findIcon("border.png")); + deathPng = QPixmap(findIcon("death.png")); + ship1p1Png = QPixmap(findIcon("ship1-1.png")); + ship1p1rPng = QPixmap(findIcon("ship1-1-r.png")); + ship2p1Png = QPixmap(findIcon("ship2-1.png")); + ship2p1rPng = QPixmap(findIcon("ship2-1-r.png")); + ship2p2Png = QPixmap(findIcon("ship2-2.png")); + ship2p2rPng = QPixmap(findIcon("ship2-2-r.png")); + ship3p1Png = QPixmap(findIcon("ship3-1.png")); + ship3p1rPng = QPixmap(findIcon("ship3-1-r.png")); + ship3p2Png = QPixmap(findIcon("ship3-2.png")); + ship3p2rPng = QPixmap(findIcon("ship3-2-r.png")); + ship3p3Png = QPixmap(findIcon("ship3-3.png")); + ship3p3rPng = QPixmap(findIcon("ship3-3-r.png")); + ship4p1Png = QPixmap(findIcon("ship4-1.png")); + ship4p1rPng = QPixmap(findIcon("ship4-1-r.png")); + ship4p2Png = QPixmap(findIcon("ship4-2.png")); + ship4p2rPng = QPixmap(findIcon("ship4-2-r.png")); + ship4p3Png = QPixmap(findIcon("ship4-3.png")); + ship4p3rPng = QPixmap(findIcon("ship4-3-r.png")); + ship4p4Png = QPixmap(findIcon("ship4-4.png")); + ship4p4rPng = QPixmap(findIcon("ship4-4-r.png")); +} + +void KGridWidget::setValues(int x, int y, int size) +{ + m_x = x; + m_y = y; + m_size = size; +} + +void KGridWidget::drawSquare() +{ + drawIcon(seaPng); +} + +void KGridWidget::drawWaterIcon() +{ + drawIcon(waterPng); +} + +void KGridWidget::drawHitIcon() +{ + drawIcon(hitPng); +} + +void KGridWidget::drawDeathBorder() +{ + drawIcon(borderPng); +} +void KGridWidget::drawDeathIcon() +{ + drawIcon(deathPng); +} + +void KGridWidget::drawShipIcon(int type, bool rotate, bool hit, bool water) +{ + int ship = 0; + int part = 0; + + switch(type) + { + case KBattleField::SHIP1P1: + ship = 1; + part = 1; + break; + + case KBattleField::SHIP2P1: + ship = 2; + if(!rotate) + part = 1; + else + part = 2; + break; + + case KBattleField::SHIP2P2: + ship = 2; + if(!rotate) + part = 2; + else + part = 1; + break; + + case KBattleField::SHIP3P1: + ship = 3; + if(!rotate) + part = 1; + else + part = 3; + break; + + case KBattleField::SHIP3P2: + ship = 3; + if(!rotate) + part = 2; + else + part = 2; + break; + + case KBattleField::SHIP3P3: + ship = 3; + if(!rotate) + part = 3; + else + part = 1; + break; + + case KBattleField::SHIP4P1: + ship = 4; + if(!rotate) + part = 1; + else + part = 4; + break; + + case KBattleField::SHIP4P2: + ship = 4; + if(!rotate) + part = 2; + else + part = 3; + break; + + case KBattleField::SHIP4P3: + ship = 4; + if(!rotate) + part = 3; + else + part = 2; + break; + + case KBattleField::SHIP4P4: + ship = 4; + if(!rotate) + part = 4; + else + part = 1; + break; + } + + switch(ship) + { + case 1: + if(!rotate) + drawIcon(ship1p1Png, hit, water); + else + drawIcon(ship1p1rPng, hit, water, rotate); + break; + + case 2: + switch(part) + { + case 1: + if(!rotate) + drawIcon(ship2p1Png, hit, water); + else + drawIcon(ship2p1rPng, hit, water, rotate); + break; + + case 2: + if(!rotate) + drawIcon(ship2p2Png, hit, water); + else + drawIcon(ship2p2rPng, hit, water, rotate); + break; + } + break; + + case 3: + switch(part) + { + case 1: + if(!rotate) + drawIcon(ship3p1Png, hit, water); + else + drawIcon(ship3p1rPng, hit, water, rotate); + break; + + case 2: + if(!rotate) + drawIcon(ship3p2Png, hit, water); + else + drawIcon(ship3p2rPng, hit, water, rotate); + break; + + case 3: + if(!rotate) + drawIcon(ship3p3Png, hit, water); + else + drawIcon(ship3p3rPng, hit, water, rotate); + break; + } + break; + + case 4: + switch(part) + { + case 1: + if(!rotate) + drawIcon(ship4p1Png, hit, water); + else + drawIcon(ship4p1rPng, hit, water, rotate); + break; + + case 2: + if(!rotate) + drawIcon(ship4p2Png, hit, water); + else + drawIcon(ship4p2rPng, hit, water, rotate); + break; + + case 3: + if(!rotate) + drawIcon(ship4p3Png, hit, water); + else + drawIcon(ship4p3rPng, hit, water, rotate); + break; + + case 4: + if(!rotate) + drawIcon(ship4p4Png, hit, water); + else + drawIcon(ship4p4rPng, hit, water, rotate); + break; + } + break; + } +} + +void KGridWidget::drawShipIcon(int ship, int part, bool rotate, bool hit) +{ + switch(ship) + { + case 1: + if(!rotate) + drawIcon(ship1p1Png, hit); + else + drawIcon(ship1p1rPng, hit, false, rotate); + break; + + case 2: + switch(part) + { + case 1: + if(!rotate) + drawIcon(ship2p1Png, hit); + else + drawIcon(ship2p1rPng, hit, false, rotate); + break; + + case 2: + if(!rotate) + drawIcon(ship2p2Png, hit); + else + drawIcon(ship2p2rPng, hit, false, rotate); + break; + } + break; + + case 3: + switch(part) + { + case 1: + if(!rotate) + drawIcon(ship3p1Png, hit); + else + drawIcon(ship3p1rPng, hit, false, rotate); + break; + + case 2: + if(!rotate) + drawIcon(ship3p2Png, hit); + else + drawIcon(ship3p2rPng, hit, false, rotate); + break; + + case 3: + if(!rotate) + drawIcon(ship3p3Png, hit); + else + drawIcon(ship3p3rPng, hit, false, rotate); + break; + } + break; + + case 4: + switch(part) + { + case 1: + if(!rotate) + drawIcon(ship4p1Png, hit); + else + drawIcon(ship4p1rPng, hit, false, rotate); + break; + + case 2: + if(!rotate) + drawIcon(ship4p2Png, hit); + else + drawIcon(ship4p2rPng, hit, false, rotate); + break; + + case 3: + if(!rotate) + drawIcon(ship4p3Png, hit); + else + drawIcon(ship4p3rPng, hit, false, rotate); + break; + + case 4: + if(!rotate) + drawIcon(ship4p4Png, hit); + else + drawIcon(ship4p4rPng, hit, false, rotate); + break; + } + break; + } +} + +void KGridWidget::drawIcon(const QPixmap &icon, bool hitBlend, bool waterBlend, bool rotate) +{ + QPainter painter; + painter.begin(m_doubleBuffer); + if(!hitBlend && waterBlend) + { + QImage first = icon.convertToImage(); + QImage second = seaPng.convertToImage(); + painter.drawPixmap(m_x, m_y, seaPng); + if(rotate) + painter.drawImage(m_x, m_y, KImageEffect::blend(first, second, KImageEffect::VerticalGradient, 30, 30)); + else + painter.drawImage(m_x, m_y, KImageEffect::blend(first, second, KImageEffect::HorizontalGradient, 30, 30)); + + } + else if(hitBlend && !waterBlend) + { + painter.drawPixmap(m_x, m_y, icon); + painter.drawPixmap(m_x, m_y, hitPng); + } + else + painter.drawPixmap(m_x, m_y, icon); + + if(!m_drawGrid) + painter.end(); + else + { + painter.setBrush(Qt::NoBrush); + painter.setPen(Qt::black); + painter.drawRect(m_x, m_y, m_size, m_size); + painter.end(); + } +} + +QString KGridWidget::findIcon(const QString &name) const +{ + return locate("data", "kbattleship/pictures/" + name); +} + +void KGridWidget::finished() +{ + bitBlt(m_parent, 0, 0, m_doubleBuffer); + cleanBuffer(); +} + +void KGridWidget::cleanBuffer() +{ + m_doubleBuffer->fill(QApplication::palette().color(QPalette::Normal, QColorGroup::Background)); +} diff --git a/kbattleship/kbattleship/kgridwidget.h b/kbattleship/kbattleship/kgridwidget.h new file mode 100644 index 00000000..91f48eb0 --- /dev/null +++ b/kbattleship/kbattleship/kgridwidget.h @@ -0,0 +1,66 @@ +/*************************************************************************** + kgridwidget.h + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KGRIDWIDGET_H +#define KGRIDWIDGET_H + +#include + +class KGridWidget +{ +public: + KGridWidget(QWidget *parent, bool draw); + ~KGridWidget(); + + void enableGrid() { m_drawGrid = true; } + void disableGrid() { m_drawGrid = false; } + +protected: + void cleanBuffer(); + void finished(); + void setValues(int x, int y, int size); + void drawSquare(); + void drawWaterIcon(); + void drawDeathBorder(); + void drawDeathIcon(); + void drawHitIcon(); + void drawShipIcon(int type, bool rotate = false, bool hit = false, bool water = false); + void drawShipIcon(int ship, int part, bool rotate = false, bool hit = false); + +private: + void cacheImages(); + void drawIcon(const QPixmap &icon, bool hitBlend = false, bool waterBlend = false, bool rotate = false); + QString findIcon(const QString &name) const; + + bool m_drawGrid; + int m_x, m_y, m_size; + QPixmap *m_doubleBuffer; + QPixmap seaPng, waterPng, hitPng, borderPng,deathPng; + QPixmap ship1p1Png, ship1p1rPng; + QPixmap ship2p1Png, ship2p1rPng; + QPixmap ship2p2Png, ship2p2rPng; + QPixmap ship3p1Png, ship3p1rPng; + QPixmap ship3p2Png, ship3p2rPng; + QPixmap ship3p3Png, ship3p3rPng; + QPixmap ship4p1Png, ship4p1rPng; + QPixmap ship4p2Png, ship4p2rPng; + QPixmap ship4p3Png, ship4p3rPng; + QPixmap ship4p4Png, ship4p4rPng; + QWidget *m_parent; +}; + +#endif diff --git a/kbattleship/kbattleship/kmessage.cpp b/kbattleship/kbattleship/kmessage.cpp new file mode 100644 index 00000000..5eccacd2 --- /dev/null +++ b/kbattleship/kbattleship/kmessage.cpp @@ -0,0 +1,101 @@ +/*************************************************************************** + kmessage.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +//#define XMLDUMP + +#include + +#ifdef XMLDUMP +#include +#endif + +#include "kmessage.h" + +const char *clientName = I18N_NOOP("KBattleship"); +const char *clientVersion = "1.1"; +const char *clientDescription = I18N_NOOP("The KDE Battleship clone"); +const char *protocolVersion = "0.1.0"; + +KMessage::KMessage(int type) +{ + m_xmlDocument = QDomDocument("kmessage"); + m_xmlDocument.appendChild(m_xmlDocument.createElement("kmessage")); + m_messageType = type; + addField("msgtype", QString::number(type)); +} + +KMessage::KMessage(KMessage *msg) +{ + m_xmlDocument.setContent(msg->m_xmlDocument.toString(), false); + m_messageType = msg->type(); +} + +KMessage::KMessage() +{ + m_xmlDocument = QDomDocument("kmessage"); +} + +void KMessage::addField(const QString &name, const QString &content) +{ + QDomElement xmlElement = m_xmlDocument.createElement(name); + QDomText xmlText = m_xmlDocument.createTextNode(content); + xmlElement.appendChild(xmlText); + m_xmlDocument.documentElement().appendChild(xmlElement); +} + +void KMessage::setDataStream(const QString &stream) +{ + m_xmlDocument.setContent(stream); +#ifdef XMLDUMP + kdDebug() << "*** XML-IN ***" << endl << stream << endl << "*** END ***" << endl; +#endif +} + +QString KMessage::sendStream() const +{ +#ifdef XMLDUMP + kdDebug() << "*** XML OUT ***" << endl << m_xmlDocument.toString() << endl << "*** END ***" << endl; +#endif + return m_xmlDocument.toString(); +} + +QString KMessage::field(const QString &name) const +{ + QDomNode xmlNode = m_xmlDocument.documentElement().namedItem(name); + if(!xmlNode.isNull()) + return (xmlNode.toElement()).text(); + return QString::null; +} + +int KMessage::type() +{ + return field("msgtype").toInt(); +} + +void KMessage::chatMessage(const QString &nickname, const QString &message) +{ + addField("nickname", nickname); + addField("chat", message); +} + +void KMessage::versionMessage() +{ + addField("protocolVersion", protocolVersion); + addField("clientName", clientName); + addField("clientVersion", clientVersion); + addField("clientDescription", clientDescription); +} diff --git a/kbattleship/kbattleship/kmessage.h b/kbattleship/kbattleship/kmessage.h new file mode 100644 index 00000000..f1d6a0b5 --- /dev/null +++ b/kbattleship/kbattleship/kmessage.h @@ -0,0 +1,49 @@ +/*************************************************************************** + kmessage.h + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KMESSAGE_H +#define KMESSAGE_H + +#include +#include + +class KMessage +{ +public: + enum{GETVERSION, DISCARD, GREET, SHIPSREADY, SHOOT, ANSWER_SHOOT, WON, REPLAY, CHAT}; + + KMessage(int type); + KMessage(KMessage *msg); + KMessage(); + + int type(); + + void addField(const QString &name, const QString &content); + QString field(const QString &name) const; + + void setDataStream(const QString &stream); + QString sendStream() const; + + void chatMessage(const QString &nickname, const QString &message); + void versionMessage(); + +private: + QDomDocument m_xmlDocument; + int m_messageType; +}; + +#endif diff --git a/kbattleship/kbattleship/konnectionhandling.cpp b/kbattleship/kbattleship/konnectionhandling.cpp new file mode 100644 index 00000000..bcfefe04 --- /dev/null +++ b/kbattleship/kbattleship/konnectionhandling.cpp @@ -0,0 +1,246 @@ +/**************************************MM************************************* + konnectionhandling.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "konnectionhandling.moc" + +extern const char *protocolVersion; + +KonnectionHandling::KonnectionHandling(QWidget *parent, KBattleshipServer *server) : QObject(parent) +{ + m_kbserver = server; + m_kbclient = 0; + m_type = KonnectionHandling::SERVER; + connect(server, SIGNAL(sigServerFailure()), this, SIGNAL(sigAbortNetworkGame())); + connect(server, SIGNAL(sigNewConnect()), this, SLOT(slotNewClient())); + connect(server, SIGNAL(sigEndConnect()), this, SLOT(slotLostClient())); + connect(server, SIGNAL(sigNewMessage(KMessage *)), this, SLOT(slotNewMessage(KMessage *))); + connect(server, SIGNAL(sigMessageSent(KMessage *)), this, SLOT(slotMessageSent(KMessage *))); +} + +KonnectionHandling::KonnectionHandling(QWidget *parent, KBattleshipClient *client) : QObject(parent) +{ + m_kbclient = client; + m_kbserver = 0; + m_type = KonnectionHandling::CLIENT; + connect(client, SIGNAL(sigEndConnect()), this, SLOT(slotLostServer())); + connect(client, SIGNAL(sigSocketFailure(int)), this, SLOT(slotSocketError(int))); + connect(client, SIGNAL(sigNewMessage(KMessage *)), this, SLOT(slotNewMessage(KMessage *))); + connect(client, SIGNAL(sigMessageSent(KMessage *)), this, SLOT(slotMessageSent(KMessage *))); +} + +void KonnectionHandling::updateInternal(KBattleshipServer *server) +{ + m_kbserver = server; + m_kbclient = 0; + m_type = KonnectionHandling::SERVER; + connect(server, SIGNAL(sigServerFailure()), this, SIGNAL(sigAbortNetworkGame())); + connect(server, SIGNAL(sigNewConnect()), this, SLOT(slotNewClient())); + connect(server, SIGNAL(sigEndConnect()), this, SLOT(slotLostClient())); + connect(server, SIGNAL(sigNewMessage(KMessage *)), this, SLOT(slotNewMessage(KMessage *))); + connect(server, SIGNAL(sigMessageSent(KMessage *)), this, SLOT(slotMessageSent(KMessage *))); +} + +void KonnectionHandling::updateInternal(KBattleshipClient *client) +{ + m_kbclient = client; + m_kbserver = 0; + m_type = KonnectionHandling::CLIENT; + connect(client, SIGNAL(sigEndConnect()), this, SLOT(slotLostServer())); + connect(client, SIGNAL(sigSocketFailure(int)), this, SLOT(slotSocketError(int))); + connect(client, SIGNAL(sigNewMessage(KMessage *)), this, SLOT(slotNewMessage(KMessage *))); + connect(client, SIGNAL(sigMessageSent(KMessage *)), this, SLOT(slotMessageSent(KMessage *))); +} + +void KonnectionHandling::slotNewClient() +{ +} + +void KonnectionHandling::slotLostClient() +{ + KMessageBox::error(0L, i18n("Connection to client lost. Aborting the game.")); + emit sigClientLost(); +} + +void KonnectionHandling::slotMessageSent(KMessage *msg) +{ + if(msg->type() == KMessage::SHOOT) + emit sigShootable(false); + + delete msg; +} + +void KonnectionHandling::slotNewMessage(KMessage *msg) +{ + KMessage *copy; + if(type() == KonnectionHandling::CLIENT) + { + switch(msg->type()) + { + // First possible message + case KMessage::DISCARD: + if(msg->field("kmversion") == QString("true")) + { + KMessageBox::error(0L, i18n("Connection dropped by enemy. The client's protocol implementation (%1) is not compatible with our (%2) version.").arg(msg->field("reason")).arg(protocolVersion)); + emit sigAbortNetworkGame(); + } + else + KMessageBox::error(0L, i18n(msg->field("reason").latin1())); + break; + + // Got some informations + case KMessage::GETVERSION: + emit sigClientInformation(msg->field("clientName"), msg->field("clientVersion"), msg->field("clientDescription"), msg->field("protocolVersion")); + break; + + // Got the enemy's nickname + case KMessage::GREET: + emit sigEnemyNickname(msg->field("nickname")); + emit sigStatusBar(i18n("Waiting for other player to place their ships...")); + break; + + // The server wants ous to place the ships + case KMessage::SHIPSREADY: + emit sigPlaceShips(true); + emit sigStatusBar(i18n("Please place your ships. Use the \"Shift\" key to place the ships vertically.")); + break; + + // The server shot and wants the field state + case KMessage::SHOOT: + emit sigSendFieldState(msg->field("fieldx").toInt(), msg->field("fieldy").toInt()); + emit sigStatusBar(i18n("Enemy has shot. Shoot now.")); + emit sigShootable(true); + break; + + // The server gave us the field data of our last shot + case KMessage::ANSWER_SHOOT: + emit sigShootable(false); + emit sigEnemyFieldData(msg->field("fieldx").toInt(), msg->field("fieldy").toInt(), msg->field("fieldstate").toInt(), msg->field("xstart").toInt(), msg->field("xstop").toInt(), msg->field("ystart").toInt(), msg->field("ystop").toInt(), (msg->field("death") == QString("true"))); + break; + + // The server starts a new game + case KMessage::REPLAY: + emit sigStatusBar(i18n("Waiting for other player to place their ships...")); + emit sigReplay(); + break; + + // We lost the game + case KMessage::WON: + emit sigStatusBar(i18n("You lost the game :(")); + copy = new KMessage(msg); + emit sigLost(copy); + break; + + // We got a chat message + case KMessage::CHAT: + emit sigChatMessage(msg->field("nickname"), msg->field("chat"), true); + break; + } + } + else + { + switch(msg->type()) + { + // First message....got client information + case KMessage::GETVERSION: + if(msg->field("protocolVersion") != QString::fromLatin1(protocolVersion)) + { + m_kbserver->slotDiscardClient(protocolVersion, true, false); + KMessageBox::error(0L, i18n("Connection to client dropped. The client's protocol implementation (%1) is not compatible with our (%2) version.").arg(msg->field("protocolVersion")).arg(protocolVersion)); + } + else + emit sigClientInformation(msg->field("clientName"), msg->field("clientVersion"), msg->field("clientDescription"), msg->field("protocolVersion")); + break; + + // Got the enemy's nickname + case KMessage::GREET: + KMessageBox::information(0L, i18n("We have a player. Let's start...")); + emit sigStatusBar(i18n("Please place your ships. Use the \"Shift\" key to place the ships vertically.")); + emit sigEnemyNickname(msg->field("nickname")); + emit sigSendNickname(); + emit sigPlaceShips(true); + break; + + // The client placed his ships...we can shoot now + case KMessage::SHIPSREADY: + emit sigShootable(true); + emit sigStatusBar(i18n("You can shoot now.")); + break; + + // The client gave us the field data of our last shot + case KMessage::ANSWER_SHOOT: + emit sigShootable(false); + emit sigEnemyFieldData(msg->field("fieldx").toInt(), msg->field("fieldy").toInt(), msg->field("fieldstate").toInt(), msg->field("xstart").toInt(), msg->field("xstop").toInt(), msg->field("ystart").toInt(), msg->field("ystop").toInt(), (msg->field("death") == QString("true"))); + break; + + // The client shot and wants the field state + case KMessage::SHOOT: + emit sigSendFieldState(msg->field("fieldx").toInt(), msg->field("fieldy").toInt()); + emit sigStatusBar(i18n("Enemy has shot. Shoot now.")); + emit sigShootable(true); + break; + + // The client asks for a replay + case KMessage::REPLAY: + emit sigReplay(); + break; + + // We lost the game + case KMessage::WON: + emit sigStatusBar(i18n("You lost the game :(")); + copy = new KMessage(msg); + emit sigLost(copy); + break; + + // We got a chat message + case KMessage::CHAT: + emit sigChatMessage(msg->field("nickname"), msg->field("chat"), true); + break; + } + } + + delete msg; +} + +void KonnectionHandling::slotSocketError(int error) +{ + switch(error) + { + case IO_ConnectError: + KMessageBox::error(0L, i18n("Connection refused by other host.")); + break; + + case IO_LookupError: + KMessageBox::error(0L, i18n("Couldn't lookup host.")); + break; + + case IO_ReadError: + KMessageBox::error(0L, i18n("Couldn't connect to server.")); + break; + + default: + KMessageBox::error(0L, i18n("Unknown error; No: %1").arg(error)); + break; + } + + emit sigAbortNetworkGame(); +} + +void KonnectionHandling::slotLostServer() +{ + KMessageBox::error(0L, i18n("Connection to server lost. Aborting the game.")); + emit sigServerLost(); +} diff --git a/kbattleship/kbattleship/konnectionhandling.h b/kbattleship/kbattleship/konnectionhandling.h new file mode 100644 index 00000000..baa823df --- /dev/null +++ b/kbattleship/kbattleship/konnectionhandling.h @@ -0,0 +1,73 @@ +/*************************************************************************** + konnectionhandling.h + ----------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KONNECTIONHANDLING_H +#define KONNECTIONHANDLING_H + +#include +#include + +#include + +#include "kbattleshipclient.h" +#include "kbattleshipserver.h" +#include "kmessage.h" + +class KonnectionHandling : public QObject +{ + Q_OBJECT +public: + enum{SERVER, CLIENT}; + KonnectionHandling(QWidget *parent, KBattleshipServer *server); + KonnectionHandling(QWidget *parent, KBattleshipClient *client); + + int type() { return m_type; } + + void updateInternal(KBattleshipServer *server); + void updateInternal(KBattleshipClient *client); + +public slots: + void slotNewMessage(KMessage *msg); + void slotMessageSent(KMessage *msg); + void slotNewClient(); + void slotLostClient(); + void slotLostServer(); + void slotSocketError(int error); + +signals: + void sigStatusBar(const QString &); + void sigEnemyNickname(const QString &); + void sigEnemyFieldData(int, int, int, int, int, int, int, bool); + void sigClientInformation(const QString &, const QString &, const QString &, const QString &); + void sigSendNickname(); + void sigSendFieldState(int, int); + void sigPlaceShips(bool); + void sigShootable(bool); + void sigClientLost(); + void sigServerLost(); + void sigReplay(); + void sigLost(KMessage *); + void sigAbortNetworkGame(); + void sigChatMessage(const QString &, const QString &, bool); + +private: + KBattleshipServer *m_kbserver; + KBattleshipClient *m_kbclient; + int m_type; +}; + +#endif diff --git a/kbattleship/kbattleship/kserverdialog.cpp b/kbattleship/kbattleship/kserverdialog.cpp new file mode 100644 index 00000000..3936bebf --- /dev/null +++ b/kbattleship/kbattleship/kserverdialog.cpp @@ -0,0 +1,67 @@ +/*************************************************************************** + kserverdialog.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include + +#include "kserverdialog.h" + +KServerDialog::KServerDialog(QWidget *parent, const char *name) : + KDialogBase(Plain, i18n("Start Server"), Ok|Cancel, Ok, parent, name, true, false, KGuiItem(i18n("&Start"))) +{ + QFrame* page = plainPage(); + QGridLayout* pageLayout = new QGridLayout(page, 1, 1, 0, 0); + m_mainWidget = new serverStartDlg(page); + pageLayout->addWidget(m_mainWidget, 0, 0); + + KUser u; + m_mainWidget->nicknameEdit->setText(u.loginName()); + + QString gamename = u.fullName(); + if(gamename.isEmpty()) gamename = u.loginName(); + m_mainWidget->gamenameEdit->setText(gamename); +} + +void KServerDialog::slotOk() +{ + hide(); + emit okClicked(); +} + +void KServerDialog::slotCancel() +{ + hide(); + emit cancelClicked(); +} + +QString KServerDialog::port() const +{ + return QString::number(m_mainWidget->portEdit->value()); +} + +QString KServerDialog::nickname() const +{ + return m_mainWidget->nicknameEdit->text(); +} + +QString KServerDialog::gamename() const +{ + return m_mainWidget->gamenameEdit->text(); +} + +#include "kserverdialog.moc" diff --git a/kbattleship/kbattleship/kserverdialog.h b/kbattleship/kbattleship/kserverdialog.h new file mode 100644 index 00000000..e6655c1a --- /dev/null +++ b/kbattleship/kbattleship/kserverdialog.h @@ -0,0 +1,49 @@ +/*************************************************************************** + kserverdialog.h + ----------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KSERVERDIALOG_H +#define KSERVERDIALOG_H + +#include +#include +#include +#include + +#include + +#include "dialogs/serverDlg.h" + + +class KServerDialog : public KDialogBase +{ + Q_OBJECT +public: + KServerDialog(QWidget *parent = 0, const char *name = 0); + + QString port() const; + QString nickname() const; + QString gamename() const; + +public slots: + virtual void slotOk(); + virtual void slotCancel(); + +private: + serverStartDlg *m_mainWidget; +}; + +#endif diff --git a/kbattleship/kbattleship/kship.cpp b/kbattleship/kbattleship/kship.cpp new file mode 100644 index 00000000..a9fbb558 --- /dev/null +++ b/kbattleship/kbattleship/kship.cpp @@ -0,0 +1,105 @@ +/*************************************************************************** + kship.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kship.h" + +#include "kbattlefield.h" + +KShip::KShip(int _shipxstart, int _shipxstop, int _shipystart, int _shipystop, int _shiplength, bool _placedLeft) +{ + m_shipxstart = _shipxstart; + m_shipxstop = _shipxstop; + m_shipystart = _shipystart; + m_shipystop = _shipystop; + m_shiptype = _shiplength; + m_placedLeft = _placedLeft; +} + +int KShip::shipxstart() +{ + return m_shipxstart; +} + +int KShip::shipxstop() +{ + return m_shipxstop; +} + +int KShip::shipystart() +{ + return m_shipystart; +} + +int KShip::shipystop() +{ + return m_shipystop; +} + +int KShip::shiptype() +{ + return m_shiptype; +} + +bool KShip::placedLeft() +{ + return m_placedLeft; +} + +bool KShip::contains(int x, int y) +{ + return (x >= m_shipxstart && x <= m_shipxstop) && (y >= m_shipystart && y <= m_shipystop); +} + +int KShip::shipTypeEnum(int x, int y) +{ + int ret; + ret = KBattleField::WATER; + if (contains(x, y)) + { + switch(m_shiptype) + { + case 0: + ret = KBattleField::SHIP1P1; + break; + case 1: + if (x == m_shipxstart && y == m_shipystart) ret = KBattleField::SHIP2P1; + else ret = KBattleField::SHIP2P2; + break; + case 2: + if (x == m_shipxstart && y == m_shipystart) ret = KBattleField::SHIP3P1; + else if (x == m_shipxstop && y == m_shipystop) ret = KBattleField::SHIP3P3; + else ret = KBattleField::SHIP3P2; + break; + case 3: + if (x == m_shipxstart && y == m_shipystart) ret = KBattleField::SHIP4P1; + else if (x == m_shipxstop && y == m_shipystop) ret = KBattleField::SHIP4P4; + else if (placedLeft()) + { + if (x == m_shipxstart + 1 && y == m_shipystart) ret = KBattleField::SHIP4P2; + else ret = KBattleField::SHIP4P3; + } + else + { + if (x == m_shipxstart && y == m_shipystart + 1) ret = KBattleField::SHIP4P2; + else ret = KBattleField::SHIP4P3; + } + break; + } + } + return ret; +} diff --git a/kbattleship/kbattleship/kship.h b/kbattleship/kbattleship/kship.h new file mode 100644 index 00000000..df436c7a --- /dev/null +++ b/kbattleship/kbattleship/kship.h @@ -0,0 +1,46 @@ +/*************************************************************************** + kship.h + ----------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KSHIP_H +#define KSHIP_H + +class KShip +{ +public: + KShip(int, int, int, int, int, bool = false); + + int shipxstart(); + int shipxstop(); + int shipystart(); + int shipystop(); + int shiptype(); + int shipTypeEnum(int x, int y); + + bool placedLeft(); + bool contains(int x, int y); + +private: + int m_shipxstart; + int m_shipxstop; + int m_shipystart; + int m_shipystop; + int m_shiptype; + bool m_placedLeft; +}; + +#endif diff --git a/kbattleship/kbattleship/kshiplist.cpp b/kbattleship/kbattleship/kshiplist.cpp new file mode 100644 index 00000000..8a17ad37 --- /dev/null +++ b/kbattleship/kbattleship/kshiplist.cpp @@ -0,0 +1,175 @@ +/*************************************************************************** + kshiplist.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kshiplist.moc" +#include + +KShipList::KShipList() : QObject() +{ + m_shiplist.setAutoDelete(true); + m_shipsadded = 4; + + m_fieldx = 10; + m_fieldy = 10; +} + +void KShipList::clear() +{ + m_shipsadded = 4; + m_shiplist.clear(); +} + +int KShipList::shipTypeAt(int x, int y) +{ + int tempx, tempy; + KShip *shipiterator; + for(shipiterator = m_shiplist.first(); shipiterator != 0; shipiterator = m_shiplist.next()) + { + if(shipiterator->shipystart() != shipiterator->shipystop()) + { + for(tempy = shipiterator->shipystart(); tempy <= shipiterator->shipystop(); tempy++) + { + if(tempy == y) + { + if(shipiterator->shipxstart() != shipiterator->shipxstop()) + { + for(tempx = shipiterator->shipxstart(); tempx <= shipiterator->shipxstop(); tempx++) + { + if(tempx == x) + return shipiterator->shiptype(); + } + } + else + { + tempx = shipiterator->shipxstart(); + if(tempx == x) + return shipiterator->shiptype(); + } + } + } + } + else + { + tempy = shipiterator->shipystart(); + if(tempy == y) + { + if(shipiterator->shipxstart() != shipiterator->shipxstop()) + { + for(tempx = shipiterator->shipxstart(); tempx <= shipiterator->shipxstop(); tempx++) + { + if(tempx == x) + return shipiterator->shiptype(); + } + } + else + { + tempx = shipiterator->shipxstart(); + if(tempx == x) + return shipiterator->shiptype(); + } + } + } + } + return 99; +} + +KShip *KShipList::shipAt(int x, int y) +{ + int tempx, tempy; + KShip *shipiterator; + for(shipiterator = m_shiplist.first(); shipiterator != 0; shipiterator = m_shiplist.next()) + { + for(tempy = shipiterator->shipystart(); tempy <= shipiterator->shipystop(); tempy++) + { + if(tempy == y) + { + for(tempx = shipiterator->shipxstart(); tempx <= shipiterator->shipxstop(); tempx++) + { + if(tempx == x) + return shipiterator; + } + } + } + } + return 0; +} + +bool KShipList::canAddShips() +{ + if(m_shipsadded >= 1) + return true; + return false; +} + +void KShipList::addNewShip(int button, int fieldx, int fieldy) +{ + if(!addNewShip(!(button == LeftButton), fieldx, fieldy)) + KMessageBox::information(0L, i18n("You cannot place the ship here.")); +} + +bool KShipList::addNewShip(bool vertical, int fieldx, int fieldy) +{ + QRect ship = vertical ? QRect(fieldx, fieldy, 1, m_shipsadded) : QRect(fieldx, fieldy, m_shipsadded, 1); + QRect field = QRect(0, 0, m_fieldx, m_fieldy); + if(!field.contains(ship)) + return false; + + for(KShip *placedShip = m_shiplist.first(); placedShip != 0; placedShip = m_shiplist.next()) + { + for(int i = fieldx-1; i < (fieldx + ship.width()+1); i++) + { + if(placedShip->contains(i, fieldy - 1) || placedShip->contains(i, fieldy + ship.height())) + return false; + } + + for(int i = fieldy-1; i < (fieldy + ship.height()+1); i++) + { + if(placedShip->contains(fieldx - 1, i) || placedShip->contains(fieldx + ship.width(), i)) + return false; + } + } + + m_shipsadded--; + + if(!vertical) + m_shiplist.append(new KShip(fieldx, fieldx + shipCount(), fieldy, fieldy, shipCount(), true)); + else + m_shiplist.append(new KShip(fieldx, fieldx, fieldy, fieldy + shipCount(), shipCount(), false)); + + for(int i = 0; i < shipCount() + 1; i++) + { + int start = 0; + if(shipCount() == 3) + start = KBattleField::SHIP4P1; + else if(shipCount() == 2) + start = KBattleField::SHIP3P1; + else if(shipCount() == 1) + start = KBattleField::SHIP2P1; + else if(shipCount() == 0) + start = KBattleField::SHIP1P1; + + if(!vertical) + emit sigOwnFieldDataChanged(fieldx + i, fieldy, start + i); + else + emit sigOwnFieldDataChanged(fieldx, fieldy + i, start + i); + } + + if(m_shipsadded == 0) + emit sigLastShipAdded(); + return true; +} diff --git a/kbattleship/kbattleship/kshiplist.h b/kbattleship/kbattleship/kshiplist.h new file mode 100644 index 00000000..59915a5b --- /dev/null +++ b/kbattleship/kbattleship/kshiplist.h @@ -0,0 +1,58 @@ +/*************************************************************************** + kshiplist.h + ----------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KSHIPLIST_H +#define KSHIPLIST_H + +#include +#include +#include +#include "kbattlefield.h" +#include "kship.h" + +class KShipList : public QObject +{ + Q_OBJECT +public: + KShipList(); + + KShip *shipAt(int x, int y); + int shipTypeAt(int x, int y); + + void clear(); + + void addNewShip(int button, int fieldx, int fieldy); + bool addNewShip(bool vertical, int fieldx, int fieldy); + + bool canAddShips(); + + int shipCount() { return m_shipsadded; } + + int m_fieldx; + int m_fieldy; + +signals: + void sigLastShipAdded(); + void sigOwnFieldDataChanged(int, int, int); + +private: + QPtrList m_shiplist; + int m_shipsadded; +}; + +#endif diff --git a/kbattleship/kbattleship/kstatdialog.cpp b/kbattleship/kbattleship/kstatdialog.cpp new file mode 100644 index 00000000..9db2afe4 --- /dev/null +++ b/kbattleship/kbattleship/kstatdialog.cpp @@ -0,0 +1,92 @@ +/*************************************************************************** + kstatdlg.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include "kstatdialog.moc" + +KStatDialog::KStatDialog(QWidget *parent, const char *name) : statDlg(parent, name) +{ +} + +void KStatDialog::slotAddOwnWon() +{ + OwnLabel->setText(QString::number(OwnLabel->text().toInt() + 1)); +} + +void KStatDialog::slotAddEnemyWon() +{ + EnemyLabel->setText(QString::number(EnemyLabel->text().toInt() + 1)); +} + +void KStatDialog::setShot() +{ + setShot(shot() + 1); +} + +void KStatDialog::setShot(int shot) +{ + ShotLCD->display(shot); +} + +void KStatDialog::setHit() +{ + setHit(hit() + 1); +} + +void KStatDialog::setHit(int hit) +{ + HitLCD->display(hit); +} + +void KStatDialog::setWater() +{ + setWater(water() + 1); +} + +void KStatDialog::setWater(int water) +{ + WaterLCD->display(water); +} + +void KStatDialog::clear() +{ + ShotLCD->display(0); + HitLCD->display(0); + WaterLCD->display(0); +} + +void KStatDialog::clearWon() +{ + OwnLabel->setText(QString::number(0)); + EnemyLabel->setText(QString::number(0)); +} + +int KStatDialog::shot() +{ + return ShotLCD->intValue(); +} + +int KStatDialog::hit() +{ + return HitLCD->intValue(); +} + +int KStatDialog::water() +{ + return WaterLCD->intValue(); +} diff --git a/kbattleship/kbattleship/kstatdialog.h b/kbattleship/kbattleship/kstatdialog.h new file mode 100644 index 00000000..fd9b3dd7 --- /dev/null +++ b/kbattleship/kbattleship/kstatdialog.h @@ -0,0 +1,48 @@ +/*************************************************************************** + kstatdialog.h + ----------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KSTATDIALOG_H +#define KSTATDIALOG_H + +#include "dialogs/statDlg.h" + +class KStatDialog : public statDlg +{ + Q_OBJECT +public: + KStatDialog(QWidget *parent = 0, const char *name = 0); + + void setShot(); + void setShot(int shot); + void setHit(); + void setHit(int hit); + void setWater(); + void setWater(int water); + + void clear(); + void clearWon(); + + int shot(); + int hit(); + int water(); + +public slots: + void slotAddOwnWon(); + void slotAddEnemyWon(); +}; + +#endif diff --git a/kbattleship/kbattleship/main.cpp b/kbattleship/kbattleship/main.cpp new file mode 100644 index 00000000..2fdba01e --- /dev/null +++ b/kbattleship/kbattleship/main.cpp @@ -0,0 +1,70 @@ +/*************************************************************************** + main.cpp + ---------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include "kbattleship.h" + +extern const char *clientName, *clientVersion, *clientDescription; + +static KCmdLineOptions options[] = +{ + {"!+[URL]", I18N_NOOP("URL of server to attach to. In the form kbattleship://host:port/ or host:port"), 0}, + KCmdLineLastOption +}; + +int main(int argc, char *argv[]) +{ + KAboutData aboutData("kbattleship", clientName, clientVersion, clientDescription, KAboutData::License_GPL, "(c) 2000-2005 Nikolas Zimmermann, Daniel Molkentin"); + + aboutData.addAuthor("Nikolas Zimmermann", I18N_NOOP("Project Founder, GUI Handling, Client/Server"), "wildfox@kde.org"); + aboutData.addAuthor("Daniel Molkentin", I18N_NOOP("Dialog Stuff, Client/Server"), "molkentin@kde.org"); + aboutData.addAuthor("Kevin Krammer", I18N_NOOP("Computer Player"), "kevin.krammer@gmx.at"); + aboutData.addCredit("Benjamin Adler", I18N_NOOP("Icon"), "benadler@bigfoot.de"); + aboutData.addCredit("Nils Trzebin", I18N_NOOP("Sounds"), "nils.trzebin@stud.uni-hannover.de"); + aboutData.addCredit("Elmar Hoefner", I18N_NOOP("GFX"), "elmar.hoefner@uibk.ac.at"); + aboutData.addCredit("Lukas Tinkl", I18N_NOOP("Non-Latin1 Support"), "lukas@kde.org"); + aboutData.addCredit("Malte Starostik", I18N_NOOP("Various improvements"), "malte.starostik@t-online.de"); + aboutData.addCredit("Albert Astals Cid", I18N_NOOP("Various improvements and bugfixes"), "tsdgeos@terra.es"); + aboutData.addCredit("John Tapsell", I18N_NOOP("Various improvements and bugfixes"), "john@geola.co.uk"); + aboutData.addCredit("Inge Wallin", I18N_NOOP("Bugfixes and refactoring"), "inge@lysator.liu.se"); + aboutData.addCredit("Jakub Stachowski", I18N_NOOP("DNS-SD discovery"), "qbast@go2.pl"); + + KCmdLineArgs::init(argc, argv, &aboutData); + KCmdLineArgs::addCmdLineOptions( options ); // Add our own options. + KApplication app; + KGlobal::locale()->insertCatalogue("libkdegames"); + + QString picDirCheck = locate("data", "kbattleship/pictures/"); + QString onePicCheck = locate("data", "kbattleship/pictures/hit.png"); + if(picDirCheck.isEmpty() || onePicCheck.isEmpty()) + { + KMessageBox::error(0, i18n("You don't have KBattleship pictures installed. The game cannot run without them!")); + return 1; + } + + if( app.isRestored() ) + RESTORE(KBattleshipWindow) + else { + KBattleshipWindow *mainwin = new KBattleshipWindow; + mainwin->show(); + } + return app.exec(); +} + diff --git a/kbattleship/kbattleship/pictures/Makefile.am b/kbattleship/kbattleship/pictures/Makefile.am new file mode 100644 index 00000000..5bb50353 --- /dev/null +++ b/kbattleship/kbattleship/pictures/Makefile.am @@ -0,0 +1,30 @@ +picsdir = $(kde_datadir)/kbattleship/pictures +pics_DATA = sea.png \ + water.png \ + hit.png \ + death.png \ + border.png \ + ship1-1.png \ + ship2-1.png \ + ship2-2.png \ + ship3-1.png \ + ship3-2.png \ + ship3-3.png \ + ship4-1.png \ + ship4-2.png \ + ship4-3.png \ + ship4-4.png \ + ship1-1-r.png \ + ship2-1-r.png \ + ship2-2-r.png \ + ship3-1-r.png \ + ship3-2-r.png \ + ship3-3-r.png \ + ship4-1-r.png \ + ship4-2-r.png \ + ship4-3-r.png \ + ship4-4-r.png + +KDE_ICON = kbattleship + +EXTRA_DIST = $(pics_DATA) diff --git a/kbattleship/kbattleship/pictures/border.png b/kbattleship/kbattleship/pictures/border.png new file mode 100644 index 00000000..575b7d6e Binary files /dev/null and b/kbattleship/kbattleship/pictures/border.png differ diff --git a/kbattleship/kbattleship/pictures/death.png b/kbattleship/kbattleship/pictures/death.png new file mode 100644 index 00000000..518450a5 Binary files /dev/null and b/kbattleship/kbattleship/pictures/death.png differ diff --git a/kbattleship/kbattleship/pictures/hi128-app-kbattleship.png b/kbattleship/kbattleship/pictures/hi128-app-kbattleship.png new file mode 100644 index 00000000..93db2a3a Binary files /dev/null and b/kbattleship/kbattleship/pictures/hi128-app-kbattleship.png differ diff --git a/kbattleship/kbattleship/pictures/hi16-app-kbattleship.png b/kbattleship/kbattleship/pictures/hi16-app-kbattleship.png new file mode 100644 index 00000000..b7971c96 Binary files /dev/null and b/kbattleship/kbattleship/pictures/hi16-app-kbattleship.png differ diff --git a/kbattleship/kbattleship/pictures/hi22-app-kbattleship.png b/kbattleship/kbattleship/pictures/hi22-app-kbattleship.png new file mode 100644 index 00000000..27b8816d Binary files /dev/null and b/kbattleship/kbattleship/pictures/hi22-app-kbattleship.png differ diff --git a/kbattleship/kbattleship/pictures/hi32-app-kbattleship.png b/kbattleship/kbattleship/pictures/hi32-app-kbattleship.png new file mode 100644 index 00000000..07e05fa9 Binary files /dev/null and b/kbattleship/kbattleship/pictures/hi32-app-kbattleship.png differ diff --git a/kbattleship/kbattleship/pictures/hi48-app-kbattleship.png b/kbattleship/kbattleship/pictures/hi48-app-kbattleship.png new file mode 100644 index 00000000..2f501a41 Binary files /dev/null and b/kbattleship/kbattleship/pictures/hi48-app-kbattleship.png differ diff --git a/kbattleship/kbattleship/pictures/hi64-app-kbattleship.png b/kbattleship/kbattleship/pictures/hi64-app-kbattleship.png new file mode 100644 index 00000000..1b4ac1e1 Binary files /dev/null and b/kbattleship/kbattleship/pictures/hi64-app-kbattleship.png differ diff --git a/kbattleship/kbattleship/pictures/hit.png b/kbattleship/kbattleship/pictures/hit.png new file mode 100644 index 00000000..ca2684bc Binary files /dev/null and b/kbattleship/kbattleship/pictures/hit.png differ diff --git a/kbattleship/kbattleship/pictures/sea.png b/kbattleship/kbattleship/pictures/sea.png new file mode 100644 index 00000000..ab14e95d Binary files /dev/null and b/kbattleship/kbattleship/pictures/sea.png differ diff --git a/kbattleship/kbattleship/pictures/ship1-1-r.png b/kbattleship/kbattleship/pictures/ship1-1-r.png new file mode 100644 index 00000000..eb96026c Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship1-1-r.png differ diff --git a/kbattleship/kbattleship/pictures/ship1-1.png b/kbattleship/kbattleship/pictures/ship1-1.png new file mode 100644 index 00000000..4794706c Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship1-1.png differ diff --git a/kbattleship/kbattleship/pictures/ship1-view.png b/kbattleship/kbattleship/pictures/ship1-view.png new file mode 100644 index 00000000..eb96026c Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship1-view.png differ diff --git a/kbattleship/kbattleship/pictures/ship2-1-r.png b/kbattleship/kbattleship/pictures/ship2-1-r.png new file mode 100644 index 00000000..9dba502d Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship2-1-r.png differ diff --git a/kbattleship/kbattleship/pictures/ship2-1.png b/kbattleship/kbattleship/pictures/ship2-1.png new file mode 100644 index 00000000..b451e834 Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship2-1.png differ diff --git a/kbattleship/kbattleship/pictures/ship2-2-r.png b/kbattleship/kbattleship/pictures/ship2-2-r.png new file mode 100644 index 00000000..2020e637 Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship2-2-r.png differ diff --git a/kbattleship/kbattleship/pictures/ship2-2.png b/kbattleship/kbattleship/pictures/ship2-2.png new file mode 100644 index 00000000..6bfc6209 Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship2-2.png differ diff --git a/kbattleship/kbattleship/pictures/ship2-view.png b/kbattleship/kbattleship/pictures/ship2-view.png new file mode 100644 index 00000000..7d175815 Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship2-view.png differ diff --git a/kbattleship/kbattleship/pictures/ship3-1-r.png b/kbattleship/kbattleship/pictures/ship3-1-r.png new file mode 100644 index 00000000..d86e783f Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship3-1-r.png differ diff --git a/kbattleship/kbattleship/pictures/ship3-1.png b/kbattleship/kbattleship/pictures/ship3-1.png new file mode 100644 index 00000000..f67b6079 Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship3-1.png differ diff --git a/kbattleship/kbattleship/pictures/ship3-2-r.png b/kbattleship/kbattleship/pictures/ship3-2-r.png new file mode 100644 index 00000000..1586eb25 Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship3-2-r.png differ diff --git a/kbattleship/kbattleship/pictures/ship3-2.png b/kbattleship/kbattleship/pictures/ship3-2.png new file mode 100644 index 00000000..416c972d Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship3-2.png differ diff --git a/kbattleship/kbattleship/pictures/ship3-3-r.png b/kbattleship/kbattleship/pictures/ship3-3-r.png new file mode 100644 index 00000000..1d5dda2e Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship3-3-r.png differ diff --git a/kbattleship/kbattleship/pictures/ship3-3.png b/kbattleship/kbattleship/pictures/ship3-3.png new file mode 100644 index 00000000..ab1ada4b Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship3-3.png differ diff --git a/kbattleship/kbattleship/pictures/ship3-view.png b/kbattleship/kbattleship/pictures/ship3-view.png new file mode 100644 index 00000000..19bcb136 Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship3-view.png differ diff --git a/kbattleship/kbattleship/pictures/ship4-1-r.png b/kbattleship/kbattleship/pictures/ship4-1-r.png new file mode 100644 index 00000000..4d25ab10 Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship4-1-r.png differ diff --git a/kbattleship/kbattleship/pictures/ship4-1.png b/kbattleship/kbattleship/pictures/ship4-1.png new file mode 100644 index 00000000..51241360 Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship4-1.png differ diff --git a/kbattleship/kbattleship/pictures/ship4-2-r.png b/kbattleship/kbattleship/pictures/ship4-2-r.png new file mode 100644 index 00000000..187f0b17 Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship4-2-r.png differ diff --git a/kbattleship/kbattleship/pictures/ship4-2.png b/kbattleship/kbattleship/pictures/ship4-2.png new file mode 100644 index 00000000..59f03e8f Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship4-2.png differ diff --git a/kbattleship/kbattleship/pictures/ship4-3-r.png b/kbattleship/kbattleship/pictures/ship4-3-r.png new file mode 100644 index 00000000..351843b7 Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship4-3-r.png differ diff --git a/kbattleship/kbattleship/pictures/ship4-3.png b/kbattleship/kbattleship/pictures/ship4-3.png new file mode 100644 index 00000000..d6242c14 Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship4-3.png differ diff --git a/kbattleship/kbattleship/pictures/ship4-4-r.png b/kbattleship/kbattleship/pictures/ship4-4-r.png new file mode 100644 index 00000000..cfebae84 Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship4-4-r.png differ diff --git a/kbattleship/kbattleship/pictures/ship4-4.png b/kbattleship/kbattleship/pictures/ship4-4.png new file mode 100644 index 00000000..b34c6ca2 Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship4-4.png differ diff --git a/kbattleship/kbattleship/pictures/ship4-view.png b/kbattleship/kbattleship/pictures/ship4-view.png new file mode 100644 index 00000000..d1e53f62 Binary files /dev/null and b/kbattleship/kbattleship/pictures/ship4-view.png differ diff --git a/kbattleship/kbattleship/pictures/water.png b/kbattleship/kbattleship/pictures/water.png new file mode 100644 index 00000000..8f4adc3d Binary files /dev/null and b/kbattleship/kbattleship/pictures/water.png differ diff --git a/kbattleship/kbattleship/sounds/Makefile.am b/kbattleship/kbattleship/sounds/Makefile.am new file mode 100644 index 00000000..459b8e50 --- /dev/null +++ b/kbattleship/kbattleship/sounds/Makefile.am @@ -0,0 +1,5 @@ +sounddir = $(kde_datadir)/kbattleship/sounds +sound_DATA = ship-player-shoot-water.ogg \ + ship-player1-shoot.ogg \ + ship-player2-shoot.ogg \ + ship-sink.ogg diff --git a/kbattleship/kbattleship/sounds/ship-player-shoot-water.ogg b/kbattleship/kbattleship/sounds/ship-player-shoot-water.ogg new file mode 100644 index 00000000..ad677670 Binary files /dev/null and b/kbattleship/kbattleship/sounds/ship-player-shoot-water.ogg differ diff --git a/kbattleship/kbattleship/sounds/ship-player1-shoot.ogg b/kbattleship/kbattleship/sounds/ship-player1-shoot.ogg new file mode 100644 index 00000000..5fa88f22 Binary files /dev/null and b/kbattleship/kbattleship/sounds/ship-player1-shoot.ogg differ diff --git a/kbattleship/kbattleship/sounds/ship-player2-shoot.ogg b/kbattleship/kbattleship/sounds/ship-player2-shoot.ogg new file mode 100644 index 00000000..d4becd0a Binary files /dev/null and b/kbattleship/kbattleship/sounds/ship-player2-shoot.ogg differ diff --git a/kbattleship/kbattleship/sounds/ship-sink.ogg b/kbattleship/kbattleship/sounds/ship-sink.ogg new file mode 100644 index 00000000..f5dfac9b Binary files /dev/null and b/kbattleship/kbattleship/sounds/ship-sink.ogg differ diff --git a/kblackbox/CHANGES b/kblackbox/CHANGES new file mode 100644 index 00000000..985fa5c9 --- /dev/null +++ b/kblackbox/CHANGES @@ -0,0 +1,52 @@ +1999-06-07 Petter Reinholdtsen + * kbbgame.cpp: Changed text Quit to Exit to match KDE style guide. + +0.3.0 from 0.2.6 (17.1.1999) + - KBBGraphic derived only from QWidget, not QTableView + -> a complete rewrite of gfx stuff + -> massive changes almost everywhere + - minimal size fixed + - new icons (I am just learning the povray :-) +0.2.6 from 0.2.5 (25.8.1998) + - the game is active after starting + - fixed bad drawing after setting the board size + - config. scripts from new kexample (KDE 1.0) + - moc dependencies in Makefile.am done by automoc to + avoid problems on Solaris (bug report by David Faure) + - some string fixes for better translation +0.2.5 from 0.2.4 (19.3.1998) + - es language support added + - sk language support added +0.2.4 from 0.2.3 (6.3.1998) + - pt language support added + - it language support added +0.2.3 from 0.2.2 (10.2.1998) + - new icon for fired laser + - new icon for "give up" + - "done" added to menu and toolbar (new icon) + - help file updated +0.2.2 from 0.2.1 (2.2.1998) + - "done" button removed - middle mouse button instead + - kde toolbar + - kde status bar + - new icons + - help file updated +0.2.1 from 0.2.0: + - KBlackBox.kdelnk + - icon kblackbox.xpm + - double updating during resize fixed + - saves the application size + - doesn't autoresize after changing a board size +0.2.0 from 0.1.1: + - kblackbox is now a KDE application -> help, configuration, ... + - autoconf usage (by kexample-0.4.0) + - remembers last used options + - pixmaps on fields in the box +0.1.1 from 0.1.0: + - you are warned if you try to change # of balls when a game is running + - game->resize works properly, even if you moved scrollbars now + - mouseMoveEvent reimplemented, so that you can "draw" when a button + is pressed + - marking with the right mouse button + - displaying more game statistics + - possibility to give up the game (shows you where the balls were) diff --git a/kblackbox/Makefile.am b/kblackbox/Makefile.am new file mode 100644 index 00000000..813aff83 --- /dev/null +++ b/kblackbox/Makefile.am @@ -0,0 +1,22 @@ +SUBDIRS = pics + +INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes) + +bin_PROGRAMS = kblackbox +kblackbox_SOURCES = kbbgfx.cpp kbbgame.cpp util.cpp main.cpp +kblackbox_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kblackbox_LDADD = $(LIB_KDEGAMES) +kblackbox_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +METASOURCES = AUTO +KDE_ICON = kblackbox + +xdg_apps_DATA = kblackbox.desktop + +EXTRA_DIST = VERSION CHANGES $(xdg_apps_DATA) + +rcdir = $(kde_datadir)/kblackbox +rc_DATA = kblackboxui.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kblackbox.pot diff --git a/kblackbox/README b/kblackbox/README new file mode 100644 index 00000000..a7678515 --- /dev/null +++ b/kblackbox/README @@ -0,0 +1,28 @@ +Program: kblackbox +Author: Robert Cimrman, e-mail: cimrman3@students.zcu.cz + +A simple logical game for the KDE project. + +FEATURES: + -stunning gfx (in the next version :-) + -high logical requirements +USING: + -see help +KNOWN BUGS: + -none +UNKNOWN BUGS: + -still unknown + ->report any "bogus features" to the author, please +TODO: + -improve gfx? +HISTORY: + 23.4.1997 -> start + 28.4.1997 -> 0.1.0 + 29.4.1997 -> 0.1.1 + 29.7.1997 -> 0.2.0 + 2.2.1997 -> 0.2.2 + 10.2.1998 -> 0.2.3 + 6.3.1998 -> 0.2.4 + 19.3.1998 -> 0.2.5 + 25.8.1998 -> 0.2.6 + 17.1.1999 -> 0.3.0 diff --git a/kblackbox/VERSION b/kblackbox/VERSION new file mode 100644 index 00000000..064bd513 --- /dev/null +++ b/kblackbox/VERSION @@ -0,0 +1,2 @@ +kblackbox-0.3.0 + diff --git a/kblackbox/big_kblackbox.xpm b/kblackbox/big_kblackbox.xpm new file mode 100644 index 00000000..e448e5a7 --- /dev/null +++ b/kblackbox/big_kblackbox.xpm @@ -0,0 +1,269 @@ +/* XPM */ +static char * kblackbox_xpm[] = { +"48 48 218 2", +" c None", +". c #414141", +"+ c #404040", +"@ c #464646", +"# c #424242", +"$ c #484848", +"% c #585858", +"& c #818181", +"* c #7D7D7D", +"= c #7F7F7F", +"- c #4D4D4D", +"; c #727272", +"> c #5A5A5A", +", c #878787", +"' c #4B4B4B", +") c #6E6E6E", +"! c #666666", +"~ c #626262", +"{ c #767676", +"] c #555555", +"^ c #6F6F6F", +"/ c #5F5F5F", +"( c #797979", +"_ c #454545", +": c #858585", +"< c #272727", +"[ c #595959", +"} c #787878", +"| c #696969", +"1 c #5B5B5B", +"2 c #868686", +"3 c #262626", +"4 c #2F2F2F", +"5 c #2E2E2E", +"6 c #A5A5A5", +"7 c #575757", +"8 c #4C4C4C", +"9 c #4F4F4F", +"0 c #5E5E5E", +"a c #2D2D2D", +"b c #565656", +"c c #7E7E7E", +"d c #303030", +"e c #6B6B6B", +"f c #363636", +"g c #505050", +"h c #545454", +"i c #3F3F3F", +"j c #4E4E4E", +"k c #4A4A4A", +"l c #373737", +"m c #5D5D5D", +"n c #393939", +"o c #383838", +"p c #353535", +"q c #676767", +"r c #3D3D3D", +"s c #3E3E3E", +"t c #343434", +"u c #606060", +"v c #262600", +"w c #2F2F01", +"x c #5A5A06", +"y c #4B4B04", +"z c #2C2C01", +"A c #2D2D01", +"B c #2E2E01", +"C c #3C3C3C", +"D c #737373", +"E c #292900", +"F c #333301", +"G c #494949", +"H c #D4D400", +"I c #606000", +"J c #272700", +"K c #242400", +"L c #313103", +"M c #5C5C5C", +"N c #3B3B3B", +"O c #6B6B00", +"P c #404001", +"Q c #363603", +"R c #E0E000", +"S c #5E5E00", +"T c #5D5D00", +"U c #252500", +"V c #333304", +"W c #393907", +"X c #282828", +"Y c #878700", +"Z c #AAAA05", +"` c #373704", +" . c #303002", +".. c #323232", +"+. c #E9E902", +"@. c #5A5A00", +"#. c #565600", +"$. c #383806", +"%. c #343406", +"&. c #5A5A11", +"*. c #313131", +"=. c #292929", +"-. c #FFFF2B", +";. c #FFFF25", +">. c #5E5E03", +",. c #606002", +"'. c #545400", +"). c #5B5B05", +"!. c #353506", +"~. c #585811", +"{. c #515151", +"]. c #525252", +"^. c #F5F50A", +"/. c #646401", +"(. c #696903", +"_. c #EFEF0E", +":. c #535305", +"<. c #4D4D06", +"[. c #444406", +"}. c #54540D", +"|. c #6A6A0E", +"1. c #444444", +"2. c #DEDE00", +"3. c #626200", +"4. c #6E6E00", +"5. c #D6D60A", +"6. c #3F3F05", +"7. c #363605", +"8. c #3E3E05", +"9. c #53530C", +"0. c #61610D", +"a. c #4E4E0E", +"b. c #474747", +"c. c #B5B509", +"d. c #3D3D04", +"e. c #313104", +"f. c #83830C", +"g. c #7D7D0D", +"h. c #4D4D0D", +"i. c #646464", +"j. c #333333", +"k. c #434343", +"l. c #9F9F09", +"m. c #474706", +"n. c #51510A", +"o. c #7C7C0B", +"p. c #7C7C0C", +"q. c #7E7E0E", +"r. c #727205", +"s. c #202000", +"t. c #494909", +"u. c #7B7B0A", +"v. c #7D7D0C", +"w. c #3A3A3A", +"x. c #474708", +"y. c #484808", +"z. c #484809", +"A. c #7A7A0A", +"B. c #7B7B0B", +"C. c #9D9D0C", +"D. c #9D9D0D", +"E. c #2C2C2C", +"F. c #636363", +"G. c #464606", +"H. c #474707", +"I. c #999909", +"J. c #9B9B0A", +"K. c #8F8F0B", +"L. c #90900C", +"M. c #535353", +"N. c #585806", +"O. c #464607", +"P. c #656507", +"Q. c #676708", +"R. c #8D8D09", +"S. c #6D6D02", +"T. c #6C6C02", +"U. c #62620F", +"V. c #606005", +"W. c #454506", +"X. c #4C4C06", +"Y. c #2B2B01", +"Z. c #636301", +"`. c #616102", +" + c #7C7C02", +".+ c #70700B", +"++ c #646411", +"@+ c #575701", +"#+ c #939301", +"$+ c #989801", +"%+ c #949401", +"&+ c #8F8F01", +"*+ c #686801", +"=+ c #717103", +"-+ c #94940B", +";+ c #868601", +">+ c #8F8F02", +",+ c #707000", +"'+ c #B0B00E", +")+ c #787800", +"!+ c #A5A504", +"~+ c #717171", +"{+ c #686868", +"]+ c #D5D50A", +"^+ c #555502", +"/+ c #5C5C02", +"(+ c #353504", +"_+ c #616161", +":+ c #FFFF21", +"<+ c #606001", +"[+ c #626201", +"}+ c #6A6A02", +"|+ c #E4E404", +"1+ c #D8D801", +"2+ c #616100", +"3+ c #686800", +"4+ c #949400", +"5+ c #747474", +" ", +" ", +" ", +" . + + . + @ + + # ", +" . . + . + $ + % & . + + + . + . * + + . + + . . + + ", +" + + . . = - ; > + + + . # + . , . ' ) ! ~ + + . { + . . % + ] + + ^ . / + + ", +" + + . # . + + . . . . . + . . . ( + + . + + . ~ _ + . . + . + . + # : / . + + + + < < ", +"$ _ _ . + . . + . + . + # . + + . . + . # [ ^ % - $ } | 1 % 2 + + # . + . # ) . + + 3 < 3 ", +"4 5 5 4 4 4 - . . + . . # . + + . . + + . # . + + + # 6 . + + . 7 8 9 . . . 0 [ + 3 4 3 3 ", +"4 4 5 a ' 5 5 4 4 4 4 5 + # * 7 . b c . . # + . # + # . . + . . . . . + . . + + 3 3 < 3 ", +"5 5 d e 4 4 4 4 4 4 4 4 5 f g 5 # 4 . . . . + . . + . + + . . + h + . + = i { < < < < < ", +"d 5 5 g 5 4 4 d j d 4 4 7 5 4 5 5 k l 5 5 5 5 5 + + + . + . . m @ + . + # 3 7 < 3 < 3 < ", +"4 n o 4 5 4 4 ! 5 l p 4 o 4 a 4 4 7 q 5 4 5 4 b ' 5 5 d 4 5 + + + [ ) j 3 3 3 3 3 3 < < ", +"5 5 ' 4 4 4 4 4 5 4 9 5 4 5 5 5 5 5 ; g r 4 4 5 s k t 4 d 4 5 4 4 4 5 < < 3 < 3 < < 3 < ", +"5 4 5 u d 5 v w x y z A B B 4 5 C [ D 0 5 5 4 4 5 d # 5 4 4 5 4 # 4 4 3 3 < < < < 3 < ", +" 4 5 u 4 E w F 5 5 p G H I I J K L % M f 5 5 5 4 4 5 4 5 4 4 4 5 G 4 < < f 3 3 3 3 < ", +" 4 5 N O P Q 9 p n u 4 4 4 4 R S T U V W 5 5 d 4 4 5 4 4 4 5 5 % 4 4 a < < X 3 < 3 X ", +" 8 p 5 Y Z ` ...4 q g 5 5 _ 9 5 +.@.#.$.%.&.5 4 5 4 5 5 5 4 4 *.@ 4 < < < =.3 < l 5 ", +" 5 d / 5 -.;.>.,.4 4 k 4 4 N o 5 4 5 '.).!.%.~.5 5 5 5 4 4 5 d i {.[ < < # ] < o < < ", +" d 4 5 ].^./.(.5 4 5 5 4 5 4 4 5 4 _.:.<.[.}.}.|.5 4 5 4 5 4 i {.p i < < 1.3 < s ", +" 4 5 4 5 5 2.3.4.4 4 d 4 N 4 5 4 4 j 5.6.7.8.9.0.a.4 5 5 4 5 5 s ' 3 3 < 3 C < h ", +" p G b.4 4 4 5 4 5 5 4 4 4 f 5 s 4 [ s c.d.e.f.g.h.a.4 5 4 o G i.j.< < p u < =.3 ", +" 4 4 5 4 5 d 4 4 # 4 5 d 4 5 # 0 . 4 k.l.m.n.o.p.g.q.4 5 k.1 4 r 5 3 p 3 < 3 < < ", +" ) 4 5 5 5 ; 5 d 5 4 4 5 5 4 $ 5 4 5 r.s.t.u.o.v.g.q.5 4 4 5 N 3 3 s 3 X < *.3 ", +" r 4 4 4 5 5 s 4 4 _ d 4 d w.4 4 4 4 x.y.z.A.B.o.C.D.4 5 4 4 4 < =.E.3 < k < ", +" 5 ^ 4 4 4 4 4 4 4 F.p t j.5 5 4 d G.H.x.y.I.J.K.L.S *.d 5 4 4 3 < X 3 1.3 < ", +" M.p *.5 1.4 5 4 5 ] 5 d 4 5 4 4 N.G.O.P.Q.R.S S.T.T 4 4 4 5 4 < < 3 e t 3 3 ", +" 5 # 4 r 4 4 4 4 4 4 5 5 4 5 U.V.W.X.Y.Z.`.T.T. +5 d 5 4 5 4 < 3 o < 3 < 3 ", +" 4 4 5 - 5 4 5 d 4 4 d 4 s. ..+++@+#+$+%+&+u 4 5 4 4 4 5 5 < < < 3 3 < < ", +" 5 4 4 p 5 4 d *.d 5 5 *+=+-+;+>+#+4 4 M.4 5 D 4 5 5 5 5 w.3 3 3 < 3 < % ", +" 5 o 5 4 k s 5 5 5 5 ,+'+)+5 5 4 4 s g 4 4 s . 4 4 4 5 % < < ..3 j.< 3 ", +" l 5 b.5 ] ..) ' 4 4 !+4 4 4 4 4 4 + 4 4 4 4 5 4 4 5 5 p < < 3 < b.3 ", +" p t r 5 a 4 ~+5 7 5 4 5 4 4 4 4 5 + m 4 d 5 4 4 4 5 j.4 ' 3 C 3 w.N ", +" d 4 4 d 5 5 s + 4 ~ 5 @ 1.5 5 5 + 1.d 5 4 4 t 5 d d 4 4 ]._ 3 =.< ", +" 5 4 d {+5 4 5 ]+^+/+(+5 _ k 5 _+5 5 4 5 5 d 5 4 5 5 4 X < l < 5 ", +" 4 5 4 a 5 4 t 4 :+<+[+}+4 4 d 4 i 5 4 4 5 8 $ h 5 4 4 5 < 3 < ", +" 5 4 d _ 4 *.k.|+1+2+3+4 4 4 4 d 4 a 5 5 - % d 5 5 3 ' 3 X ", +" > 5 5 5 M.5 _+4 4+4 5 w.b.C 4 4 4 4 a 4 4 4 3 < < k. ", +" 4 8 s 5 5 4 4 5 4 g 5+d 0 4 4 4 4 5 5 4 3 3 < b ", +" 4 4 4 b.5 @ 5 5 i i.4 s 8 5 4 4 5 4 i < G ", +" d 5 4 4 4 _ 5 4 5 [ ' 5 j.4 {.l . d 3 < ", +" 4 5 5 5 j.5 4 4 5 d 5 d h 4 4 =.3 < ", +" 4 4 5 ].4 d 4 4 4 4 4 r 5 < 3 ", +" 4 4 5 u 4 4 5 _ ; 1.- 3 < ", +" 5 5 5 4 d N 5 4 4 3 ", +" 5 5 d 1 4 a < 3 ", +" 5 _ % 4 3 ", +" 4 4 p "}; diff --git a/kblackbox/hi128-app-kblackbox.png b/kblackbox/hi128-app-kblackbox.png new file mode 100644 index 00000000..76d581e0 Binary files /dev/null and b/kblackbox/hi128-app-kblackbox.png differ diff --git a/kblackbox/hi16-app-kblackbox.png b/kblackbox/hi16-app-kblackbox.png new file mode 100644 index 00000000..2b28f5ee Binary files /dev/null and b/kblackbox/hi16-app-kblackbox.png differ diff --git a/kblackbox/hi22-app-kblackbox.png b/kblackbox/hi22-app-kblackbox.png new file mode 100644 index 00000000..77d0d2b2 Binary files /dev/null and b/kblackbox/hi22-app-kblackbox.png differ diff --git a/kblackbox/hi32-app-kblackbox.png b/kblackbox/hi32-app-kblackbox.png new file mode 100644 index 00000000..6e8f897e Binary files /dev/null and b/kblackbox/hi32-app-kblackbox.png differ diff --git a/kblackbox/hi48-app-kblackbox.png b/kblackbox/hi48-app-kblackbox.png new file mode 100644 index 00000000..77c86d12 Binary files /dev/null and b/kblackbox/hi48-app-kblackbox.png differ diff --git a/kblackbox/hi64-app-kblackbox.png b/kblackbox/hi64-app-kblackbox.png new file mode 100644 index 00000000..f97a1f5c Binary files /dev/null and b/kblackbox/hi64-app-kblackbox.png differ diff --git a/kblackbox/kbbgame.cpp b/kblackbox/kbbgame.cpp new file mode 100644 index 00000000..82028871 --- /dev/null +++ b/kblackbox/kbbgame.cpp @@ -0,0 +1,740 @@ +// +// KBlackbox +// +// A simple game inspired by an emacs module +// +// File: kbbgame.cpp +// +// The implementation of the KBBGame widget +// + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kbbgame.h" +#include "util.h" +#include "version.h" + +/* + Names of pixmap files. +*/ + +const char *pFNames[NROFTYPES] = { + "white", + "gray", + "green", + "red", + "blue", + "cyan", + "brown", + "green2" +}; + +/* + Creates the KBBGame widget and sets saved options (if any). +*/ + +KBBGame::KBBGame() +{ + int i; + + QPixmap **pix = new QPixmap * [NROFTYPES]; + pix[0] = new QPixmap(); + *pix[0] = BarIcon( pFNames[0] ); + if (!pix[0]->isNull()) { + kdDebug(12009) << "Pixmap \"" << pFNames[0] << "\" loaded." << endl; + for (i = 1; i < NROFTYPES; i++) { + pix[i] = new QPixmap; + *pix[i] = BarIcon( pFNames[i] ); + if (!pix[i]->isNull()) { + kdDebug(12009) << "Pixmap \"" << pFNames[i] << "\" loaded." << endl; + } else { + pix[i] = pix[i-1]; + pix[i]->detach(); + kdDebug(12009) << "Cannot find pixmap \"" << pFNames[i] << "\". Using previous one." << endl; + } + } + } else { + kdDebug(12009) << "Cannot find pixmap \"" << pFNames[0] << "\". Pixmaps will not be loaded." << endl; + delete pix[0]; + delete pix; + pix = NULL; + } + gr = new KBBGraphic( pix, this, "KBBGraphic" ); + + statusBar()->insertItem(i18n("Score: 0000"), SSCORE); + statusBar()->insertItem(i18n("Placed: 00 / 00"), SBALLS); + statusBar()->insertItem(i18n("Run: yesno"), SRUN); + statusBar()->insertItem(i18n("Size: 00 x 00"), SSIZE); + + initKAction(); + + connect( gr, SIGNAL(inputAt(int,int,int)), + this, SLOT(gotInputAt(int,int,int)) ); + connect( this, SIGNAL(gameRuns(bool)), + gr, SLOT(setInputAccepted(bool)) ); + connect( gr, SIGNAL(endMouseClicked()), + this, SLOT(gameFinished()) ); + + /* + QToolTip::add( doneButton, i18n( + "Click here when you think you placed all the balls.") ); + */ + + /* + Game initializations + */ + running = FALSE; + gameBoard = NULL; + + KConfig *kConf; + int j; + kConf = kapp->config(); + kConf->setGroup( "KBlackBox Setup" ); + if (kConf->hasKey( "Balls" )) { + i = kConf->readNumEntry( "Balls" ); + balls = i; + switch (i) { + case 4: ballsAction->setCurrentItem(0); break; + case 6: ballsAction->setCurrentItem(1); break; + case 8: ballsAction->setCurrentItem(2); break; + } + } else { + balls = 4; + ballsAction->setCurrentItem(0); + } + if ((kConf->hasKey( "Width" )) && + (kConf->hasKey( "Balls" ))) { + i = kConf->readNumEntry( "Width" ); + j = kConf->readNumEntry( "Height" ); + gr->setSize( i+4, j+4 ); // +4 is the space for "lasers" and an edge... + gameBoard = new RectOnArray( gr->numC(), gr->numR() ); + switch (i) { + case 8: sizeAction->setCurrentItem(0); break; + case 10: sizeAction->setCurrentItem(1); break; + case 12: sizeAction->setCurrentItem(2); break; + } + } else { + gr->setSize( 8+4, 8+4 ); // +4 is the space for "lasers" and an edge... + gameBoard = new RectOnArray( gr->numC(), gr->numR() ); + sizeAction->setCurrentItem(0); + } + if (kConf->hasKey( "tutorial" )) { + tutorial = (bool) kConf->readNumEntry( "tutorial" ); + } else tutorial = FALSE; + tutorialAction->setChecked(tutorial); + + setCentralWidget( gr ); + + setScore( 0 ); + ballsPlaced = 0; + + updateStats(); + + newGame(); + setMinSize(); + + setupGUI(); +} + +/* + Saves the options and destroys the KBBGame widget. +*/ + +KBBGame::~KBBGame() +{ + KConfig *kConf; + QString s; + + kConf = kapp->config(); + kConf->setGroup( "KBlackBox Setup" ); + kConf->writeEntry( "Balls", balls ); + kConf->writeEntry( "Width", gr->numC() - 4); + kConf->writeEntry( "Height", gr->numR() - 4); + kConf->writeEntry( "tutorial", (int) tutorial ); + + delete gameBoard; + // All the rest has "this" for parent so it doesn't need to be deleted. +} + + +/* + Resizes yourself to fit the contents perfectly, from menu. +*/ +void KBBGame::gameResize() +{ + resize( gr->wHint(), gr->hHint() + menuBar()->height() + statusBar()->height() + + toolBar()->height() ); +} + +void KBBGame::setMinSize() +{ + setMinimumSize( gr->wHint(), gr->hHint() + menuBar()->height() + statusBar()->height() + + toolBar()->height() ); +} + +/* + Settings of various options. +*/ +void KBBGame::slotSize() +{ + int i = sizeAction->currentItem(); + bool ok = false; + switch (i) { + case 0: + ok = setSize( 8, 8 ); + break; + case 1: + ok = setSize( 10, 10 ); + break; + case 2: + ok = setSize( 12, 12 ); + break; + } + + if (!ok) { + switch(gr->numR() - 4) { + case 8: + sizeAction->setCurrentItem(0); break; + case 10: + sizeAction->setCurrentItem(1); break; + case 12: + sizeAction->setCurrentItem(2); break; + } + } +} + +void KBBGame::slotBalls() +{ + int i = ballsAction->currentItem(); + bool ok = false; + switch (i) { + case 0: + ok = setBalls( 4 ); + break; + case 1: + ok = setBalls( 6 ); + break; + case 2: + ok = setBalls( 8 ); + break; + } + if (!ok) { + switch (balls) { + case 4: + ballsAction->setCurrentItem(0); break; + case 6: + ballsAction->setCurrentItem(1); break; + case 8: + ballsAction->setCurrentItem(2); break; + } + } +} + +void KBBGame::tutorialSwitch() +{ + tutorial = !tutorial; +} + +/* + Creates a new game. +*/ +void KBBGame::newGame() +{ + int i, j; + + if (running) { + bool cancel; + cancel = KMessageBox::warningContinueCancel(0, + i18n("Do you really want to give up this game?"),QString::null,i18n("Give Up")) + == KMessageBox::Cancel; + if (cancel) + return; + + abortGame(); + } + + gameBoard->fill( INNERBBT ); + for (j = 0; j < (gr->numR()); j++) { + gameBoard->set( 0, j, OUTERBBT ); + gameBoard->set( gr->numC()-1, j, OUTERBBT ); + } + for (i = 0; i < (gr->numC()); i++) { + gameBoard->set( i, 0, OUTERBBT ); + gameBoard->set( i, gr->numR()-1, OUTERBBT ); + } + for (j = 2; j < (gr->numR()-2); j++) { + gameBoard->set( 1, j, LASERBBT ); + gameBoard->set( gr->numC()-2, j, LASERBBT ); + } + for (i = 2; i < (gr->numC()-2); i++) { + gameBoard->set( i, 1, LASERBBT ); + gameBoard->set( i, gr->numR()-2, LASERBBT ); + } + gameBoard->set( 1, 1, OUTERBBT ); + gameBoard->set( 1, gr->numR()-2, OUTERBBT ); + gameBoard->set( gr->numC()-2, 1, OUTERBBT ); + gameBoard->set( gr->numC()-2, gr->numR()-2, OUTERBBT ); + + randomBalls( balls ); + remap( gameBoard, gr->getGraphicBoard() ); + gr->repaint( TRUE ); + setScore( 0 ); + detourCounter = -1; + ballsPlaced = 0; + running = TRUE; + updateStats(); + emit gameRuns( running ); +} + +/* + Ends the current game. +*/ + +void KBBGame::gameFinished() +{ + if (running) { + QString s; + if (ballsPlaced == balls) { + getResults(); + abortGame(); + if (score <= (balls*3)) + s = i18n("Your final score is: %1\n" + "You did really well!"); + else + s = i18n("Your final score is: %1\n" + "I guess you need more practice."); + + KMessageBox::information(this, + s.arg(KGlobal::locale()->formatNumber(score, 0))); + } else { + s = i18n( "You should place %1 balls!\n" + "You have placed %2.") + .arg(KGlobal::locale()->formatNumber(balls, 0)) + .arg(KGlobal::locale()->formatNumber(ballsPlaced, 0)); + + KMessageBox::sorry(this, s); + } + } +} + +/* + Computes the final score and indicate errors. +*/ + +void KBBGame::getResults() +{ + int i, j, tgam, tgra; + RectOnArray *r = gr->getGraphicBoard(); + for (j = 0; j < (gr->numR()); j++) { + for (i = 0; i < (gr->numC()); i++) { + tgam = gameBoard->get( i, j ); + tgra = r->get( i, j ); + if ((tgam == BALLBBT) && (tgra != TBALLBBG)) { + setScore( score+5 ); + r->set( i, j, WBALLBBG ); + gr->updateElement( i, j ); + } + if ((tgam != BALLBBT) && (tgra == TBALLBBG)) { + r->set( i, j, FBALLBBG ); + gr->updateElement( i, j ); + } + } + } +} + +/* + Aborts the current game. +*/ + +void KBBGame::abortGame() +{ + if (running) { + running = FALSE; + ballsPlaced = 0; + updateStats(); + gr->clearFocus(); + emit gameRuns( running ); + } +} + +/* + Gives the game up. +*/ + +void KBBGame::giveUp() +{ + if (running) { + bool stop; + stop = KMessageBox::warningContinueCancel(0, + i18n( + "Do you really want to give up this game?"),QString::null,i18n("Give Up")) + == KMessageBox::Continue; + + if (stop) { + getResults(); + abortGame(); + } + } +} + +/* + Displays game statistics. +*/ + +void KBBGame::updateStats() +{ + QString tmp; + QString s = i18n("Run: "); + if (running) + s += i18n("Yes"); + else + s += i18n("No"); + statusBar()->changeItem( s, SRUN ); + s = i18n( "Size: " ); + s += tmp.sprintf( "%2d x %2d", + gr->numC()-4, gr->numR()-4 ); + statusBar()->changeItem( s, SSIZE ); + s = i18n( "Placed: " ); + s += tmp.sprintf( "%2d / %2d", + ballsPlaced, balls ); + statusBar()->changeItem( s, SBALLS ); +} + +/* + Sets the score value to n. +*/ + +void KBBGame::setScore( int n ) +{ + score = n; + statusBar()->changeItem( i18n("Score: %1").arg(n), SSCORE ); +} + +/* + Sets the size of the black box. +*/ + +bool KBBGame::setSize( int w, int h ) +{ + bool ok = FALSE; + if (((w+4) != gr->numC()) || ((h+4) != gr->numR())) { + if (running) { + ok = KMessageBox::warningContinueCancel(0, + i18n( + "This will be the end of the current game!"),QString::null,i18n("End Game")) + == KMessageBox::Continue; + + } else ok = TRUE; + if (ok) { + gr->setSize( w+4, h+4 ); // +4 is the space for "lasers" and an edge... + setMinSize(); + gameResize(); + delete gameBoard; + gameBoard = new RectOnArray( gr->numC(), gr->numR() ); + if (running) abortGame(); + newGame(); + // gr->repaint( TRUE ); + } + } + return ok; +} + +/* + Sets the number of balls in the black box to n. +*/ + +bool KBBGame::setBalls( int n ) +{ + bool ok = FALSE; + if (balls != n) { + if (running) { + ok = KMessageBox::warningContinueCancel(0, + i18n("This will be the end of the current game!"),QString::null,i18n("End Game")) + == KMessageBox::Continue; + } else ok = TRUE; + if (ok) { + balls = n; + if (running) abortGame(); + newGame(); + } + } + return ok; +} + +/* + Puts n balls in the black box on random positions. +*/ + +void KBBGame::randomBalls( int n ) +{ + int i; + random.setSeed(0); + for (i = 0; i < n; i++) { + int x=0, y=0; // there is OUTERBBT... + while (gameBoard->get( x, y ) != INNERBBT ) { + x = 2 + random.getLong(gameBoard->width()-4); + y = 2 + random.getLong(gameBoard->height()-4); + } + gameBoard->set( x, y, BALLBBT ); + } +} + +/* + This is, in fact, the whole game... +*/ + +int KBBGame::traceRay( int startX, int startY, int *endX, int *endY ) +{ + int type, x, y, d, refl; + int slx, scx, srx, sly, scy, sry; + bool directionChanged; + *endX = x = startX; + *endY = y = startY; + /* + Just to avoid compiler warnings + */ + type = slx = scx = srx = sly = scy = sry = 0; + /* + Get the initial direction d. + 0 .. up, 1 .. right, 2 .. down, 3 .. left + (0,0) is the upper-left corner. + */ + if ((gameBoard->get( x, y-1 ) == INNERBBT) || + (gameBoard->get( x, y-1 ) == BALLBBT)) { d = 0; } + else if ((gameBoard->get( x+1, y ) == INNERBBT) || + (gameBoard->get( x+1, y ) == BALLBBT)) { d = 1; } + else if ((gameBoard->get( x, y+1 ) == INNERBBT) || + (gameBoard->get( x, y+1 ) == BALLBBT)) { d = 2; } + else if ((gameBoard->get( x-1, y ) == INNERBBT) || + (gameBoard->get( x-1, y ) == BALLBBT)) { d = 3; } + else return WRONGSTART; + /* + And now trace the ray. + */ + while (1) { + switch (d) { + case 0: + slx = -1; scx = 0; srx = 1; + sly = -1; scy = -1; sry = -1; + break; + case 1: + slx = 1; scx = 1; srx = 1; + sly = -1; scy = 0; sry = 1; + break; + case 2: + slx = 1; scx = 0; srx = -1; + sly = 1; scy = 1; sry = 1; + break; + case 3: + slx = -1; scx = -1; srx = -1; + sly = 1; scy = 0; sry = -1; + break; + } + directionChanged = FALSE; + if (gameBoard->get( x+scx, y+scy ) == LASERBBT) { + type = DETOUR; + *endX = x+scx; + *endY = y+scy; + break; + } + if (gameBoard->get( x+scx, y+scy ) == BALLBBT) { + type = HIT; + break; + } + refl = 0; + if (gameBoard->get( x+slx, y+sly ) == BALLBBT) { + type = REFLECTION; + if (gameBoard->get( x, y ) == LASERBBT) break; + directionChanged = TRUE; + refl += 1; + } + if (gameBoard->get( x+srx, y+sry ) == BALLBBT) { + type = REFLECTION; + if (gameBoard->get( x, y ) == LASERBBT) break; + directionChanged = TRUE; + refl +=2; + } + // turn to the right + if (refl == 1) d = (d + 1) % 4; + // turn to the left + if (refl == 2) if ((d -= 1) < 0) d += 4; + // turn back -- no need to trace again the same way + if (refl == 3) break; + if (!directionChanged) { + x += scx; + y += scy; + } + } + return type; +} + +/* + Remaps the gameBoard to its graphic representation. +*/ + +void KBBGame::remap( RectOnArray *gam, RectOnArray *gra ) +{ + int i, j; + for (j = 0; j < (gam->height()); j++) { + for (i = 0; i < (gam->width()); i++) { + switch (gam->get( i,j )) { + case BALLBBT: if (tutorial) { gra->set( i,j, WBALLBBG ); break; } + case INNERBBT: gra->set( i,j, INNERBBG ); break; + case OUTERBBT: gra->set( i,j, OUTERBBG ); break; + case LASERBBT: gra->set( i,j, LASERBBG ); break; + default: gra->set( i,j, OUTERBBG ); + } + } + } +} + +/* + Processes the user input. +*/ + +void KBBGame::gotInputAt( int col, int row, int state ) +{ + RectOnArray *r = gr->getGraphicBoard(); + int type = r->get( col, row ); + int x, y; + int ex, ey; + int w = gameBoard->width() - 2; + int h = gameBoard->height() - 2; + + if (state & LeftButton) { + switch (type) { + case WBALLBBG: // because of the tutorial mode + case INNERBBG: + r->set( col, row, TBALLBBG ); + ballsPlaced++; + break; + case MARK1BBG: + r->set( col, row, INNERBBG ); + break; + case TBALLBBG: + r->set( col, row, INNERBBG ); + ballsPlaced--; + break; + case LASERBBG: + int endX, endY, result; + result = traceRay( col, row, &endX, &endY ); + r->set( col, row, LFIREBBG ); + //kdDebug << endX << " " << endY << endl; + if (col == 1) x = 0; else + if (col == w) x = w + 1; + else x = col; + + if (row == 1) y = 0; else + if (row == h) y = h + 1; + else y = row; + + switch (result) { + case DETOUR: + r->set( endX, endY, LFIREBBG ); + r->set( x, y, detourCounter ); + if (endX == 1) ex = 0; else + if (endX == w) ex = w + 1; + else ex = endX; + if (endY == 1) ey = 0; else + if (endY == h) ey = h + 1; + else ey = endY; + r->set( ex, ey, detourCounter-- ); + gr->updateElement( x, y ); + gr->updateElement( ex, ey ); + gr->updateElement( endX, endY ); + setScore( score+2 ); + break; + case REFLECTION: + r->set( x, y, RLASERBBG ); + gr->updateElement( x, y ); + setScore( score+1 ); + break; + case HIT: + r->set( x, y, HLASERBBG ); + gr->updateElement( x, y ); + setScore( score+1 ); + break; + case WRONGSTART: + kdDebug(12009) << "Wrong start?! It should't happen!!" << endl; + break; + } + break; + } + } else if (state & RightButton) { + switch (type) { + case INNERBBG: + r->set( col, row, MARK1BBG ); + break; + /*case MARK1BBG: + r->set( col, row, INNERBBG ); + break;*/ + } + } + gr->updateElement( col, row ); + updateStats(); +} + +void KBBGame::initKAction() +{ +// game + KStdGameAction::gameNew(this, SLOT(newGame()), actionCollection()); + (void)new KAction( i18n("&Give Up"), SmallIcon("giveup"), 0, this, SLOT(giveUp()), actionCollection(), "game_giveup" ); + (void)new KAction( i18n("&Done"), SmallIcon("done"), 0, this, SLOT(gameFinished()), actionCollection(), "game_done" ); + (void)new KAction( i18n("&Resize"), 0, this, SLOT(slotResize()), actionCollection(), "game_resize" ); + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + + +// settings + sizeAction = new KSelectAction( i18n("&Size"), 0, this, SLOT(slotSize()), actionCollection(), "options_size"); + QStringList list; + list.append(i18n(" 8 x 8 ")); + list.append(i18n(" 10 x 10 ")); + list.append(i18n(" 12 x 12 ")); + sizeAction->setItems(list); + + ballsAction = new KSelectAction( i18n("&Balls"), 0, this, SLOT(slotBalls()), actionCollection(), "options_balls"); + list.clear(); + list.append(i18n(" 4 ")); + list.append(i18n(" 6 ")); + list.append(i18n(" 8 ")); + ballsAction->setItems(list); + tutorialAction = new KToggleAction( i18n("&Tutorial"), 0, this, SLOT(tutorialSwitch()), actionCollection(), "options_tutorial" ); +// KStdAction::keyBindings(guiFactory(), SLOT(configureShortcuts()), +//actionCollection()); + +// keyboard only + (void)new KAction( i18n("Move Down"), Qt::Key_Down, gr, SLOT(slotDown()), actionCollection(), "move_down" ); + (void)new KAction( i18n("Move Up"), Qt::Key_Up, gr, SLOT(slotUp()), actionCollection(), "move_up" ); + (void)new KAction( i18n("Move Left"), Qt::Key_Left, gr, SLOT(slotLeft()), actionCollection(), "move_left" ); + (void)new KAction( i18n("Move Right"), Qt::Key_Right, gr, SLOT(slotRight()), actionCollection(), "move_right" ); + (void)new KAction( i18n("Trigger Action"), Qt::Key_Return, gr, SLOT(slotInput()), actionCollection(), "move_trigger" ); +} + +void KBBGame::slotResize() +{ + setMinSize(); + gameResize(); +} + +#include "kbbgame.moc" diff --git a/kblackbox/kbbgame.h b/kblackbox/kbbgame.h new file mode 100644 index 00000000..70f6ad48 --- /dev/null +++ b/kblackbox/kbbgame.h @@ -0,0 +1,104 @@ +// +// +// KBlackBox +// +// A simple game inspired by an emacs module +// +// File: kbbgame.h +// +// The definition of the KBBGame widget +// + +#ifndef KBBGAME_H +#define KBBGAME_H + +#include "kbbgfx.h" +#include "util.h" + +#include +#include + +class KSelectAction; +class KToggleAction; + +/* + Types of the boxes (used f.e.g. in the traceRay() method) +*/ + +#define OUTERBBT 0 +#define INNERBBT 1 +#define LASERBBT 2 +#define BALLBBT 3 + +/* + Ray-tracing results. +*/ + +#define WRONGSTART -1 +#define DETOUR 0 +#define REFLECTION 1 +#define HIT 2 + +/* + * Statusbar IDs. + */ +#define SSCORE 0 +#define SBALLS 1 +#define SRUN 2 +#define SSIZE 3 + +class KBBGame : public KMainWindow +{ + Q_OBJECT +public: + KBBGame(); + ~KBBGame(); + +signals: + void gameRuns( bool ); + +private slots: + void slotSize(); + void slotBalls(); + + void tutorialSwitch(); + + void newGame(); + + bool setSize( int w, int h ); + bool setBalls( int n ); + void setMinSize(); + void randomBalls( int n ); + void slotResize(); + void gameResize(); + void setScore( int n ); + void updateStats(); + void gameFinished(); + void abortGame(); + void giveUp(); + void gotInputAt( int col, int row, int state ); + +private: + int traceRay( int startX, int startY, int *endX, int *endY ); + void remap( RectOnArray *gam, RectOnArray *gra ); + void getResults(); + void initKAction(); + + int balls; + int detourCounter; + int ballsPlaced; + bool running; + bool tutorial; + RectOnArray *gameBoard; + KBBGraphic *gr; + + int score; + /* QLabel *scoreText; + QLabel *statusText;*/ + KRandomSequence random; + + KSelectAction *ballsAction, *sizeAction; + KToggleAction *tutorialAction; +}; + +#endif // KBBGAME_H diff --git a/kblackbox/kbbgfx.cpp b/kblackbox/kbbgfx.cpp new file mode 100644 index 00000000..d37edc1e --- /dev/null +++ b/kblackbox/kbbgfx.cpp @@ -0,0 +1,473 @@ +// +// +// KBlackBox +// +// A simple game inspired by an emacs module +// +// File: kbbgfx.cpp +// +// The implementation of the KBBGraphic widget +// + +#include +#include +#include +#include +#include + +#include "kbbgfx.h" +#include "util.h" + +/* + Constructs a KBBGraphic widget. +*/ + +KBBGraphic::KBBGraphic( QPixmap **p, QWidget* parent, const char* name ) + : QWidget( parent, name ) +{ + int i; + + curRow = curCol = 0; + setFocusPolicy( NoFocus ); + setBackgroundColor( gray ); + setCellWidth( CELLW ); // set width of cell in pixels + setCellHeight( CELLH ); // set height of cell in pixels + setMouseTracking( FALSE ); + + pix = p; + if (pix == NULL) pixScaled = NULL; + else { + pixScaled = new QPixmap * [NROFTYPES]; + for (i = 0; i < NROFTYPES; i++) { + pixScaled[i] = new QPixmap; + } + } + graphicBoard = NULL; + drawBuffer = NULL; +} + +/* + Destructor: deallocates memory for contents +*/ + +KBBGraphic::~KBBGraphic() +{ + int i; + + if (pix != NULL) { + for (i = 0; i < NROFTYPES; i++) { + delete pix[i]; + } + delete pix; + } + if (pixScaled != NULL) { + for (i = 0; i < NROFTYPES; i++) { + delete pixScaled[i]; + } + delete pixScaled; + } + delete graphicBoard; + delete drawBuffer; +} + +/* + Sets the size of the table +*/ + +void KBBGraphic::setSize( int w, int h ) +{ + if ((w != numCols) || (h != numRows)) { + delete graphicBoard; + graphicBoard = new RectOnArray( w, h ); + graphicBoard->fill( OUTERBBG ); + setNumCols( w ); + setNumRows( h ); + setCellWidth( CELLW ); + setCellHeight( CELLH ); + minW = cellW * numRows; + minH = cellH * numCols; + emit(sizeChanged()); + } +} + +void KBBGraphic::setCellWidth( int w ) +{ + cellW = w; +} + +void KBBGraphic::setCellHeight( int h ) +{ + cellH = h; +} + +void KBBGraphic::setNumRows( int rows ) +{ + numRows = rows; +} + +void KBBGraphic::setNumCols( int cols ) +{ + numCols = cols; +} + +/* + Scales all pixmaps to desired size. +*/ + +void KBBGraphic::scalePixmaps( int w, int h ) +{ + int i, w0, h0; + QWMatrix wm; + + w0 = pix[0]->width(); + h0 = pix[0]->height(); + wm.scale( (float) w / (float) w0, (float) h / (float) h0 ); + for (i = 0; i < NROFTYPES; i++) { + *pixScaled[i] = pix[i]->xForm( wm ); + } +} + +/* + Returns the sizes of the table +*/ + +int KBBGraphic::numC() { return numCols; } +int KBBGraphic::numR() { return numRows; } +int KBBGraphic::width() { return cellW * numRows; } +int KBBGraphic::height() { return cellH * numCols; } +int KBBGraphic::wHint() const { return minW; } +int KBBGraphic::hHint() const { return minH; } +QSize KBBGraphic::sizeHint() const { return QSize(wHint(), hHint()); } + +/* + Returns a pointer to graphicBoard +*/ + +RectOnArray *KBBGraphic::getGraphicBoard() { return graphicBoard; } + +/* + Handles cell painting for the KBBGraphic widget. +*/ + +void KBBGraphic::paintCell( QPainter* p, int row, int col ) +{ + if (pix == NULL) paintCellDefault( p, row, col ); + else paintCellPixmap( p, row, col ); +} + +void KBBGraphic::paintCellPixmap( QPainter* p, int row, int col ) +{ + int w = cellW; + int h = cellH; + int x2 = w - 1; + int y2 = h - 1; + int type; + QPixmap pm; + + // kdDebug(12009) << p->viewport().width() << endl; + + switch (type = graphicBoard->get( col, row )) { + case MARK1BBG: pm = *pixScaled[MARK1BBG]; break; + case OUTERBBG: pm = *pixScaled[OUTERBBG]; break; + case INNERBBG: pm = *pixScaled[INNERBBG]; break; + case LASERBBG: pm = *pixScaled[LASERBBG]; break; + case LFIREBBG: pm = *pixScaled[LFIREBBG]; break; + case FBALLBBG: pm = *pixScaled[FBALLBBG]; break; + case TBALLBBG: pm = *pixScaled[TBALLBBG]; break; + case WBALLBBG: pm = *pixScaled[WBALLBBG]; break; + default: pm = *pixScaled[OUTERBBG]; + } + // kdDebug(12009) << pm.width() << " " << w << endl; + p->drawPixmap( 0, 0, pm ); + // bitBlt( this, col * w, row * h, &pm ); + + p->setPen( black ); + + if (type == INNERBBG) { + p->drawLine( x2, 0, x2, y2 ); // draw vertical line on right + p->drawLine( 0, y2, x2, y2 ); // draw horiz. line at bottom + p->drawLine( 0, 0, x2, 0 ); + p->drawLine( 0, 0, 0, y2 ); + } + + /* + Extra drawings for boxes aroud lasers. + */ + QString s; + switch (type) { + case RLASERBBG: + s.sprintf( "%c", 'R' ); + p->drawText( 1, 1, x2-1, y2-1, AlignCenter, s ); + break; + case HLASERBBG: + s.sprintf( "%c", 'H' ); + p->drawText( 1, 1, x2-1, y2-1, AlignCenter, s ); + break; + } + if (type < 0) { + s.sprintf( "%d", -type ); + p->drawText( 1, 1, x2-1, y2-1, AlignCenter, s ); + } + + /* + Draw extra frame inside if this is the current cell. + */ + p->setPen( yellow ); + if ( (row == curRow) && (col == curCol) ) { // if we are on current cell, + if ( hasFocus() ) { + p->drawRect( 0, 0, x2, y2 ); + } + else { // we don't have focus, so + p->setPen( DotLine ); // use dashed line to + p->drawRect( 0, 0, x2, y2 ); + p->setPen( SolidLine ); // restore to normal + } + } +} + +void KBBGraphic::paintCellDefault( QPainter* p, int row, int col ) +{ + int w = cellW; + int h = cellH; + int x2 = w - 1; + int y2 = h - 1; + int type; + QColor color; + + switch (type = graphicBoard->get( col, row )) { + case MARK1BBG: color = darkRed; break; + case OUTERBBG: color = white; break; + case INNERBBG: color = gray; break; + case LASERBBG: color = darkGreen; break; + case LFIREBBG: color = green; break; + case FBALLBBG: color = red; break; + case TBALLBBG: color = blue; break; + case WBALLBBG: color = cyan; break; + default: color = white; + } + p->fillRect( 0, 0, x2, y2, color ); + + p->setPen( black ); + p->drawLine( x2, 0, x2, y2 ); // draw vertical line on right + p->drawLine( 0, y2, x2, y2 ); // draw horiz. line at bottom + + /* + Extra drawings for boxes aroud lasers. + */ + QString s; + switch (type) { + case RLASERBBG: + s.sprintf( "%c", 'R' ); + p->drawText( 1, 1, x2-1, y2-1, AlignCenter, s ); + break; + case HLASERBBG: + s.sprintf( "%c", 'H' ); + p->drawText( 1, 1, x2-1, y2-1, AlignCenter, s ); + break; + } + if (type < 0) { + s.sprintf( "%d", -type ); + p->drawText( 1, 1, x2-1, y2-1, AlignCenter, s ); + } + + /* + Draw extra frame inside if this is the current cell. + */ + if ( (row == curRow) && (col == curCol) ) { // if we are on current cell, + if ( hasFocus() ) { + p->drawEllipse( 1, 1, x2-2, y2-2 ); // draw ellipse + } + else { // we don't have focus, so + p->setPen( DotLine ); // use dashed line to + p->drawEllipse( 1, 1, x2-2, y2-2 ); // draw ellipse + p->setPen( SolidLine ); // restore to normal + } + } +} + +/* + Xperimantal... +*/ + +void KBBGraphic::paintEvent( QPaintEvent* ) +{ + int i, j; + QPainter paint( drawBuffer ); + + // kdDebug(12009) << drawBuffer->width() << endl; + for (i = 0; i < numRows; i++) { + for (j = 0; j < numCols; j++) { + paint.setViewport( j * cellW, i * cellH, width(), height() ); + paintCell( &paint, i, j ); + } + } + bitBlt( this, 0, 0, drawBuffer ); +} + +/* + Resize event of the KBBGraphic widget. +*/ + +void KBBGraphic::resizeEvent( QResizeEvent* ) +{ + int w = QWidget::width(); + int h = QWidget::height(); + int wNew, hNew; + + // kbDebug() << w << " " << h << " " << minW << " " << minH << endl; + if (w > minW) { + wNew = w / numC(); + } else { + wNew = CELLW; + } + if (h > minH) { + hNew = h / numR(); + } else { + hNew = CELLH; + } + if (pix != NULL) scalePixmaps( wNew, hNew ); + setCellWidth( wNew ); + setCellHeight( hNew ); + + delete drawBuffer; + drawBuffer = new QPixmap( cellW * numRows, cellH * numCols ); +} + +/* + Handles mouse press events for the KBBGraphic widget. +*/ +void KBBGraphic::mousePressEvent( QMouseEvent* e ) +{ + if (inputAccepted) { + /* + * Middle click finishes the game. + */ + if (e->button() == MidButton) { + emit endMouseClicked(); + return; + } + int oldRow = curRow; + int oldCol = curCol; + QPoint pos = e->pos(); // extract pointer position + curRow = pos.y() / cellH; + curCol = pos.x() / cellW; + //kdDebug(12009) << e->state() << " " << LeftButton << " " << e->state()&LeftButton << endl; + updateElement( oldCol, oldRow ); + emit inputAt( curCol, curRow, e->button() ); + } +} + + +/* + Handles mouse move events for the KBBGraphic widget. +*/ + +void KBBGraphic::mouseMoveEvent( QMouseEvent* e ) { + if (inputAccepted) { + int oldRow = curRow; + int oldCol = curCol; + QPoint pos = e->pos(); // extract pointer position + int movRow = pos.y() / cellH; + int movCol = pos.x() / cellW; + // kdDebug(12009) << movRow << " " << curRow << endl; + if ( (curRow != movRow) // if current cell has moved, + || (curCol != movCol) ) { + curRow = movRow; + curCol = movCol; + updateElement( oldCol, oldRow ); + emit inputAt( curCol, curRow, e->state() ); + } + } +} + +void KBBGraphic::slotUp() +{ + if( curRow > 0 ) { + moveSelection( -1, 0 ); + } +} + +void KBBGraphic::slotDown() +{ + if( curRow < numRows-1 ) { + moveSelection( 1, 0 ); + } +} + +void KBBGraphic::slotLeft() +{ + if( curCol > 0 ) { + moveSelection( 0, -1 ); + } +} + +void KBBGraphic::slotRight() +{ + if( curCol < numCols-1 ) { + moveSelection( 0, 1 ); + } +} + +void KBBGraphic::slotInput() +{ + if ( !inputAccepted ) { + return; + } + emit inputAt( curCol, curRow, LeftButton ); +// updateElement( curCol, curRow ); +} + +void KBBGraphic::moveSelection(int drow, int dcol) +{ + if ( !dcol && !drow || !inputAccepted ) { + return; + } + curCol += dcol; + curRow += drow; + updateElement( curCol - dcol, curRow - drow ); + updateElement( curCol, curRow ); +} + +/* + Handles focus reception events for the KBBGraphic widget. +*/ + +void KBBGraphic::focusInEvent( QFocusEvent* ) +{ + repaint( FALSE ); +} + + +/* + Handles focus loss events for the KBBGraphic widget. +*/ + +void KBBGraphic::focusOutEvent( QFocusEvent* ) +{ + repaint( FALSE ); +} + +/* + Sets whether user input is processed or not. +*/ + +void KBBGraphic::setInputAccepted( bool b ) +{ + inputAccepted = b; + if (b) setFocusPolicy( StrongFocus ); + else setFocusPolicy( NoFocus ); +} + +/* + Updates the cell at (col,row). +*/ + +void KBBGraphic::updateElement( int col, int row ) +{ + QPainter paint( this ); + + paint.setViewport( col * cellW, row * cellH, width(), height() ); + paintCell( &paint, row, col ); +} + +#include "kbbgfx.moc" diff --git a/kblackbox/kbbgfx.h b/kblackbox/kbbgfx.h new file mode 100644 index 00000000..ae4d207c --- /dev/null +++ b/kblackbox/kbbgfx.h @@ -0,0 +1,120 @@ +// +// +// KBlackBox +// +// A simple game inspired by an emacs module +// +// File: kbbgfx.h +// +// The definition of the KBBGraphic widget +// + +#ifndef KBBGFX_H +#define KBBGFX_H + +#include +#include + +#include "util.h" + +/* + Default size of a cell +*/ + +#define CELLW 32 +#define CELLH 32 + +/* + Graphical types of the boxes +*/ + +#define OUTERBBG 0 +#define INNERBBG 1 +#define LASERBBG 2 +#define FBALLBBG 3 +#define TBALLBBG 4 +#define WBALLBBG 5 +#define MARK1BBG 6 +#define LFIREBBG 7 + +/* + These have the same pixmaps as some of those above... +*/ + +#define RLASERBBG 8 +#define HLASERBBG 9 + +#define NROFTYPES 8 + +/* + Negative numbers are deflected lasers... +*/ + +class KBBGraphic : public QWidget +{ + Q_OBJECT +public: + KBBGraphic( QPixmap** p=0, QWidget* parent=0, const char* name=0 ); + ~KBBGraphic(); + + friend class KBBGame; + + void setSize( int w, int h ); + RectOnArray *getGraphicBoard(); + int numC(); + int numR(); + int width(); + int height(); + int wHint() const; + int hHint() const; + void setCellWidth( int w ); + void setCellHeight( int h ); + void setNumRows( int rows ); + void setNumCols( int cols ); + +public slots: + void setInputAccepted( bool b ); + void updateElement( int col, int row ); + void slotUp(); + void slotDown(); + void slotLeft(); + void slotRight(); + void slotInput(); + +signals: + void sizeChanged(); + void inputAt( int, int, int ); + void endMouseClicked(); + +protected: + virtual QSize sizeHint() const; + void paintEvent( QPaintEvent* ); + void mousePressEvent( QMouseEvent* ); + void mouseMoveEvent( QMouseEvent* ); + void focusInEvent( QFocusEvent* ); + void focusOutEvent( QFocusEvent* ); + void resizeEvent( QResizeEvent* e ); + + void moveSelection(int drow, int dcol); + +private: + void paintCell( QPainter* p, int row, int col ); + void paintCellDefault( QPainter*, int row, int col ); + void paintCellPixmap( QPainter*, int row, int col ); + void scalePixmaps( int w, int h ); + RectOnArray *graphicBoard; + int curRow; + int curCol; + bool inputAccepted; + int minW; + int minH; + int cellW; + int cellH; + int numCols; + int numRows; + QPixmap **pix; + QPixmap **pixScaled; + QPixmap *drawBuffer; +}; + +#endif // KBBGFX_H diff --git a/kblackbox/kblackbox.desktop b/kblackbox/kblackbox.desktop new file mode 100644 index 00000000..a599cec6 --- /dev/null +++ b/kblackbox/kblackbox.desktop @@ -0,0 +1,77 @@ +[Desktop Entry] +GenericName=Blackbox Logic Game +GenericName[be]=Ð›Ð°Ð³Ñ–Ñ‡Ð½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ Ð· чорнай ÑкрынÑй +GenericName[bg]=ЛогичеÑка игра +GenericName[bn]=যà§à¦•à§à¦¤à¦¿à¦° খেলা বà§à¦²à§à¦¯à¦¾à¦•à¦¬à¦•à§à¦¸ +GenericName[bs]=LogiÄka igra "Crna kutija" +GenericName[ca]=Joc de lògica Blackbox +GenericName[cs]=Logická hra s Äernou skříňkou +GenericName[cy]=Gêm Resymeg Blackbox +GenericName[da]=Blackbox logikspil +GenericName[de]=Logikspiel +GenericName[el]=Παιχνίδι λογικής Blackbox +GenericName[eo]=Divenlogika ludo +GenericName[es]=Juego de lógica Blackbox +GenericName[et]=Blackboxi loogikamäng +GenericName[eu]=Blackbox joko logikoa +GenericName[fa]=بازی Blackbox Logic +GenericName[fi]=Logiikkapeli +GenericName[fr]=Jeu de logique Blackbox +GenericName[he]=משחק לוגיקה +GenericName[hr]=Blackbox logiÄka igra +GenericName[hu]=Logikai +GenericName[is]=Blackbox kænskuleikur +GenericName[it]=Gioco di logica Blackbox +GenericName[ja]=ブラックボックス論ç†ã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​ážáž€áŸ’កវិជ្ជា Blackbox +GenericName[ko]=블랙박스 논리 게임 +GenericName[lt]=Blackbox loginis žaidimas +GenericName[lv]=MelnÄs kastes loÄ£ikas spÄ“le +GenericName[mk]=Логичка игра Blackbox +GenericName[nb]=Logikkspillet Blackbox +GenericName[nds]=Blackbox Logikspeel +GenericName[ne]=कालोबाकस यà¥à¤•à¥à¤¤à¤¿à¤¸à¤‚गत खेल +GenericName[nl]=Blackbox: Logisch spel +GenericName[nn]=Logikkspelet Blackbox +GenericName[pl]=Gra logiczna - Czarna skrzynka +GenericName[pt]=Jogo de Lógica Caixa Preta +GenericName[pt_BR]=Jogo lógico de Caixa Preta +GenericName[ru]=Чёрный Ñщик +GenericName[se]=Logihkkaspeallu Blackbox +GenericName[sk]=Blackbox logická hra +GenericName[sl]=LogiÄna igra Blackbox +GenericName[sr]=Логичка игра црне кутије +GenericName[sr@Latn]=LogiÄka igra crne kutije +GenericName[sv]=Svart lÃ¥da logiskt spel +GenericName[ta]=கறà¯à®ªà¯à®ªà¯à®ªà¯†à®Ÿà¯à®Ÿà®¿ லாஜிக௠விளையாடà¯à®Ÿà¯ +GenericName[uk]=Гра на логіку (чорний Ñщик) +GenericName[wa]=Djeu d' lodjike noere boesse +GenericName[zh_TW]=黑盒å­æ™ºåŠ›éŠæˆ² +Exec=kblackbox %i %m -caption "%c" +Name=KBlackBox +Name[af]=Kblackbox +Name[ar]=لعبة الصندوق الأسود (KBlackBox) +Name[be]=Ð§Ð¾Ñ€Ð½Ð°Ñ ÑÐºÑ€Ñ‹Ð½Ñ +Name[bn]=কে-বà§à¦²à§à¦¯à¦¾à¦•à¦¬à¦•à§à¦¸ +Name[hi]=के-बà¥à¤²à¥‡à¤•à¤¬à¥‰à¤•à¥à¤¸ +Name[hu]=Fekete doboz +Name[is]=Svarti kassinn +Name[ne]=केडीई कालोबाकस +Name[pa]=ਕੇ-ਬਲੈਕ ਬਾਕਸ +Name[pl]=Czarna skrzynka +Name[pt_BR]=KCaixa Preta +Name[sv]=Kblackbox +Name[ta]=கேகரà¯à®ªà¯à®ªà¯ பெடà¯à®Ÿà®¿ +Name[tg]=KҚуттии Сиёҳ +Name[th]=à¸à¸¥à¹ˆà¸­à¸‡à¸”ำ - K +Name[tr]=Kara Kutu +Name[uk]=Чорна Ñкринька +Name[zh_TW]=KBlackBox é»‘ç›’å­ +Icon=kblackbox +Path= +DocPath=kblackbox/index.html +Type=Application +Terminal=false +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;BoardGame; diff --git a/kblackbox/kblackboxui.rc b/kblackbox/kblackboxui.rc new file mode 100644 index 00000000..c7c2b634 --- /dev/null +++ b/kblackbox/kblackboxui.rc @@ -0,0 +1,24 @@ + + + + + &Game + + + + + + &Settings + + + + + + +Main Toolbar + + + + + + diff --git a/kblackbox/main.cpp b/kblackbox/main.cpp new file mode 100644 index 00000000..ba0db293 --- /dev/null +++ b/kblackbox/main.cpp @@ -0,0 +1,46 @@ +// +// KBlackbox +// +// A simple game inspired by an emacs module +// +// File: main.cpp +// +// The main() function +// + + +#include +#include +#include +#include + +#include "kbbgame.h" +#include "version.h" + + +static const char description[] = I18N_NOOP("KDE Blackbox Game"); + +/* + The program starts here. +*/ + +int main( int argc, char **argv ) +{ + KAboutData aboutData( "kblackbox", I18N_NOOP("KBlackBox"), + KBVERSION, description, KAboutData::License_GPL, + "(c) 1999-2000, Robert Cimrman"); + aboutData.addAuthor("Robert Cimrman",0, "cimrman3@students.zcu.cz"); + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + + if (a.isRestored()) + RESTORE(KBBGame) + else { + KBBGame *w = new KBBGame; + a.setMainWidget(w); + w->show(); + } + return a.exec(); +} diff --git a/kblackbox/pics/Makefile.am b/kblackbox/pics/Makefile.am new file mode 100644 index 00000000..bfc5d607 --- /dev/null +++ b/kblackbox/pics/Makefile.am @@ -0,0 +1,10 @@ + +# add here all files +pics_DATA = blue.xpm brown.xpm cyan.xpm gray.xpm green.xpm red.xpm white.xpm\ + green2.xpm giveup.xpm done.xpm + +picsdir = $(kde_datadir)/kblackbox/pics + +# extra files for "make dist" +EXTRA_DIST = $(pics_DATA) + diff --git a/kblackbox/pics/blue.xpm b/kblackbox/pics/blue.xpm new file mode 100644 index 00000000..fca9f04c --- /dev/null +++ b/kblackbox/pics/blue.xpm @@ -0,0 +1,199 @@ +/* XPM */ +static char *blue[] = { +/* width height num_colors chars_per_pixel */ +" 48 48 144 2", +/* colors */ +".. c #04021c", +".# c #7476fc", +".a c #3c3e3c", +".b c #040284", +".c c #2422bc", +".d c #1412a4", +".e c #3c3edc", +".f c #acaefc", +".g c #242224", +".h c #3432cc", +".i c #5c5afc", +".j c #0402a4", +".k c #04024c", +".l c #1412bc", +".m c #4c4ef4", +".n c #9492fc", +".o c #2c2ac4", +".p c #cccafc", +".q c #141214", +".r c #545654", +".s c #0c0aa4", +".t c #3432e4", +".u c #1c1abc", +".v c #0c0a9c", +".w c #04022c", +".x c #8486fc", +".y c #040294", +".z c #1c1ab4", +".A c #3c3eec", +".B c #bcbefc", +".C c #343234", +".D c #6c6afc", +".E c #04026c", +".F c #a4a2fc", +".G c #dcdafc", +".H c #0c0ab4", +".I c #4c4a4c", +".J c #3c3adc", +".K c #2c2ad4", +".L c #646264", +".M c #1c1acc", +".N c #1412b4", +".O c #3432dc", +".P c #0406b4", +".Q c #4c4aec", +".R c #0c0a0c", +".S c #7c7efc", +".T c #b4b6fc", +".U c #2c2a2c", +".V c #6462fc", +".W c #0406a4", +".X c #04025c", +".Y c #1416bc", +".Z c #5456fc", +".0 c #9c9afc", +".1 c #d4d2fc", +".2 c #1c1a1c", +".3 c #0c0ea4", +".4 c #3c3ae4", +".5 c #1c1ebc", +".6 c #04023c", +".7 c #2426cc", +".8 c #040694", +".9 c #4442ec", +"#. c #444644", +"## c #04068c", +"#a c #1416ac", +"#b c #4442e4", +"#c c #3436d4", +"#d c #2c2ecc", +"#e c #5c5e5c", +"#f c #4446f4", +"#g c #8c8efc", +"#h c #c4c6fc", +"#i c #3c3a3c", +"#j c #7472fc", +"#k c #04027c", +"#l c #acaafc", +"#m c #e4e2fc", +"#n c #0c0eb4", +"#o c #545254", +"#p c #2c2ed4", +"#q c #6c6a6c", +"#r c #040224", +"#s c #7c7afc", +"#t c #444244", +"#u c #04028c", +"#v c #2422c4", +"#w c #1412ac", +"#x c #3c3ee4", +"#y c #242624", +"#z c #3432d4", +"#A c #5c5efc", +"#B c #0402ac", +"#C c #040254", +"#D c #4c4efc", +"#E c #9496fc", +"#F c #2c2acc", +"#G c #cccefc", +"#H c #141614", +"#I c #5c5a5c", +"#J c #4446ec", +"#K c #0c0aac", +"#L c #3436e4", +"#M c #1c1ac4", +"#N c #0c0e9c", +"#O c #040234", +"#P c #2422d4", +"#Q c #8c8afc", +"#R c #04029c", +"#S c #1c1eb4", +"#T c #3c3ef4", +"#U c #c4c2fc", +"#V c #343634", +"#W c #6c6efc", +"#X c #040274", +"#Y c #a4a6fc", +"#Z c #dcdefc", +"#0 c #4c4e4c", +"#1 c #1c1ecc", +"#2 c #1416b4", +"#3 c #3436dc", +"#4 c #8482fc", +"#5 c #bcbafc", +"#6 c #0c0e0c", +"#7 c #2c2e2c", +"#8 c #6466fc", +"#9 c #9c9efc", +"a. c #d4d6fc", +"a# c #1c1e1c", +"aa c #4c4af4", +"ab c #2426c4", +"ac c #0406ac", +"ad c #040264", +"ae c #1416c4", +"af c #0c0eac", +"ag c #3c3aec", +"ah c #1c1ec4", +"ai c #040244", +"aj c #2426d4", +"ak c #04069c", +"al c #4442f4", +"am c #0c0ebc", +"an c #2c2edc", +/* pixels */ +".I.C.Ua#.C#0#i.2#H.q#y#i#t.U.g#H.g#7.R.U#i#V#i#t#7a#.g.g#V#y#7.g#i#.#0#7#o.g.2a##y.R#i#.#0.U.g.2", +"#.#H.g#0a#.I.ga#.2#7.I.U.U#y#.a##t.U#7#7a#.2a##7#t#o#i#ya#.C.2#V.a.U.C#i.2#Va#.q#7.a.I.I#V#y.g#.", +".C.C#6#i#H#t#Va#.U#.#e#V#H.g.I#H#q.a#i.C#y.U.2#H#y#0#e#..g#H#H.2a##7#6.g#V#o#t#7#7.C#..I.2#y.q#7", +"a##ia##7#V#7#ia##t.L.L#o.U#Va##V#e.C.kad#X.b#u.y#u#u.b#u.b#k.X#V#..I#H.R.r#..ra#.qa#.g#i#y#i.Ua#", +"#t.g.C#V.Ia##6.2.a.C.C.a#V.2#7.Cadad.E#k#k.b.E#X#X#k.b#u.y#u#u#u.b.C#6.U#e.I.C.2#H#y#7#V#V.a.Ca#", +"#.#H#o#t#y#t.g.2#y#y#y#Va#.gad.E.b.b.y.y#R#R.y#R.y#R.y.y.b.b#u#R.y#u.b.a.I.I#y.q.U.C.Ua#.U#..g.U", +".I.U#0#0.C#0#Va##7.g#y#7.kad#X#k.b.y#u#k#k.b.y#R#R#Bac#B.j#u.b.y.j.j.y#k.b#V.aa#.2.U#7.a.2#H#7.r", +"#0#6#o#oa##0#i.U.2.g#0.k#Xad.E#X#k#k#k.b#k.b.b.b.b.b#Rac.P#B#R.b#B#B.j.j#R#u.L.g.U#H#7#i.C#7.2.C", +"#..U.U.U.C.C#7#0#7.g.X.Ead#X#X#k.b.b#u.b#u.y.b#u#Ram#1#P.l#B#R#u#uamac#R.j.j#k.g#H.2#V.I.Ca#a#.2", +".I.I#.#y#0#0#i#7#.#C.E.E.E#X#k#k.b#u#u#u#u#u.y#R.K.9.tae#B#R.y#u#R.jam#B#B.j#R.b.C#6.2.a.U#H.q.C", +".g#0#o#..C#0#V.U.kad#k#X#k.b.b.b.y.y.y#u.y.y.jac#A#W.H.y#R#B.jam.K.Haeam#B#R#R#R.ba#.R#y#6.I#V.g", +".U.2#V#7a#.g.g.k.k.X.E#X#k.b#u#u.y.y.y.W.Y.u.4#W.K#R.j#4.T#A#8ag#A#L#Pae.P#B#R#R.y.E#y#i#V#e.U#y", +"a#.2#ya#.g.C#y#C#C.k#X#k.b#u#u.y.y#R.W#2ab.O.m.j.j#B#A.B#1#1.Paj.##Wanam#n#B#B.j.y#u#y.2.U.2.C#y", +"a#a#.2#t#7.C.k.Xadad#X#k.b#k.b#u#Rak.W#R.j.jac#L.S#g#5am.j#R#R#B#B#Taaae.lam.P.j.y#u#X.Ua#.a#0#y", +"a#.g#y#t#t#7ai.Ead#X#X#k.b#u#u#u.yak.j#n#v.K#dab#v#x#Dam.Ham#B.j#R#1.V.Kae#M.P.j#R#u#k#y.C#t.r#i", +".g#y.U.g#.#O.k.X#X#k.b#k.b.y.y.yak.sac#F#w#2.z.5#v#pag#P#s.T.G#L.y#B.S.t#Mae.P#R#R#u#u#k#0#.#0.C", +"#7.q#V#y.C#Cad.X#X.b.b#k#u.y#R#R.W.s#K.7#aak#2.c#F#L.t.Sa.#Ua.#j#R#Ral.A#n.H.H#R.y.y#u.b.C.r#e#i", +".2a##i#V#O.kadad#k#u#u.b#uak#R.W.W.Y#F.5#N.s.dah.4.Z.i.F#m#G.T.B#B.jae.lac#n.l.j.y.y.b#uada##I.I", +"#y.g#y#i.kai.X.E#k.b.b.b#u.yak#2.z.5#d.z.v#a.e#s.#.D#9#h#5#9#l#Y.M.P#M.H.P.Namac.y.y.b.b.E#6#..L", +"#i#ia##o#C#Cad.E#X#k.b.b.b.b#uak.3#w.5.c#c#z.i.p#U#W.p.G.f#5#G.F#D.Y.M#n#Kac.j.P.y.y.y#u.b.q#.#o", +"#7#o#H.a#C.E.Xad.E#X#X#u#u#u.8.8.v#N#w.c.##Q#E.B.x#5#Ua.#m#Z.1.f#D.M.H#B#B#R#R.j.j#R#R.y#ka##V#.", +"#0.C#H#0.6.k#Cadad.E#X#k.b.yak.N.z#w#N#w#S#A#l#E#9#5#Y.T.p#5.1.G.xae.P.j.j.j#R.y#u#R.y.y.b.g#V#y", +".2#y.2a#.6.6#Cadad.E#X.b#u.yaf#3#f.Q.3.3#Sab#5#E#l.x.D#9#l#E#Z#Y#Q#L.Ham.j.y#R.b.b.j.b.b.b.g#V#y", +"#7.g.ga##O.kai.Xadad#k.b#u.s.A#j.S#A.d.v.z#a.f.0.n#s#8.p#Z.x.0#G.p.F#j.#ah.j#u#u#k#u#k.E.b.L.C#y", +".2.R#H.U#r#Cai#C.X#C.E#X#u#n.c#8#4#Q#wak#N#b#5.0.m.m#9.p.G#Q.Q#x#J.J.Z.O.N.y.y#u.b.b#k.X#k.a#y.2", +"a#.g.g.2..#Caiai.k#C.X#X#u#wak.c#E.x#j#A#4#E#W.e.h#Q.T.1.S#f#d#v.5.u#2.s#R.y.y.b.b.b#k.E.E.2.2a#", +"#H#V#Va#.wadai.6.k.k#Cad.b.y.yak.caa.m#b.Q#p#S.z.D#4.#.x#8.O#J#J#z#paf#R.y.y.b.b.b#u#k.b#u.I.U#y", +".U.U#V#y.kadai#O#C#C.Xad.b.8.b.b#u###u.v.3.d#a#c#L#c.zab.Z.V.V#A#L.O#n#R#R#u#u#u.y.y.y.b#k#y#6#V", +".r#H.g#yaiai.X.6.k.Xad#C#kak.b.b#k.b.b#u#u#w.c#F.7ab.z#a.o.9.4.sak.s#nac.j.y#R.y#R#u.y#k.E#y.a.2", +".C.U#6.2#r#O#Cai#C.X.X.X.E.W.b.b#k.b#u##akab.5.z#z#F.z.zab#aak#u.y.y#u.y#R.y#R#R#R#R#u#k.X.q.qa#", +"#7.U#V#0.r#O.k#C.Xad.X#C.y.3.8#X#X.b#u#w.5.cak.8.v#aab#v#a.s#u#R#nak.y.b#u.y.y.y.y.y#k.E#Ha#.g#i", +"#i.2.g#V#0.wai#C#C.X.Xad#u#u#k.E.E#k.y.3.v.8.b.8#u.8#N.3.s.3.W.N.u.Yac.y#u#u.y.y.y#u#X.X#i.U.C#I", +"#i#y..#.#7#Vai#C.X#Cad.E#X#k#Xadad#k#u#u.8.b#u.v#wak.b.y.s#wafafaf.N#K#u#k.y#u#u.b.E#X#7.g#7#H#7", +"a##ya#.aa#.2#r.6.k.Xad.E.Ead.E.Ead#X.b#u.3.y#k.b#w#2.3.s.3.s.s.W#Rak.y#k#k#u.b#X.Eadad#7#H.2.a#i", +"#.#0.C.U#7#t#0.6#Oai.Xadad.Xad.Ead#k#k.bak.8#X#uak.s.s.Wakak.y.b#k#u#X.E#k#k#X.Ead#Ca#.q.a#7.g#V", +".2.C#V#i#t#H.C#O.w#Oai#C#C#Cad#X#X.b.b.b.b.b#X#Xak.vakak#R.y#k#X#X#X.E.E#X#k#C.X.X.k#7.U.U#i.g.a", +".2#ta#.g#o.C#7#6.6#O#Oai.k.k.X.E#X#X#k#X#X#X#X#kak.sakakak#R#X#X#X#X.Ead.Ead.Xad#C#e.g#7#7.g.q.C", +".C#y.a.g.U#7.C.U.a..ai.6ai#C.X.Xadad.E#X#X#X.E#k#u.sak.yakak#R.y#u.Eadad#X#k.X.X#V.ra##i.a#H.2#y", +"#i#y#y#ya#.a.C#H.a#V.w.6ai.k#C#C.Xadad.E.E#X#X#X#k.y.y#k.b#u.b.y#u#uadad#k#X.X#H.2#o#7.2.2#H#7#i", +".a.2.2#.#V.2#V.q#0#0a#..#Oai.k.X.Xad.Eadad#X#k#u.b#k.X.Xadadad#k.b#k#Cad.k.k.aa#a##V#o#y#V.U#y.C", +"#o#y#V.C.a.C.U#y.a.I#V#y#r.w.6ai.X.Xad.E.E.E.X#C.k#C.k#C.k#C.k#X.b.X.kai#O#.#i.U.I#V.a#y.U#..r#y", +"#0.C#H.C.g#H.q.q#y.q.2#i#I#e#r.6.k#C#C.Xaiai.k.X#C.X.X#C#C.kai.Ead.k#O.a#V#7#0#o#.#H#i#i#y#o.I#0", +"#i#7.ga#.a#V#7#H.U#V.C#6#i#..C.U#Oai#O#O#O.6aiad.E.X.k#C.k.6.X.k.6#H#7a##y#y.a#o.I.g#0.Ca##t.r#t", +".g#6#0.I#y.q.U#t#7#y.U.a#y#H.U#V.a.g#r..#O.k#O.6.k.6ai.6#O.6#O.g.Ca##V#o#t#t.C#ta##i#i#7a##y#V#.", +".2.U.ga#.g#ya##0#i.2.C.q#V#V#y.Ca##I#e.U#H.g#7#6#7#H.C#7.C#V#7a#.C.g.g.r.r#o#0.g.g#o.C#y.ga#.qa#", +"#y#H.C.C.g.U.C#i#H.C.r.C#t#..U.2.a.r.r.a#7.2a#.2.C.C#H#H#H#t.2.2.U#y#y.g#.#o#7a#.ga##y.I.I.C.g#H", +".C.Ua#.2#V#i#7.2a##7#.#t.2#q.aa##t#o#o#0.C.q#H#V#y#y.2.a#7a#.U#y#o.a.C#6.C#o#t#y.2.r#0#0.I#t.C.U", +".Ua#.I#i#i#6#y#7#I.C#V.I#V#7a##H.U#H#i#y.2#H#i.L#t#t.C.q#t.g#i#V.a#I#.#i#i#..a#i#H#I#o#..g#V#7a#" +}; diff --git a/kblackbox/pics/brown.xpm b/kblackbox/pics/brown.xpm new file mode 100644 index 00000000..4ccd55fe --- /dev/null +++ b/kblackbox/pics/brown.xpm @@ -0,0 +1,77 @@ +/* XPM */ +static char * image_name[] = { +"48 48 26 1", +" c #69A645141861", +". c #514434D31040", +"X c #30C220810820", +"o c #41032CB21040", +"O c #618541031861", +"+ c #492430C21040", +"@ c #59653CF31040", +"# c #596538E31040", +"$ c #28A21C710820", +"% c #514438E31040", +"& c #30C21C710820", +"* c #61853CF31861", +"= c #28A218610820", +"- c #18610C300000", +"; c #38E324920820", +": c #186110400820", +"> c #208114510820", +", c #082004100000", +"< c #104008200000", +"1 c #410328A21040", +"2 c #49242CB21040", +"3 c #71C649241861", +"4 c #38E328A21040", +"5 c #69A649241861", +"6 c #208118610820", +"7 c #000000000000", +" .Xo o O ++X+@+#@O.$%.. ##+&o*.=-$;:>-,<1..#+ ", +"O2;O3# ##1o%O .##.oX*#@O.#.1&o#X1;.OX;%o+O@1+o@", +"$::+#+#@%%X$+%#1;&4.+@#@@.*#.;#O;%1#*1o*.. #+", +"&&$++411X$-=1%+;..o;+o>4o#O.++.##o##;o", +"@;o*O#*@.%+X%.o##@@#+o* +;+%+;;4>o.@*+o+#oX>;;=.", +"@1o%OO @.1>%*.#.+#@.#@ +o+++1.@+##OO#+@O%.1*@+O", +"#;o.#@#.+.o1 5@##.o1X&1o;;+o+o#O+o#* .%O*.+oO *5", +"XXo4++..1112 #+@++@%++o++ooo##++X&+@@++#ooo&+.1o", +"1.%%@#OOo+2.*## ;1+o1++%1;4% #.@o;*O*+.#1o%+#+#+", +"4o+@ @5*X+@@O@*OX1*;;o1%++%* @%O%1OO*.++o+#o%#*.", +"o*#+#o%1:X+oo+++X;@&o+++1#O#+1&&X&+41o1+;o.%4o@#", +"+ O#@1o+$+%4;+.%;1@X;.%#o11X11X&1X;1++++4211XXo1", +".@+1*#@*1@O+##@+&1*;%@++X++1#@..*## 5@@.1@+;1+*%", +"..XX+1+.&o%X4+%o$1.&+O1X=+##.+.@@+O @1o4=.12**O@", +".@11%.+oX+1&+o14X#%X1%;+# *+.#%#oX++;=XX6%;1#+.+", +"O 1o*.o;;.%o@*@#.OOX+%1o@O#+@ @%+1OO+o%#1O++@4.@", +"%+;.*.1;+ @o.+.O*OO;. +%%*#.@ @#O# O.++@+@+o%Xo+", +".+1.+o+o@ @1+2%%114:$#X;.#.+#%++##@+44;++@2+@;.@", +"++1+X+@1**+;+1.#o.+>+O+. %+*#+%#..;1+;++.o#O1.@", +"1+o+%o@o@@#2;&+#o.1&O oo#%+o+11%#+%X4.;1o%+.%$1+", +"+#11o;++@+X;X$%*++1$#@;411o&1o+++X+X;1=$;o111&%.", +".@1%@.O*O#%@2;##X+.X.%&$+%+&;.%o1;o&X%o12.1411##", +"$1;##1+.#41%o>+#=;.X*@$X+#o;+**##%*11*o1+O@#%# +", +"X+Xo;-=>$->;;-oO1@ 1O ;+ #+@ O%%#@1;.+;+O+...*+", +"$#+@@+ @%;@ +X 3o*5+@@&.#@*1+#%+1o.1X%+o# +1+oo$", +"X;$+@%** # 524 3o@@;#@X%@@@X+*%+o.%1o#.;%O;1#@O+", +".*+ ##@@o%@$&++X2%&o+$+###&1*#..%*oo+.1#@4o*OO.", +";o+5 #* O;@*X>++X1@12@X.*.@1.@;1+1o44o+;1+&;+++o", +"X+o%+1.#.;.@&=O +2+;X%;+@o#o+#;;11o4X+o4++1++%%1", +".#%#.+o++;+.>>o2&1@;XoX+%1o++%;#@.*@+@*.%#OO#O *", +"o.+%.+o.#1.%X;#o&+@XX4;+@1+%+.+*@+@@%@*+@##O@@O*", +"$>;+o2@@Oo+@.oO*# ;&@.##.#@%++@++@@@O*#@O O*@O#", +"4;+*#O OO%.@+1@.%#@1X*#.@#* O%#@++#1o1XX14o+;1#.", +"X;+@+%#@O%.@.4@@+.+oX.## @#%#++#%.#%@%+;++++X%#X", +">>>&:$Xo#2+...OO+@*#+@OO5##O++..+o%@*O@+#@@@1.@>", +"1+1+;.@+%X++1+#%11+;+.@++o+*.1.;+1o1o++4##o.1.@&", +"1o+#+@*o1$$+4@%.%++X4o1;XX&X>>4=XXX;X1;>o#+*1#Oo", +"oo.%;+@++oo@4++1%.%++#..+##%+o*++#@O.*.&oo;@+@O+", +";++1$2*...*O2++2.11X1#@@4+@@..O@%@OO+%@;@*;o1.#X", +"+%%1X@ @@#O5X@%@Oo1&1* %&+O*oo*@@@@.;# o* +@;XX;", +"o;1;o@5@++##>4o1.111.@.X:+@1>6+++1+#+O +@*1.;111", +".;;>>;.+1;.#<;+11;1++1X>-o+$6X+4;1+.X1+X11;%X#*#", +" OO+11@@#% >#@%*#.+.+O+X* .%* @@ *%1.#+2;;@;;;.", +" @1X22O @..@$*O.O##..+.>>#O#1.1@*O+1+@O.%;o@11o*", +" @>&+;@#&;o+$@#1%+++++1:X* .X1$1++44#+%%@X1 #++ ", +"o+:1@X@@o+@OoO@1o.++oo+=# %X.X@*.@*@#@@+>$.X>-X", +"o=7:+=@*@O@#1#@o.#*1;o4XO5 *+#153%1%+;1X<7$%o1X."}; diff --git a/kblackbox/pics/cyan.xpm b/kblackbox/pics/cyan.xpm new file mode 100644 index 00000000..591f9c90 --- /dev/null +++ b/kblackbox/pics/cyan.xpm @@ -0,0 +1,193 @@ +/* XPM */ +static char *cyan[] = { +/* width height num_colors chars_per_pixel */ +" 48 48 138 2", +/* colors */ +".. c #0c0a0c", +".# c #048684", +".a c #7cfefc", +".b c #14c6c4", +".c c #044a4c", +".d c #4ceeec", +".e c #04a6a4", +".f c #34e2e4", +".g c #0cb6b4", +".h c #042a2c", +".i c #4c4a4c", +".j c #2cc6c4", +".k c #2c2a2c", +".l c #046a6c", +".m c #b4fefc", +".n c #1cb6b4", +".o c #14a6a4", +".p c #2cd6d4", +".q c #049694", +".r c #3cf2f4", +".s c #041e1c", +".t c #64fefc", +".u c #24d2d4", +".v c #5c5e5c", +".w c #045a5c", +".x c #04aeac", +".y c #043a3c", +".z c #3c3a3c", +".A c #9cfefc", +".B c #24c6c4", +".C c #4cfefc", +".D c #44e2e4", +".E c #047a7c", +".F c #d4fefc", +".G c #1cbebc", +".H c #3cdedc", +".I c #049e9c", +".J c #2ccecc", +".K c #2cdedc", +".L c #1c1a1c", +".M c #545654", +".N c #14aeac", +".O c #141214", +".P c #048e8c", +".Q c #8cfefc", +".R c #1ccecc", +".S c #045254", +".T c #0ca6a4", +".U c #3ceaec", +".V c #14b6b4", +".W c #043234", +".X c #343234", +".Y c #047274", +".Z c #c4fefc", +".0 c #34d6d4", +".1 c #046264", +".2 c #0caeac", +".3 c #044244", +".4 c #444244", +".5 c #5cfefc", +".6 c #24bebc", +".7 c #0c9e9c", +".8 c #34cecc", +".9 c #242224", +"#. c #1cc6c4", +"## c #3ce2e4", +"#a c #545254", +"#b c #24babc", +"#c c #0c9a9c", +"#d c #44eeec", +"#e c #74fefc", +"#f c #acfefc", +"#g c #54fafc", +"#h c #44eaec", +"#i c #14bebc", +"#j c #048284", +"#k c #e4fefc", +"#l c #1cb2b4", +"#m c #0c0e0c", +"#n c #048a8c", +"#o c #84fefc", +"#p c #044e4c", +"#q c #4cf2f4", +"#r c #04aaac", +"#s c #34e6e4", +"#t c #0cbabc", +"#u c #042e2c", +"#v c #4c4e4c", +"#w c #2ccacc", +"#x c #2c2e2c", +"#y c #046e6c", +"#z c #bcfefc", +"#A c #1cbabc", +"#B c #14aaac", +"#C c #2cdadc", +"#D c #049a9c", +"#E c #042624", +"#F c #6cfefc", +"#G c #24d6d4", +"#H c #646264", +"#I c #045e5c", +"#J c #04b2b4", +"#K c #043e3c", +"#L c #3c3e3c", +"#M c #a4fefc", +"#N c #24cacc", +"#O c #047e7c", +"#P c #dcfefc", +"#Q c #1cc2c4", +"#R c #04a2a4", +"#S c #2cd2d4", +"#T c #34dedc", +"#U c #1c1e1c", +"#V c #5c5a5c", +"#W c #14b2b4", +"#X c #141614", +"#Y c #049294", +"#Z c #94fefc", +"#0 c #045654", +"#1 c #0caaac", +"#2 c #14babc", +"#3 c #043634", +"#4 c #343634", +"#5 c #6c6a6c", +"#6 c #ccfefc", +"#7 c #0cb2b4", +"#8 c #24c2c4", +"#9 c #0ca2a4", +"a. c #34d2d4", +"a# c #1ccacc", +"aa c #44f2f4", +"ab c #14c2c4", +"ac c #047674", +"ad c #046664", +"ae c #044644", +"af c #444644", +"ag c #242624", +"ah c #54fefc", +/* pixels */ +".i.X.k#U.X#v.z.L#X.Oag.z.4.k.9#X.9#x...k.z#4.z.4#x#U.9.9#4ag#x.9.zaf#v#x#a.9.L#Uag...zaf#v.k.9.L", +"af#X.9#v#U.i.9#U.L#x.i.k.kagaf#U.4.k#x#x#U.L#U#x.4#a.zag#U.X.L#4#L.k.X.z.L#4#U.O#x#L.i.i#4ag.9af", +".X.X#m.z#X.4#4#U.kaf.v#4#X.9.i#X#5#L.z.Xag.k.L#Xag#v.vaf.9#X#X.L#U#x#m.9#4#a.4#x#x.Xaf.i.Lag.O#x", +"#U.z#U#x#4#x.z#U.4#H#H#a.k#4#U#4.v.X#p.1.Y#j.P#Y.P.##n.##j.E#I#4af.i#X...Maf.M#U.O#U.9.zag.z.k#U", +".4.9.X#4.i#U#m.L#L.X.X#L#4.L#x.X.1.l.l#O#O.#.l.Yac.E.##n#Y#n.P.#.##4..#x#V.i.X.L#Xag#x#4#4#L.X#U", +"af#X#a.4ag.4.9.Lagagag#4#Uag#I#y#j.##Y.q#D#D#Y#D#D.I#Y.q.#.##n.I#Y#n#j#L#v.iag.O.k.X.k#U.kaf.9.k", +".i.k#v#v.X#v#4#U#x.9ag#x.cadacac#n.P#n#O#O.#.q#D#D#r.x.x.e.P.#.q#R.e.q.E.##4#L#U.L.k#x#L.L#X#x.M", +"#v#m#a#a#U#v.z.k.L.9#v#p.Yad#yac.E#O#j#j#O#j.#.#.#.#.I#r#J.e.I#j.x.x#R#R#D.P#Hag.k#X#x.z.X#x.L.X", +"af.k.k.k.X.X#x#v#x.9#0.lad#y.Y.E#j#j.#.#.P.P#n.P.q#t.R.uab#r#D.P.P#t#J.I.e.I#j.9#X.L#4.i.X#U#U.L", +".i.iafag#v#v.z#xaf.S#y#y#yac.E#j.#.P.P#n.P.P.P#D.paa.fab#r.I#Y.P#D#R#t.x#r#R.I.#.X#m.L#L.k#X.O.X", +".9#v#aaf.X#v#4#x.cad.Eac.E#j#j.##Y.q.q.P#Y#Y#R.x.5#F.2#D.I#r.e#t.K#t.b#t#r#D#R.q.##U..ag#m.i#4.9", +".k.L#4#x#U.9.9ae.c#I#y.Y#O.##n.P.P.q.q.e#2#Q###F#C.I#R#o.m.5.t.U.5#s.uab#J#r.I#D.q.lag.z#4.v.kag", +"#U.Lag#U.9.Xag#0.S#p#y.E#j#n.P#Y.q#D#R#W.B.0#q#R.I#r.5#za#a##J#G#e#F.K#t#t#r#r#R.q#nag.L.k.L.Xag", +"#U#U.L.4#x.X#p#I.lad.Y.E.##j#j#Y#D#D#R#R#R.e#r#s.a.Q#z#t.e.I.I#r.x.r#q.b#i#t#J.e.q#n.Y.k#U#L#vag", +"#U.9ag.4.4#xae.l.1.Y.E.E#j#n#n#n#Y.I#R.g.B.J#S#8.6##.C#t.g#t.x.I#D.R.t.K#i#.#J.e.q.P#jag.X.4.M.z", +".9ag.kagaf.W#p.w#y#O#j.E.##Y#Y#D#D.T.e.J.N#W.n.G#8.p.U.u.a.m#P#s#D.e.a#T#.ab#J#R#D.P#nac#vaf#v.X", +"#x.O#4ag.X.Sad#Iac.#.##j#n#D#D.I#R#1.2#N#B.I#W#8.J.f.f.a.F.Z.F#e#D.Iaa.U.g#7.g#D.q#Y#n#n.X.M.v.z", +".L#U.z#4.W.c.1ad.E.##n#j.P#D.I.e.e#2#w#A.7#9.o#Q##ah.5#M#k#6.m#z#r.I.b#2.x.g#i.I#D#Y.##nad#U#V.i", +"ag.9ag.z#pae.w.l#O#j#n.##n.P.I.N.n#A.J#l.7#B.H.a#e#F.A.Z#z.A#f#Ma##Ja#.g.x#2#t.x.q#Y.##j#y#maf#H", +".z.z#U.M#p#0ad#yac.E#O.#.##n#n.q.T#B#A#b.0a..5#6.Z#F#6#P#f#z#6#M.Cab#..g#7.e.e.x.q#Y#D#n#O.Oaf#a", +"#x#a#X#L.S.l.wad#y.Yac.##n#n#Y.q#c#9#B#b#e.Q#Z#z#o#z.Z.F#k#P.F#f.Ca#.g#r#r#R.I.e#R.I#D#Y#j#U#4af", +"#v.X#X#v.y#p#0.1ad#yac#O.##Y.I.V.n.o#9.o.n.5#f#Z.A#z#M.m#6#z.F#P#o.b.x#r#R#R.q.q.P#D#Y#Y#j.9#4ag", +".Lag.L#U.y.y.S.1.1.lac#O#n#D.2#T#d#q.7#9.n#8#z#Z#f#o#F.A#f#Z#P#M.Q#s#7#t#R#D.I.#.##R#n#j.#.9#4ag", +"#x.9.9#U.W#p.c.wad.1#O#j.P.T.U#e.a.5.o.7#l#B#f.A#Z.a.t#6#P#o.A#6#6#M#e#ea##R.P#n#O#n.E#y.##H.Xag", +".L..#X.k#E#0.3.S#I#0.lac.P.2.6.t#o.Q#B#D#c.D#z.A#q#q.A#6#P.Q.d###h.Hah.0#W.q.P.P.##j.E#I.E#Lag.L", +"#U.9.9.L.s#0ae#K#p#0#I.Y.P#B#D.6#Z#o#e.5#o#Z#F.H.8.Q.m.F.aaa.J#8#A.G#W.T#D.q#Y.##j#j.E#y.l.L.L#U", +"#X#4#4#U.h.1ae#K.c#p#0ad#j#D#Y.I#b#q.d#h.d.J.n#l#F#o#e#o.t#T#h#d.0#S.2#D#Y#Y#n#j.#.P#O#j.P.i#xag", +".k.k#4ag.cad.3#3.S#0.w.1#j.q.##O#n#Y#n#c.7.o.N.0#T.0#l#8#g.t.t.5.f#T#7#R#D#Y#n.P#Y.q#Y#j.Eag#m#4", +".M#X.9ag.3ae#0#K.c#0.1.w.E#D.##j#j#j.##n.P#B.6#w#N.B.n#B.j#h.H.T#D#9#7#r#R.q.q.q#D.P#Y#O#yag.z.L", +".X.k#m.L#E.W.w.3#0.1#I#0#y#R.##j.E.##n.P#D#8.G.na.#w#l.n.B.N#D#Y#Y#Y#Y#Y.I.q#D#D#D#D#n#O.w#X.O#U", +"#x.k#4#v.M#u.c#0.w.1#I#0#Y.T#Yacac#j.P.N#A.6#D#Y#c.N#8#8.N.7#Y#D#7.I.P#n#n#Y.q#Y.q#Y#O.l#X#U.9.z", +".z.L.9#4#v.h.c.S.S#I.w.1.P#n#j.l.l.E#Y.T.7.P.#.q#Y#Y.7#1#9.T.I#W.G#i#r.q.#.P.q.q#Y#nac.w.z.k.X#V", +".zag...4#x#4#K.S.w.w.1#yac#O#yadad.E#n.P#Y#j.P#c.N.q#n.P#9.N.2#1.2#W.2.P.E#Y#n.P#n#yac#x.9#x#X#x", +"#U.9.L#L#U.L#E#K#p.wad#y.l.1.l.l.1.E#j.P.T#Y.E.##B#W.T#9.T.T.T#R.I.I#Yac#j#n.#ac.lad#I#x#X#U#L.z", +"af#v#x.k#x.4#a#K.W.3.wad.1#Iad.ladac#O#j#D#Dac#n#D.T#9#R#R#D#Y.#ac#n.E#y.E#O.Y.lad.S#U#X.z#x.9#4", +".L.X.z.zaf#X#x.W#u#3.3.S.S#0.1acac#j.##j.##jac.E#D.7.I#D#D.P#Oac.Yac.l.lacac.w.w.w.c#x.k#x.z.9#L", +".L.4#U#U#a.X.X#m#3.W#3ae.c.S.w.l.Yac.Eacac.Y.Y.E#D.T.I#D.I.I.Y.Y.E.Y.lad#yad.w.1#0.v.9#x#x.9.O.X", +".Xag#Lag.k#x.Xag.4.s#K#Kae#0.w.1.1ad#y#y.Yac#y.E.P#9.I.q.I#D.I.q.##y.1ad.Yac#I.w#4.M#U.z.z#X.Lag", +".zag.9ag.L#L.X.L#L#4.h.yae.c.S#0#I.1ad.l#y#yac.Y#O#Y#Y#O.##n.##Y#Y#n.l.1.E.Y.w#X.L#a#x.L#U#X#x.z", +".4.L#X.i#4#U#4.O#v#v.9.s#3.3.c#0#Iad.ladadac.E#n.##O#I.w.1.1ad#O#j#O.S.1#p.S#L#U#U#4#aag.X.kag.X", +"#aag.z.X#L.Xagag#Laf#4ag.s.W.yae.w#I.1.l#y.l.1.S#p#p#p.S#p.S#pac#j.w.c.3#3af.z.k.i#4#Lag.kaf.Mag", +"#v.X#X#x.9.L#X.O.9#X.L#4.v.v#E#K.c.S.w.waeaeae.w#0#I.w.S#0.cae.ladae.y#L#4#x#v.Maf#X.z.zag#a.i#v", +".z#x.9#U#L#4.k#X#x#4.X#m.zaf.X#x.W.3#3.W#3#3.cad#y.w.S#p.c#K.w#p.y#X#x#Uagag#L#a.i.9#v.X#Uaf.M.4", +".9#m#v.iag.Oag.4#xagag.4ag#X.k#4#Lag#E.s.W#p.W.y.c#K.3.y.y.y.W.9#4#U#4#a.4.4.X.4#U.z.z#x#Uag#4af", +".L.k.9#U.9.k#U#a.z#U.X.O.X#4ag.X#U#V.v.k#X.9#x#m#x#X#4#x.X#4#x#U.X.9.9.M.M#a.i.9ag#a.Xag.9#U.O#U", +"ag#X.X.Xagag.X#4.L.X#a.X.4af.k.L#L.M.M#L#x.L#U.L#4.X.O#X#X.4#U.L.kagag.9af#a#x#U.9#Uag.i.i.X.9#X", +".X.k#U.L#4.z.X.L#U#xaf.4.L#5#L#U.4#a#a.i#4.O#X#4agag.L#L#x#U.k.9.M#L.X#m.X#a.4ag.L#a#v#v.i.4.X.k", +".k#U.i.z.z#mag#x.M.X#4.i#4#x#U#X.k.O.zag.L#X.z#H.4.4.X.O.4.9.z#4#L#Vaf#L.zaf#L.z#X.v#aaf.9#4#x#U" +}; diff --git a/kblackbox/pics/done.xpm b/kblackbox/pics/done.xpm new file mode 100644 index 00000000..218de4e2 --- /dev/null +++ b/kblackbox/pics/done.xpm @@ -0,0 +1,33 @@ +/* XPM */ +static char * done_xpm[] = { +"26 26 4 1", +" c None", +". c #000000000000", +"X c #C71B9E799E79", +"o c #861786178617", +" ", +" ", +" ", +" .. ", +" .X. ", +" .Xo. ", +" .Xo. ", +" .XX. ", +" .XX. ", +" .XX. ", +" ..XX. ", +" ......XXX. ", +" .XXX.XoXXXX. ", +" .XXXX.XoXXXXX. ", +" .....XXoXXXXXX...... ", +" .XXXX.XoXXXXXXoXXXX. ", +" .XXXX.XXoXXXXoXXXXX. ", +" .....XXXXooooXXXoXX. ", +" .XXXX.XXXXXXXXXoXXX. ", +" .XXXX.XXoXXXXXoXXX. ", +" .....XXoXXXX....... ", +" .XXXX.XoXX.. ", +" ......... ", +" ", +" ", +" "}; diff --git a/kblackbox/pics/giveup.xpm b/kblackbox/pics/giveup.xpm new file mode 100644 index 00000000..4c67802a --- /dev/null +++ b/kblackbox/pics/giveup.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static char * giveup_xpm[] = { +"22 21 4 1", +" c None", +". c #000000000000", +"X c #B6DA8E388617", +"o c #FFFFFFFFFFFF", +" ", +" .. ", +" .X.... ", +" .XX.ooo. ", +" .X.ooooo. ", +" .X.oooooo... ", +" .XX.ooooooooo. ", +" .X.ooooooooooo. ", +" .X.oooooooooooo. ", +" .XX.oooooooooooo. ", +" .X. ....oooooooo. ", +" .X. .ooooooo. ", +" .X. .ooooo. ", +" .XX. ...oo. ", +" .X. .o. ", +" .X. . ", +" .XX. ", +" .X. ", +" .. ", +" ", +" "}; diff --git a/kblackbox/pics/gray.xpm b/kblackbox/pics/gray.xpm new file mode 100644 index 00000000..913b3afc --- /dev/null +++ b/kblackbox/pics/gray.xpm @@ -0,0 +1,150 @@ +/* XPM */ +static char *gray[] = { +/* width height num_colors chars_per_pixel */ +" 48 48 95 2", +/* colors */ +".. c #070707", +".# c #080808", +".a c #090909", +".b c #0a0a0a", +".c c #0b0b0b", +".d c #0c0c0c", +".e c #0d0d0d", +".f c #0e0e0e", +".g c #0f0f0f", +".h c #101010", +".i c #111111", +".j c #121212", +".k c #131313", +".l c #141414", +".m c #151515", +".n c #161616", +".o c #171717", +".p c #181818", +".q c #191919", +".r c #1a1a1a", +".s c #1b1b1b", +".t c #1c1c1c", +".u c #1d1d1d", +".v c #1e1e1e", +".w c #1f1f1f", +".x c #202020", +".y c #212121", +".z c #222222", +".A c #232323", +".B c #242424", +".C c #252525", +".D c #262626", +".E c #272727", +".F c #282828", +".G c #292929", +".H c #2a2a2a", +".I c #2b2b2b", +".J c #2c2c2c", +".K c #2d2d2d", +".L c #2e2e2e", +".M c #2f2f2f", +".N c #303030", +".O c #313131", +".P c #323232", +".Q c #333333", +".R c #343434", +".S c #353535", +".T c #363636", +".U c #373737", +".V c #383838", +".W c #393939", +".X c #3a3a3a", +".Y c #3b3b3b", +".Z c #3c3c3c", +".0 c #3d3d3d", +".1 c #3e3e3e", +".2 c #3f3f3f", +".3 c #404040", +".4 c #414141", +".5 c #424242", +".6 c #434343", +".7 c #444444", +".8 c #454545", +".9 c #464646", +"#. c #474747", +"## c #484848", +"#a c #494949", +"#b c #4a4a4a", +"#c c #4b4b4b", +"#d c #4c4c4c", +"#e c #4d4d4d", +"#f c #4e4e4e", +"#g c #4f4f4f", +"#h c #505050", +"#i c #515151", +"#j c #525252", +"#k c #535353", +"#l c #545454", +"#m c #555555", +"#n c #565656", +"#o c #575757", +"#p c #585858", +"#q c #595959", +"#r c #5a5a5a", +"#s c #5b5b5b", +"#t c #5c5c5c", +"#u c #5d5d5d", +"#v c #5e5e5e", +"#w c #5f5f5f", +"#x c #606060", +"#y c #616161", +"#z c #626262", +"#A c #636363", +"#B c #646464", +"#C c #696969", +/* pixels */ +"#c.P.H.t.R#d.Y.r.p.j.D.X.5.I.z.n.x.N.b.F.Z.S.X.4.N.u.z.y.V.A.K.B.V.9#f.K#g.z.s.u.C.c.V#a#f.H.z.r", +".7.n.B#f.u#b.z.s.s.J#a.I.I.B.9.w.6.H.M.L.w.o.w.O.5#j.W.D.v.O.r.S.2.I.N.X.r.W.u.k.J.4#c#b.Q.G.y##", +".N.R.f.V.q.6.R.v.G.8#y.R.l.z#c.l#C.2.U.O.D.J.q.j.E#c#u.9.y.o.n.s.w.M.f.B.W#i.4.M.K.N.8#b.r.D.j.M", +".x.X.t.L.S.M.V.x.8#y#A#i.K.R.w.T#w.P.8.t.n.w.6.y.y.Q#g.6.q.o.J.T#.##.l.a#m.9#n.w.g.w.A.W.G.W.E.u", +".3.z.Q.S#b.w.g.o.2.Q.P.4.Q.q.M.K.u.0.2.L.y.u.J.E.2.J.P.E.o.G.Y.z.O.S.e.J#s#b.M.s.m.F.L.R.T.2.Q.v", +".8.n#j.4.C.3.A.s.E.B.B.T.v.C.r.x.C#t#l#i.e.x.o.o.Y.W.Q.E.G.Y.Y.D.w.C.n.1#d##.H.l.G.O.H.u.I.6.z.I", +"##.I#h#e.O#j.R.w.J.A.C.L.T#g.1.y.K.s.M#r.7.E.t.L.X.U.C.h#..1.A.Y#a.t.M#b#q.W.X.x.q.I.L.0.s.p.L#n", +"#d.i#j#i.v#d.Y.I.t.x#h.Z.T#b##.w.9#d.k.Z.E.P.q.5.S.B.s.6.1.l.O.6.7.w.I#e#c.R#A.B.F.p.J.Y.R.I.t.M", +".7.H.J.I.R.N.L#e.L.A.D.B.R.A.F.z.E.J.3.G.C.q.t.p.u.D.F.m.s.d.O.S.J.C#f.7.X.J#p.A.q.q.U#b.O.w.u.t", +"#c#a.7.D#f#h.Z.J##.R.T.O.C.3.5.j.W.L.7.S.N.N.7.2.s.p.S.t.B.s.N.m##.z.7#g.O.J.y.w.P.g.p.0.F.o.l.N", +".z#f#l.8.O#f.R.J.z.A.X#b.0.0#u.L.K.J.2.R.t.i.S.x.k.O.W.D.J#a.5.F.D.L.F.Q.S.K.l.I.M.w.c.F.h#b.S.z", +".H.s.T.K.t.z.z.B.I.t.O.4.9.O.P.p.g.S.Z.f.C.T.6.K.S.F.y.Y.H.x.v.C.E.S.Y.k.O.X.H.z.p.m.D.V.Q#v.E.F", +".v.p.C.v.z.O.G.s.X.z.u.u.q.u.2.E.4#m.Y.s.J.J.X.C.v.2.A.u#e#f#..6#g.p#i.F#q#i.1.q.X.Y.F.q.I.s.P.C", +".u.v.u.4.O.Q.V.p.C.l.Q.r.R#z#k.C#a#u.I.M#k.U.P.1.I.Q.B.G.1#e.V#k#h.u#q.4.Q.0.z.u.u.B.R.G.w.Z#e.F", +".v.z.B.6.5.K.Y.G.I.m.U.y#d.7.5.F.1.y#b#s#o.X.g.N.G.L.o.p.T#B#.#m#h.4#r.X.P#f.z.M.l.n.R.H.O.5#n.X", +".A.F.G.B.9.3.V.3.V.A.4.S##.O.u.t.0.k#e#o.R.v.w.B.p.J.L#n.K.6#j.V.i.2.Y.V.O.L.o.C.7#d.Q.A#g###e.O", +".J.j.W.D.N.J.t.O.h.z.H.C.N.s.y.o.s.Q.c.h.M.L.j.z.P.I#c#e#i.y.J.u.q.X.t.M.K.w.t.F.8#..1.V.R#k#u.X", +".s.t.V.T.y.1.T.l.w.n.q.M.A.C.R.X.7.N.R.y.I.u.C#q#x.A#.#i#a.0.9.A.I.t.O.s.5#j.D.r.t.M.V.U.K.x#s#a", +".C.z.D.Z.q.K.G.C.0.D.E.A.D.U#g#a.Y.D.I.P.q.O.q#e.V.J.I.H.4.7#..u.j.A#c##.z#s.F.S#j.y.l.K.Y.i.6#B", +".W.Z.u#l.B.z.t.E.S.J#k#b.h#f.9.o.G.g.C.t.P#t.X.U.8.t.5.W.v.V.Q.S.j.T.R#e.U.F.w.0.7.S.T.Y.P.j###i", +".L#k.q.2#d.I.Y.u.S.K.S#w.C.U.w.5.P.G.Q.P#d#h.G.Y.9.k.7.K.U.R.A.N.O.E.Q#m#h.5.j.F.O.c.2.W.5.v.R#.", +"#g.Q.k#f.1.3.9.J.s.U.R#u.6.C.h.y.A.E.k.J.P.M.q#d#f.v.D.h#e#x.K.g.u.R.4#y#y.9.F...X.r.A#a.N.x.T.B", +".p.F.n.v#d.2.0.R.8.A.U.6.1.H.U.m.P.w.D.H.N.B.9.8.V.F.z.Z.3.0.T.u.X.T.T#d#m.M.n.D.l.I##.0.v.B.V.C", +".O.y.z.y.Z#l#b.6.8.N.l.2.V.5.9.O.M.n.S.Z.8.1#o#e.T.A.v.B.Q.m.F.t.q.O.z.0.2.v.4#r.T.p.7#k.I#y.N.C", +".o.d.p.G.v.i.N.S.W.C.B.I.U.0.i.T.u.u.H.Z.E.C.F.7#..v.K.P#n#d.w.Y#g.Q.3.R.R.r###k#e.S.q.9.v.2.D.t", +".v.z.z.t.6.3.6.3.H.h.s.y#.#p#e.p#n#d.V.N.Q.B#g#..F.P.r.1.N.F.F#a.Q.P.s.d.O.A.s#e#s#a.4.y.J.r.s.u", +".n.R.U.v.3.0.W#a#d.P.Q.t.U.3#c.J.J.O.N.U.x.w.9#i.G.A.H.W.B.5#r.D.d.r.z.1#q.K.y#d.5#b#c.x#f#c.J.E", +".G.G.R.E.V.I.u.2#h.D#h.T.Q.U.Z.W.J.O.M.E.C.G.x.x.B.P.O.y.3.2.J.9.8#b.v#c#d.R.C.P.M.6#i.X.F.C.g.S", +"#o.m.x.F.O.j.T.Y.s.U.J.r.N.K.x.D.T#g.Y.S.N.C.U#i#u.L.E.0.U.o.V.2.1.2.v.2#i.Y.0.F.j.P#k#a.G.F.Y.q", +".Q.G.h.r.j.s.U#l.7.o.F.K#a.2.T.y.E#g.2.E.O.d.F.2.Q.s.G.M#h.z.x.K.5.I.M.2#j#e.8.D.T.s.J.S.K.l.i.v", +".K.G.S#e#q.V.i#..T.x.C.n#e#c.6.Y.T#f#a.w.w.c.L.w.A.n.B.s.Q.4.H.R.C.O.K.v.J.I.1.Q.m.o.K.B.q.u.z.W", +".Y.r.A.U#f.1.m.K.7#j.X.W#d#a##.9.V.w.F.Y.L.e.z.z.Z.M.V.T.H.5.w.G.l.V.t.M.w.m#a.7.5#a.M#g.X.I.S#q", +".Y.B.#.7.J.U.k.8#s.9.J.S.V.S.E#e#l.H.C#e.L.f.F.P#B.X.C.7.M.G.G.X.P.5.R.B.K.D.7#b#t#h.S.O.z.K.p.J", +".v.B.s.Z.v.s.V#g#l.q.Q.J.o.o.I#h#q.4.W#f.6.y.H.l.Z.Q.N.o.N.I.g.n.M#v#r.m.K.J.z.7.w.s.H.H.q.t.Z.X", +".9#e.M.H.M.4#h#f.S.v.5.G.w.E.X.V.5.5#e.9.p#f#o.4.y.u.V.E.N#b#..q.I.K.9.B.6.S.o.E.H.m.x.l.Y.K.A.T", +".p.R.W.Y.7.o.P.I.u.J.7.E.h.E.v.t.w.J.E.H.x#o#u#a#z.O.i.B.7#v#..B.M.k.K.x.D.L.M.0.I.P.L.H.J.X.y.1", +".s.6.w.w#j.P.O.f.i.y.S.F.n.P#e.0.P.x.Q#a.S.Q#q#A.U.w.N.E.2#e.U.A.B.n.v.4.L.v.P.J#a#A.x.O.M.z.j.P", +".O.D.0.B.G.N.O.F.6.x.Q.G.W.n.0.9#m.O.4#e#d#p#k.6.A.n.t.E.0.s.z.#.x.r.m.u.O.E.H.z.S#p.t.W.W.p.q.F", +".W.E.A.C.t.1.O.q.1.U#s#f#s.2#o#z#q.X.A.1.G.L.W.V.U##.F#l.G.7.K.A.P.e.w.z.S.9.R.q.r#h.N.q.t.q.I.Y", +".4.s.o##.R.t.R.j#c#e.A#e##.0.3.3.0.3.V#b.I.6.A.j.1.1.E#e.x#j.L.I#b.O.F#.#b#d.0.w.u.R#i.H.Q.F.B.Q", +"#j.B.W.P.1.Q.G.F.Z##.U.D.Y.O.G.x.I.e.E.y.V#r#f#f.D.w.A.M.s.M.g.G.2.s.J.3.J##.V.G#b.W.0.B.I#.#m.B", +"#e.R.n.N.z.q.l.h.B.l.q.V#t#y.X.F.Z.t.l.E#h#g#.#m.X.s.E.M.J.o.m.R.R.N.B.3.T.M#f#l##.m.Y.T.F#i#b#e", +".X.M.w.v.3.R.I.o.J.U.P.f.X#..Q.J.N.z.s.L#k.5.I.E.5.O.W.h.P.o.C.S.9.o.K.w.B.C.0#l#a.y#f.P.v.7#o.2", +".y.e#g#b.C.j.G.3.J.C.F.1.E.l.G.V.1.C.D.s.u.2.z.T.y.L.G.X.n.R.R.A.R.w.U#g.6.5.N.4.u.W.X.M.u.D.S#.", +".p.I.B.t.x.G.w#g.Z.t.Q.k.R.T.E.M.x#q#z.H.m.C.O.d.N.p.R.M.Q.W.K.x.O.z.z#p#n#k#e.A.C#k.R.B.C.t.l.t", +".F.n.P.P.D.G.N.U.q.P#l.P.2#..G.r.2#m#k.4.K.s.t.r.R.N.m.m.m.4.s.s.G.C.G.w.8#i.M.v.y.y.D#a##.O.A.m", +".Q.G.v.t.R.Y.P.s.w.L.8.7.t#C.4.t.5#k#k#c.R.i.n.W.E.D.q.1.M.w.G.B#l.3.M.f.R#i.6.E.p#k#h#f#b.7.O.G", +".J.t#b.X.Z.f.B.M#p.O.T#b.V.K.v.m.I.k.X.E.s.m.W#x.6.4.P.j.3.z.X.T.3#q#a.Z.W##.2.X.q#t#h.8.B.S.N.u" +}; diff --git a/kblackbox/pics/green.xpm b/kblackbox/pics/green.xpm new file mode 100644 index 00000000..f5d4a505 --- /dev/null +++ b/kblackbox/pics/green.xpm @@ -0,0 +1,218 @@ +/* XPM */ +static char *green[] = { +/* width height num_colors chars_per_pixel */ +" 48 48 163 2", +/* colors */ +".. c #040204", +".# c #047e04", +".a c #14be14", +".b c #044204", +".c c #7c7e7c", +".d c #049e04", +".e c #2cde2c", +".f c #1cce1c", +".g c #0cae0c", +".h c #444244", +".i c #042204", +".j c #048e04", +".k c #3cee3c", +".l c #2cbe2c", +".m c #046204", +".n c #149e14", +".o c #34ce34", +".p c #1cae1c", +".q c #242224", +".r c #646264", +".s c #3cde3c", +".t c #049604", +".u c #041a04", +".v c #14c614", +".w c #24d624", +".x c #048604", +".y c #045204", +".z c #04a604", +".A c #0cb60c", +".B c #545254", +".C c #043204", +".D c #047204", +".E c #14a614", +".F c #1cb61c", +".G c #343234", +".H c #34d634", +".I c #747274", +".J c #3ce63c", +".K c #24be24", +".L c #34e634", +".M c #148e14", +".N c #4cee4c", +".O c #2cc62c", +".P c #149614", +".Q c #141214", +".R c #8c8a8c", +".S c #2cce2c", +".T c #0c0a0c", +".U c #0c7e0c", +".V c #1cbe1c", +".W c #044a04", +".X c #0c9e0c", +".Y c #34de34", +".Z c #14ae14", +".0 c #4c4a4c", +".1 c #042a04", +".2 c #0c8e0c", +".3 c #046a04", +".4 c #1c9e1c", +".5 c #24ae24", +".6 c #2c2a2c", +".7 c #6c6a6c", +".8 c #0c960c", +".9 c #24c624", +"#. c #2cd62c", +"## c #0c860c", +"#a c #045a04", +"#b c #0ca60c", +"#c c #14b614", +"#d c #5c5a5c", +"#e c #043a04", +"#f c #047a04", +"#g c #1ca61c", +"#h c #24b624", +"#i c #3c3a3c", +"#j c #3cd63c", +"#k c #44e644", +"#l c #34c634", +"#m c #1c1a1c", +"#n c #848684", +"#o c #24ce24", +"#p c #44ee44", +"#q c #3cd23c", +"#r c #44e244", +"#s c #1cc61c", +"#t c #7c7a7c", +"#u c #4cf64c", +"#v c #040604", +"#w c #048204", +"#x c #14c214", +"#y c #044604", +"#z c #848284", +"#A c #04a204", +"#B c #2ce22c", +"#C c #0cb20c", +"#D c #444644", +"#E c #042604", +"#F c #049204", +"#G c #2cc22c", +"#H c #046604", +"#I c #14a214", +"#J c #34d234", +"#K c #1cb21c", +"#L c #242624", +"#M c #646664", +"#N c #3ce23c", +"#O c #049a04", +"#P c #041e04", +"#Q c #24da24", +"#R c #048a04", +"#S c #045604", +"#T c #04aa04", +"#U c #0cba0c", +"#V c #545654", +"#W c #043604", +"#X c #047604", +"#Y c #14aa14", +"#Z c #1cba1c", +"#0 c #343634", +"#1 c #34da34", +"#2 c #747674", +"#3 c #3cea3c", +"#4 c #24c224", +"#5 c #34ea34", +"#6 c #149214", +"#7 c #2cca2c", +"#8 c #149a14", +"#9 c #141614", +"a. c #0c7a0c", +"a# c #8c8e8c", +"aa c #2cd22c", +"ab c #0c0e0c", +"ac c #0c820c", +"ad c #1cc21c", +"ae c #044e04", +"af c #0ca20c", +"ag c #34e234", +"ah c #4c4e4c", +"ai c #042e04", +"aj c #0c920c", +"ak c #046e04", +"al c #1ca21c", +"am c #24b224", +"an c #2c2e2c", +"ao c #6c6e6c", +"ap c #0c9a0c", +"aq c #2cda2c", +"ar c #0c8a0c", +"as c #045e04", +"at c #0caa0c", +"au c #14ba14", +"av c #5c5e5c", +"aw c #043e04", +"ax c #1caa1c", +"ay c #24ba24", +"az c #3c3e3c", +"aA c #3cda3c", +"aB c #44ea44", +"aC c #34ca34", +"aD c #1c1e1c", +"aE c #24d224", +"aF c #44f244", +"aG c #1cca1c", +/* pixels */ +"#i#M.G#L#Laoa#.0.Ban#maz.G.raoaoaz.q#D#D#0.B#M.q.q#0.Gan.0aD.6.7ah.r#Dah#D.q#9.h#Lah#D.B#ianaz.G", +".Bab#0#m#m#L.0.6abanah.6azah.B.qan.0.caz.6#i.c#V.h.7#D#Van#Laz#0.0#0.G.B#V.7#0.G#maD.6.G.0#D.h.q", +"#V#i.0.B.hazanah#L.r.0anaz.B#L.B.Q#i.Q#d.q#L.Rahanaz.q.0#i.6#V#iao.r#L.G#2.7.q#V.Q.6#0#DavaD.G#M", +"#9#i#Dah#i.0an.I#M#z#D.q.q.Gaz#D#9az.T#Maz#9#V.6.Gaz.6aDaD#m#D.0.qah#0#M.r.Baz#9.h#L#i.6#LaD.6az", +"an.haz.0az.G#i#d#V.Bao.r.6#0#m.B.i.C#eaw.b#y#y.W.W.W.W#y.baw#Waw#Wao#L#M.7#L.haD.q#L.qanah#z#m#L", +"#V#m#0#i#V.Gan#L#iazah.B.G#E#W#e#y.y#S.m.m#H.3.Dakak#H#Hak.Dak.3#H.y.y.Wao.haz#d.Ianan#d.qao.I#D", +"#d#d.q.6#L.0ah.r.Gah.G#E#e#y.Wae#S#a#H.3.D.x#R#R.j#R#R.j#R#R.x#R#R#R#R.D.W.b.6.B#D.G#ian#i#Dav.h", +"#Mavan#M#D.c#n#M.Ban.1.b.W.y#S#a.m#H#H.3.3.3#f#R#R.t#O#O.d.d.d#O.t#F#F#F#R.y.b#0#Dan#m.G.q.B.7ah", +"#D#0.B.q.h#0avan#d.1#e.Wae#S#a.D#X.3.3.3akak.#.##w#w#R#R.d.Aau.A#T.g.z#A#A#X.y.W.qazaD#Lao#Daz.r", +".r#Vah#0.G#ianah#E#e.bae.y#a.3#f#X.D.D#f#w.##f#w.#.#.##R#O#caG.a.A.A.A.A#T.z#X#S#y#D#m#i.I.0az#D", +"ao.B#i#i.7ah#i.u#W.b#y.y#S.m#X#f#X.x#F.j.j.t#F#R#R.x.x#R#O#A#s.waG.v#x.v.A.z#w.3ae#W.Qah#2#0.B#L", +".7.Qaz.T#V.7.6#Eaw#y.W.yas.3#X#f.##R#F#O.t.t.t#F.j.j.t#O#c.9.w.eaqaEaGaG#U#T#F.3.m#San.G#L#m.q.6", +"aDaD#0.hanaz#P.1#y.W.y#S#Hak#X#w.x#F.X#b#bat#A.d#Aat.V#o.Y.k.k.k#B.w.a.A#C#C.d#Rak.m#e.q#d#0ah.q", +"#L#L#dan.6#0#P.C#y.y#a.m#Hak#f#w.j.8af#Y#b#b#batau#1#p#p.L.eaG.L#5#Baq.f#U.A#O#R#f.3.W.Qah.0.7.h", +".0.h#L#m.6#V.i.C.W#a#a.m#Hak#f#R#Fapafat#Z.V#s#.#raB.YaG#saGaE.w#3.Laq#o.a.g.d#F.#ak.yab.7ao#L.h", +".Gaz.haz#L.u.i#W.bae#S#Hakak#f#R.8af#Y#4.9.o.HaA#N.J#o#sadaG#s.f#3.L.wadau#T.d#F#w#X.y.1.q#0#i.Q", +"#V.haz.h#0.u#E#eawae#a.3#f#f#war.P.E#K.5#G.o.s.J.L.YaE#..Y#.aG#x#Qaq.w.a.Aat.d#F#w.D#S.b.B#M#2.0", +"#0.0#dav#d.u.1.Caw.yasak#f#waraj.2.nalax.S.Y#N#Nag#Bag#3aBaF.L#xaG.waa.a#C.g#O#F.x.D.m.y#L#Dah#t", +"an#i#Mao.r#P.1.C#eae.m.D#f#war#6ar.M#8.5#J.J.J.L#3#3aF#kaF#u.k.w#x#x#s#Uau.A#A.t.j.Das.W#M#D.0av", +".0.haz#Man#Pai#Waw.y.m.D#f##aj.8.P#8.p.O.s#3.k#p.kaFaB#p#u#u#u#BaE.v#x#c#C#c.z#F.x.Das.yao#i.6.G", +"#V.G#9az#i.i#W.C#yas#Hak#f#war.8.n.Eay#7aa#N.J#3#p#p#N#u#u#u#u.k.e#o.a#C#U#x#C#R#w.D.m.yao#Dav#i", +"aD#0.Q#L#M#E.C.C.W#Sas#H.D.#araj#8.E.K.S.Saa.s.J.saA#r.N#p#uaF.k.L.w.fau.a#c#T#F#w.Das.y#d#0.6.q", +".6.6an.qaz#Eai.C.b.y#a#Hak#faraj.2#6ay#7.o.H.s.s#jaC#j.saAaB#paF.J.e.9.V.g.X.z#O#wak#S#S#D#9an.h", +".6.Baz#9aD.iai.C#eae#a#H.3.D.#acarar#8.O#J.H.saC..#l.H#r#J#j#J.s.H#Gad#b.t#F.j.x#w#H.y#S#L#Lah#D", +"#0#0#9.6#i#E.C.C#eaeas#H.D#f#f.#.D.U.Pay.O#J#J#G.5.l#J.s.Oay#hay#h.p#K#b#F#R#R.##Xakas#S#V.BaD#i", +"#0az.G.h.6#Eai.1#e.y.m.D#f.x.U#f.3.D.U.2#g#h.5.5#h#q#N.H.o#G.l#Gax#g.X.X.j.x.x#f#X#X.Dae.r#d.G.q", +"#i.q#L#D#0.i#E#E#W.y.3#f.#.2.2#Xak.3#Xa.##.P.4ax.OaA.s#7.YaA#1#J#Y.Eaj.2aj#R#R.x.#.D.D.Wavaoan#L", +"ab.G#ian#d.i.iai#W.W.3#f.#ac#6.#.3ak#X##.M.P.n#h.O#j.o.9aA#p#p#3.H.F.X.Eaf#O.j#R#fakakaw.B.6#z.q", +"#dan#d#V.0#P.i.Cai#e#H.D#facajacakak.D.M#8#gay#GaC.O.F.K#1#N#1.Y#1#..Fau#Y#A.d.x.D.D.3awan.7avao", +"#0aD#i#Vah#P.i#W#W#e#H.3#X#facak.m.makar#Iamamax.p.paxay.S.H#G.F.9.9#s#c.X.X.d#wak.3.m#W#L.haDaz", +"#D.6an#iav.u.i.C.C#eae.m.3akakas#a#a.3ac.Paj#8#8#8#g.F.K#h#K.E.8#8.V.K.Zap#F.jakak.3ae.C#Vanan#D", +".0aD#V#iaz.u.i#Wai#e#y.yasas.mas#S#a.3#wac.2.P#8.2#6#I.n#g.E.p.naj#Y#Z#O.##f.D.3.3#a#y.1.Iah.Baz", +".6.0.B.r#Lab#P.C#W.C.bae.y#S#a.m#Has#H#X.Uacaj.Pac#f.U.2#8.E.E.2.xap#Y.8.D#H.m#Has.y#y#M#D#M#9.7", +".Iahah#0.Gab#Pai#W#W#yae#S#Sas#H.3.3.D#X#X#X.D.#.D.makak.#.8.2.xac.Xap#w#Hasasas.yae#e#t#d#i#z.0", +".G.6.6#0ah.T.u.i.C#e.b#y.y#S#aas#H#H.3#H#Has.mak.3asas#aas.3.D#f.##Rajak#a.y#a.yae.W.1.canaDav.G", +"#2#Van.q#iahaD#P.1#Waw.b.W.W.y#S#S#S#a#a#a#a#Sasas#S.Wae.y#S#a.m#X.D#w.m#a.y.y.W.y#eaz.T#d#L.h.q", +"#M.G.q#9.6#L.Q.u.iai#W#e#e.b#y.W.Waeaeaeaeaeaeae.W.W.yaeaeae.y.y#aak.D.3.m#Sae#y.b.1.T#L.6#L.0#9", +".G#V#d#D.0ah#0#9.u.iai.C#W#eaw.b.b#y.Wae.W.W.yae.yasasas.m#Saeae.m.3.m.mas.y.W#eaian#Da#aoananan", +"an.q#Lanaz.G.h#9#9#P.i.1ai#W#e#eawawaw.b.W#S#aas.m.masas#Sae.b.W#a#a#a.y.y#y#e.1aoan.c.r.q.0aDaz", +".G#D.c#0.0.h#M.qaD#v.u#P.1.1.C.C.C#W#eaw.b#y.y.y.y.yae.W.y#y#e.b#yae.W.b.b#W#E.7#2#z.r#V.G#L.7#i", +"az.r.q#M#M#i#9#V#i.TaD.u#P.i#E.1.1aiai#Waw.b#y#y.b.b.bawaw#Wai#W#eaw#Wai.1#P#d.R#2.B.B#0.h.G#iaD", +".B.h#m#d.0.Q#0#L#L#maD#9ab.u.u#P.i.i#Eai.C#W#W.C.Caiai.C.C.1.1aiai#E.i.u#D.QaD#M#2#Lanah#d#0.6#M", +"#0az.6anaz.q#m.0.G#L.hav.0abab#v.u.u.u#P.i#P.i#P.i#P.i#P#P.u#P.u.uavaoah#dab#m.q#t#L#0.0az#m#L#M", +".q.G#L.6#V#D.6.G#D#9az#0.h.G.h#0#0an.hao#M.Gazan.Q#Daz.B#M#Mazah#0anah#V.G#0.qavav.7#V.0aDaz#L#m", +".6ah.q#V#M#daz#0.han.6az.6#maz#Lahav#L.7ahan.6#0anazan#9.hav.6#d#mazav.0#M.0anav.G#daz#0.0.G#9an", +"an#M.0.I.IaD.6#D#0.6an#m#i#0#d#0#i.B.G.h#L#0an#iaD.0.q#V.6#zah.Gaz#D.6#i.Bao.c#Vaz#V#m#m.Qan.q.B", +".r#i.r#Vah#V.h#iaD.6aDazan#v.B#0.r#M#VaD.ran.G#Lah.rav#D.G.Ban.I#t.G#0ahao.0.0.0#i#9.6#iav.c#D.0", +"#Lan.0.6.Ban.q#man.0#0#t#i.q#V.0#2#dan#D.G.q.qan#mav.0.0#L#L#L.R#V#L#M.G.B#i.G#L.G#m#0.G#D.0ah.q" +}; diff --git a/kblackbox/pics/green2.xpm b/kblackbox/pics/green2.xpm new file mode 100644 index 00000000..92ef2acb --- /dev/null +++ b/kblackbox/pics/green2.xpm @@ -0,0 +1,300 @@ +/* XPM */ +static char *green2[] = { +/* width height num_colors chars_per_pixel */ +" 48 48 245 2", +/* colors */ +".. c #040204", +".# c #3cc23c", +".a c #044204", +".b c #848284", +".c c #444244", +".d c #2ca22c", +".e c #0c620c", +".f c #8cc68c", +".g c #4ce24c", +".h c #448244", +".i c #2c622c", +".j c #045604", +".k c #042204", +".l c #249224", +".m c #6ca66c", +".n c #147214", +".o c #449244", +".p c #44d244", +".q c #242224", +".r c #34b234", +".s c #447244", +".t c #2c722c", +".u c #2c822c", +".v c #a4a2a4", +".w c #1c521c", +".x c #54b254", +".y c #1c421c", +".z c #5cf65c", +".A c #043204", +".B c #5c925c", +".C c #1c621c", +".D c #5cc25c", +".E c #3ca23c", +".F c #94e294", +".G c #84d284", +".H c #349234", +".I c #6cb66c", +".J c #646264", +".K c #147a14", +".L c #2c7a2c", +".M c #2c8a2c", +".N c #044a04", +".O c #5ce25c", +".P c #449e44", +".Q c #54d254", +".R c #043a04", +".S c #44ca44", +".T c #448a44", +".U c #5ca25c", +".V c #7cc27c", +".W c #1c5a1c", +".X c #041a04", +".Y c #8c8a8c", +".Z c #2c6a2c", +".0 c #0c5a0c", +".1 c #3cba3c", +".2 c #7cb67c", +".3 c #1c8e1c", +".4 c #545254", +".5 c #2caa2c", +".6 c #343234", +".7 c #1c6a1c", +".8 c #747274", +".9 c #0c6a0c", +"#. c #c4c2c4", +"## c #249a24", +"#a c #b4b3b4", +"#b c #5ceb5c", +"#c c #54dc54", +"#d c #5cae5c", +"#e c #4cc34c", +"#f c #144414", +"#g c #042c04", +"#h c #247524", +"#i c #549354", +"#j c #44b544", +"#k c #7cfe7c", +"#l c #3c753c", +"#m c #3c833c", +"#n c #1c4d1c", +"#o c #5c9c5c", +"#p c #3cad3c", +"#q c #8cfe8c", +"#r c #349d34", +"#s c #247c24", +"#t c #3c7d3c", +"#u c #3c8c3c", +"#v c #143b14", +"#w c #548c54", +"#x c #7ccc7c", +"#y c #2c5d2c", +"#z c #141514", +"#A c #949694", +"#B c #248424", +"#C c #8cde8c", +"#D c #145514", +"#E c #447d44", +"#F c #64b464", +"#G c #143614", +"#H c #144b14", +"#I c #549d54", +"#J c #3c6e3c", +"#K c #7cbe7c", +"#L c #a4fea4", +"#M c #44c444", +"#N c #0c440c", +"#O c #4c4c4c", +"#P c #34a334", +"#Q c #146314", +"#R c #54ee54", +"#S c #4c854c", +"#T c #346434", +"#U c #6cce6c", +"#V c #2c942c", +"#W c #6cb26c", +"#X c #1c741c", +"#Y c #4c944c", +"#Z c #4cda4c", +"#0 c #2c2c2c", +"#1 c #3cb43c", +"#2 c #6cfe6c", +"#3 c #347434", +"#4 c #348334", +"#5 c #6c9e6c", +"#6 c #0c0d0c", +"#7 c #4cae4c", +"#8 c #acacac", +"#9 c #54cd54", +"a. c #6c6c6c", +"a# c #5c5c5c", +"aa c #3c3c3c", +"ab c #7c7c7c", +"ac c #1c1c1c", +"ad c #9cfe9c", +"ae c #74be74", +"af c #949294", +"ag c #cccacc", +"ah c #bcbabc", +"ai c #9c9e9c", +"aj c #8cce8c", +"ak c #84da84", +"al c #1c851c", +"am c #54e554", +"an c #0c540c", +"ao c #4cd34c", +"ap c #245324", +"aq c #5cb25c", +"ar c #64f364", +"as c #0c340c", +"at c #246424", +"au c #44a244", +"av c #74b874", +"aw c #1c7c1c", +"ax c #347c34", +"ay c #348c34", +"az c #0c4c0c", +"aA c #64e264", +"aB c #4c9b4c", +"aC c #0c3c0c", +"aD c #4ccc4c", +"aE c #4c8d4c", +"aF c #64a364", +"aG c #84c684", +"aH c #245c24", +"aI c #346c34", +"aJ c #145c14", +"aK c #44bb44", +"aL c #84ba84", +"aM c #248c24", +"aN c #34ac34", +"aO c #64bc64", +"aP c #246c24", +"aQ c #146c14", +"aR c #2c9c2c", +"aS c #64ee64", +"aT c #5cdc5c", +"aU c #64ac64", +"aV c #0c2d0c", +"aW c #244b24", +"aX c #64fc64", +"aY c #649d64", +"aZ c #44aa44", +"a0 c #3c9d3c", +"a1 c #84ce84", +"a2 c #9c9a9c", +"a3 c #4c7e4c", +"a4 c #74ae74", +"a5 c #5cca5c", +"a6 c #54a254", +"a7 c #3c923c", +"a8 c #84fe84", +"a9 c #94fe94", +"b. c #94de94", +"b# c #acfeac", +"ba c #74ce74", +"bb c #74fe74", +"bc c #3cc63c", +"bd c #044604", +"be c #848684", +"bf c #444644", +"bg c #2ca62c", +"bh c #0c660c", +"bi c #8cca8c", +"bj c #448644", +"bk c #2c662c", +"bl c #042604", +"bm c #249624", +"bn c #6caa6c", +"bo c #147614", +"bp c #449644", +"bq c #242624", +"br c #34b634", +"bs c #6cfa6c", +"bt c #2c762c", +"bu c #2c862c", +"bv c #a4a6a4", +"bw c #1c561c", +"bx c #54b654", +"by c #1c461c", +"bz c #043604", +"bA c #5c965c", +"bB c #1c661c", +"bC c #84d684", +"bD c #646664", +"bE c #147e14", +"bF c #2c7e2c", +"bG c #2c8e2c", +"bH c #5ce65c", +"bI c #54d654", +"bJ c #043e04", +"bK c #44ce44", +"bL c #448e44", +"bM c #5ca65c", +"bN c #7cc67c", +"bO c #1c5e1c", +"bP c #041e04", +"bQ c #8c8e8c", +"bR c #2c6e2c", +"bS c #0c5e0c", +"bT c #7cba7c", +"bU c #545654", +"bV c #2cae2c", +"bW c #343634", +"bX c #1c6e1c", +"bY c #747674", +/* pixels */ +".4af.cbWbWa2agbD.8aabqbU.cbea2#AbU#0a#a##Oa..Y#0#0#Oaaaa.JbqaabQa..b.JbDa#.6acbU.6bDa#a..4aabUbf", +"bY#z#ObqbqaabDaa#z.ca.aaa#bD.8.6aaa.#abUaa#O#8aba#afa#bY.c.6.4bfbDbf.c.8.8bQ#Obfac#0aabf.Ja#a##0", +"abbUa..8a#bU.ca.bW.ba.aabU.8.6.8#z#O#zab#0bWahbDaabU#0bD#ObW.8#Oaf.bbW.caiaf#0bYacbW#Oa#ab#0bfbQ", +"ac.4.J.8.4a.aaa2bQ#a.J.6.6bfbUa#.qa##6bQ.4acbYaaaa.4aa#0bq.q.JbD.6a.bfbebea.bUaca#.6.4aabWbqbW.4", +".ca#bUbDa#.cbU.baba.#Abeaa#Obqa..Rbd.0aQ#saM#raNaK#j.E.3aw.e.j.abz#A.6.YbQbWbUbq#0bW#0aabD#a.qbW", +"bYbq.4#Oabbf.c.6.4a#.8bYbfbP#g.AaC#nbk#E#I.I#x#C#C.G#F#daF#w#lap#N#gbl.kafbUbUaba2aa.cbY#0#Aa2.J", +".b.b#0aabWa.a.bebfbD.c.XbP.k#g.A#f.wbk#E#i.maL.fbiaj#Ka4#5#w#l#yaW#GaVbPbP.XbW.8a#.c#O.c#O.JabbU", +"bQbe.cbQ.J#8ahbQ.8.c.X.XbP.k#gasby#yaI#E#w.Uav.2avbnaYbAa3.s#TaW.yas#g.kbP.X.X#Oa#aa.q.c#0a.bQa.", +".J#O.8.6a##Obeaaab.X.X.XbP.k#gas#fapaI#t#Y.U#WavavavbnaF#EaH#n#n#vas#g.kbP.X.X.X#0.4bq.6#A.J.4be", +".YbY.8.4bfbU.ca..X.X.XbPbPbl#gas#fapaI#EaE#oaUaU#FaUaF#I#Sat#H#faCas#g.k.kbP.X.X.X.J.q#Oaia#.4.J", +"aia.bU#O#Aa.#ObP.XbP.XbP.k#g#gas#H#DbwbR#m.T#I.UbMbMa6#i#maI#D#H#Nas.A#g.kbPbP.X.X.X#za..vbf.8#0", +"#Aaca##6ab#AaabPbP.k.k.k.k#g.AaC#HbwbOaP#tbL.oaB#IaB.o#mbt.CaJ#Daz.abz#g#gblbPbPbPbP.cbf.6.q#0aa", +"#0#0#Oa#.cbU.k.k.kblblbl#g#gbzbJaz#DaJ.7#s.u#ua7a7ay#B#s#s#s.7.Can#N.R.A#g#gbl.kbP.k.k#0abbfa.#0", +"bW.6.b.caa#Obl#g#gaV.A.A.AbzbJ.aanbS#Q#XbFayayaybG.HaZ#r#B#X#Q.7#Q#DazbJbzbz#g#g#g#gbl#za.bDbQa#", +"bDa#bWbqaabY.AasaV#gasaC.RbJ.aazan#Q#Xaw#BaM#V#P#9#9#PaM#sboaQ#QbX#QaJaz#N#v#f.y.yas.A#zbQafbWa#", +"#Oa#.JbUbW.RaCasaC#v#v#N#Nazan.0#QaQbo#r#e#9.QaAbIaKaRaMalaw.nbh#XbXaJ#D#H#f#n#naW#nbJ.R#0bf#Oac", +"bYa#bUa#.4bd#DaW#n#n.w#Haz#D#Qaw#saM#P#e.Q.OaTbI#MaN.d.d.d.3bobh.n#Q#QaJ#D.wap#y#T#T#N.a.8bQ.va#", +".4a..bbeaban#3#t#yaI#y#D#D#QbX#B#V#PaK.Q.OaSaSaobcbrbVao.Q.Q.l.9.9.nbXaJ#D.WaI#J#E#l#D.C.6a#a.bv", +"aa#Oaf#AbQ.ebj#w.h#E.i.W#Q.7#sbG#r#p#M.OaSbH#Z#Z.pbKam#2aS#c#1bEbhbhaQ.CaJbOaI#E#waEat#h.Ya#.Jab", +"a.a#a#bQ.cbo#YbnaY#SbRatbBbX#B#V#P#1aoaS#b#b#R.g.g.zbb#kararao.l.KaQaQ#hatat#3#waY#ibX#uaf#Oaa.c", +"ab#O.qbU.4a0aUavaF#E#3aPaPaw#sbmaR#1.S#b#b.zaXaXaX#ka9#kbb#2#c#P.3bo#Q#Q.7aP.Z#o.m#IbF.P#Aa#ab.4", +"#0bfac.6af.x.V#xbn#w#tbt#h#s#B#V.d.r.Sam.z#2bbbb#q#L#La9#qbbaS.1aR.3aw#XaPbt#3#t.Uae.H#7bY#Oaa#0", +"aaaa.c.6bU.DaOa1bT#o.hax.L#saM#raN#Marar#2a8#ka9#Lb##Lad#qa8bsao#p#rbG.u#4.oaEbAaEaq#r.Da#acaaa#", +"aa.8bUac.6#UbabCbN.Ubj#m.u#s.MaRaN#Mam#2#2#q#q#L..b##La9#q#k#2bH#eaNbG.ubp#I#YavbN#dbma5.6.6bD.J", +"#O.4.qaa#Obx#Ca1.VaUbjax.L#saM#VaR#1ambbbb#qad#La9#L#La8bb#2aSaDaK#P#B#BbL.TaEaU.VaB.E.D.8.8bq#O", +"#Oa#bfa#aa.x#x#Fav#o#t.t#h#B#B#V##bg.#ambb#qa9#qad#L#qaXaXbb#bbI.r#Vaw#saP#mbjaE#wbn.GaZbebY.c.6", +".4.6bWbD#Oa0auaB#W.B#3aP#h.ubGbG.l##aNbc.gaXbba8ad#La8#Rbb#karaT#PaMaQ#QbB.t#t#w#tbAbT#V.b#AaabW", +"#zbfbU.c.b.uay#I#i#iaIaPaP#s.HaMaM##.5.S#caX#2#qadad#2.garbbaTaDaNbGbobXaP.taE#waI#JaFaw.8bW#a#0", +"beaabeaba.#X.L#S#l#latbObB#hbG.MalaM.dbK.O#2a8#q#qbb#ZbH#b.O.Q#j#PbGawbXbBbR#l#E#y#T#i#Qbfaf.baf", +"#O#0#Oab.8aJaP#J#yaH#nbwaJbB#sbo.n.K.l#1#cbs#2bsarao.S#caT.Q#1aR#V#B#h.7.Watbk#y#n#y#3.0.6bUbqbU", +"bDaa.c.4.b.a.W#y#nby#N#N#DanaJbS.e.9al#PaK.1#eaoaDbKaD.Q#M#1aRaw#X#X.7aJ#D#n.wbyaW#y#HbdbY.caa.J", +"bD#0ab.4a#.R#faW#vaCasaC#N.Nanan.0.ebo#V.d#p#1#1#1aKaK#1aN#rbGawaQaQbBazbJ.Ras#fby#naC.RaibD.8bU", +".ca.bYbQbWaa#gas#vas.A.A.RbJazan.0.eaQ#BaMaR#p#paRaRaR.H#VbG#BbX.0aJan#Nbz#g#gasasas.A.Y.J.Y.qaf", +"ai.8.8.4#ObWbl.kaV#gas#g.A.R.aazan#QaQ#X#s#B#B#VbG.M.M#Baw#sbX#Qanan#NbJ#g#gbl#gbl#gbl#8ab#O#8bD", +"#Oaaaa#Oa.#0bl.kbP.kbl#g#g.A.R#NanaJbB#h#hbF.ububuayay#4.L.7#Qan#N.abJ.A#g.k.k.kbP.k.k#8.c#0.bbf", +"#8ab.c.6bUa.ab.XbPbPbP.kbl#gbzbJ#N#H.C.t.L#m#u#u#u.o.o.TaxaPbO#H#N.R.A#g.k.kbPbPbPbPbU#6bY.6a#.6", +"#A#O#0.qaaaa#O.X.X.XbPbP.k#g#g.A#N.wat#3.hbL#i#I.U#I#YbL.hbRaH#DaCbz#gbl.kbPbP.X.XbP#6aaaabWbD.q", +"bfab.bbDbD.8.4bD.X.X.XbPbP.kblas#fapaI#laE#i#i#I#i.T.h#t#3aI.i#naC.A#g#g.kbP.X.X.X.ca##.a2aa.c.c", +"bfbWbWbfa#.ca#.JbU.X.X.XbP.k#gasby#y#l#E#w.haE.haE.h#S#E#E#l#T#n#vasaV.kbP.X.X.X#A.c#8bQ.6bD#0bU", +"bfbD#a.4bDbDbQ#0.8ac.X.XbPbPblaCaW.i.s#S.BaYaYaY.m.m.maY#wa3aIap.yasaV.kbP.X.X#Abv#abebY.cbWaf#O", +"a#af#0a2#Aa#ac.b.4#0bYbPbP.k#g#v#n#y#l#w.Bbn.2aGajbiaGbTaY.haIap#fasaV.kbP.X.b#.bv.8bY#Oa#bfbU#0", +"ab.Jbq.ba.ac#ObWaabq#0bUbW.k#g.A#fbwbk.haFavajb..Faka1.VaFaxaH#naC#g#g.k.J#zbq.YbvbWbfa.ab#OaabQ", +"#Oa#aabfa#.6bqa.bfaa.Jbea.aabfac.RbdanaQbFbG.E#p#1#p##albo.e.j.NbJ.Y#A.8ab#zbq.6#8bW#ObDa#bq.6af", +".6#ObWaa.bbD.c#ObDacbU.4a#bfa##O#O.c.Ja2#Abfa#.cac.Ja#.8bQ#AbUa.#Oaa.8bYbf#O.6.bbeafabbD#0bUaabq", +".c.8bW.bbQ.ba#.4.J.c.cbUaa#0bUaaa..bbWa2.8aaaa.4aabUaa.qa#.baaab.qbUbebD#AbD.cbebf.bbU.4a.bfac.c", +"bf#Aa.#8bv#0aabD.4aa.c#0bU#O.b#ObUbYbfa#bW#O.c#O#0a.#0abaaah.8#ObU.Jaa.4bY#A#aabbUbYbqbq#z.c.6.8", +"afa#af.bbYabbD#Obq.c#0bU.c#6bY#Oaf#Aab#0bQ.cbfaaa.bQ.Ya#bfbY.cai#8bf#Oa.a2a.bDbD.4.qaa.4.Y#8bDa.", +"aa.ca..cbY.c.6#0bfbD.4#a.4.6aba.bv.b.c.Jbf.6#0.cbq.ba.a.bW.6bW#.abaabQ#ObY.4#ObWbfbq#O.ca.bD.8.6" +}; diff --git a/kblackbox/pics/red.xpm b/kblackbox/pics/red.xpm new file mode 100644 index 00000000..8e96946e --- /dev/null +++ b/kblackbox/pics/red.xpm @@ -0,0 +1,202 @@ +/* XPM */ +static char *red[] = { +/* width height num_colors chars_per_pixel */ +" 48 48 147 2", +/* colors */ +".. c #0c0a0c", +".# c #fc7674", +".a c #8c0204", +".b c #dc3e3c", +".c c #3c3a3c", +".d c #ac1614", +".e c #fcaeac", +".f c #c42a2c", +".g c #4c0204", +".h c #ac0204", +".i c #fc5a5c", +".j c #1c1e1c", +".k c #f44e4c", +".l c #dc2e2c", +".m c #fc9294", +".n c #fccacc", +".o c #9c0e0c", +".p c #545654", +".q c #ec3e3c", +".r c #c41e1c", +".s c #2c0204", +".t c #ac0e0c", +".u c #9c0204", +".v c #bc2224", +".w c #6c0204", +".x c #dc3634", +".y c #ec4644", +".z c #d42224", +".A c #141214", +".B c #fc8684", +".C c #4c4a4c", +".D c #bc1614", +".E c #fcbebc", +".F c #fc6a6c", +".G c #2c2e2c", +".H c #fca2a4", +".I c #fcdadc", +".J c #646264", +".K c #c42624", +".L c #bc0e0c", +".M c #9c0a0c", +".N c #d42a2c", +".O c #ac0a0c", +".P c #ec3a3c", +".Q c #1c0204", +".R c #fc7e7c", +".S c #940204", +".T c #e44644", +".U c #c41a1c", +".V c #444244", +".W c #b41e1c", +".X c #fcb6b4", +".Y c #5c0204", +".Z c #fc6264", +".0 c #242624", +".1 c #fc5654", +".2 c #fc9a9c", +".3 c #fcd2d4", +".4 c #f43e3c", +".5 c #cc1e1c", +".6 c #3c0204", +".7 c #b40e0c", +".8 c #a40204", +".9 c #7c0204", +"#. c #e43634", +"## c #f44644", +"#a c #e43e3c", +"#b c #b41614", +"#c c #cc2a2c", +"#d c #b40604", +"#e c #fc4e4c", +"#f c #e43234", +"#g c #a40e0c", +"#h c #5c5e5c", +"#i c #d43234", +"#j c #1c1a1c", +"#k c #fc8e8c", +"#l c #545254", +"#m c #bc1e1c", +"#n c #fcc6c4", +"#o c #fc7274", +"#p c #343634", +"#q c #fcaaac", +"#r c #fce2e4", +"#s c #6c6a6c", +"#t c #0c0e0c", +"#u c #fc7a7c", +"#v c #8c0604", +"#w c #c41614", +"#x c #3c3e3c", +"#y c #540204", +"#z c #ac0604", +"#A c #fc5e5c", +"#B c #242224", +"#C c #f45254", +"#D c #dc3234", +"#E c #fc9694", +"#F c #fccecc", +"#G c #5c5a5c", +"#H c #ec4244", +"#I c #c42224", +"#J c #340204", +"#K c #ac1214", +"#L c #9c0604", +"#M c #740204", +"#N c #dc3a3c", +"#O c #ec4a4c", +"#P c #d42624", +"#Q c #141614", +"#R c #fc8a8c", +"#S c #4c4e4c", +"#T c #bc1a1c", +"#U c #fcc2c4", +"#V c #fc6e6c", +"#W c #343234", +"#X c #fca6a4", +"#Y c #fcdedc", +"#Z c #646664", +"#0 c #cc2624", +"#1 c #bc1214", +"#2 c #a40a0c", +"#3 c #d42e2c", +"#4 c #b40a0c", +"#5 c #240204", +"#6 c #fc8284", +"#7 c #2c2a2c", +"#8 c #cc1a1c", +"#9 c #640204", +"a. c #440204", +"a# c #840204", +"aa c #fcbabc", +"ab c #f44244", +"ac c #cc2224", +"ad c #b41214", +"ae c #e43a3c", +"af c #f44a4c", +"ag c #e44244", +"ah c #b41a1c", +"ai c #a41214", +"aj c #940604", +"ak c #444644", +"al c #fc6664", +"am c #fc9e9c", +"an c #fcd6d4", +"ao c #a40604", +"ap c #cc2e2c", +"aq c #d43634", +/* pixels */ +".C#W#7.j#W#S.c#j#Q.A.0.c.V#7#B#Q#B.G..#7.c#p.c.V.G.j#B#B#p.0.G#B.cak#S.G#l#B#j.j.0...cak#S#7#B#j", +"ak#Q#B#S.j.C#B.j#j.G.C#7#7.0ak.j.V#7.G.G.j#j.j.G.V#l.c.0.j#W#j#p#x#7#W.c#j#p.j.A.G#x.C.C#p.0#Bak", +"#W#W#t.c#Q.V#p.j#7ak#h#p#Q#B.C#Q#s#x.c#W.0#7#j#Q.0#S#hak#B#Q#Q#j.j.G#t#B#p#l.V.G.G#Wak.C#j.0.A.G", +".j.c.j.G#p.G.c.j.V.J.J#l#7#p.j#p#h#W.g#9#Ma#.a.S.a.aa#.aa#.9.Y#pak.C#Q...pak.p.j.A.j#B.c.0.c#7.j", +".V#B#W#p.C.j#t#j#x#W#W#x#p#j.G#W#9#9.w.9.9a#.w#M#M.9a#.a.S.a.a.aa##W#t#7#h.C#W#j#Q.0.G#p#p#x#W.j", +"ak#Q#l.V.0.V#B#j.0.0.0#p.j#B#9.wa#a#.S.S.u.u.S.u.S.u.S.Sa#a#.a.u.S.aa##x.C.C.0.A#7#W#7.j#7ak#B#7", +".C#7#S#S#W#S#p.j.G#B.0.G.g#9#M.9a#.S.a.9.9a#.S.u.u.h#z.h.8.aa#.S.8.8.S.9a#.c.c.j#j#7.G#x#j#Q.G.p", +"#S#t#l#l.j#S.c#7#j.j#l.g#M#9.w#M.9.9.9a#.9a#a#a#a#a#.u#z#d.h.ua#.h.h.u.8.u.a.J.0.0#Q.G.c#W.G#j#W", +"ak#7#7#7#W#W.G#S#W#B#y.w.w.w#M.9a#a#.aa#.a.Sa#.a.u.L.5.z#1.h.u.a.S.L#d.u.8.u.9#B#j#j#p.C#W.j.j#j", +".C.Cak.0#S#S#x#7ak#y#M.w.w#M.9a#a#.a.a.a.a.a.S.u.N#H#f#w.h.u.S.a.S.8.L.h.h.8.8a##W#t#j#x#7#Q.A#W", +"#B#S#lak#W#S#W.G.g#9.9#M.9a#a#a#.a.u.S.a.S.S.8#z#A#V#4.S.u.h.8.L.l.L#w.L.8.u.u.Sa#.j...0#t.C#p#B", +"#7#j#p.G#j#B#Ba..g.Y.w#M.9a#a#.a.S.S.Sao.D#Tae#V.l.u.8#6.X.Zal.P.i.Pac#w#4.h.u.u.S.w.0.c#p#h#7.0", +".j#j.0.j#B#W.0#y#y.g#M.9a#.a.a.S.S.uao#b.K#D.k.8.u.h.Z.E#8#8#d#P.#.F.l.L#4.h.h.8.S.a#7#Q#7#j#W.0", +".j.j#j.V#W#W.g.Y.w#9.w.9a#.9a#.S.u#L#2.u.8.8#zae#u#k.X.L.8.u.u.h#z.4###w#1.L#d.8.ua##M#7.j.c#S#7", +".j#B.0.V#x.G.g.w#9#M.9#Ma#.a.a.a.a#L.8.7#I#c#3#I.v#a#e.L#4.L#z.8.u.5.Z.l#w#w#4.8.S.aa##7#W.V.p.c", +"#B.0#7.0ak#J.g.Y.w.9a#.9a#.S.S.u#L#2ao.N#K#bah#m#I#i.P.z#u.X.I.P.S.h.R#D.U#w#d.8.u.a.a#M#Sak#S#W", +".G.A.c.0#W#y#9.Y#M.aa#.9.a.u.S.uao.O.O#c#K.M#b#I#c#.#f.Ran#Uan#V.u.uab.q.7.O#4.u.S.Sa#.a#W.p#h.c", +"#j#j#p#p#J.g#9#9.9a#.aa#.a#L.uaoao.D#c#m.M#g#g#mae#e.i.H#Y#F.X.E.h.8#w.L#d.7#1.8.S.Sa#.a#9.j#G.C", +".0#B.0.c.ga..Y.w.9a#a#a#.a.a#2#K#Tah#iah.M#K.b#u.##V.H#naaam#X#q#8#4.U.7#z#1#1.h.S.Sa#a#.w.A.V.J", +".c#x.j#l#y#y#9.w#M.9a#a#.aa#.a#L#g#K.W.vaqaq.i.n#U#o.n.I.eaa.3.H#e#1#8.7#4.8.8#d.S.S.S.a.9.Aak#l", +".G#l#j#x#y#9.Y#9.w.w#Ma#.a.a.Sajajai#K#m#u#R.2.E#6aa#U.I#r#Y.3.e#e#8#4.h.h.u.u.8.8.u.u.Sa#.j#pak", +"#S#W.A#S#J#y#y#9.w.w.9.9a#.S#Lad#Tai#gai.W.i#q#Eamaa#X.X.naa.3.I.B#w#d.8.8.8.u.S.a.u.S.aa##B#p.0", +"#j.0#Q#B.6.6#y.Y#9.w#M.9.a.u.t.x.yaf.o#g#m.Kaa#E#q#6#V.2#q.2#Y.H#R#.#4.L.8.S.u.aa#.8.aa#a##B.c#B", +"#W#B#B.j#J.ga..Y#9#9.9a#.a#2#H#V#6#Aai.o.d.d.e.2.m#ual.n#Y#6.2#F.n.H#o.#.r.8.a.a.9a#.9.wa#.J.G.0", +"#Q#t#j#7#5#ya.#y.Y#y.w#M.aad#mal#6.B#K.M.Magaa.2#C.kam.nan#k#Oag.y#N.i#Dad.S.S.aa#a#.9.Y.9#x.0#j", +".j#B#B#j.Q#ya.a..g#y.Y#M.a#K#L.v#E#R#o.i#6#E#V.bap#R.X.3#6##ap#I#m#T#b#2.S.u.aa#a#a#.9.w.w#j#j.j", +"#Q#W#p.j.s#9a..6.g#y#y#9a#.S.S.M#mafaf.T#O#i.Wah.F#6.#.Bal#D.y.y#D.N.t.u.S.S.aa#a#.a.9a#.a.C#7.0", +"#7#7#W#7a..w.6.6.g#y.Y#9a#.Sa#a#.a#v.a.M#g#g.d#iaeaqah.K.1.Z#A#A#.#Dad.8.u.Sa#.a.S.S.S.9.9#B.A#p", +".p#Q.j#7a.a..Y.6.g#y#9#y.9#La#a#.9a#a#.a.a.d.v#c#0.Kah.d.f#Hae#2#Lao.t#z.u.S.u.S.u.a.S.9.w.0.c#j", +"#W#7#t#j#5#J#ya.#y#9.Y.Y.w#2a#a#.9a#.a#v#L#I.v#b#i#cahah.K.d#L.a.S.S.S.S.u.S.u.u.u.u.a.9.Y#Q.A.j", +".G#7#p#S#G.s.g#y.Y#9.Y#y.S#g.S#M#Ma#.a#K#m.v.Maj.M.d.K#I.d#2.a#L#K#L.a.a.a.S.S.S.S.S.9.w#j#j#B#p", +".c#j#B#p#S.sa.#y#y.Y.Y#9.a.a.9.w.w.9.S#g.M.Sa#aj.aaj.M#g#g#2.Mad.U.D.O.Sa#.a.S.S.S.a#M#y.c#7#p#G", +".c.0...V#7#pa.#y.Y#y#9.w#M.9.w#9#9.9.a.aaja#.a.M#K#La#.S#2#K.t.O.t.7.O.S.9.S.a.aa#.w#M#W#B.G#Q.G", +".j#B#j#x.j#j#5.6.g.Y#9.w.w#9.w.w#9#Ma#.a#g.S.9a##K#b#g#2#g.O#gao.u#L.S#M.9.aa##M.w.w.Y#7#j.j.c.c", +"ak#S.G#7#W.V#S.6#Ja..Y#9#9.Y#9.w#9.9.9a##Laj#M.a#L#2#2ao#L#L.Sa#.9.a#M#M.9.9#M.w#9#y.j.A.c.G#B#p", +"#j#W.c.c.V#Q#W#J.s.6.6#y#y#y#9#M#Ma#a#a#a#a##M#M#L.M#L#L.u.a.9#M#M#M.w.w#M.9#y.Y.Y.g.G#7#7.c#B#x", +"#j.V.j.j#l#W.G#t#J#J#Ja..g#y.Y.w#M#M.9#M#M#M#M.9#L#2#L#L#L.u#M#M#M#M#9#9.w#9.Y.Y#y.J#B.G.G#B#t#p", +"#W.0#x.0#7.G#W#7.V.Qa..6a.#y.Y.Y#9#9.w#M#M#M.w.9.aao#L.S#L#L.u.S.a.w#9#9#M.9.Y.Y#p.p.j.c#x#Q#j#7", +".c.0#B.0#j#x#W#Q#x.c#5.6a..g#y#y.Y#9#9.w.w#M#M#M.9.S.S.9.a.aa#.S.a.a.w#9.9#M.Y#j#j#l.G#j#j#j#7#p", +"#x#j#jak#p#j#p.A.C.C#B#5#J.6.g.Y.Y#9.w#9#9#M.9.aa#.9.Y.Y#9#9#9.9a#.9#y.Y.g.g.c.j.j#W#l.0#p.0.0#p", +"#l.0#p#W#x#W#7.0#xak#p.0#5.s.6a..Y.Y#9.w.w.w.Y#y.g#y.g.g.g#y.g#Ma#.Y.ga.#J.C#p#7.C#p#x.0#7ak.p.0", +"#S#p#Q#W.0#Q.A.A.0#Q#j.c#G.J#5.6.g#y#y.Ya.a..g.Y#y.Y.Y#y#y.ga..w#9a.#J.V#W#W#S.pak#Q.c.c.0#l.C#S", +".c.G#B.j#x#p#7#Q#7#p#W#t.cak#W#7#Ja.#J#J#J.6a.#9.w.Y.g#y.g.6.Y.g.6#Q#7.j#B.0.c.p.C#B#S#W.jak.p.V", +"#B#t#S.C.0.A#7.V.G.0#7#x.0.A#7.c#x#B#5.Q#J.g#J.6.g.6a..6#J.6#J#B#W#B#p#l.V.V.G.V#j.c.c.G.j.0#pak", +"#j#7#B.j#B.0.j#S.c.j#W.A#p#p.0.G#B#G.J#7.A.0.G...G#j#W.G#W.c#7.j#W#B#B.p.p#l#S#B.0#l#W.0#B.j.A.j", +".0#Q#W#W.0#7#W#p#j#W#l#W.Vak#7#j#x.p#l.V.G#j.j#j#W#W#Q#Q#Q#x#j.j.0.0.0#Bak#l.G.j#B.j.0.C.C#W#B#Q", +"#W#7.j#j#W.c#W#j.j.Gak.V#j#Z.V#j.V#l#lak#p.A#Q.c.0.0#j#x#W.j#7.0.p.V.G#t#W#l.V.0#j#l#S#S.C.V#W#7", +"#7.j.C.c#x#t.0.G.p#W#pak.c.G.j#Q#7.A#x.0#j#Q.c#hak#x#W.A#x#B.c#p#x#G.C#x#p.C#x.c#Q#h#lak#B#p.G.j" +}; diff --git a/kblackbox/pics/white.xpm b/kblackbox/pics/white.xpm new file mode 100644 index 00000000..06c38cec --- /dev/null +++ b/kblackbox/pics/white.xpm @@ -0,0 +1,100 @@ +/* XPM */ +static char *white[] = { +/* width height num_colors chars_per_pixel */ +" 48 48 45 1", +/* colors */ +". c #9e9e9e", +"# c #9f9f9f", +"a c #a0a0a0", +"b c #a1a1a1", +"c c #a2a2a2", +"d c #a3a3a3", +"e c #a4a4a4", +"f c #a5a5a5", +"g c #a6a6a6", +"h c #a7a7a7", +"i c #a8a8a8", +"j c #a9a9a9", +"k c #aaaaaa", +"l c #ababab", +"m c #acacac", +"n c #adadad", +"o c #aeaeae", +"p c #afafaf", +"q c #b0b0b0", +"r c #b1b1b1", +"s c #b2b2b2", +"t c #b3b3b3", +"u c #b4b4b4", +"v c #b5b5b5", +"w c #b6b6b6", +"x c #b7b7b7", +"y c #b8b8b8", +"z c #b9b9b9", +"A c #bababa", +"B c #bbbbbb", +"C c #bcbcbc", +"D c #bdbdbd", +"E c #bebebe", +"F c #bfbfbf", +"G c #c0c0c0", +"H c #c1c1c1", +"I c #c2c2c2", +"J c #c3c3c3", +"K c #c4c4c4", +"L c #c5c5c5", +"M c #c6c6c6", +"N c #c7c7c7", +"O c #c8c8c8", +"P c #c9c9c9", +"Q c #cacaca", +/* pixels */ +".aefbgjl.jtledrehhigdfkmrnehqkoffilj#ehrgefdaecd", +"abgmennigjiiichejkkyJnpglnkprkpsIFGrmcmptrleacc.", +"edcbkCtGCing#mIHINLLHLHjgGKNIILADIGJGCangjxFxumw", +"srlxyzHzFxIAGFLLGKJIJHILNLLIMFFIFJJDFFyDxptxzxqn", +"mwopxAwCzCyLFGDNLMNEFILOMOKFIxGIGLGBKIDFHysqutzi", +"usnEExCGEyGMuDHilsirFMMCyKGFIDxsBNJECEBDIzxptumu", +"qwyykvgonihhpoptsssvqltvurwNIFGtqkJFICDCDgdjgios", +"if#ilefjiehojprpxquqjvvuxstkrCBxBsrhrljmrtqljfpf", +"iebkjjqfmkrwztsvqxwrGtsuuxyutqotvjnmgiiipslndidi", +"fcptmgpBpvGJyzCyFCwBDrmppyqpnkovposormdilkuomwnd", +"vAtmtDDyEBMGCGAuBGMMHONMPMkINNMMKNHFJBFFmBurxqAw", +"uDwtuCBGADJzwJJLKONNNPPPOOMNPNPKLMLNGJJGDxEmwtBp", +"rFwGyyFxHDDJAHEMNPNNPPNNOOKNOPNIPMKKHHKLAGwuyEsv", +"xArkjvzyrzvGDIIFHLHMPPOPNPuunwuzqOKJIAIKIwtCDlvu", +"mrnwtusxqDvuwuuwltuxDmwGxtFxFCyvCtBBowilzJxuBktp", +"jpivtAuACsDCBHEFvBzwwxBDvyFIGzEEBCzvzDnfnmwAsqyv", +"msmxoryqtvywEutuyBCHFCFDGCyNNLHzzxDACwvtklhqmtso", +"wpklzBAFCxDEttJKMJMOPMMMQOQQPLNCECzzCABCGsrnrprl", +"tDyxvwxFyAEEFJKCOJFONNNPOQPQOOQOLGHGHBwIJKAEAlyt", +"mCAvvvDJwsACHJPGMNFMLNPOQOPNNOPPKDxEExHIJCFHvirw", +"uwsvyuAFxwCJEJKIKLHJLMQNQLDMINPGEvHJFHIIFHuDupnm", +"xtxxCvszAGDAEHIDKHGMNQNNONIIFJPJBJNEHFvEzxvHxtpv", +"sBBvruxpsGwKEELDHMNDGyyGIHKIBGHGILKFBuGzAAntzsBx", +"twyxlqCqIAKKvEDJIKEKFJIIMOJINELJKIOHstyJyEynryBw", +"rslylqqsFGLExGzHKHMCMKPPIONMMGDJDMIJKuABBptzzsBy", +"yqnprxCzutGMBEDCFJINIIKIIQJGEwwQHNHGCsrrDHFrzovf", +"tqjrtvAzyoAFMEDEEGMNNLCDDHDEJCECEEuDzAxDzzuqpnmj", +"oskqzyGwutuvEDDHuKGINJKDEEBGEEHDACvxzyBysCrqrwlo", +"tBvumruxDpzBFKyAwFNMFBPPOQHNPNIFBvIHByECJyAqyvpw", +"uEDCzBvByKJyEEIKJCGMJLOPPQPOQOIIFPJIGGExFEyCtArv", +"rtCAAxAEJECGzNKIMMLLJQPPOOOONKPFKIPMIEBFDJHEBxuw", +"xsHGxxxFAyxAKOKDPPNKIKLQLJOOPLPNLHALOyHFEJzEBzqz", +"ghkeDtsyHKruqMMJDwNyrEPPOMPFAIEwrBwoJJEIzuppsxrq", +"hmpkkhsolrsvsDywwGxzFHzDoCGFyAAmrxoErAxuvslyvskq", +"igkjkntygplnwrwABGvHzIvssytusDuAwqAzuyrshnlAtowg", +"qqouloxxywlgpBvzEFABBwwspxzuomqyPJxupgxrvpmvojnh", +"vwikzgwCEEFHMgitruxuszNPNOqrPKLOKJKGJGxpspCztwoh", +"yDwirCtzAJNJLMKMNMJJOOONNKOKMONLNLFJGEDGEFFttptr", +"uByvvAvzyDKHLHKKOHOKNLNPPJPMJFNJJIEILHAGEDxAwozz", +"vpwxssvznoIJKIFGELINNFJMEHNOLMMOJHHBjpGwDCzsuwBy", +"#avzxjqixhkmjJGBkhmrttqyvxqxxLrvvqlmioqsxxDBvyuu", +"phghddtolkmkilpqmyjtqtnnBsswDqvrswhgttmrqqvpjhdc", +"hdffkporpwskmupqfjshwsuugppuxkkpikmkhmmlgnjeejc.", +"dceelgkkvtjllqqrhtslnnniedke#ekorefeIzkgblcdhdgd", +"nvxBxglcnifGqdhFHMHLHIIJHDljmipKHKGEzzzEuhbg#wsy", +"ouwuwzqEDEyFEADAwFHIJNEFzIFJIGJICCEzBDFswpAwEvDw", +"osuIzFuECvDADBHGGJIJIJKDFKIGGHExCCJxzDBwstCyFEwr", +"tpvjzBtvCABHFEDEGBoHfeiILILLGKGAEHGyClgcBwGFuuie" +}; diff --git a/kblackbox/util.cpp b/kblackbox/util.cpp new file mode 100644 index 00000000..7282fe41 --- /dev/null +++ b/kblackbox/util.cpp @@ -0,0 +1,68 @@ +// +// +// KBlackBox +// +// A simple game inspired by an emacs module +// +// File: util.cpp +// +// The implementation of the RectOnArray class +// + +#include "util.h" + +RectOnArray::RectOnArray( int newWidth, int newHeight ) +{ + w = newWidth; + h = newHeight; + array = new ArrayType[w*h]; +} + +RectOnArray::~RectOnArray() +{ + delete[] array; +} + +/* + Size info... +*/ + +int RectOnArray::width() { return w; } +int RectOnArray::height() { return h; } + +/* + Utility function for mapping from 2D table to 1D array +*/ + +int RectOnArray::indexOf( int col, int row ) const +{ + return (row * w) + col; +} + +/* + Return content of cell +*/ + +ArrayType RectOnArray::get( int col, int row ) +{ + return array[indexOf( col, row )]; +} + +/* + Set content of cell +*/ + +void RectOnArray::set( int col, int row, ArrayType type ) +{ + array[indexOf( col, row )] = type; +} + +/* + Fill all cells witj type +*/ + +void RectOnArray::fill( ArrayType type ) +{ + int i; + for (i = 0; i < w*h; i++) array[i] = type; +} diff --git a/kblackbox/util.h b/kblackbox/util.h new file mode 100644 index 00000000..9effb880 --- /dev/null +++ b/kblackbox/util.h @@ -0,0 +1,42 @@ +// +// +// KBlackBox +// +// A simple game inspired by an emacs module +// +// File: util.h +// +// The definition of the RectOnArray class +// + +#ifndef UTIL_H +#define UTIL_H + +/* + This is used for gameBoard and graphicBoard fields +*/ + +#define ArrayType int + +class RectOnArray +{ +public: + RectOnArray( int w, int h ); + ~RectOnArray(); + + int get( int col, int row ); + void set( int col, int row, ArrayType type ); + void fill( ArrayType type ); + + int width(); + int height(); + +private: + int indexOf( int col, int row ) const; + + int w; + int h; + ArrayType *array; +}; + +#endif // UTIL_H diff --git a/kblackbox/version.h b/kblackbox/version.h new file mode 100644 index 00000000..f959cc8c --- /dev/null +++ b/kblackbox/version.h @@ -0,0 +1 @@ +#define KBVERSION "0.3.0" diff --git a/kbounce/Makefile.am b/kbounce/Makefile.am new file mode 100644 index 00000000..25d0909b --- /dev/null +++ b/kbounce/Makefile.am @@ -0,0 +1,76 @@ +if include_ARTS +artslib = -lartskde +else +artslib = +endif + +SUBDIRS = pics sounds + +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) +METASOURCES = AUTO +KDE_CXXFLAGS = $(KDE_USE_FPIE) + +bin_PROGRAMS = kbounce +kbounce_SOURCES = main.cpp kbounce.cpp game.cpp highscores.cpp +kbounce_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(KDE_USE_PIE) +kbounce_LDADD = $(LIB_KDEGAMES) $(LIB_KFILE) $(LIB_ARTS) $(artslib) +kbounce_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +xdg_apps_DATA = kbounce.desktop + +EXTRA_DIST = RULES $(xdg_apps_DATA) + +rcdir = $(kde_datadir)/kbounce +rc_DATA = kbounceui.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kbounce.pot + +# for system-wide highscore file +DESTBIN = $(DESTDIR)$(bindir)/$(bin_PROGRAMS) +DESTHIGHSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY) +DESTSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY)/$(bin_PROGRAMS).scores + +install-data-local: + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + echo "********************************************************" ;\ + echo "" ;\ + echo "This game is installed sgid \"games\" to use the" ;\ + echo "system-wide highscore file (in "$(HIGHSCORE_DIRECTORY)")." ;\ + echo "" ;\ + echo "If the system-wide highscore file does not exist, it is" ;\ + echo "created with the correct ownership and permissions. See the" ;\ + echo "INSTALL file in \"kdegames/libkdegames/highscore\" for details." ;\ + echo "" ;\ + echo "********************************************************" ;\ + fi + +install-exec-hook: + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + chown $(highscore_user):$(highscore_group) $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + mkdir -p $(DESTHIGHSCORES) && \ + chown $(highscore_user):$(highscore_group) $(DESTHIGHSCORES) \ + && chmod 750 $(DESTHIGHSCORES) \ + || echo "Error: Could not create the highscore directory with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + chown $(highscore_user):$(highscore_group) $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test ${setgid} = true; then \ + chmod 2755 $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + touch $(DESTSCORES) && chown $(highscore_user):$(highscore_group) $(DESTSCORES) \ + && chmod 0660 $(DESTSCORES) \ + || echo "Error: Could not create system-wide highscore file with correct permissions !!" ;\ + fi + diff --git a/kbounce/RULES b/kbounce/RULES new file mode 100644 index 00000000..1636814b --- /dev/null +++ b/kbounce/RULES @@ -0,0 +1,25 @@ +====================== += Rules of Jezz Ball = +====================== + +Game/Level Length +1. The game will be over if + a) your number of lifes is zero or + b) your time is over. +2. When at least 75% of the field is filled. a level is completed. + +Lifes +3. A level is started with one life per ball on the field. +4. You will lose a life if a ball hits the inner of a wall. + +Balls +5. Each level include one more ball. +6. A ball is reflected by blue and black blocks. + +Walls +7. You build a wall with the left mouse buttoyn and change the direction with the right. +8. You can _always_ build _two_ "half" walls concurrently. If one half is already finished, + you can build another "half" wall. +9. A wall is finished if it is hit by a ball at its end or it hits a black block. +10. A wall is removed if it is hit by a ball at the wall's inner or two "half" walls hit each + other with their blue ends. \ No newline at end of file diff --git a/kbounce/configure.in.in b/kbounce/configure.in.in new file mode 100644 index 00000000..d34f1b32 --- /dev/null +++ b/kbounce/configure.in.in @@ -0,0 +1,4 @@ +if test "x$build_arts" != "xno"; then + AC_DEFINE_UNQUOTED(HAVE_ARTS, 1, [Define if aRts is available]) + LIB_ARTS="-lsoundserver_idl" +fi diff --git a/kbounce/game.cpp b/kbounce/game.cpp new file mode 100644 index 00000000..853a645d --- /dev/null +++ b/kbounce/game.cpp @@ -0,0 +1,699 @@ +/* + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "game.h" + + +#define TILE_SIZE 16 + +#define TILE_FIRST ((FIELD_WIDTH-2)*(FIELD_HEIGHT-2)) +#define TILE_FREE (TILE_FIRST + 0) +#define TILE_BORDER (TILE_FIRST + 1) +#define TILE_WALLEND (TILE_FIRST + 2) +#define TILE_WALLUP (TILE_FIRST + 3) +#define TILE_WALLDOWN (TILE_FIRST + 4) +#define TILE_WALLLEFT (TILE_FIRST + 5) +#define TILE_WALLRIGHT (TILE_FIRST + 6) + +#define GAME_DELAY 15 +#define BALL_ANIM_DELAY 60 +#define WALL_DELAY 100 + + +#if HAVE_ARTS +SimpleSoundServer *JezzGame::m_artsServer = 0; +#endif +QString JezzGame::m_soundPath; +bool JezzGame::m_sound = true; + +#define MS2TICKS( ms ) ((ms)/GAME_DELAY) + +Ball::Ball(QCanvasPixmapArray* array, QCanvas* canvas) + : QCanvasSprite( array, canvas ), m_animDelay( 0 ), m_soundDelay( MS2TICKS(BALL_ANIM_DELAY)/2 ) +{ +} + +void Ball::update() +{ + // set pixmap frame + m_animDelay--; + if ( m_animDelay<=0 ) + { + m_animDelay = MS2TICKS(BALL_ANIM_DELAY); + int frameNum = frame(); + frameNum++; + if ( frameNum>=frameCount() ) + frameNum = 0; + setFrame( frameNum ); + } +} + +void Ball::advance(int stage) +{ + bool reflectX = false; + bool reflectY = false; + + m_soundDelay++; + + // ball already on a wall? (should normally never happen) + // commented out to stop bug which causes balls to + // sometimes stop when clicked on + // if ( collide(0, 0) ) setVelocity( 0, 0 ); + + // check for collisions + if ( collide(xVelocity(), 0) ) reflectX = true; + if ( collide(0, yVelocity()) ) reflectY = true; + if ( !reflectX && !reflectY && collide(xVelocity(), yVelocity()) ) reflectX = reflectY = true; + + // emit collision + QRect r = boundingRect(); + r.moveBy( xVelocity(), yVelocity() ); + JezzField* field = (JezzField *)canvas(); + + int ul = field->tile( r.left() / TILE_SIZE, r.top() / TILE_SIZE ); + int ur = field->tile( r.right() / TILE_SIZE, r.top() / TILE_SIZE ); + int bl = field->tile( r.left() / TILE_SIZE, r.bottom() / TILE_SIZE ); + int br = field->tile( r.right() / TILE_SIZE, r.bottom() / TILE_SIZE ); + + if ( ul!=TILE_FREE ) field->emitBallCollisiton( this, r.left() / TILE_SIZE, r.top() / TILE_SIZE, ul ); else + if ( ur!=TILE_FREE ) field->emitBallCollisiton( this, r.right() / TILE_SIZE, r.top() / TILE_SIZE, ur ); else + if ( bl!=TILE_FREE ) field->emitBallCollisiton( this, r.left() / TILE_SIZE, r.bottom() / TILE_SIZE, bl ); else + if ( br!=TILE_FREE ) field->emitBallCollisiton( this, r.right() / TILE_SIZE, r.bottom() / TILE_SIZE, br ); + + // apply reflection + if ( reflectX ) setXVelocity( -xVelocity() ); + if ( reflectY ) setYVelocity( -yVelocity() ); + + // play collision sound + if ( reflectX || reflectY ) + { + if ( m_soundDelay>50 ) JezzGame::playSound( "reflect.au" ); + m_soundDelay = 0; + } + + // update field + update(); + QCanvasSprite::advance( stage ); +} + +bool Ball::collide( double dx, double dy ) +{ + QRect r = boundingRect(); + r.moveBy( dx, dy ); + JezzField* field = (JezzField *)canvas(); + + int ul = field->tile( r.left() / TILE_SIZE, r.top() / TILE_SIZE ); + int ur = field->tile( r.right() / TILE_SIZE, r.top() / TILE_SIZE ); + int bl = field->tile( r.left() / TILE_SIZE, r.bottom() / TILE_SIZE ); + int br = field->tile( r.right() / TILE_SIZE, r.bottom() / TILE_SIZE ); + + return ( ul!=TILE_FREE || ur!=TILE_FREE || bl!=TILE_FREE || br!=TILE_FREE ); +} + +/*************************************************************************/ + +Wall::Wall( JezzField *field, int x, int y, Direction dir, int tile, QObject *parent, const char *name ) + : QObject( parent, name ), m_dir( dir ), m_field( field ), m_startX( x ), m_startY( y ), + m_tile( tile ), m_delay( MS2TICKS(WALL_DELAY)/2 ), m_active( true ) +{ + //kdDebug(12008) << "Wall::Wall" << endl; + + // setup position and direction + m_dx = 0; + m_dy = 0; + switch ( m_dir ) + { + case Up: m_dy = -1; break; + case Down: m_dy = 1; break; + case Left: m_dx = -1; break; + case Right: m_dx = 1; break; + } + + m_x = m_startX; + m_y = m_startY; + + m_field->setTile( m_x, m_y, m_tile ); +} + +void Wall::finish() +{ + m_active = false; +} + +bool Wall::isFree( int x, int y ) +{ + if ( m_field->tile(x, y)==TILE_FREE ) + { + // check whether there is a ball at the moment + QCanvasItemList cols = m_field->collisions( QRect(x*TILE_SIZE, y*TILE_SIZE, + TILE_SIZE, TILE_SIZE) ); + if ( cols.count()==0 ) + return true; + } + + return false; +} + +void Wall::update() +{ +} + +void Wall::advance() +{ + update(); + + // move wall + if ( m_active ) + { + m_delay--; + if ( m_delay<=0 ) + { + m_delay = MS2TICKS(WALL_DELAY); + + // set previous tile + m_field->setTile( m_x, m_y, m_tile ); + + // check whether next place is still free + if ( isFree(m_x+m_dx, m_y+m_dy) ) + { + // move ball + m_x += m_dx; + m_y += m_dy; + + // set tile + m_field->setTile( m_x, m_y, TILE_WALLEND ); + } else + { + finish(); + emit finished( this, m_field->tile( m_x+m_dx, m_y+m_dy ) ); + } + } + } +} + +void Wall::fill( bool black ) +{ + if ( m_dx ) + { + for ( int x=m_startX ; x!=m_x; x+=m_dx ) + if ( m_field->tile(x, m_startY)==m_tile ) + m_field->setGameTile( x, m_startY, black ); + + m_field->setGameTile( m_x, m_startY, black ); + } else + { + for ( int y=m_startY ; y!=m_y; y+=m_dy ) + if ( m_field->tile(m_startX, y)==m_tile ) + m_field->setGameTile( m_startX, y, black ); + + m_field->setGameTile( m_startX, m_y, black ); + } +} + +/*************************************************************************/ + +JezzField::JezzField( const QPixmap &tiles, const QPixmap &background, QObject* parent, const char* name ) + : QCanvas( parent, name ), m_tiles( tiles ) +{ + setPixmaps( tiles, background ); +} + +void JezzField::setGameTile( int x, int y, bool black ) +{ + if ( m_background ) + setTile( x, y, black ? ((x-1)+(y-1)*(FIELD_WIDTH-2)) : TILE_FREE ); + else + setTile( x, y, black ? TILE_BORDER : TILE_FREE ); +} + +void JezzField::setBackground( const QPixmap &background ) +{ + // copy current field into buffer + int backup[FIELD_WIDTH][FIELD_HEIGHT]; + for ( int y=0; ybutton() & RightButton ) + { + m_vertical = !m_vertical; + if ( m_vertical ) setCursor( sizeVerCursor ); else setCursor( sizeHorCursor ); + } + + if ( ev->button() & LeftButton ) + { + emit buildWall( ev->x()/TILE_SIZE, ev->y()/TILE_SIZE, m_vertical ); + } +} + +/*************************************************************************/ + +JezzGame::JezzGame( const QPixmap &background, int ballNum, QWidget *parent, const char *name ) + : QWidget( parent, name ), m_wall1( 0 ), m_wall2( 0 ), + m_text( 0 ), m_running( false ), m_percent( 0 ), m_pictured( false ) +{ + QString path = kapp->dirs()->findResourceDir( "data", "kbounce/pics/ball0000.png" ) + "kbounce/pics/"; + + // load gfx + m_ballPixmaps = new QCanvasPixmapArray( path + "ball%1.png", 25 ); + for ( unsigned n=0; ncount(); n++ ) + m_ballPixmaps->image(n)->setOffset( 0, 0 ); + QPixmap tiles( path + "tiles.png" ); + + // setup arts +#if HAVE_ARTS + m_artsServer = new SimpleSoundServer; + *m_artsServer = Arts::Reference("global:Arts_SimpleSoundServer"); + if ( m_artsServer->isNull() ) + kdDebug(12008) << "Can't connect to aRts sound server" << endl; +#endif + m_soundPath = kapp->dirs()->findResourceDir( "data", "kbounce/sounds/death.au" ) + + "kbounce/sounds/"; + + // create field + m_field = new JezzField( tiles, background, this, "m_field" ); + m_field->resize( TILE_SIZE*FIELD_WIDTH, TILE_SIZE*FIELD_HEIGHT ); + + for ( int x=0; xsetTile( x, 0, TILE_BORDER ); + for ( int y=1; ysetTile( 0, y, TILE_BORDER ); + for ( int x=1; xsetTile( x, y, TILE_FREE ); + m_field->setTile( FIELD_WIDTH-1, y, TILE_BORDER ); + } + for ( int x=0; xsetTile( x, FIELD_HEIGHT-1, TILE_BORDER ); + + connect( m_field, SIGNAL(ballCollision(Ball *, int, int, int)), this, SLOT(ballCollision(Ball *, int, int, int)) ); + + // create view + m_view = new JezzView( m_field, this, "m_view" ); + m_view->move( 0, 0 ); + m_view->adjustSize(); + connect( m_view, SIGNAL(buildWall(int, int, bool)), this, SLOT(buildWall(int, int, bool)) ); + + // create balls + for ( int n=0; nsetVelocity( ((kapp->random() & 1)*2-1)*2, ((kapp->random() & 1)*2-1)*2 ); + ball->setFrame( kapp->random() % 25 ); + ball->move( 4*TILE_SIZE + kapp->random() % ( (FIELD_WIDTH-8)*TILE_SIZE ), + 4*TILE_SIZE + kapp->random() % ( (FIELD_HEIGHT-8)*TILE_SIZE ) ); + ball->show(); + } + + // create text label + m_text = new QCanvasText( m_field ); + + // create game clock + m_clock = new QTimer( this ); + connect( m_clock, SIGNAL(timeout()), this, SLOT(tick()) ); + m_clock->start( GAME_DELAY ); + + // setup geometry + setFixedSize( m_view->size() ); +} + +JezzGame::~JezzGame() +{ + m_balls.clear(); + delete m_view; + delete m_field; + delete m_ballPixmaps; +#if HAVE_ARTS + delete m_artsServer; +#endif +} + + +void JezzGame::display( const QString &text, int size ) +{ + qDebug("This function \"display\" shouldn't be called!!!"); + if ( !text.isEmpty() ) + { + //kdDebug(12008) << "text = " << text << endl; + + QFont font = KGlobalSettings::generalFont(); + font.setBold(true); + font.setPointSize(size); + m_text->setFont( font ); + m_text->setText( text ); + + QRect size = m_text->boundingRect(); + m_text->move( ( FIELD_WIDTH*TILE_SIZE - size.width() ) / 2, + ( FIELD_HEIGHT*TILE_SIZE - size.height() ) / 2 ); + + m_text->show(); + } else + { + m_text->hide(); + } +} + +void JezzGame::playSound( const QString &name ) +{ +#if HAVE_ARTS + if( !m_artsServer->isNull() && m_sound) + { + QString path = m_soundPath + name; + m_artsServer->play( path.latin1() ); + } +#else + return; +#endif +} + +void JezzGame::setBackground( const QPixmap &background ) +{ + m_field->setBackground( background ); +} + +void JezzGame::setSound( bool sound ) +{ + m_sound = sound; +} + +void JezzGame::start() +{ + m_running = true; +} + +void JezzGame::stop() +{ + m_running = false; +} + + +void JezzGame::makeBlack() +{ + // copy current field into buffer + for ( int y=0; ytile( x, y ); + + // fill areas that contains a ball + for ( Ball *ball=m_balls.first(); ball!=0; ball=m_balls.next() ) + fill( ball->x()/TILE_SIZE, ball->y()/TILE_SIZE ); + + // areas still free can be blacked now + for ( int y=0; ysetGameTile( x, y, true ); + } + + m_field->update(); + m_view->repaint(); + + // count percent value of occupied area + int p = percent(); + if ( p!=m_percent ) + { + m_percent = p; + emit newPercent( m_percent ); + } +} + +int JezzGame::percent() +{ + int notFree = 0; + for ( int y=1; ytile(x,y)!=TILE_FREE ) + notFree++; + } + + return 100 * notFree / ( (FIELD_WIDTH-2) * (FIELD_HEIGHT-2) ); +} + +void JezzGame::fill( int x, int y ) +{ + if ( m_buf[x][y]!=TILE_FREE) return; + + // go left + int _x=x; + for ( ; m_buf[_x][y]==TILE_FREE; _x-- ) + m_buf[_x][y] = TILE_BORDER; + int stopx = _x; + + // fill above + for ( _x=x; _x>stopx; _x-- ) + if ( m_buf[_x][y-1]==TILE_FREE ) fill( _x, y-1 ); + + // fill below + for ( _x=x; _x>stopx; _x-- ) + if ( m_buf[_x][y+1]==TILE_FREE ) fill( _x, y+1 ); + + // go right + for ( _x=x+1; m_buf[_x][y]==TILE_FREE; _x++ ) + m_buf[_x][y] = TILE_BORDER; + stopx = _x; + + // fill above + for ( _x=x+1; _xTILE_FREE && tile!=TILE_WALLEND ) + { + kdDebug(12008) << "Collision" << endl; + + // play explosion sound + playSound( "death.au" ); + + // stop walls + if ( (tile==TILE_WALLUP || tile==TILE_WALLLEFT) && m_wall1 ) + { + kdDebug(12008) << "up or left" << endl; + m_wall1->finish(); + m_wall1->fill( false ); + delete m_wall1; + m_wall1 = 0; + } + + if ( (tile==TILE_WALLDOWN || tile==TILE_WALLRIGHT) && m_wall2 ) + { + kdDebug(12008) << "down or right" << endl; + m_wall2->finish(); + m_wall2->fill( false ); + delete m_wall2; + m_wall2 = 0; + } + + // update view + m_field->update(); + m_view->repaint(); + + // send death msg + emit died(); + } +} + +void JezzGame::buildWall( int x, int y, bool vertical ) +{ + if ( !m_running ) return; + + kdDebug(12008) << "JezzGame::buildWall( x=" << x << " y=" << y << " vertical=" << vertical << " )" << endl; + if ( m_field->tile(x, y)==TILE_FREE ) + { + playSound( "wallstart.au" ); + + // check whether there is a ball at the moment + QCanvasItemList cols = m_field->collisions( QRect(x*TILE_SIZE, y*TILE_SIZE, TILE_SIZE, TILE_SIZE) ); + if ( cols.count()>0 ) + { + kdDebug(12008) << "Direct collision" << endl; + emit ballCollision( (Ball*)cols.first(), x, y, TILE_WALLUP ); + return; + } + + // start walls + if ( !m_wall1 ) + { + m_wall1 = new Wall( m_field, x, y, + vertical? Wall::Up : Wall::Left, + vertical? TILE_WALLUP : TILE_WALLLEFT, + this, "m_wall1" ); + connect( m_wall1, SIGNAL(finished(Wall *, int)), + this, SLOT(wallFinished(Wall *, int)) ); } + + if ( !m_wall2 ) + { + m_wall2 = new Wall( m_field, x, y, + vertical? Wall::Down: Wall::Right, + vertical? TILE_WALLDOWN : TILE_WALLRIGHT, + this, "m_wall2" ); + connect( m_wall2, SIGNAL(finished(Wall *, int)), + this, SLOT(wallFinished(Wall *, int)) ); + } + } +} + +void JezzGame::wallFinished( Wall *wall, int tile ) +{ + //kdDebug(12008) << "wallFinished" << endl; + playSound( "wallend.au" ); + + if ( tile==TILE_WALLEND ) + { + if ( m_wall1 ) + { + m_wall1->fill( false ); + delete m_wall1; + m_wall1 = 0; + } + + if ( m_wall2 ) + { + m_wall2->fill( false ); + delete m_wall2; + m_wall2 = 0; + } + } else + { + if ( m_wall1==wall && m_wall1 ) + { + m_wall1->fill( true ); + delete m_wall1; + m_wall1 = 0; + } + + if ( m_wall2==wall && m_wall2 ) + { + m_wall2->fill( true ); + delete m_wall2; + m_wall2 = 0; + } + } + + m_field->update(); + m_view->repaint(); + + makeBlack(); +} + +void JezzGame::tick() +{ + if ( m_running ) + { + if ( m_field ) m_field->advance(); + if ( m_wall1 ) m_wall1->advance(); + if ( m_wall2 ) m_wall2->advance(); + } else + { + for ( Ball *ball=m_balls.first(); ball!=0; ball=m_balls.next() ) + ball->update(); + + if ( m_field ) m_field->update(); + if ( m_wall1 ) m_wall1->update(); + if ( m_wall2 ) m_wall2->update(); + } + + //kapp->syncX(); +} + +#include "game.moc" diff --git a/kbounce/game.h b/kbounce/game.h new file mode 100644 index 00000000..eb5208cd --- /dev/null +++ b/kbounce/game.h @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef GAME_H_INCLUDED +#define GAME_H_INCLUDED + +#include +#include +#include + +#if HAVE_ARTS +#include + +using namespace Arts; +#endif + +class QTimer; +class JezzField; + +#define FIELD_WIDTH 32 +#define FIELD_HEIGHT 20 + +class Ball : public QCanvasSprite +{ + public: + Ball(QCanvasPixmapArray* array, QCanvas* canvas); + + void update(); + void advance(int stage); + bool collide( double dx=0, double dy=0 ); + + protected: + int m_animDelay; + int m_soundDelay; +}; + + +class Wall : public QObject +{ + Q_OBJECT +public: + enum Direction { Up, Down, Left, Right }; + + Wall( JezzField *field, int x, int y, Direction dir, int tile, + QObject *parent=0, const char *name=0 ); + + void finish(); + void fill( bool black ); + +signals: + void finished( Wall *wall, int tile ); + +public slots: + void advance(); + void update(); + +private: + bool isFree( int x, int y ); + + Direction m_dir; + JezzField *m_field; + int m_dx, m_dy; + int m_x, m_y; + int m_startX, m_startY; + int m_tile; + int m_delay; + bool m_active; +}; + + +class JezzField : public QCanvas +{ + Q_OBJECT +public: + JezzField( const QPixmap &tiles, const QPixmap &background, QObject* parent = 0, const char* name = 0 ); + + void setGameTile( int x, int y, bool black ); + void setBackground( const QPixmap &background ); + +signals: + void ballCollision( Ball *ball, int x, int y, int tile ); + +private: + friend class Ball; + bool m_background; + QPixmap m_tiles; + QMemArray m_backTiles; + + void setPixmaps( const QPixmap &tiles, const QPixmap &background ); + void emitBallCollisiton( Ball *ball, int x, int y, int tile ) + { emit ballCollision( ball, x, y, tile ); } + +}; + + +class JezzView : public QCanvasView +{ + Q_OBJECT +public: + JezzView(QCanvas* viewing=0, QWidget* parent=0, const char* name=0, WFlags f=0); + +signals: + void buildWall( int x, int y, bool vertical ); + +protected: + void viewportMouseReleaseEvent( QMouseEvent * ); + +private: + bool m_vertical; +}; + + +class JezzGame : public QWidget +{ + Q_OBJECT + +public: + JezzGame( const QPixmap &background, int ballNum, QWidget *parent=0, const char *name=0 ); + ~JezzGame(); + + int percent(); + static void playSound( const QString &name ); + void display( const QString &text, int size=20 ); + void setBackground( const QPixmap &background ); + +signals: + void died(); + void newPercent( int percent ); + +public slots: + void start(); + void stop(); + void setSound( bool sound ); + +protected slots: + void tick(); + void buildWall( int x, int y, bool vertical ); + void wallFinished( Wall *wall, int tile ); + void ballCollision( Ball *ball, int x, int y, int tile ); + +protected: + void makeBlack(); + void fill( int x, int y ); + void fillLeft( int x, int y ); + int m_buf[FIELD_WIDTH][FIELD_HEIGHT]; + + JezzField *m_field; + JezzView *m_view; + + Wall *m_wall1, *m_wall2; + + QPtrList m_balls; + QCanvasPixmapArray *m_ballPixmaps; + QCanvasText *m_text; + + QTimer *m_clock; + bool m_running; + int m_percent; + bool m_pictured; + +#if HAVE_ARTS + static SimpleSoundServer *m_artsServer; +#endif + static QString m_soundPath; + static bool m_sound; +}; + +#endif diff --git a/kbounce/highscores.cpp b/kbounce/highscores.cpp new file mode 100644 index 00000000..fec2d1e6 --- /dev/null +++ b/kbounce/highscores.cpp @@ -0,0 +1,18 @@ +#include "highscores.h" + +#include + +using namespace KExtHighscore; + +ExtManager::ExtManager() +{ + Item *item = new Item((uint)0, i18n("Level"), Qt::AlignRight); + addScoreItem("level", item); +} + +bool ExtManager::isStrictlyLess(const Score &s1, const Score &s2) const +{ + if ( s1.score()==s2.score() ) + return s1.data("level").toUInt()>s2.data("level").toUInt(); + return Manager::isStrictlyLess(s1, s2); +} diff --git a/kbounce/highscores.h b/kbounce/highscores.h new file mode 100644 index 00000000..59372ed6 --- /dev/null +++ b/kbounce/highscores.h @@ -0,0 +1,17 @@ +#ifndef HIGHSCORES_H +#define HIGHSCORES_H + +#include + +using namespace KExtHighscore; + +class ExtManager : public Manager +{ + public: + ExtManager(); + + private: + bool isStrictlyLess(const Score &s1, const Score &s2) const; +}; + +#endif diff --git a/kbounce/kbounce.cpp b/kbounce/kbounce.cpp new file mode 100644 index 00000000..eab691b8 --- /dev/null +++ b/kbounce/kbounce.cpp @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License,Life or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kbounce.h" +#include "game.h" + +KJezzball::KJezzball() + : m_gameWidget( 0 ) +{ + // setup variables + m_game.level = 1; + m_game.score = 0; + m_state = Idle; + + KConfig *config = kapp->config(); + m_backgroundDir = config->readPathEntry( "BackgroundDir" ); + m_showBackground = config->readBoolEntry( "ShowBackground", false ); + + statusBar(); + initXMLUI(); + + m_soundAction -> setChecked((config->readBoolEntry( "PlaySounds", true ))); + + // create widgets + m_view = new QWidget( this, "m_view" ); + setCentralWidget( m_view ); + + m_layout = new QGridLayout( m_view, 1, 3 ); + m_layout->setColStretch( 2, 1 ); + + QVBoxLayout *infoLayout = new QVBoxLayout; + m_layout->addLayout( infoLayout, 0, 1 ); + + QLabel *label = new QLabel( i18n("Level:"), m_view ); + infoLayout->addWidget( label ); + m_levelLCD = new QLCDNumber( 5, m_view ); + infoLayout->addWidget( m_levelLCD ); + + label = new QLabel( i18n("Score:"), m_view ); + infoLayout->addWidget( label ); + m_scoreLCD = new QLCDNumber( 5, m_view ); + infoLayout->addWidget( m_scoreLCD ); + + infoLayout->addSpacing( 20 ); + + label = new QLabel( i18n("Filled area:"), m_view ); + infoLayout->addWidget( label ); + m_percentLCD = new QLCDNumber( 5, m_view ); + infoLayout->addWidget( m_percentLCD ); + + label = new QLabel( i18n("Lives:"), m_view ); + infoLayout->addWidget( label ); + m_lifesLCD = new QLCDNumber( 5, m_view ); + infoLayout->addWidget( m_lifesLCD ); + + label = new QLabel( i18n("Time:"), m_view ); + infoLayout->addWidget( label ); + m_timeLCD = new QLCDNumber( 5, m_view ); + infoLayout->addWidget( m_timeLCD ); + + // create timers + m_nextLevelTimer = new QTimer( this, "m_nextLevelTimer" ); + connect( m_nextLevelTimer, SIGNAL(timeout()), this, SLOT(switchLevel()) ); + + m_gameOverTimer = new QTimer( this, "m_gameOverTimer" ); + connect( m_gameOverTimer, SIGNAL(timeout()), this, SLOT(gameOverNow()) ); + + m_timer = new QTimer( this, "m_timer" ); + connect( m_timer, SIGNAL(timeout()), this, SLOT(second()) ); + + // create demo game + createLevel( 1 ); + statusBar()->message( i18n("Press %1 to start a game!") + .arg(m_newAction->shortcut().toString()) ); + //m_gameWidget->display( i18n("Press to start a game!") ); + + setFocusPolicy(QWidget::StrongFocus); + setFocus(); + setupGUI(); +} + +KJezzball::~KJezzball() +{ + KConfig *config = kapp->config(); + config->writeEntry( "PlaySounds", m_soundAction->isChecked() ); +} + +/** + * create the action events create the gui. + */ +void KJezzball::initXMLUI() +{ + m_newAction = KStdGameAction::gameNew( this, SLOT(newGame()), actionCollection() ); + // AB: originally KBounce/KJezzball used Space for new game - but Ctrl+N is + // default. We solve this by providing space as an alternative key + KShortcut s = m_newAction->shortcut(); + s.append(KKeySequence(QKeySequence(Key_Space))); + m_newAction->setShortcut(s); + + KStdGameAction::quit(this, SLOT(close()), actionCollection() ); + KStdGameAction::highscores(this, SLOT(showHighscore()), actionCollection() ); + m_pauseButton = KStdGameAction::pause(this, SLOT(pauseGame()), actionCollection()); + KStdGameAction::end(this, SLOT(closeGame()), actionCollection()); + KStdGameAction::configureHighscores(this, SLOT(configureHighscores()),actionCollection()); + + new KAction( i18n("&Select Background Folder..."), 0, this, SLOT(selectBackground()), + actionCollection(), "background_select" ); + m_backgroundShowAction = + new KToggleAction( i18n("Show &Backgrounds"), 0, this, SLOT(showBackground()), + actionCollection(), "background_show" ); + m_backgroundShowAction->setCheckedState(i18n("Hide &Backgrounds")); + m_backgroundShowAction->setEnabled( !m_backgroundDir.isEmpty() ); + m_backgroundShowAction->setChecked( m_showBackground ); + + m_soundAction = new KToggleAction( i18n("&Play Sounds"), 0, 0, 0, actionCollection(), "toggle_sound"); +} + +void KJezzball::newGame() +{ + // Check for running game + closeGame(); + if ( m_state==Idle ) + { + // untoggles the pause button in case it was toggled + m_pauseButton->setChecked(false); + + // update displays + m_game.level = 1; + m_game.score = 0; + + m_levelLCD->display( m_game.level ); + m_scoreLCD->display( m_game.score ); + + statusBar()->clear(); + + // start new game + m_state = Running; + + createLevel( m_game.level ); + startLevel(); + } +} + +void KJezzball::closeGame() +{ + if ( m_state!=Idle ) + { + int old_state = m_state; + if (old_state == Running) + pauseGame(); + int ret = KMessageBox::questionYesNo( this, i18n("Do you really want to close the running game?"), QString::null, KStdGuiItem::close(), KStdGuiItem::cancel() ); + if ( ret==KMessageBox::Yes ) + { + stopLevel(); + m_state = Idle; + highscore(); + } + else if (old_state == Running) + { + pauseGame(); // Unpause + } + } +} + +void KJezzball::pauseGame() +{ + switch ( m_state ) + { + case Running: + m_state = Paused; + statusBar()->message(i18n("Game paused.") ); + //m_gameWidget->display( i18n("Game paused. Press P to continue!") ); + stopLevel(); + break; + + case Paused: + case Suspend: + m_state = Running; + statusBar()->clear(); + //m_gameWidget->display( QString::null ); + startLevel(); + break; + + case Idle: + break; + } +} + +void KJezzball::gameOver() +{ + stopLevel(); + m_gameOverTimer->start( 100, TRUE ); +} + + +void KJezzball::gameOverNow() +{ + m_state = Idle; + + QString score; + score.setNum( m_game.score ); + KMessageBox::information( this, i18n("Game Over! Score: %1").arg(score) ); + statusBar()->message( i18n("Game over. Press for a new game") ); + //m_gameWidget->display( i18n("Game over. Press for a new game!") ); + highscore(); +} + +/** + * Bring up the standard kde high score configure dialog. + */ +void KJezzball::configureHighscores() +{ + KExtHighscore::configure(this); +} + +/** + * Bring up the standard kde high score dialog. + */ +void KJezzball::showHighscore() +{ + KExtHighscore::show(this); +} + +/** + * Select a background image. + */ +void KJezzball::selectBackground() +{ + QString path = KFileDialog::getExistingDirectory( m_backgroundDir, this, + i18n("Select Background Image Folder") ); + if ( !path.isEmpty() && path!=m_backgroundDir ) { + m_backgroundDir = path; + + // enable action + m_backgroundShowAction->setEnabled(true); + + // save settings + KConfig *config = kapp->config(); + config->writePathEntry( "BackgroundDir", m_backgroundDir ); + config->sync(); + + // apply background setting + if ( m_showBackground ) { + if ( m_background.width()==0 ) + m_background = getBackgroundPixmap(); + + m_gameWidget->setBackground( m_background ); + } + else{ + KMessageBox::information( this, i18n("You may now turn on background images.")); + } + } +} + +void KJezzball::showBackground() +{ + bool show = m_backgroundShowAction->isChecked(); + if ( show!=m_showBackground ) { + + m_showBackground = show; + + // save setting + KConfig *config = kapp->config(); + config->writeEntry( "ShowBackground", m_showBackground ); + config->sync(); + + // update field + if ( m_showBackground ) { + if ( m_background.width()==0 ) + m_background = getBackgroundPixmap(); + } + + m_gameWidget->setBackground( m_showBackground ? m_background : QPixmap() ); + } +} + +QPixmap KJezzball::getBackgroundPixmap() +{ + // list directory + QDir dir( m_backgroundDir, "*.png *.jpg", QDir::Name|QDir::IgnoreCase, QDir::Files ); + if ( !dir.exists() ) { + kdDebug(12008) << "Directory not found" << endl; + return QPixmap(); + } + + if (dir.count() > 1) + { + // return random pixmap + int num = kapp->random() % dir.count(); + return QPixmap( dir.absFilePath( dir[num] ) ); + } + else if (dir.count()==1) + { + return QPixmap( dir.absFilePath(dir[0]) ); + } + else return QPixmap(); +} + +void KJezzball::focusOutEvent( QFocusEvent *ev ) +{ + if ( m_state==Running ) + { + stopLevel(); + m_state = Suspend; + m_pauseButton->setChecked(true); + statusBar()->message( i18n("Game suspended") ); + // m_gameWidget->display( i18n("Game suspended") ); + } + + KMainWindow::focusOutEvent( ev ); +} + +void KJezzball::focusInEvent ( QFocusEvent *ev ) +{ + if ( m_state==Suspend ) + { + startLevel(); + m_state = Running; + statusBar()->clear(); + m_pauseButton->setChecked(false); + //m_gameWidget->display( QString::null ); + } + + KMainWindow::focusInEvent( ev ); +} + +void KJezzball::second() +{ + m_level.time--; + m_timeLCD->display( m_level.time ); + if ( m_level.time<=0 ) + { + JezzGame::playSound( "timeout.au" ); + gameOver(); + } else + if ( m_level.time<=30 ) + JezzGame::playSound( "seconds.au" ); +} + +void KJezzball::died() +{ + kdDebug(12008) << "died" << endl; + m_level.lifes--; + m_lifesLCD->display( m_level.lifes ); + if ( m_level.lifes==0 ) gameOver(); +} + +void KJezzball::newPercent( int percent ) +{ + m_percentLCD->display( percent ); + if ( percent>=75 ) + { + m_level.score = m_level.lifes*15 + (percent-75)*2*(m_game.level+5); + nextLevel(); + } +} + +void KJezzball::createLevel( int level ) +{ + // destroy old game + if ( m_gameWidget ) delete m_gameWidget; + + // create new game widget + if ( m_showBackground ) + m_background = getBackgroundPixmap(); + else + m_background = QPixmap(); + + m_gameWidget = new JezzGame( m_background, level+1, m_view, "m_gameWidget" ); + m_gameWidget->setSound(m_soundAction->isChecked()); + + m_gameWidget->show(); + m_layout->addWidget( m_gameWidget, 0, 0 ); + connect( m_gameWidget, SIGNAL(died()), this, SLOT(died()) ); + connect( m_gameWidget, SIGNAL(newPercent(int)), this, SLOT(newPercent(int)) ); + connect( m_soundAction, SIGNAL(toggled(bool)), m_gameWidget, SLOT(setSound(bool)) ); + + // update displays + m_level.lifes = level+1; + m_lifesLCD->display( m_level.lifes ); + m_percentLCD->display( 0 ); + + m_level.time = (level+2)*30; + m_timeLCD->display( m_level.time ); + + m_level.score = 0; +} + +void KJezzball::startLevel() +{ + if ( m_gameWidget ) + { + m_timer->start( 1000 ); + m_gameWidget->start(); + } +} + +void KJezzball::stopLevel() +{ + if ( m_gameWidget ) + { + m_gameWidget->stop(); + m_timer->stop(); + } +} + +void KJezzball::nextLevel() +{ + stopLevel(); + m_nextLevelTimer->start( 100, TRUE ); +} + +void KJezzball::switchLevel() +{ + m_game.score += m_level.score; + + // make sure the LCD provides enough digits for the score + // (fixes #96841) + int numDigits=0; + int temp_score = m_game.score; + for ( ; temp_score > 0; ++numDigits ) temp_score /= 10; + if ( numDigits < 5 ) numDigits = 5; // set numDigits to at least 5, otherwise it does not look well + + m_scoreLCD->setNumDigits( numDigits ); + m_scoreLCD->display( m_game.score ); + + QString score; + score.setNum( m_level.score ); + + QString level; + level.setNum( m_game.level ); + +QString foo = QString( +i18n("You have successfully cleared more than 75% of the board.\n") + +i18n("%1 points: 15 points per remaining life\n").arg(m_level.lifes*15) + +i18n("%1 points: Bonus\n").arg((m_gameWidget->percent()-75)*2*(m_game.level+5)) + +i18n("%1 points: Total score for this level\n").arg(score) + +i18n("On to level %1. Remember you get %2 lives this time!")).arg(m_game.level+1).arg(m_game.level+2); + + KMessageBox::information( this,foo ); + + + // KMessageBox::information( this, i18n("You've completed level %1 with " + // "a score of %2.\nGet ready for the next one!").arg(level).arg(score)); + + m_game.level++; + m_levelLCD->display( m_game.level ); + + createLevel( m_game.level ); + startLevel(); +} + + +void KJezzball::highscore() +{ + KExtHighscore::Score score(KExtHighscore::Won); + score.setScore(m_game.score); + score.setData("level", m_game.level); + KExtHighscore::submitScore(score, this); +} + +#include "kbounce.moc" diff --git a/kbounce/kbounce.desktop b/kbounce/kbounce.desktop new file mode 100644 index 00000000..0f629644 --- /dev/null +++ b/kbounce/kbounce.desktop @@ -0,0 +1,67 @@ +[Desktop Entry] +Name=KBounce +Name[af]=K-hop +Name[ar]=لعبة الإرتداد (KBounce) +Name[be]=ÐеÑÑƒÑ†Ñ–ÑˆÐ½Ñ‹Ñ ÑˆÐ°Ñ€Ñ‹ÐºÑ– +Name[bn]=কে-বাউনà§à¦¸ +Name[hi]=के-बाउंस +Name[ne]=केडीई उफà¥à¤°à¤¿à¤¨à¥‡ +Name[pa]=ਕੇ-ਬਾਊਂਸ +Name[sv]=Kbounce +Name[ta]=கேபவà¯à®©à¯à®¸à¯ +Name[tg]=KТӯби ҷаҳанда +Name[tr]=Zıplayan toplar +Name[zh_TW]=KBounce å½ˆåŠ›çƒ + +Type=Application +Exec=kbounce %i %m -caption "%c" +DocPath=kbounce/index.html +Icon=kbounce + +GenericName=Bounce Ball Game +GenericName[be]=Ð“ÑƒÐ»ÑŒÐ½Ñ Ñž шарыкі +GenericName[bg]=ПодÑкачащи топки +GenericName[bn]=বাউনà§à¦¸ বল খেলা +GenericName[bs]=Igra odbijajuće loptice +GenericName[ca]=Joc de boles rebotadores +GenericName[cs]=Hra s poskakujícími míÄky +GenericName[cy]=Gêm Bêl Sboncio +GenericName[da]=Spil med springende bolde +GenericName[de]=Ballspiel +GenericName[el]=Παιχνίδι αναπηδοÏμενης μπάλας +GenericName[eo]=Saltpilka ludo +GenericName[es]=Juego de pelotas que rebotan +GenericName[et]=Põrkava palli mäng +GenericName[eu]=Pilota errebotatzeko jokoa +GenericName[fa]=بازی Bounce Ball +GenericName[fi]=Lautapeli +GenericName[fr]=Jeu de balles rebondissantes +GenericName[he]=משחק ×›×“×•×¨×™× ×§×•×¤×¦×™× +GenericName[hr]=Igra s poskakujućom loptom +GenericName[hu]=Labdás +GenericName[is]=Skoppandi boltaleikur +GenericName[it]=Gioco del BounceBall +GenericName[ja]=è·³ã­ã‚‹ãƒœãƒ¼ãƒ«ã®ã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​បាល់​លោហ+GenericName[lt]=Bounce Ball žaidimas +GenericName[lv]=LÄ“kÄjoÅ¡o bumbu spÄ“le +GenericName[mk]=Игра Ñо топки што Ñкокаат +GenericName[nb]=Sprettballspill +GenericName[nds]=Ballspeel +GenericName[ne]=बल हिरà¥à¤•à¤¾à¤‰à¤¨à¥‡ खेल +GenericName[nl]=Bal-stuiter spel +GenericName[nn]=Sprettballspel +GenericName[pl]=PiÅ‚ki +GenericName[pt]=Jogo de Bola Saltitante +GenericName[pt_BR]=Jogo de bolas que pulam +GenericName[ru]=Ðеуёмные шарики +GenericName[sk]=Bounce loptová hra +GenericName[sl]=Igra odbijajoÄe žoge +GenericName[sr]=Игра одбијања лопте +GenericName[sr@Latn]=Igra odbijanja lopte +GenericName[sv]=Studsande boll spel +GenericName[ta]=திரà¯à®®à¯à®ªà¯à®®à¯ பநà¯à®¤à¯ விளையாடà¯à®Ÿà¯ +GenericName[uk]=Кульки-Ñтрибунці +GenericName[zh_TW]=彈跳çƒéŠæˆ² +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;ArcadeGame; diff --git a/kbounce/kbounce.h b/kbounce/kbounce.h new file mode 100644 index 00000000..a4e0a95f --- /dev/null +++ b/kbounce/kbounce.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KBOUNCE_H +#define KBOUNCE_H + +#include + +class JezzGame; +class QLCDNumber; +class QGridLayout; +class KToggleAction; +class KAction; + +class KJezzball : public KMainWindow +{ + Q_OBJECT + +public: + KJezzball(); + ~KJezzball(); + +public slots: + void newGame(); + void pauseGame(); + void closeGame(); + void showHighscore(); + void selectBackground(); + void showBackground(); + +protected slots: + void died(); + void newPercent( int percent ); + void second(); + void switchLevel(); + void gameOverNow(); + void configureHighscores(); + +protected: + void createLevel( int level ); + void startLevel(); + void stopLevel(); + void nextLevel(); + void highscore(); + void gameOver(); + void initXMLUI(); + + void focusOutEvent( QFocusEvent * ); + void focusInEvent ( QFocusEvent * ); + + QPixmap getBackgroundPixmap(); + + JezzGame *m_gameWidget; + QWidget *m_view; + QGridLayout *m_layout; + QLCDNumber *m_levelLCD; + QLCDNumber *m_lifesLCD; + QLCDNumber *m_scoreLCD; + QLCDNumber *m_percentLCD; + QLCDNumber *m_timeLCD; + KToggleAction *m_pauseButton, *m_backgroundShowAction, *m_soundAction; + KAction *m_newAction; + + QTimer *m_timer; + QTimer *m_nextLevelTimer; + QTimer *m_gameOverTimer; + + QString m_backgroundDir; + bool m_showBackground; + QPixmap m_background; + + enum { Idle, Running, Paused, Suspend } m_state; + + struct + { + int lifes; + int time; + int score; + } m_level; + + struct + { + int level; + int score; + } m_game; +}; + +#endif // KBOUNCE_h + diff --git a/kbounce/kbounceui.rc b/kbounce/kbounceui.rc new file mode 100644 index 00000000..058ab702 --- /dev/null +++ b/kbounce/kbounceui.rc @@ -0,0 +1,17 @@ + + + + + + + + + + + +Main Toolbar + + + + + diff --git a/kbounce/main.cpp b/kbounce/main.cpp new file mode 100644 index 00000000..390b7c62 --- /dev/null +++ b/kbounce/main.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2000 Stefan Schimanski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#if HAVE_ARTS +#include +#endif + +#include "kbounce.h" + +#include +#include + +using namespace std; + +#if HAVE_ARTS +using namespace Arts; +#endif + +static const char description[] = I18N_NOOP("KDE Bounce Ball Game"); +static const char version[] = "0.5"; + +int main(int argc, char **argv) +{ + KHighscore::init("kbounce"); + + KAboutData aboutData( "kbounce", I18N_NOOP("KBounce"), + version, description, KAboutData::License_GPL, + "(c) 2000, Stefan Schimanski"); + + aboutData.addAuthor("Stefan Schimanski", I18N_NOOP("Original author"), "schimmi@kde.org"); + aboutData.addAuthor("Sandro Sigala", I18N_NOOP("Highscore"), "ssigala@globalnet.it"); + aboutData.addAuthor("Benjamin Meyer", I18N_NOOP("Contributions"), "ben+kbounce@meyerhome.net"); + + KCmdLineArgs::init( argc, argv, &aboutData ); + + QApplication::setColorSpec(QApplication::ManyColor); + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + + ExtManager manager; + + // setup MCOP +#if HAVE_ARTS + Dispatcher dispatcher; +#endif + + if (a.isRestored()) + RESTORE(KJezzball) + else { + KJezzball *w = new KJezzball; + a.setMainWidget(w); + w->show(); + } + return a.exec(); +} + diff --git a/kbounce/pics/Makefile.am b/kbounce/pics/Makefile.am new file mode 100644 index 00000000..e7ad910e --- /dev/null +++ b/kbounce/pics/Makefile.am @@ -0,0 +1,34 @@ +pics_DATA = \ + ball0000.png \ + ball0001.png \ + ball0002.png \ + ball0003.png \ + ball0004.png \ + ball0005.png \ + ball0006.png \ + ball0007.png \ + ball0008.png \ + ball0009.png \ + ball0010.png \ + ball0011.png \ + ball0012.png \ + ball0013.png \ + ball0014.png \ + ball0015.png \ + ball0016.png \ + ball0017.png \ + ball0018.png \ + ball0019.png \ + ball0020.png \ + ball0021.png \ + ball0022.png \ + ball0023.png \ + ball0024.png \ + tiles.png + +picsdir = $(kde_datadir)/kbounce/pics + +KDE_ICON = kbounce + +EXTRA_DIST = $(pics_DATA) + diff --git a/kbounce/pics/ball0000.png b/kbounce/pics/ball0000.png new file mode 100644 index 00000000..3afe8f0e Binary files /dev/null and b/kbounce/pics/ball0000.png differ diff --git a/kbounce/pics/ball0001.png b/kbounce/pics/ball0001.png new file mode 100644 index 00000000..845c833b Binary files /dev/null and b/kbounce/pics/ball0001.png differ diff --git a/kbounce/pics/ball0002.png b/kbounce/pics/ball0002.png new file mode 100644 index 00000000..e561860c Binary files /dev/null and b/kbounce/pics/ball0002.png differ diff --git a/kbounce/pics/ball0003.png b/kbounce/pics/ball0003.png new file mode 100644 index 00000000..ee1bfad0 Binary files /dev/null and b/kbounce/pics/ball0003.png differ diff --git a/kbounce/pics/ball0004.png b/kbounce/pics/ball0004.png new file mode 100644 index 00000000..cc39da5b Binary files /dev/null and b/kbounce/pics/ball0004.png differ diff --git a/kbounce/pics/ball0005.png b/kbounce/pics/ball0005.png new file mode 100644 index 00000000..a316f11a Binary files /dev/null and b/kbounce/pics/ball0005.png differ diff --git a/kbounce/pics/ball0006.png b/kbounce/pics/ball0006.png new file mode 100644 index 00000000..c3f024ae Binary files /dev/null and b/kbounce/pics/ball0006.png differ diff --git a/kbounce/pics/ball0007.png b/kbounce/pics/ball0007.png new file mode 100644 index 00000000..c2d4f803 Binary files /dev/null and b/kbounce/pics/ball0007.png differ diff --git a/kbounce/pics/ball0008.png b/kbounce/pics/ball0008.png new file mode 100644 index 00000000..c0b9a075 Binary files /dev/null and b/kbounce/pics/ball0008.png differ diff --git a/kbounce/pics/ball0009.png b/kbounce/pics/ball0009.png new file mode 100644 index 00000000..aedbd608 Binary files /dev/null and b/kbounce/pics/ball0009.png differ diff --git a/kbounce/pics/ball0010.png b/kbounce/pics/ball0010.png new file mode 100644 index 00000000..133527df Binary files /dev/null and b/kbounce/pics/ball0010.png differ diff --git a/kbounce/pics/ball0011.png b/kbounce/pics/ball0011.png new file mode 100644 index 00000000..4db6a483 Binary files /dev/null and b/kbounce/pics/ball0011.png differ diff --git a/kbounce/pics/ball0012.png b/kbounce/pics/ball0012.png new file mode 100644 index 00000000..8b2f758f Binary files /dev/null and b/kbounce/pics/ball0012.png differ diff --git a/kbounce/pics/ball0013.png b/kbounce/pics/ball0013.png new file mode 100644 index 00000000..6d551dc0 Binary files /dev/null and b/kbounce/pics/ball0013.png differ diff --git a/kbounce/pics/ball0014.png b/kbounce/pics/ball0014.png new file mode 100644 index 00000000..851cd6cb Binary files /dev/null and b/kbounce/pics/ball0014.png differ diff --git a/kbounce/pics/ball0015.png b/kbounce/pics/ball0015.png new file mode 100644 index 00000000..f706d722 Binary files /dev/null and b/kbounce/pics/ball0015.png differ diff --git a/kbounce/pics/ball0016.png b/kbounce/pics/ball0016.png new file mode 100644 index 00000000..cd762eb9 Binary files /dev/null and b/kbounce/pics/ball0016.png differ diff --git a/kbounce/pics/ball0017.png b/kbounce/pics/ball0017.png new file mode 100644 index 00000000..fba03084 Binary files /dev/null and b/kbounce/pics/ball0017.png differ diff --git a/kbounce/pics/ball0018.png b/kbounce/pics/ball0018.png new file mode 100644 index 00000000..e500118e Binary files /dev/null and b/kbounce/pics/ball0018.png differ diff --git a/kbounce/pics/ball0019.png b/kbounce/pics/ball0019.png new file mode 100644 index 00000000..46952614 Binary files /dev/null and b/kbounce/pics/ball0019.png differ diff --git a/kbounce/pics/ball0020.png b/kbounce/pics/ball0020.png new file mode 100644 index 00000000..70d1d658 Binary files /dev/null and b/kbounce/pics/ball0020.png differ diff --git a/kbounce/pics/ball0021.png b/kbounce/pics/ball0021.png new file mode 100644 index 00000000..f219ffcf Binary files /dev/null and b/kbounce/pics/ball0021.png differ diff --git a/kbounce/pics/ball0022.png b/kbounce/pics/ball0022.png new file mode 100644 index 00000000..bb4bb0a8 Binary files /dev/null and b/kbounce/pics/ball0022.png differ diff --git a/kbounce/pics/ball0023.png b/kbounce/pics/ball0023.png new file mode 100644 index 00000000..80f8d792 Binary files /dev/null and b/kbounce/pics/ball0023.png differ diff --git a/kbounce/pics/ball0024.png b/kbounce/pics/ball0024.png new file mode 100644 index 00000000..7a6db2bd Binary files /dev/null and b/kbounce/pics/ball0024.png differ diff --git a/kbounce/pics/hi128-app-kbounce.png b/kbounce/pics/hi128-app-kbounce.png new file mode 100644 index 00000000..d9c49bbe Binary files /dev/null and b/kbounce/pics/hi128-app-kbounce.png differ diff --git a/kbounce/pics/hi16-app-kbounce.png b/kbounce/pics/hi16-app-kbounce.png new file mode 100644 index 00000000..916fe18d Binary files /dev/null and b/kbounce/pics/hi16-app-kbounce.png differ diff --git a/kbounce/pics/hi22-app-kbounce.png b/kbounce/pics/hi22-app-kbounce.png new file mode 100644 index 00000000..51b7b1c8 Binary files /dev/null and b/kbounce/pics/hi22-app-kbounce.png differ diff --git a/kbounce/pics/hi32-app-kbounce.png b/kbounce/pics/hi32-app-kbounce.png new file mode 100644 index 00000000..652824f7 Binary files /dev/null and b/kbounce/pics/hi32-app-kbounce.png differ diff --git a/kbounce/pics/hi48-app-kbounce.png b/kbounce/pics/hi48-app-kbounce.png new file mode 100644 index 00000000..4b91913c Binary files /dev/null and b/kbounce/pics/hi48-app-kbounce.png differ diff --git a/kbounce/pics/hi64-app-kbounce.png b/kbounce/pics/hi64-app-kbounce.png new file mode 100644 index 00000000..98ec084a Binary files /dev/null and b/kbounce/pics/hi64-app-kbounce.png differ diff --git a/kbounce/pics/tiles.png b/kbounce/pics/tiles.png new file mode 100644 index 00000000..13461e3d Binary files /dev/null and b/kbounce/pics/tiles.png differ diff --git a/kbounce/sounds/Makefile.am b/kbounce/sounds/Makefile.am new file mode 100644 index 00000000..6b779bda --- /dev/null +++ b/kbounce/sounds/Makefile.am @@ -0,0 +1,7 @@ +AUTOMAKE_OPTIONS = foreign + +sound_DATA = death.au reflect.au wallstart.au wallend.au seconds.au timeout.au +sounddir = $(kde_datadir)/kbounce/sounds + +EXTRA_DIST = $(sound_DATA) + diff --git a/kbounce/sounds/death.au b/kbounce/sounds/death.au new file mode 100644 index 00000000..7d628632 Binary files /dev/null and b/kbounce/sounds/death.au differ diff --git a/kbounce/sounds/reflect.au b/kbounce/sounds/reflect.au new file mode 100644 index 00000000..5975c174 Binary files /dev/null and b/kbounce/sounds/reflect.au differ diff --git a/kbounce/sounds/seconds.au b/kbounce/sounds/seconds.au new file mode 100644 index 00000000..c6d4f6c1 Binary files /dev/null and b/kbounce/sounds/seconds.au differ diff --git a/kbounce/sounds/timeout.au b/kbounce/sounds/timeout.au new file mode 100644 index 00000000..515fb728 Binary files /dev/null and b/kbounce/sounds/timeout.au differ diff --git a/kbounce/sounds/wallend.au b/kbounce/sounds/wallend.au new file mode 100644 index 00000000..5093e916 Binary files /dev/null and b/kbounce/sounds/wallend.au differ diff --git a/kbounce/sounds/wallstart.au b/kbounce/sounds/wallstart.au new file mode 100644 index 00000000..d4377742 Binary files /dev/null and b/kbounce/sounds/wallstart.au differ diff --git a/kdegames.lsm b/kdegames.lsm new file mode 100644 index 00000000..2d8c9647 --- /dev/null +++ b/kdegames.lsm @@ -0,0 +1,11 @@ +Begin4 +Title: kdegames +Version: 3.5.10 +Entered-date: 2008-08-26 +Description: Games written for the K Desktop Environment (KDE) +Keywords: KDE X11 desktop Qt +Author: http://bugs.kde.org/ (KDE Bugtracking System) +Primary-site: http://www.kde.org/download/ +Platforms: Unix, Qt +Copying-policy: GPL, Artistic +End diff --git a/kenolaba/AbTop.cpp b/kenolaba/AbTop.cpp new file mode 100644 index 00000000..a2283f16 --- /dev/null +++ b/kenolaba/AbTop.cpp @@ -0,0 +1,988 @@ +/* Class AbTop */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AbTop.h" +#include "Board.h" +#include "BoardWidget.h" +#include "EvalDlgImpl.h" +#include "EvalScheme.h" +#include "Network.h" +#include "Spy.h" +#include "version.h" + +#include +#include + +// #define MYTRACE 1 + +const AbTop::Data AbTop::LEVEL[Nb_Levels] = { + { "Easy", I18N_NOOP("&Easy") }, + { "Normal", I18N_NOOP("&Normal") }, + { "Hard", I18N_NOOP("&Hard") }, + { "Challange", I18N_NOOP("&Challenge") } +}; + +const AbTop::Data AbTop::IPLAY[Nb_IPlays] = { + { "Red", I18N_NOOP("&Red") }, + { "Yellow", I18N_NOOP("&Yellow") }, + { "Both", I18N_NOOP("&Both") }, + { "None", I18N_NOOP("&None") } +}; + +AbTop::AbTop() + :KMainWindow(0) +{ + timerState = noGame; + + myPort = Network::defaultPort; + currentEvalScheme = 0; + net = 0; + + actValue = 0; + stop = false; + editMode = false; + spyLevel = 0; + pastePossible = true; + + + timer = new QTimer; + connect( timer, SIGNAL(timeout()), this, SLOT(timerDone()) ); + + board = new Board(); + setMoveNo(0); + + connect( board, SIGNAL(searchBreak()), this, SLOT(searchBreak()) ); + Q_CHECK_PTR(board); + boardWidget = new BoardWidget(*board,this); + +#ifdef SPION + spy = new Spy(*board); +#endif + + connect( boardWidget, SIGNAL(updateSpy(QString)), + this, SLOT(updateSpy(QString)) ); + + setCentralWidget(boardWidget); + boardWidget->show(); + + // this creates the GUI + setupActions(); + setupStatusBar(); + setMinimumSize(200,300); + + // RMB context menu + connect( boardWidget, SIGNAL(rightButtonPressed(int,const QPoint&)), + this, SLOT(rightButtonPressed(int,const QPoint&)) ); + + connect( boardWidget, SIGNAL(edited(int)), + this, SLOT(edited(int)) ); + + connect( board, SIGNAL(updateBestMove(Move&,int)), + this, SLOT(updateBestMove(Move&,int)) ); + + connect( boardWidget, SIGNAL(moveChoosen(Move&)), + this, SLOT(moveChoosen(Move&)) ); + + /* default */ + setLevel(Easy); + setIPlay(Red); + showMoveLong = true; + showSpy = false; + renderBalls = true; + + updateStatus(); + updateActions(); + setupGUI(); +} + +AbTop::~AbTop() +{ + /* Unregister from other abalone processes */ + delete net; + delete timer; +#ifdef SPION + delete spy; +#endif +} + + +/** + * Create all the actions... + * + * The GUI will be built in createGUI using a XML file + * + */ + +void AbTop::setupActions() +{ + newAction = KStdGameAction::gameNew( this, SLOT(newGame()), actionCollection() ); + KStdGameAction::quit( this, SLOT(close()), actionCollection() ); + + stopAction = new KAction( i18n("&Stop Search"), "stop", Key_S, this, + SLOT(stopSearch()), actionCollection(), "move_stop"); + + backAction = new KAction( i18n("Take &Back"), "back", + KStdAccel::shortcut(KStdAccel::Prior), this, + SLOT(back()), actionCollection(), "move_back"); + + forwardAction = new KAction( i18n("&Forward"), "forward", + KStdAccel::shortcut(KStdAccel::Next), this, + SLOT(forward()), actionCollection(), "move_forward"); + + hintAction = KStdGameAction::hint(this, SLOT(suggestion()), actionCollection()); + + KStdAction::copy( this, SLOT(copy()), actionCollection()); + pasteAction = KStdAction::paste( this, SLOT(paste()), actionCollection()); + + (void) new KAction( i18n("&Restore Position"), + KStdAccel::shortcut(KStdAccel::Open), + this, SLOT(restorePosition()), + actionCollection(), "edit_restore" ); + + (void) new KAction( i18n("&Save Position"), + KStdAccel::shortcut(KStdAccel::Save), + this, SLOT(savePosition()), + actionCollection(), "edit_save" ); + + KToggleAction *ta; + + ta = new KToggleAction( i18n("&Network Play"), "network", Key_N, + actionCollection(), "game_net"); + connect(ta, SIGNAL(toggled(bool)), this, SLOT(gameNetwork(bool))); + + editAction = new KToggleAction( i18n("&Modify"), "edit", + CTRL+Key_Insert, actionCollection(), "edit_modify"); + connect(editAction, SIGNAL(toggled(bool)), this, SLOT( editModify(bool))); + + showMenubar = KStdAction::showMenubar(this, SLOT(toggleMenubar()), actionCollection()); + KStdAction::saveOptions( this, SLOT(writeConfig()), actionCollection()); + + KStdAction::preferences( this, SLOT(configure()), actionCollection()); + + moveSlowAction = new KToggleAction( i18n("&Move Slow"), 0, + actionCollection(), "options_moveSlow"); + connect(moveSlowAction, SIGNAL(toggled(bool)), this, SLOT(optionMoveSlow(bool))); + + renderBallsAction = new KToggleAction( i18n("&Render Balls"), 0, + actionCollection(), "options_renderBalls"); + connect(renderBallsAction, SIGNAL(toggled(bool)), this, SLOT(optionRenderBalls(bool))); + + showSpyAction = new KToggleAction( i18n("&Spy"), 0, + actionCollection(), "options_showSpy"); + connect(showSpyAction, SIGNAL(toggled(bool)), this, SLOT(optionShowSpy(bool))); + + + levelAction = KStdGameAction::chooseGameType(0, 0, actionCollection()); + QStringList list; + for (uint i=0; isetItems(list); + connect(levelAction, SIGNAL(activated(int)), SLOT(setLevel(int))); + + iplayAction = new KSelectAction(i18n("&Computer Play"), 0, actionCollection(), "options_iplay"); + list.clear(); + for (uint i=0; isetItems(list); + connect(iplayAction, SIGNAL(activated(int)), SLOT(setIPlay(int))); +} + +void AbTop::toggleMenubar() +{ + if (menuBar()->isVisible()) + menuBar()->hide(); + else + menuBar()->show(); +} + +void AbTop::configure() +{ + KDialogBase *dlg = new KDialogBase( 0, "ConfigureEvaluation", true, + i18n("Configure Evaluation"), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok, true); + + EvalDlgImpl *edlg = new EvalDlgImpl(dlg,board); + dlg->setMainWidget(edlg); + if (dlg->exec()) { + *currentEvalScheme = *(edlg->evalScheme()); + board->setEvalScheme(currentEvalScheme); + } + delete edlg; +} + +/* Right Mouse button pressed in BoardWidget area */ +void AbTop::rightButtonPressed(int /* field */, const QPoint& pos) +{ + QPopupMenu* rmbMenu = static_cast (factory()->container("rmbPopup",this)); + if (rmbMenu) + rmbMenu->popup( pos ); +} + +/* Read config options + * + * menu must already be created! + */ +void AbTop::readConfig() +{ + kdDebug(12011) << "Reading config..." << endl; + + KConfig* config = kapp->config(); + config->setGroup("Options"); + + readOptions(config); + + applyMainWindowSettings( config, "Appearance" ); + + showMenubar->setChecked( !menuBar()->isHidden() ); + + currentEvalScheme = new EvalScheme("Current"); + currentEvalScheme->read(config); + board->setEvalScheme( currentEvalScheme ); +} + +void AbTop::readOptions(KConfig* config) +{ + QString entry = config->readEntry("Level"); + for (uint i=0; ireadEntry("Computer"); + for (uint i=0; ireadBoolEntry("MoveSlow", false); + moveSlowAction->setChecked( showMoveLong ); + + renderBalls = config->readBoolEntry("RenderBalls", true); + boardWidget->renderBalls(renderBalls); + renderBallsAction->setChecked( renderBalls ); + + showSpy = config->readBoolEntry("ShowSpy", true); + board->updateSpy(showSpy); + showSpyAction->setChecked( showSpy ); +} + +void AbTop::readProperties(KConfig *config) +{ + QString entry; + + readOptions(config); + + currentEvalScheme = new EvalScheme("Current"); + currentEvalScheme->read(config); + board->setEvalScheme( currentEvalScheme ); + + + if (!(entry = config->readEntry("TimerState")).isNull()) + timerState = entry.toInt(); + if (timerState == noGame) return; + + stop = config->readBoolEntry("GameStopped", false); + + int mNo = 0; + if (!(entry = config->readEntry("Position")).isNull()) { + mNo = board->setState(entry); + boardWidget->updatePosition(true); + } + setMoveNo(mNo, true); + + show(); + playGame(); +} + +void AbTop::writeConfig() +{ + kdDebug(12011) << "Writing config..." << endl; + + KConfig* config = kapp->config(); + config->setGroup("Options"); + + writeOptions(config); + + saveMainWindowSettings( config, "Appearance" ); + + if (currentEvalScheme) + currentEvalScheme->save(config); + config->sync(); +} + + +void AbTop::writeOptions(KConfig *config) +{ + config->writeEntry("Level", LEVEL[levelAction->currentItem()].key); + config->writeEntry("Computer", IPLAY[iplayAction->currentItem()].key); + + config->writeEntry("MoveSlow", showMoveLong); + config->writeEntry("RenderBalls", renderBalls); + config->writeEntry("ShowSpy", showSpy); +} + +void AbTop::saveProperties(KConfig *config) +{ + writeOptions(config); + if (currentEvalScheme) + currentEvalScheme->save(config); + + config->writeEntry("TimerState", timerState); + + if (timerState == noGame) return; + + config->writeEntry("GameStopped", stop); + config->writeEntry("Position", board->getState(moveNo)); + config->sync(); +} + +void AbTop::savePosition() +{ + KConfig* config = kapp->config(); + config->setGroup("SavedPosition"); + config->writeEntry("Position", board->getState(moveNo)); +} + +void AbTop::restorePosition() +{ + KConfig* config = kapp->config(); + config->setGroup("SavedPosition"); + QString entry = config->readEntry("Position"); + + timerState = notStarted; + timer->stop(); + board->begin(Board::color1); + stop = false; + setMoveNo( board->setState(entry), true ); + + if (net) + net->broadcast( board->getASCIIState( moveNo ).ascii() ); + + boardWidget->updatePosition(true); + + playGame(); +} + +void AbTop::setupStatusBar() +{ + QString tmp; + + QString t = i18n("Press %1 for a new game").arg( newAction->shortcut().toString()); + statusLabel = new QLabel( t, statusBar(), "statusLabel" ); + statusBar()->addWidget(statusLabel,1,false); + + // PERMANENT: Moving side + move No. + + // validPixmap, only visible in Modify mode: is position valid ? + warningPix = BarIcon( "warning" ); + okPix = BarIcon( "ok" ); + validLabel = new QLabel( "", statusBar(), "validLabel" ); + validLabel->setFixedSize( 18, statusLabel->sizeHint().height() ); + validLabel->setAlignment( AlignCenter ); + validLabel->hide(); + validShown = false; + + redBall = BarIcon( "redball" ); + yellowBall = BarIcon( "yellowball" ); + noBall = BarIcon( "noball" ); + ballLabel = new QLabel( "", statusBar(), "ballLabel" ); + ballLabel->setPixmap(noBall); + ballLabel->setFixedSize( 18, statusLabel->sizeHint().height() ); + ballLabel->setAlignment( AlignCenter ); + statusBar()->addWidget(ballLabel, 0, true); + + moveLabel = new QLabel( i18n("Move %1").arg("--"), statusBar(), "moveLabel" ); + statusBar()->addWidget(moveLabel, 0, true); + +#ifdef MYTRACE + /* Create a toolbar menu for debugging output level */ + KToolBar *tb = toolBar("mainToolBar"); + if (tb) { + QPopupMenu* spyPopup = new QPopupMenu; + spy0 = BarIcon( "spy0" ); + spy1 = BarIcon( "spy1" ); + spy2 = BarIcon( "spy2" ); + spy3 = BarIcon( "spy3" ); + spyPopup->insertItem(spy0, 0); + spyPopup->insertItem(spy1, 1); + spyPopup->insertItem(spy2, 2); + spyPopup->insertItem(spy3, 3); + connect( spyPopup, SIGNAL(activated(int)), + this, SLOT(setSpy(int)) ); + tb->insertButton(spy0, 30, spyPopup, + TRUE, i18n("Spy")); + } +#endif + +} + + + +void AbTop::updateSpy(QString s) +{ + if (showSpy) { + if (s.isEmpty()) { + updateStatus(); + // statusBar()->clear(); + } + else + statusLabel->setText(s); + } +} + +void AbTop::updateBestMove(Move& m, int value) +{ + if (showSpy) { + boardWidget->showMove(m,3); + boardWidget->showMove(m,0,false); + + QString tmp; + tmp.sprintf("%s : %+d", (const char*) m.name().utf8(), value-actValue); + updateSpy(tmp); + kapp->processEvents(); + } +} + + +void AbTop::updateStatus() +{ + QString tmp; + bool showValid = false; + + if (!editMode && timerState == noGame) { + tmp = i18n("Move %1").arg("--"); + ballLabel->setPixmap(noBall); + } + else { + tmp = i18n("Move %1").arg(moveNo/2 + 1); + ballLabel->setPixmap( (board->actColor() == Board::color1) + ? redBall : yellowBall); + } + moveLabel->setText(tmp); + + if (editMode) { + tmp = QString("%1: %2 %3 - %4 %5") + .arg( i18n("Edit") ) + .arg( i18n("Red") ).arg(boardWidget->getColor1Count()) + .arg( i18n("Yellow") ).arg(boardWidget->getColor2Count()); + validLabel->setPixmap( (board->validState() == Board::invalid) + ? warningPix:okPix ); + showValid = true; + } + else if (timerState == noGame) { + tmp = i18n("Press %1 for a new game").arg( newAction->shortcut().toString()); + } + else { + if (timerState == gameOver) { + tmp = (board->actColor() == Board::color2) ? + i18n("Red won"):i18n("Yellow won"); + validLabel->setPixmap( warningPix ); + showValid = true; + } + else { + tmp = QString("%1 - %2") + .arg( (board->actColor() == Board::color1) ? + i18n("Red"):i18n("Yellow") ) + .arg( iPlayNow() ? + i18n("I am thinking...") : i18n("It is your turn!") ); + } + } + statusLabel->setText(tmp); + if (validShown != showValid) { + if (showValid) { + statusBar()->addWidget(validLabel); + validLabel->show(); + } + else { + statusBar()->removeWidget(validLabel); + validLabel->hide(); + } + validShown = showValid; + } + statusBar()->clear(); + statusBar()->repaint(); +} + +void AbTop::edited(int vState) +{ + if (vState == Board::empty) + timerState = noGame; + + updateStatus(); +} + +/* only , , have to be updated */ +void AbTop::updateActions() +{ + bool iPlay = iPlayNow(); + + /* New && Copy always on */ + + /* Paste */ + pastePossible = !iPlay; + pasteAction->setEnabled(!iPlay); + + /* Edit */ + editAction->setEnabled(!iPlay); + + /* Stop search */ + stopAction->setEnabled(iPlay); + + /* Back */ + bool bBack = (editMode && moveNo>0) || + (board->movesStored() >=1 && !iPlay); + backAction->setEnabled(bBack); + + /* Forward */ + bool bForward = editMode && moveNo<999; + forwardAction->setEnabled(bForward); + + /* Hint */ + bool bHint = !editMode && !iPlay && (haveHint().type != Move::none); + hintAction->setEnabled(bHint); +} + +/* let the program be responsive even in a long search... */ +void AbTop::searchBreak() +{ + kapp->processEvents(); +} + + +void AbTop::setSpy(int id ) +{ + toolBar("mainToolBar")->setButtonPixmap(30, (id==0)?spy0:(id==1)?spy1:(id==2)?spy2:spy3 ); + spyLevel = id; + board->setSpyLevel(spyLevel); +} + +void AbTop::timerDone() +{ + int interval = 400; + + switch(timerState) { + case noGame: + case notStarted: + return; + case showMove: + case showMove+2: + case showSugg: + case showSugg+2: + case showSugg+4: + boardWidget->showMove(actMove, 2); + interval = 200; + break; + case showMove+1: + case showMove+3: + case showSugg+1: + case showSugg+3: + boardWidget->showMove(actMove, 3); + break; + case showSugg+5: + interval = 800; + case showMove+4: + boardWidget->showMove(actMove, 4); + break; + case showMove+5: + boardWidget->showMove(actMove, 0); + timerState = moveShown; + playGame(); + return; + case showSugg+6: + boardWidget->showMove(actMove, 0); + timerState = notStarted; + return; + } + timerState++; + timer->start(interval,TRUE); +} + +void AbTop::userMove() +{ + /* User has to move */ + static MoveList list; + + list.clear(); + board->generateMoves(list); + + if (list.getLength() == 0) { + stop = true; + timerState = gameOver; + playGame(); + } + else + boardWidget->choseMove(&list); +} + +bool AbTop::iPlayNow() +{ + if (editMode || + (board->validState() != Board::valid) || + timerState == gameOver) + return false; + + int c = board->actColor(); + + /* color1 is red */ + return ((iplay == Both) || + ((c == Board::color1) && (iplay == Red) ) || + ((c == Board::color2) && (iplay == Yellow) )); +} + +void AbTop::playGame() +{ + if (timerState == moveShown) { + if (actMove.type != Move::none) { + board->playMove(actMove); + moveNo++; // actColor in board is changed in playMove + + if (net) + net->broadcast( board->getASCIIState( moveNo ).ascii() ); + } + actValue = - board->calcEvaluation(); + boardWidget->updatePosition(true); + timerState = notStarted; + } + if (!board->isValid()) { + stop = true; + timerState = gameOver; + } + + updateStatus(); + updateActions(); + boardWidget->setCursor(crossCursor); + if (stop) return; + + + if (!iPlayNow()) { + userMove(); + return; + } + boardWidget->setCursor(waitCursor); + kapp->processEvents(); + + if (moveNo <4) { + /* Chose a random move making the position better for actual color */ + + /* If comparing ratings among color1/2 on move, we have to negate one */ + int v = -board->calcEvaluation(), vv; + do { + actMove = board->randomMove(); + board->playMove(actMove); + vv = board->calcEvaluation(); + board->takeBack(); + } while( (board->actColor() == Board::color1) ? (vvv) ); + } + else { + actMove = (board->bestMove()); + + if (actMove.type == Move::none) { + stop = true; + timerState = gameOver; + playGame(); + return; + } + } + + timerState = showMoveLong ? showMove : showMove+3; + timerDone(); +} + +void AbTop::moveChoosen(Move& m) +{ + actMove = m; + timerState = moveShown; + playGame(); +} + +void AbTop::newGame() +{ + /* stop a running animation */ + timerState = notStarted; + timer->stop(); + + /* reset board */ + board->begin(Board::color1); + boardWidget->updatePosition(true); + setMoveNo(0, true); + + if (net) + net->broadcast( board->getASCIIState( moveNo ).ascii() ); + + /* if not in EditMode, start Game immediately */ + if (!editMode) { + stop = false; + playGame(); + } +} + +/* Copy ASCII representation into Clipboard */ +void AbTop::copy() +{ + QClipboard *cb = QApplication::clipboard(); + cb->setText( board->getASCIIState( moveNo ).ascii() ); +} + +void AbTop::paste() +{ + if (!pastePossible) return; + + QClipboard *cb = QApplication::clipboard(); + pastePosition( cb->text().ascii() ); + /* don't do this in pastePosition: RECURSION !! */ + + if (net) + net->broadcast( board->getASCIIState( moveNo ).ascii() ); +} + +void AbTop::pastePosition(const char * text) +{ + if (!pastePossible) return; + if ( text ) { + timerState = notStarted; + timer->stop(); + board->begin(Board::color1); + stop = false; + + int mNo = board->setASCIIState(text); + if (mNo<0) mNo=0; + setMoveNo( mNo, true); + + boardWidget->updatePosition(true); + + if ( (board->validState()==Board::invalid) && !editMode) { + editAction->setChecked(true); + return; + } + + playGame(); + } +} + + +void AbTop::gameNetwork(bool on) +{ + if (!on) { + if (net != 0) { + delete net; + net = 0; + } + return; + } + + if (myPort == 0) myPort = Network::defaultPort; + net = new Network(myPort); + char *h, h2[100]; + int p, i; + for(h = hosts.first(); h!=0; h=hosts.next()) { + for(i=0;h[i]!=0 && h[i]!=':';i++); + if (h[i]==':') + p = atoi(h+i+1); + else + p = 0; + + if (p == 0) p = Network::defaultPort; + strncpy(h2,h,i); + h2[i]=0; + net->addListener(h2, p); + } + QObject::connect(net, SIGNAL(gotPosition(const char *)), + this, SLOT(pastePosition(const char *)) ); +} + + +void AbTop::editModify(bool on) +{ + int vState = board->validState(); + + editMode = boardWidget->setEditMode( on ); + if (vState != Board::valid) + timerState = noGame; + + updateActions(); + updateStatus(); + if (!editMode && vState == Board::valid) { + actMove.type = Move::none; + timerState = moveShown; + playGame(); + } +} + +void AbTop::stopGame() +{ + stop = true; + board->stopSearch(); +} + +void AbTop::stopSearch() +{ + // When computer plays both, switch back to human for next color + if (iplay == Both) { + if (board->actColor() == Board::color1) setIPlay(Red); + else setIPlay(Yellow); + } + board->stopSearch(); +} + +bool AbTop::queryClose() +{ + board->stopSearch(); + return true; +} + +void AbTop::continueGame() +{ + if (timerState != noGame && timerState != gameOver) { + stop = false; + if (timerState == notStarted) + playGame(); + } +} + +/** + * Reset the Move number of the actual game to + * If is true, update GUI actions and redraw statusbar + */ +void AbTop::setMoveNo(int m, bool updateGUI) +{ + moveNo = m; + + board->setActColor( ((moveNo%2)==0) ? Board::color1 : Board::color2 ); + + if (updateGUI) { + updateStatus(); + updateActions(); + } +} + + +/* "Back" action activated + * + * If in edit mode, simple go 1 back + * If in a game, go back 2 if possible + */ +void AbTop::back() +{ + if (editMode) { + if (moveNo > 0) + setMoveNo(moveNo-1, true); + return; + } + + if (moveNo < 1) return; + + if (timerState == gameOver) + timerState = notStarted; + if (timerState != notStarted) return; + + /* If possible, go 2 steps back */ + if (moveNo>0 && board->takeBack()) moveNo--; + if (moveNo>0 && board->takeBack()) moveNo--; + setMoveNo( moveNo, true ); + + boardWidget->updatePosition(true); + + userMove(); +} + +/* Only for edit Mode */ +void AbTop::forward() +{ + if (editMode) { + if (moveNo < 999) + setMoveNo(moveNo+1, true); + return; + } +} + +Move AbTop::haveHint() +{ + static Move m; + static int oldMoveNo = 0; + + if (timerState != notStarted) { + m.type = Move::none; + } + else if (moveNo != oldMoveNo) { + MoveList list; + + oldMoveNo = moveNo; + m = board->nextMove(); + board->generateMoves(list); + if (!list.isElement(m,0)) + m.type = Move::none; + } + return m; +} + + +void AbTop::suggestion() +{ + if (timerState != notStarted) return; + Move m = haveHint(); + if (m.type == Move::none) return; + + actMove = m; + + timerState = showSugg; + timerDone(); +} + +void AbTop::setLevel(int l) +{ + levelAction->setCurrentItem(l); + depth = l+2; + board->setDepth(depth); + // kdDebug(12011) << "Level set to " << d << endl; +} + +void AbTop::setIPlay(int i) +{ + iplayAction->setCurrentItem(i); + iplay = (IPlay)i; + continueGame(); +} + +void AbTop::optionMoveSlow(bool on) +{ + showMoveLong = on; +} + +void AbTop::optionRenderBalls(bool on) +{ + renderBalls = on; + boardWidget->renderBalls(renderBalls); +} + +void AbTop::optionShowSpy(bool on) +{ + showSpy = on; + board->updateSpy(showSpy); + +#ifdef SPION + if (showSpy) + spy->show(); + else { + spy->nextStep(); + spy->hide(); + } +#endif + +} + + +#include "AbTop.moc" diff --git a/kenolaba/AbTop.h b/kenolaba/AbTop.h new file mode 100644 index 00000000..35357452 --- /dev/null +++ b/kenolaba/AbTop.h @@ -0,0 +1,152 @@ +/* Class AbTop: the toplevel widget of Kenolaba + * + * Josef Weidendorfer, 9/97 +*/ + +#ifndef _ABTOP_H_ +#define _ABTOP_H_ + +#include + +#include "Move.h" + + +class QTimer; +class QPopupMenu; +class QLabel; + +class KAction; +class KToggleAction; +class KSelectAction; + +class Network; +class Board; +class BoardWidget; +class Move; +class EvalScheme; + +#ifdef SPION +class Spy; +#endif + + + +class AbTop: public KMainWindow +{ + Q_OBJECT + +public: + AbTop(); + ~AbTop(); + + /* timer states */ + enum { noGame, gameOver, notStarted, moveShown, + showMove = 100, showSugg=200 + }; + + void netPort(int p) { myPort = p; } + void netHost(char* h) { hosts.append(h); } + +protected: + virtual void saveProperties( KConfig * ); + virtual void readProperties( KConfig * ); + + +public slots: + void timerDone(); + void newGame(); + void copy(); + void paste(); + void pastePosition(const char *); + void stopGame(); + void continueGame(); + bool queryClose(); + void back(); + void forward(); + void suggestion(); + void stopSearch(); + void searchBreak(); + void moveChoosen(Move&); + void savePosition(); + void restorePosition(); + void setSpy(int); + void updateSpy(QString); + void edited(int); + void updateBestMove(Move&,int); + void readConfig(); + void writeConfig(); + void rightButtonPressed(int,const QPoint&); + + void gameNetwork(bool); + void editModify(bool); + void optionMoveSlow(bool); + void optionRenderBalls(bool); + void optionShowSpy(bool); + void toggleMenubar(); + void configure(); + void setLevel(int); + void setIPlay(int); + +private: + void setupActions(); + void updateStatus(); + void userMove(); + void playGame(); + void loadPixmaps(); + void setupStatusBar(); + void updateActions(); + void setMoveNo(int, bool updateGUI = false); + bool iPlayNow(); + Move haveHint(); + void readOptions(KConfig *); + void writeOptions(KConfig *); + + Move actMove; + Board* board; + int actValue; + BoardWidget *boardWidget; + EvalScheme* currentEvalScheme; + QTimer *timer; + int timerState; + int depth, moveNo; + bool showMoveLong, stop, showSpy; + bool editMode, renderBalls; + int spyLevel; + bool pastePossible, validShown; + + enum IPlay { Red = 0, Yellow, Both, None, Nb_IPlays }; + IPlay iplay; + + int stop_id, back_id, hint_id; + int easy_id, normal_id, hard_id, challange_id, slow_id, level_id; + int render_id; + int yellow_id, red_id, both_id, none_id, iplay_id; + int spy_id, paste_id, edit_id, forward_id, net_id; + + QLabel *validLabel, *ballLabel, *moveLabel, *statusLabel; + QPixmap warningPix, okPix, redBall, yellowBall, noBall, netPix; + QPixmap spy0, spy1, spy2, spy3; + + Network *net; + int myPort; + QStrList hosts; + + KAction *newAction, *stopAction, *backAction, *forwardAction, *hintAction, *pasteAction; + KToggleAction *showMenubar, *renderBallsAction, *moveSlowAction, + *showSpyAction, *editAction; + KSelectAction *levelAction, *iplayAction; + + struct Data { + const char *key, *label; + }; + enum Level { Easy = 0, Normal, Hard, Challenge, Nb_Levels }; + static const Data LEVEL[Nb_Levels]; + static const Data IPLAY[AbTop::Nb_IPlays]; + +#ifdef SPION + Spy* spy; +#endif + +}; + +#endif /* _ABTOP_H_ */ diff --git a/kenolaba/Ball.cpp b/kenolaba/Ball.cpp new file mode 100644 index 00000000..565ef296 --- /dev/null +++ b/kenolaba/Ball.cpp @@ -0,0 +1,492 @@ +/* Ball animation classes */ + +#include "Ball.h" +#include +#include +#include +#include +#include +#include + +Ball* Ball::first = 0; +//QImage Ball::back; +int Ball::sizeX, Ball::sizeY; +double Ball::lightX, Ball::lightY, Ball::lightZ; +QColor Ball::lightColor; +double Ball::rippleCount, Ball::rippleDepth; + +/* set global Ball parameter */ +void Ball::setSize(int x, int y) +{ + sizeX = x; + sizeY = y; + + invalidate(); +} + +void Ball::invalidate() +{ + Ball *b; + + /* invalidate all Balls... */ + for(b=first;b!=0;b=b->next) + b->pm.resize(0,0); +} + +void Ball::setLight(int x, int y, int z, const QColor& c) +{ + double len = sqrt(double(x*x + y*y + z*z)); + + lightX = x/len; + lightY = y/len; + lightZ = z/len; + + lightColor = c; + + invalidate(); +} + + +void Ball::setTexture(double c, double d) +{ + rippleCount = c; + rippleDepth = d; + + invalidate(); +} + + + +Ball::Ball(const QColor& c, double a, int t) +{ + if (first ==0) { + sizeX = sizeY = -1; + setLight(); + setTexture(7,.3); + } + + bColor = c; + an = a; + sina = sin(a), cosa = cos(a); + + zoom= 1.05, flip = 2.0, limit = 0; + tex = t; + + next = first; + first = this; +} + +Ball::~Ball() +{ + Ball* b; + + if (first == this) + first = next; + else { + for(b = first; b!=0; b=b->next) + if (b->next == this) break; + if (b!=0) + b->next = next; + } +} + +QPixmap* Ball::pixmap() +{ + if (pm.isNull() && sizeX>0 && sizeY>0) + render(); + return ± +} + +void Ball::render() +{ + int x,y; + double xx,yy,zz, ll,lll, red,green,blue; + + if (sizeX==0 || sizeY==0) + return; + + QImage image(sizeX,sizeY,32); + image.fill(0); + + double vv=2./(sizeX+sizeY); + + /* Go through all pixels, mapping x/y to (-1..1,-1..1) */ + for(y=0;yflip) zz=2*flip-zz; + else { + zz -= limit; + } + + if (zz>-vv) { + zz = (zz<0) ? 0 : sqrt(zz); + + /* ll: light intensity at this point */ + ll = xx*lightX + yy*lightY + zz*lightZ; + + /* some face modification */ + double mapx = xx*(2-zz); + double mapy = yy*(2-zz); + double rmapx = cosa*mapx + sina*mapy; /* rotate */ + double rmapy = -sina*mapx + cosa*mapy; + + if (tex>0) + ll += rippleDepth* cos(rippleCount*rmapx)*cos(rippleCount*rmapy); + + ll = (ll<0.01) ? 0.0 : (ll>.99) ? 1.0 : ll; + lll = ll*ll; + + // printf("x %f, y %f, z %f : ll %f lll %f\n", xx,yy,zz,ll,lll); + + + /* mix ball+light */ + red = lll * lightColor.red() + (1-lll) * bColor.red(); + green = lll * lightColor.green() + (1-lll) * bColor.green(); + blue = lll * lightColor.blue() + (1-lll) * bColor.blue(); + + /* lightness */ + red = .2 * bColor.red() + .8 * ll * red; + green = .2 * bColor.green() + .8 * ll * green; + blue = .2 * bColor.blue() + .8 * ll * blue; + + image.setPixel(x,y, qRgb( (int)red, (int)green, (int)blue )); + } + } + } + const QImage iMask = image.createHeuristicMask(); + QBitmap bMask; + bMask = iMask; + pm.convertFromImage( image, 0 ); + pm.setMask(bMask); +} + + +/* Class BallAnimation */ + +BallAnimation::BallAnimation(int s, Ball* ball1, Ball* ball2) +{ + QColor c1 = ball1->ballColor(); + double a1 = ball1->angle(); + int r1 = c1.red(), g1 = c1.green(), b1 = c1.blue(); + + QColor c2 = ball2->ballColor(); + double a2 = ball2->angle(); + int r2 = c2.red(), g2 = c2.green(), b2 = c2.blue(); + + QColor c; + double a; + int i; + + steps = s; + s--; + + balls.append( new Ball( c1,a1 ) ); + + for(i=1; i< s; i++) { + c.setRgb( r1+(r2-r1)*i/s, g1+(g2-g1)*i/s, b1+(b2-b1)*i/s ); + a = a1+(a2-a1)*i/s; + + balls.append( new Ball( c,a ) ); + } + + balls.append( new Ball( c2,a2 ) ); +} + + +/* Class BallPosition */ +BallPosition::BallPosition(int xp,int yp, Ball* d) +{ + x=xp; + y=yp; + def=d; + actStep = -1; + actType = ANIMATION_STOPPED; + actAnimation=0; +} + + +/* Class BallWidget */ + +BallWidget::BallWidget( int _freq, int bFr, QWidget *parent, const char *name ) + : QWidget(parent,name), positions(MAX_POSITION), animations(MAX_ANIMATION) +{ + int i; + + for(i=0;i= MAX_ANIMATION) return; + + if (animations[no] !=0) + delete animations[no]; + + animations[no] = new BallAnimation(s,b1,b2); +} + + +/* X, Y are coordinates in a virtual 1000x1000 area */ +void BallWidget::createBallPosition(int no, int x, int y, Ball* def) +{ + if (no<0 || no>= MAX_POSITION) return; + + if (positions[no] !=0) + delete positions[no]; + + positions[no] = new BallPosition(x,y, def); +} + +void BallWidget::startAnimation(int pos, int anim, int type) +{ + BallPosition *p; + + if (pos<0 || pos>=MAX_POSITION || positions[pos]==0) return; + if (anim<0 || anim>=MAX_ANIMATION || animations[anim]==0) return; + + p = positions.at(pos); + p->actAnimation = animations.at(anim); + + /* One step *BEFORE* start */ + p->actStep = -1; + p->actDir = 1; + p->actType = type; + + if (!isRunning) { + isRunning = true; + timer->start( 0, true ); + } +} + +/* If LOOP: Set to ONESHOT, otherwise set to last frame */ +void BallWidget::stopAnimation(int pos) +{ + BallPosition *p; + + if (pos<0 || pos>=MAX_POSITION || positions[pos]==0) return; + + p = positions.at(pos); + if (p->actType == ANIMATION_STOPPED || + p->actAnimation == 0) return; + + if (p->actType == ANIMATION_LOOP || + p->actType == ANIMATION_CYCLE) { + p->actType = ANIMATION_FORWARD; + // return; + } + /* Set last step: animate() does the rest */ + p->actDir = 1; + p->actStep = p->actAnimation->steps; +} + +void BallWidget::resizeEvent(QResizeEvent *) +{ + int w = width() *10/12, h = height(); + + realSize = (w>h) ? h:w; + + Ball::setSize( realSize/ballFraction, realSize/ballFraction ); + repaint(); +} + +void BallWidget::paintEvent(QPaintEvent *) +{ + paint(this); +} + + +void BallWidget::paint(QPaintDevice *pd) +{ + int i; + BallPosition *p; + int xReal, yReal; + + int w = width(), h = height(); + + if (realSize<0) return; + + for(i=0;ix * realSize / 500 - Ball::w() )/2; + yReal = (h + p->y * realSize / 500 - Ball::h() )/2; + + if (p->actAnimation==0 || p->actStep==-1) { + if (p->def !=0 ) + bitBlt( pd, xReal, yReal, p->def->pixmap() ); + } + else { + int s = p->actStep; + if (s>= p->actAnimation->steps) + s = p->actAnimation->steps-1; + Ball* b = p->actAnimation->balls.at(s); + bitBlt( pd, xReal, yReal, b->pixmap() ); + } + } +} + +void BallWidget::animate() +{ + bool doAnimation = false; + + int i; + BallPosition *p; + int xReal, yReal; + int w = width(), h = height(); + + for(i=0;iactType == ANIMATION_STOPPED || + p->actAnimation ==0) continue; + + p->actStep += p->actDir; + if (p->actStep <= -1) { + p->actDir = 1; + p->actStep = 1; + doAnimation = true; + } + else if (p->actStep >= p->actAnimation->steps) { + if (p->actType == ANIMATION_CYCLE) { + p->actDir = -1; + p->actStep = p->actAnimation->steps -2; + doAnimation = true; + } + else if (p->actType == ANIMATION_LOOP) { + p->actStep = 1; /*skip first frame for smooth animation */ + doAnimation = true; + } + else { + p->actType = ANIMATION_STOPPED; + p->actAnimation = 0; + emit animationFinished(i); + } + } + else { + doAnimation = true; + } + + /* Update Pixmap */ + xReal = (w + p->x * realSize / 500 - Ball::w() )/2; + yReal = (h + p->y * realSize / 500 - Ball::h() )/2; + if (p->actAnimation==0 || p->actStep==-1) { + if (p->def !=0 ) + bitBlt( this, xReal, yReal, p->def->pixmap() ); + } + else { + int s = p->actStep; + if (s>= p->actAnimation->steps) + s = p->actAnimation->steps-1; + Ball* b = p->actAnimation->balls.at(s); + bitBlt( this, xReal, yReal, b->pixmap() ); + } + } + if (!doAnimation) { + isRunning = false; + emit animationsFinished(); + } + else { + timer->start(1000/freq,true); + } + + // repaint( false ); +} + + +/* Ball Test */ + + +BallTest::BallTest( QWidget *parent, const char *name ) + : BallWidget(10,2,parent,name) +{ + int w,h; + + w = h = 150; + resize(w,h); + // Ball::setSize( w/2, h/2, this ); + + Ball *b1 = new Ball( green ); + Ball *b2 = new Ball( yellow ); + Ball *b3 = new Ball( red ); + Ball *b4 = new Ball( red, 3.14/2 ); + + createBlending(0,5,b1,b2); + createBallPosition( 0,250, 250, b1); + + createBlending(1,10,b1,b3); + createBallPosition(1, 250, 750, b1); + + createBlending(2,15,b3,b2); + createBallPosition( 2, 750, 250, b3); + + createBlending(3,20,b3,b4); + createBallPosition(3, 750, 750, b3); +} + +/* +void BallTest::paintEvent( QPaintEvent * ) +{ + bitBlt(this,0,0, b.pixmap()); +} +*/ + +void BallTest::mousePressEvent( QMouseEvent * ) +{ + startAnimation(0,0, ANIMATION_CYCLE); + startAnimation(1,1); + startAnimation(2,2); + startAnimation(3,3, ANIMATION_LOOP); +} + +void BallTest::mouseReleaseEvent( QMouseEvent * ) +{ + stopAnimation(0); + stopAnimation(1); + stopAnimation(3); +} + +/* Test... + +#include + +int main(int argc, char *argv[]) +{ + zoom=.52; + flip=.85; + limit=.75; + + KApplication app(argc, argv, "BallTest"); + BallTest top; + + app.setMainWidget( &top ); + top.show(); + return app.exec(); +} + +*/ +#include "Ball.moc" diff --git a/kenolaba/Ball.h b/kenolaba/Ball.h new file mode 100644 index 00000000..1a3e0d28 --- /dev/null +++ b/kenolaba/Ball.h @@ -0,0 +1,155 @@ +/* Class Ball, BallWidget + * + * Online rendered balls with caching + animation widget + * + * Supported static effects + * - ball color + * - ripple texture + * + * Supported animation sequences for now: + * - Color Blending + * - Texture rotate + * + * April 1999, Josef Weidendorfer + */ + +#ifndef _BALL_H_ +#define _BALL_H_ + +#include +#include +#include +#include +#include + +/* textures for balls */ +#define TEX_FLAT 0 +#define TEX_RIPPLE 1 + +class Ball { + + public: + Ball(const QColor& c, double a = 0.0, int t=TEX_RIPPLE ); + ~Ball(); + + QPixmap* pixmap(); + + double angle() { return an; } + QColor ballColor() { return bColor; } + void setSpecials(double z, double f, double l) + { zoom = z, flip=f, limit=l; } + + static int w() { return sizeX; } + static int h() { return sizeY; } + static void setSize(int x,int y); + static void setLight(int x=5, int y=3, int z=10, + const QColor& c = QColor(200,230,255) ); + static void setTexture(double c=13., double d=.2); + + private: + + void render(); + static void invalidate(); + + //static QImage back; + static int sizeX, sizeY; + static double lightX, lightY, lightZ; + static QColor lightColor; + static double rippleCount, rippleDepth; + + QPixmap pm; + QColor bColor; + double an, sina, cosa; + double zoom, flip, limit; + int tex; + + Ball *next; + static Ball* first; +}; + + +class BallAnimation { + public: + BallAnimation(int s, Ball*, Ball*); + + int steps; + QPtrList balls; +}; + +#define ANIMATION_STOPPED 0 +#define ANIMATION_FORWARD 1 +#define ANIMATION_BACK 2 +#define ANIMATION_LOOP 3 +#define ANIMATION_CYCLE 4 + +class BallPosition { + public: + BallPosition(int xp,int yp, Ball* d); + + int x, y, actStep, actDir, actType; + Ball* def; + BallAnimation* actAnimation; +}; + +#define MAX_POSITION 130 +#define MAX_ANIMATION 20 + +class BallWidget : public QWidget +{ + Q_OBJECT + + public: + BallWidget(int _freq, int bFr, QWidget *parent = 0, const char *name = 0); + ~BallWidget(); + + void createBlending(int, int, Ball* , Ball* ); + void createBallPosition(int, int x, int y, Ball*); + + void startAnimation(int pos, int anim, int type=ANIMATION_FORWARD); + void stopAnimation(int pos); + + void paint(QPaintDevice *); + + virtual void resizeEvent(QResizeEvent *); + virtual void paintEvent(QPaintEvent *); + + signals: + void animationFinished(int); + void animationsFinished(void); + + protected: + void drawBackground(); + + private slots: + void animate(); + + protected: + QMemArray positions; + QMemArray animations; + + private: + int freq; + int xStart, yStart, realSize, ballFraction; + bool isRunning; + QTimer *timer; +}; + + +/* Ball Test */ + +class BallTest: public BallWidget +{ + Q_OBJECT +public: + BallTest(QWidget *parent=0, const char *name=0 ); +protected: + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + + +}; + + + + +#endif // _BALL_H_ diff --git a/kenolaba/Board.cpp b/kenolaba/Board.cpp new file mode 100644 index 00000000..b8546fbc --- /dev/null +++ b/kenolaba/Board.cpp @@ -0,0 +1,1493 @@ +/* Class Board + * + * with methods for + * - play/take back moves + * - generate allowed moves + * - calculate rating for position + * - search for best move + * + * Josef Weidendorfer, 28.8.97 +*/ + +#include + +#include +#include + +#include +#include + +#include "Board.h" +#include "EvalScheme.h" + +// #define MYTRACE 1 + +#if 0 +#define CHECK(b) Q_ASSERT(b) +#else +#define CHECK(b) +#endif + + +static int ratedPositions, wonPositions, searchCalled, moveCount; +static int normalCount, pushCount, outCount, cutoffCount; + +/*********************** Class PrincipalVariation *************************/ + +void PrincipalVariation::clear(int d) +{ + int i,j; + + for(i=0;iactMaxDepth) return; + for(i=d+1;i<=actMaxDepth;i++) { + move[d][i]=move[d+1][i]; + move[d+1][i].type = Move::none; + } + move[d][d]=m; +} + + + +/****************************** Class Board ****************************/ + + +/* Board for start of a game */ +int Board::startBoard[]={ + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 1, 1, 1, 1, 1, 10, 10, 10, 10, 10, + 10, 1, 1, 1, 1, 1, 1, 10, 10, 10, 10, + 10, 0, 0, 1, 1, 1, 0, 0, 10, 10, 10, + 10, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, + 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 10, + 10, 10, 10, 0, 0, 2, 2, 2, 0, 0, 10, + 10, 10, 10, 10, 2, 2, 2, 2, 2, 2, 10, + 10, 10, 10, 10, 10, 2, 2, 2, 2, 2, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 }; + + +/* first centrum of board, then rings around (numbers are indexes) */ +int Board::order[]={ + 60, + 61,72,71,59,48,49, + 62,73,84,83,82,70,58,47,36,37,38,50, + 63,74,85,96,95,94,93,81,69,57,46,35,24,25,26,27,39,51, + 64,75,86,97,108,107,106,105,104,92,80,68,56,45,34,23,12, + 13,14,15,16,28,40,52 }; + +/* See EvalScheme.{h|cpp} + * + // Ratings for fields are calculated out of these values + // (see setFieldValues) + int Board::ringValue[] = { 45, 35, 25, 10, 0 }; + int Board::ringDiff[] = { 0, 10, 10, 8, 5 }; + + // Value added to board rating according to the difference of + // stones in game of player1 and player2 + int Board::stoneValue[]= { 0,-800,-1800,-3000,-4400,-6000 }; + + // Default Values for moves values (see Move.h) + int Board::moveValue[]= { 40,30,30, 15,14,13, 5,5,5, 2,2,2, 1 }; + + // Default Values for inARow values (see Move.h) + int Board::inARowValue[]= { 2, 5, 4, 3 }; +*/ + +int Board::fieldValue[61]; +int Board::direction[]= { -11,1,12,11,-1,-12,-11,1 }; + + +Board::Board() +{ + clear(); + breakOut = bUpdateSpy = false; + spyLevel = 1; + spyDepth = 0; + debug = 0; + realMaxDepth = 1; + _evalScheme = 0; +} + +void Board::setEvalScheme(EvalScheme* scheme) +{ + if (!scheme) + scheme = new EvalScheme( QString("Default") ); + + _evalScheme = scheme; + setFieldValues(); +} + +void Board::setFieldValues() +{ + if (!_evalScheme) return; + + int i, j = 0, k = 59; + int ringValue[5], ringDiff[5]; + + for(i=0;i<5;i++) { + ringDiff[i] = _evalScheme->ringDiff(i); + ringValue[i] = _evalScheme->ringValue(i); + if (ringDiff[i]<1) ringDiff[i]=1; + } + + fieldValue[0] = ringValue[0]; + for(i=1;i<7;i++) + fieldValue[i] = ringValue[1] + ((j+=k) % ringDiff[1]); + for(i=7;i<19;i++) + fieldValue[i] = ringValue[2] + ((j+=k) % ringDiff[2]); + for(i=19;i<37;i++) + fieldValue[i] = ringValue[3] + ((j+=k) % ringDiff[3]); + for(i=37;i<61;i++) + fieldValue[i] = ringValue[4] + ((j+=k) % ringDiff[4]); +} + + + +void Board::begin(int startColor) +{ + int i; + + for(i=0;i */ +void Board::generateFieldMoves(int startField, MoveList& list) +{ + int d, dir, c, actField; + bool left, right; + int opponent = (color == color1) ? color2 : color1; + + Q_ASSERT( field[startField] == color ); + + /* 6 directions */ + for(d=1;d<7;d++) { + dir = direction[d]; + + /* 2nd field */ + c = field[actField = startField+dir]; + if (c == free) { + /* (c .) */ + list.insert(startField, d, Move::move1); + continue; + } + if (c != color) + continue; + + /* 2nd == color */ + + left = (field[startField+direction[d-1]] == free); + if (left) { + left = (field[actField+direction[d-1]] == free); + if (left) + /* 2 left */ + list.insert(startField, d, Move::left2); + } + + right = (field[startField+direction[d+1]] == free); + if (right) { + right = (field[actField+direction[d+1]] == free); + if (right) + /* 2 right */ + list.insert(startField, d, Move::right2); + } + + /* 3rd field */ + c = field[actField += dir]; + if (c == free) { + /* (c c .) */ + list.insert(startField, d, Move::move2); + continue; + } + else if (c == opponent) { + + /* 4th field */ + c = field[actField += dir]; + if (c == free) { + /* (c c o .) */ + list.insert(startField, d, Move::push1with2); + } + else if (c == out) { + /* (c c o |) */ + list.insert(startField, d, Move::out1with2); + } + continue; + } + if (c != color) + continue; + + /* 3nd == color */ + + if (left) { + if (field[actField+direction[d-1]] == free) + /* 3 left */ + list.insert(startField, d, Move::left3); + } + + if (right) { + if (field[actField+direction[d+1]] == free) + /* 3 right */ + list.insert(startField, d, Move::right3); + } + + /* 4th field */ + c = field[actField += dir]; + if (c == free) { + /* (c c c .) */ + list.insert(startField, d, Move::move3); + continue; + } + if (c != opponent) + continue; + + /* 4nd == opponent */ + + /* 5. field */ + c = field[actField += dir]; + if (c == free) { + /* (c c c o .) */ + list.insert(startField, d, Move::push1with3); + continue; + } + else if (c == out) { + /* (c c c o |) */ + list.insert(startField, d, Move::out1with3); + continue; + } + if (c != opponent) + continue; + + /* 5nd == opponent */ + + /* 6. field */ + c = field[actField += dir]; + if (c == free) { + /* (c c c o o .) */ + list.insert(startField, d, Move::push2); + } + else if (c == out) { + /* (c c c o o |) */ + list.insert(startField, d, Move::out2); + } + } +} + + +void Board::generateMoves(MoveList& list) +{ + int actField, f; + + for(f=0;f delete oldest entry */ + if (storedLast == storedFirst) + if (++storedFirst == MvsStored) storedFirst = 0; + + storedMove[storedLast] = m; + + f = m.field; + CHECK( (m.type >= 0) && (m.type < Move::none)); + CHECK( field[f] == color ); + field[f] = free; + dir = direction[m.direction]; + + switch(m.type) { + case Move::out2: /* (c c c o o |) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == opponent ); + CHECK( field[f + 4*dir] == opponent ); + CHECK( field[f + 5*dir] == out ); + field[f + 3*dir] = color; + break; + case Move::out1with3: /* (c c c o |) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == opponent ); + CHECK( field[f + 4*dir] == out ); + field[f + 3*dir] = color; + break; + case Move::move3: /* (c c c .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == free ); + field[f + 3*dir] = color; + break; + case Move::out1with2: /* (c c o |) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == opponent ); + CHECK( field[f + 3*dir] == out ); + field[f + 2*dir] = color; + break; + case Move::move2: /* (c c .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == free ); + field[f + 2*dir] = color; + break; + case Move::push2: /* (c c c o o .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == opponent ); + CHECK( field[f + 4*dir] == opponent ); + CHECK( field[f + 5*dir] == free ); + field[f + 3*dir] = color; + field[f + 5*dir] = opponent; + break; + case Move::left3: + dir2 = direction[m.direction-1]; + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + dir2] == free ); + CHECK( field[f + dir+dir2] == free ); + CHECK( field[f + 2*dir+dir2] == free ); + field[f+dir2] = color; + field[f+=dir] = free; + field[f+dir2] = color; + field[f+=dir] = free; + field[f+dir2] = color; + break; + case Move::right3: + dir2 = direction[m.direction+1]; + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + dir2] == free ); + CHECK( field[f + dir+dir2] == free ); + CHECK( field[f + 2*dir+dir2] == free ); + field[f+dir2] = color; + field[f+=dir] = free; + field[f+dir2] = color; + field[f+=dir] = free; + field[f+dir2] = color; + break; + case Move::push1with3: /* (c c c o .) => (. c c c o) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == opponent ); + CHECK( field[f + 4*dir] == free ); + field[f + 3*dir] = color; + field[f + 4*dir] = opponent; + break; + case Move::push1with2: /* (c c o .) => (. c c o) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == opponent ); + CHECK( field[f + 3*dir] == free ); + field[f + 2*dir] = color; + field[f + 3*dir] = opponent; + break; + case Move::left2: + dir2 = direction[m.direction-1]; + CHECK( field[f + dir] == color ); + CHECK( field[f + dir2] == free ); + CHECK( field[f + dir+dir2] == free ); + field[f+dir2] = color; + field[f+=dir] = free; + field[f+dir2] = color; + break; + case Move::right2: + dir2 = direction[m.direction+1]; + CHECK( field[f + dir] == color ); + CHECK( field[f + dir2] == free ); + CHECK( field[f + dir+dir2] == free ); + field[f+dir2] = color; + field[f+=dir] = free; + field[f+dir2] = color; + break; + case Move::move1: /* (c .) => (. c) */ + CHECK( field[f + dir] == free ); + field[f + dir] = color; + break; + default: + break; + } + + if (m.isOutMove()) { + if (color == color1) + color2Count--; + else + color1Count--; + } + + /* change actual color */ + color = opponent; + + CHECK( isConsistent() ); + +} + +bool Board::takeBack() +{ + int f, dir, dir2; + int opponent = color; + Move& m = storedMove[storedLast]; + + CHECK( isConsistent() ); + + if (storedFirst == storedLast) return false; + + /* change actual color */ + color = (color == color1) ? color2:color1; + + if (m.isOutMove()) { + if (color == color1) + color2Count++; + else + color1Count++; + } + + f = m.field; + CHECK( field[f] == free ); + field[f] = color; + dir = direction[m.direction]; + + switch(m.type) { + case Move::out2: /* (. c c c o |) => (c c c o o |) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == color ); + CHECK( field[f + 4*dir] == opponent ); + CHECK( field[f + 5*dir] == out ); + field[f + 3*dir] = opponent; + break; + case Move::out1with3: /* (. c c c |) => (c c c o |) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == color ); + CHECK( field[f + 4*dir] == out ); + field[f + 3*dir] = opponent; + break; + case Move::move3: /* (. c c c) => (c c c .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == color ); + field[f + 3*dir] = free; + break; + case Move::out1with2: /* (. c c | ) => (c c o |) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == out ); + field[f + 2*dir] = opponent; + break; + case Move::move2: /* (. c c) => (c c .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + field[f + 2*dir] = free; + break; + case Move::push2: /* (. c c c o o) => (c c c o o .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == color ); + CHECK( field[f + 4*dir] == opponent ); + CHECK( field[f + 5*dir] == opponent ); + field[f + 3*dir] = opponent; + field[f + 5*dir] = free; + break; + case Move::left3: + dir2 = direction[m.direction-1]; + CHECK( field[f + dir] == free ); + CHECK( field[f + 2*dir] == free ); + CHECK( field[f + dir2] == color ); + CHECK( field[f + dir+dir2] == color ); + CHECK( field[f + 2*dir+dir2] == color ); + field[f+dir2] = free; + field[f+=dir] = color; + field[f+dir2] = free; + field[f+=dir] = color; + field[f+dir2] = free; + break; + case Move::right3: + dir2 = direction[m.direction+1]; + CHECK( field[f + dir] == free ); + CHECK( field[f + 2*dir] == free ); + CHECK( field[f + dir2] == color ); + CHECK( field[f + dir+dir2] == color ); + CHECK( field[f + 2*dir+dir2] == color ); + field[f+dir2] = free; + field[f+=dir] = color; + field[f+dir2] = free; + field[f+=dir] = color; + field[f+dir2] = free; + break; + case Move::push1with3: /* (. c c c o) => (c c c o .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == color ); + CHECK( field[f + 4*dir] == opponent ); + field[f + 3*dir] = opponent; + field[f + 4*dir] = free; + break; + case Move::push1with2: /* (. c c o) => (c c o .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == opponent ); + field[f + 2*dir] = opponent; + field[f + 3*dir] = free; + break; + case Move::left2: + dir2 = direction[m.direction-1]; + CHECK( field[f + dir] == free ); + CHECK( field[f + dir2] == color ); + CHECK( field[f + dir+dir2] == color ); + field[f+dir2] = free; + field[f+=dir] = color; + field[f+dir2] = free; + break; + case Move::right2: + dir2 = direction[m.direction+1]; + CHECK( field[f + dir] == free ); + CHECK( field[f + dir2] == color ); + CHECK( field[f + dir+dir2] == color ); + field[f+dir2] = free; + field[f+=dir] = color; + field[f+dir2] = free; + break; + case Move::move1: /* (. c) => (c .) */ + CHECK( field[f + dir] == color ); + field[f + dir] = free; + break; + default: + break; + } + + if (--storedLast < 0) storedLast = MvsStored-1; + + CHECK( isConsistent() ); + + return true; +} + +int Board::movesStored() +{ + int c = storedLast - storedFirst; + if (c<0) c+= MvsStored; + return c; +} + + +/** countFrom + * + * Used for board evaluation to count allowed move types and + * connectiveness. VERY similar to move generation. + * + * Returns number of found moves + */ +void Board::countFrom(int startField, int color, + MoveTypeCounter& TCounter, + InARowCounter& CCounter) +{ + int d, dir, c, actField, c2; + bool left, right; + + /* 6 directions */ + for(d=1;d<7;d++) { + dir = direction[d]; + + /* 2nd field */ + c = field[actField = startField+dir]; + if (c == free) { + TCounter.incr( Move::move1 ); + continue; + } + + if (c != color) + continue; + + /* 2nd == color */ + + CCounter.incr( InARowCounter::inARow2 ); + + /* left side move 2 */ + left = (field[startField+direction[d-1]] == free); + if (left) { + left = (field[actField+direction[d-1]] == free); + if (left) + TCounter.incr( Move::left2 ); + } + + /* right side move 2 */ + right = (field[startField+direction[d+1]] == free); + if (right) { + right = (field[actField+direction[d+1]] == free); + if (right) + TCounter.incr( Move::right2 ); + } + + /* 3rd field */ + c = field[actField += dir]; + if (c == free) { + /* (c c .) */ + TCounter.incr( Move::move2 ); + continue; + } + else if (c == out) { + continue; + } + else if (c != color) { + + /* 4th field */ + c = field[actField += dir]; + if (c == free) { + /* (c c o .) */ + TCounter.incr( Move::push1with2 ); + } + else if (c == out) { + /* (c c o |) */ + TCounter.incr( Move::out1with2 ); + } + continue; + } + + /* 3nd == color */ + + CCounter.incr( InARowCounter::inARow3 ); + + /* left side move 3 */ + if (left) { + left = (field[actField+direction[d-1]] == free); + if (left) + TCounter.incr( Move::left3 ); + } + + /* right side move 3 */ + if (right) { + right = (field[actField+direction[d+1]] == free); + if (right) + TCounter.incr( Move::right3 ); + } + + /* 4th field */ + c = field[actField += dir]; + if (c == free) { + /* (c c c .) */ + TCounter.incr( Move::move3 ); + continue; + } + else if (c == out) { + continue; + } + else if (c != color) { + + /* 4nd == opponent */ + + /* 5. field */ + c2 = field[actField += dir]; + if (c2 == free) { + /* (c c c o .) */ + TCounter.incr( Move::push1with3 ); + continue; + } + else if (c2 == out) { + /* (c c c o |) */ + TCounter.incr( Move::out1with3 ); + continue; + } + if (c2 != c) + continue; + + /* 5nd == opponent */ + + /* 6. field */ + c2 = field[actField += dir]; + if (c2 == free) { + /* (c c c o o .) */ + TCounter.incr( Move::push2 ); + } + else if (c2 == out) { + /* (c c c o o |) */ + TCounter.incr( Move::out2 ); + } + + continue; + } + + /* 4nd == color */ + + CCounter.incr( InARowCounter::inARow4 ); + + /* 5th field */ + c = field[actField += dir]; + if (c != color) + continue; + + /* 4nd == color */ + + CCounter.incr( InARowCounter::inARow5 ); + } +} + +/** indent + * + * Internal: for debugging output only + */ +void indent(int d) +{ + char tmp[]=" "; + tmp[d*3] = 0; + printf("> %s",tmp); +} + + + +/** validState + * + * Check for a valid board position to play from: + * (1) Number of balls for each color has to be between 9 and 14 + * (2) There must be a move possible for actual color + */ +int Board::validState() +{ + MoveTypeCounter tc; + InARowCounter cc; + + int c1 = 0, c2 = 0; + int i,j, moveCount, res; + + for(i=0;i8 && c1<15 && c2>8 && c2<15 && moveCount>0 ) + res = valid; + else + res = invalid; + +#ifdef MYTRACE + if (spyLevel>2) { + indent(spyDepth); + printf("Valid: %s (Color1 %d, Color2 %d, moveCount of %d: %d)\n", + (res == empty) ? "empty" : (res==valid) ? "valid":"invalid", + c1,c2,color,moveCount); + } +#endif + + return res; +} + + + +/* Calculate a evaluation for actual position + * + * A higher value means a better position for opponent + * NB: This means a higher value for better position of + * 'color before last move' + */ +int Board::calcEvaluation() +{ + MoveTypeCounter tcColor, tcOpponent; + InARowCounter ccColor, ccOpponent; + + int f,i,j; + + /* different evaluation types */ + int fieldValueSum=0, stoneValueSum=0; + int moveValueSum=0, inARowValueSum=0; + int valueSum; + + /* First check simple winner condition */ + if (color1Count <9) + valueSum = (color==color1) ? 16000 : -16000; + else if (color2Count <9) + valueSum = (color==color2) ? 16000 : -16000; + else { + + /* Calculate fieldValueSum and count move types and connectivity */ + for(i=0;imoveValue(t) * + (tcOpponent.get(t) - tcColor.get(t)); + + for(int i=0;i < InARowCounter::inARowCount;i++) + inARowValueSum += _evalScheme->inARowValue(i) * + (ccOpponent.get(i) - ccColor.get(i)); + + if (color == color2) + stoneValueSum = _evalScheme->stoneValue(14 - color1Count) - + _evalScheme->stoneValue(14 - color2Count); + else + stoneValueSum = _evalScheme->stoneValue(14 - color2Count) - + _evalScheme->stoneValue(14 - color1Count); + + valueSum = fieldValueSum + moveValueSum + + inARowValueSum + stoneValueSum; + } + } + +#ifdef MYTRACE + if (spyLevel>2) { + indent(spyDepth); + printf("Eval %d (field %d, move %d, inARow %d, stone %d)\n", + valueSum, fieldValueSum, moveValueSum, + inARowValueSum, stoneValueSum ); + } +#endif + + return valueSum; +} + +bool Board::isConsistent() +{ + int c1 = 0, c2 = 0; + int i,j; + + for(i=0;i1) { + indent(depth); + printf("%s (%6d .. %6d) MaxType %s\n", depth, + (color==color1)?"O":"X", alpha,beta, + (depth < maxDepth-1) ? "Moving" : + (depth < maxDepth)? "Pushing" : "PushOUT" ); + } + */ +#endif + + /* check for a old best move in main combination */ + if (inPrincipalVariation) { + m = pv[depth]; + + if ((m.type != Move::none) && + (!list.isElement(m, 0, true))) + m.type = Move::none; + + if (m.type == Move::none) + inPrincipalVariation = false; + +#ifdef MYTRACE + else { + if (spyLevel>1) { + indent(spyDepth); + printf("Got from pv !\n" ); + } + } +#endif + } + + // first, play all moves with depth search + depthPhase = true; + + while (1) { + + // get next move + if (m.type == Move::none) { + if (depthPhase) + depthPhase = list.getNext(m, maxType); + if (!depthPhase) + if (!list.getNext(m, Move::none)) break; + } + // we could start with a non-depth move from principal variation + doDepthSearch = depthPhase && (m.type <= maxType); + +#ifdef MYTRACE + + if (m.isOutMove()) outCount++; + else if (m.isPushMove()) pushCount++; + else normalCount++; + + if (doDepthSearch) { + oldRatedPositions = ratedPositions; + oldWonPositions = wonPositions; + oldSearchCalled = searchCalled; + oldMoveCount = moveCount; + oldNormalCount = normalCount; + oldPushCount = pushCount; + oldOutCount = outCount; + oldCutoffCount = cutoffCount; + + if (spyLevel>1) { + indent(spyDepth); + printf("%s [%6d .. %6d] ", + (color==color1)?"O":"X", alpha,beta); + m.print(); + printf("\n"); + } + +#ifdef SPION + if (bUpdateSpy) emit update(depth, 0, m, false); +#endif + } +#endif + + playMove(m); + if (!isValid()) { + /* Possibility (1) to win: Ball Count <9 */ + value = 14999-depth; + // value = ((depth < maxDepth) ? 15999:14999) - depth; +#ifdef MYTRACE + wonPositions++; +#endif + } + else { + + if (doDepthSearch) { + /* opponent searches for his maximum; but we want the + * minimum: so change sign (for alpha/beta window too!) + */ + value = - search(depth+1,-beta,-alpha); + } + else { + ratedPositions++; + + value = calcEvaluation(); + } + } + takeBack(); + + /* For GUI response */ + if (doDepthSearch && (maxDepth - depth >2)) + emit searchBreak(); + +#ifdef MYTRACE + + if (doDepthSearch) { + spyDepth = depth; + + if (spyLevel>1) { + + indent(spyDepth); + if (oldSearchCalled < searchCalled) { + printf(" %d Calls", searchCalled-oldSearchCalled); + if (cutoffCount>oldCutoffCount) + printf(" (%d Cutoffs)", cutoffCount-oldCutoffCount); + printf(", GenMoves %d (%d/%d/%d played)", + moveCount - oldMoveCount, + normalCount - oldNormalCount, + pushCount-oldPushCount, + outCount-oldOutCount); + printf(", Rate# %d", + ratedPositions+wonPositions + - oldRatedPositions - oldWonPositions); + if (wonPositions > oldWonPositions) + printf(" (%d Won)", wonPositions- oldWonPositions); + printf("\n"); + indent(spyDepth); + } + + printf(" => Rated %d%s\n", + value, + (value>14900) ? ", WON !": + (value>=beta) ? ", CUTOFF !": + (value>actValue) ? ", Best !": ""); + } + } + else { + if (spyLevel>2) { + indent(spyDepth); + printf("%s (%6d .. %6d) %-25s => Rating %6d%s\n", + (color==color1)?"O":"X", alpha,beta, + m.name().latin1(), + value, + (value>14900) ? ", WON !": + (value>=beta) ? ", CUTOFF !": + (value>actValue) ? ", Best !": ""); + } + } + + if (value>=beta) cutoffCount++; +#endif + +#ifdef SPION + if (bUpdateSpy) { + if (value > actValue) + emit updateBest(depth, value, m, value >= beta); + emit update(depth, value, m, true); + } +#endif + if (value > actValue) { + actValue = value; + pv.update(depth, m); + + // Only update best move if not stopping search + if (!breakOut && (depth == 0)) { + _bestMove = m; + + if (bUpdateSpy) { + emit updateBestMove(m, actValue); +#ifdef MYTRACE + if (spyLevel>0) { + int i; + printf("> New pv (Rating %d):", actValue); + for(i=0;i<=maxDepth;i++) { + printf("\n> D %d: %s", + i, pv[i].name().latin1() ); + } + printf("\n>\n"); + } +#endif + } + } + + if (actValue>14900 || actValue >= beta) + return actValue; + + /* maximize alpha */ + if (actValue > alpha) alpha = actValue; + } + + if (breakOut) depthPhase=false; + m.type = Move::none; + } + + return actValue; +} + + +Move& Board::bestMove() +{ + int alpha=-15000,beta=15000; + int nalpha,nbeta, actValue; + + if (!_evalScheme) { + // Use default values if not set + setEvalScheme(); + } + + pv.clear(realMaxDepth); + _bestMove.type = Move::none; + + maxDepth=1; + show = false; + breakOut = false; + spyDepth = 0; + + if (spyLevel>0) + printf("\n> New Search\n>\n"); + + /* iterative deepening loop */ + do { + if (spyLevel>0) + printf("> MaxDepth: %d\n>\n", maxDepth); + + /* searches on same level with different alpha/beta windows */ + while(1) { + if (spyLevel>0) + printf("> AB-Window: (%d ... %d)\n>\n", alpha, beta); + + nalpha=alpha, nbeta=beta; + inPrincipalVariation = (pv[0].type != Move::none); + + /* Statistics */ + searchCalled = 0; + moveCount = 0; + ratedPositions = 0; + wonPositions = 0; + normalCount = 0; + pushCount = 0; + outCount = 0; + cutoffCount = 0; + + actValue = search(0,alpha,beta); + + if (spyLevel>0) + { + int i; + if (spyLevel>1) + printf(">\n"); + printf("> Got PV with Rating %d:",actValue); + for(i=0;i<=maxDepth;i++) { + printf("\n> D %d: ", i); + pv[i].print(); + } + printf("\n>\n"); + + printf("> Search called : %6d / %d Cutoffs\n", + searchCalled, cutoffCount); + printf("> Moves generated : %6d / %d Played\n", + moveCount, normalCount+pushCount+outCount); + printf("> Nrml/Push/Out : %6d / %d / %d\n", + normalCount,pushCount,outCount); + printf("> Positions rated : %6d / %d Won\n>\n", + ratedPositions+wonPositions, wonPositions); + + } + + if (actValue > 14900 || actValue < -14900) + breakOut=true; + + /* Don't break out if we haven't found a move */ + if (_bestMove.type == Move::none) + breakOut=false; + + if (breakOut) break; + + // widen alpha-beta window if needed + if (actValue <= nalpha) { + alpha = -15000; + if (beta<15000) beta=actValue+1; + continue; + } + if (actValue >= nbeta) { + if (alpha > -15000) alpha = actValue-1; + beta=15000; + continue; + } + break; + } + + /* Window in both directions cause of deepening */ + alpha=actValue-200, beta=actValue+200; + + if (breakOut) break; + + maxDepth++; + } + while(maxDepth <= realMaxDepth); + + /* If Spy is On, we want replayable search: don't change rating! */ + if (spyLevel==0) + changeEvaluation(); + else { + printf(">>> Got Move : "); + pv[0].print(); + printf("\n\n"); + } + + spyDepth = 0; + + return _bestMove; +} + +Move Board::randomMove() +{ + Move m; + MoveList list; + + generateMoves(list); + int l = list.getLength(); + + int j = random.getLong(l) +1; + + while(j != 0) { + list.getNext(m, Move::none); + j--; + } + + return m; +} + + +void Board::print(int ) +{ + int row,i; + char spaces[]=" "; + const char *z[]={". ","O ","X ", "o ", "x "}; + + printf("\n -----------\n"); + for(row=0;row<4;row++) { + printf("%s/ ",spaces+row); + for(i=0;i<5+row;i++) printf("%s",z[field[row*11+12+i]]); + printf("\\\n"); + } + printf(" | "); + for(i=0;i<9;i++) printf("%s",z[field[56+i]]); + printf("|\n"); + for(row=0;row<4;row++) { + printf("%s\\ ",spaces+3-row); + for(i=0;i<8-row;i++) printf("%s",z[field[68+row*12+i]]); + printf("/\n"); + } + printf(" ----------- O: %d X: %d\n", + color1Count, color2Count); +} + +QString Board::getASCIIState(int moveNo) +{ + QString state, tmp; + + int row,i; + char spaces[]=" "; + const char *z[]={". ","O ","X ", "o ", "x "}; + + state += tmp.sprintf("\n #%-3d ----------- O: %d X: %d\n", + moveNo, color1Count, color2Count); + for(row=0;row<4;row++) { + state += tmp.sprintf("%s/ ",spaces+row); + for(i=0;i<5+row;i++) + state += tmp.sprintf("%s",z[field[row*11+12+i]]); + state += "\\\n"; + } + state += " | "; + for(i=0;i<9;i++) + state += tmp.sprintf("%s",z[field[56+i]]); + state += "|\n"; + for(row=0;row<4;row++) { + state += tmp.sprintf("%s\\ ",spaces+3-row); + for(i=0;i<8-row;i++) + state += tmp.sprintf("%s",z[field[68+row*12+i]]); + state += "/\n"; + } + state += " -----------\n\n"; + + return state; +} + +int Board::setASCIIState(const QString& state) +{ + int moveNo=-1, index; + int len = state.length(); + int color1Count = 0; + int color2Count = 0; + + /* get moveNo if supplied */ + if ((index = state.find("#"))>=0) + moveNo = state.mid(index+1,3).toInt(); + + int f=12, row=0, rowEnd = 17; + char c = ' '; + + index=state.find("/"); + + while(index>=0) { + + while(++index= len) index=-1; + continue; + } + + if (f == rowEnd) { + row++; + if (row <4) { + index = state.find("/",index); + f = 12 + row*11; + rowEnd = row*12+17; + } + else if (row==4) { + index = state.find("|",index); + f = 56; + rowEnd = 65; + } + else if (row <9) { + index = state.find("\\",index); + f = 8 + row*12; + rowEnd = 21 + row*11; + } + else + break; + // printf("Row %d: %d - %d, Idx %d\n", row, f, rowEnd, index); + } + } + return moveNo; +} + + +QString Board::getState(int moveNo) +{ + QString state; + QString entry, tmp; + int i; + + /* Color + Counts */ + state += (char) ('A' + moveNo /25 ); + state += (char) ('A' + moveNo %25 ); + state += (char) ('A' + color1Count); + state += (char) ('A' + color2Count); + state += (char) ('A' + 4*color + field[order[0]]); + + /* Board (field values can be 0-3; 2 fields coded in one char */ + for(i=1;i<61;i+=2) + state+= (char) ('A' + 4*field[order[i]] + field[order[i+1]] ); + + /* -> 35 chars */ + return state; +} + +int Board::setState(QString& _state) +{ + int moveNo; + const char *state = _state.ascii(); + + if (_state.length() != 35) return 0; + + moveNo = 25*(state[0] - 'A') + (state[1] - 'A'); + color1Count = state[2] - 'A'; + color2Count = state[3] - 'A'; + color = (state[4] - 'A') / 4; + field[order[0]] = (state[4] - 'A') %4; + + int i = 1; + for(int j = 5; j<35; j++) { + int w = state[j] - 'A'; + field[order[i++]] = w/4; + field[order[i++]] = w % 4; + } + return moveNo; +} + +void Board::setSpyLevel(int level) +{ + spyLevel = level; +} +#include "Board.moc" diff --git a/kenolaba/Board.h b/kenolaba/Board.h new file mode 100644 index 00000000..61871d1f --- /dev/null +++ b/kenolaba/Board.h @@ -0,0 +1,198 @@ +/* Class Board - represents a game state + * + * Josef Weidendorfer, 28.8.97 +*/ + +#ifndef _BOARD_H_ +#define _BOARD_H_ + +#include +#include +#include "Move.h" + +class KConfig; +class EvalScheme; + +/* Class for best moves so far */ +class PrincipalVariation +{ +public: + PrincipalVariation() + { clear(1); } + + enum { maxDepth = 10 }; + + bool hasMove(int d) + { return (d>actMaxDepth) ? + false : (move[0][d].type != Move::none); } + + Move& operator[](int i) + { return (i<0 || i>=maxDepth) ? move[0][0] : move[0][i]; } + + void update(int d, Move& m); + void clear(int d); + void setMaxDepth(int d) + { actMaxDepth = (d>maxDepth) ? maxDepth-1 : d; } + +private: + Move move[maxDepth][maxDepth]; + int actMaxDepth; + +}; + + +class Board : public QObject +{ + Q_OBJECT + + public: + Board(); + ~Board() {} + + /* different states of one field */ + enum { + out = 10, free = 0, + color1, color2, color1bright, color2bright + }; + enum { AllFields = 121, /* visible + ring of unvisible around */ + RealFields = 61, /* number of visible fields */ + MvsStored = 100 }; + + int debug; + + /* fill Board with defined values */ + void begin(int startColor); /* start of a game */ + void clear(); /* empty board */ + + /* fields can't be changed ! */ + int operator[](int no) const; + + int actColor() const + { return color; } + + /* Generate list of allowed moves for player with + * Returns a calculated value for actual position */ + void generateMoves(MoveList& list); + + /* Functions handling moves + * played moves can be taken back ( moves are remembered) */ + void playMove(const Move& m); + bool takeBack(); /* if not remembered, do nothing */ + int movesStored(); /* return how many moves are remembered */ + + Move& lastMove() + { return storedMove[storedLast]; } + + void showHist(); + + /* Evaluation Scheme to use */ + void setEvalScheme( EvalScheme* scheme = 0); + EvalScheme* evalScheme() { return _evalScheme; } + + /* Calculate a value for actual position + * (greater if better for color1) */ + int calcEvaluation(); + + /* Evalution is based on values which can be changed + * a little (so computer's moves aren't always the same) */ + void changeEvaluation(); + + void setActColor(int c) { color=c; } + void setColor1Count(int c) { color1Count = c; } + void setColor2Count(int c) { color2Count = c; } + void setField(int i, int v) { field[i] = v; } + + void setSpyLevel(int); + + int getColor1Count() { return color1Count; } + int getColor2Count() { return color2Count; } + + enum { empty=0, valid, invalid }; + int validState(); + bool isValid() { return (color1Count>8 && color2Count>8); } + + /* Check that color1Count & color2Count is consisten with board */ + bool isConsistent(); + + /* Searching best move: alpha/beta search */ + void setDepth(int d) + { realMaxDepth = d+1; } + Move& bestMove(); + + /* next move in main combination */ + Move& nextMove() { return pv[1]; } + + Move randomMove(); + void stopSearch() { breakOut = true; } + + /* Compressed ASCII representation */ + QString getState(int); + int setState(QString&); + + /* Readable ASCII representation */ + QString getASCIIState(int); + int setASCIIState(const QString&); + + void updateSpy(bool b) { bUpdateSpy = b; } + + /* simple terminal view of position */ + void print(int); + + static int fieldDiffOfDir(int d) { return direction[d]; } + + signals: + void searchBreak(); + void updateBestMove(Move&,int); + + void update(int,int,Move&,bool); + void updateBest(int,int,Move&,bool); + + private: + void setFieldValues(); + + /* helper function for generateMoves */ + void generateFieldMoves(int, MoveList&); + /* helper function for calcValue */ + void countFrom(int,int, MoveTypeCounter&, InARowCounter&); + /* helper functions for bestMove (recursive search!) */ + int search(int, int, int); + int search2(int, int, int); + + KRandomSequence random; /* random generator */ + + int field[AllFields]; /* actual board */ + int color1Count, color2Count; + int color; /* actual color */ + Move storedMove[MvsStored]; /* stored moves */ + int storedFirst, storedLast; /* stored in ring puffer manner */ + + /* for search */ + PrincipalVariation pv; + Move _bestMove; + bool breakOut, inPrincipalVariation, show, bUpdateSpy; + int maxDepth, realMaxDepth; + + int spyLevel, spyDepth; + EvalScheme* _evalScheme; + + /* ratings; semi constant - are rotated by changeRating() */ + static int fieldValue[RealFields]; + + /* constant arrays */ + static int startBoard[AllFields]; + static int order[RealFields]; + static int direction[8]; + + // static int stoneValue[6]; + // static int moveValue[Move::typeCount]; + // static int connectValue[ConnectCounter::connectCount]; + // static int ringValue[5], ringDiff[5]; +}; + + +inline int Board::operator[](int no) const +{ + return (no<12 || no>120) ? out : field[no]; +} + +#endif diff --git a/kenolaba/BoardWidget.cpp b/kenolaba/BoardWidget.cpp new file mode 100644 index 00000000..9d420cc3 --- /dev/null +++ b/kenolaba/BoardWidget.cpp @@ -0,0 +1,1027 @@ +/* Implementation of class BoardWidget + * + * This class handles rendering of a Board to a KDE/QT widget, + * shows moves (with a timer) and manages input of moves + * + * Josef Weidendorfer, 9/97 + */ + +#include +#include +#include + +#include +#include + +#ifdef HAVE_KIR +#include +#endif + +#include "Board.h" +#include "BoardWidget.h" + +/* Cursors */ +#include "bitmaps/Arrow1" +#include "bitmaps/Arrow1Mask" +#include "bitmaps/Arrow2" +#include "bitmaps/Arrow2Mask" +#include "bitmaps/Arrow3" +#include "bitmaps/Arrow3Mask" +#include "bitmaps/Arrow4" +#include "bitmaps/Arrow4Mask" +#include "bitmaps/Arrow5" +#include "bitmaps/Arrow5Mask" +#include "bitmaps/Arrow6" +#include "bitmaps/Arrow6Mask" + +BoardWidget::BoardWidget(Board& b, QWidget *parent, const char *name) + : BallWidget(10,9,parent, name), board(b) +{ + pList =0; + gettingMove = false; + editMode = false; + renderMode = false; + +#ifdef HAVE_KIR + m_backRenderer = KIRManager::attach( this, "Background" ); + connect( m_backRenderer, SIGNAL(rendered()), + this, SLOT(drawBoard()) ); +#endif + + /* setup cursors */ + +#define createCursor(bitmap,name) \ + static QBitmap bitmap(bitmap##_width, bitmap##_height, \ + (unsigned char *) bitmap##_bits, TRUE); \ + static QBitmap bitmap##Mask(bitmap##Mask_width, bitmap##Mask_height, \ + (unsigned char *) bitmap##Mask_bits, TRUE); \ + name = new QCursor(bitmap, bitmap##Mask, bitmap##_x_hot, bitmap##_y_hot); + + createCursor(Arrow1, arrow[1]); + createCursor(Arrow2, arrow[2]); + createCursor(Arrow3, arrow[3]); + createCursor(Arrow4, arrow[4]); + createCursor(Arrow5, arrow[5]); + createCursor(Arrow6, arrow[6]); + + setCursor(crossCursor); + + // boardColor = new QColor("lightblue"); + boardColor = new QColor(backgroundColor()); + redColor = new QColor("red2"); + yellowColor = new QColor("yellow2"); + redHColor = new QColor("orange"); + yellowHColor = new QColor("green"); + + initBalls(); + + updatePosition(); +} + +BoardWidget::~BoardWidget() +{ + for(int i=1; i<7; i++) + if (arrow[i] != 0) + delete arrow[i]; + +#ifdef HAVE_KIR + if (m_backRenderer != 0) + delete m_backRenderer; +#endif + delete boardColor; + delete redColor; + delete yellowColor; + delete redHColor; + delete yellowHColor; + +} + +void BoardWidget::configure() +{ +#ifdef HAVE_KIR + if (m_backRenderer != 0) { + m_backRenderer->setup(); + m_backRenderer->manager()->saveModules(); + } +#endif +} + + +void BoardWidget::createPos(int pos, int i, int j, Ball* b) +{ + int x=(465*(2*(i)-(j))/9); + int y=(500*19*(j)/100); + createBallPosition(pos, x,y, b); +} + +void BoardWidget::initBalls() +{ + n2 = new Ball( *yellowColor ); + h2 = new Ball( *yellowHColor ); + d2 = new Ball( *yellowHColor, 3.14/2 ); + + n1 = new Ball( *redColor ); + h1 = new Ball( *redHColor ); + d1 = new Ball( *redHColor, 3.14/2 ); + + // e = new Ball( white,0,0 ); + // e->setSpecials(.6,.85,.75); + + createBlending(1,10,h1,n1); + createBlending(2,10,h1,d1); + createBlending(3,10,h2,n2); + createBlending(4,10,h2,d2); + + int i,j,pos; + for(j=-4;j<5;j++) + for(i= ((j>0)?j-4:-4) ; i< ((j<0)?5+j:5) ;i++) { + pos=60+j*11+i; + createPos(pos, i,j, 0); + } + + pos = 0; + /* the outer marks of color1 */ + for(i=0;i<3;i++) createPos(pos++, -6, i-4, 0 ); + for(i=0;i<3;i++) createPos(pos++, 2+i, i-4, 0 ); + + /* the outer marks of color2 */ + for(i=0;i<3;i++) createPos(pos++, 6, 4-i, 0 ); + for(i=0;i<3;i++) createPos(pos++, -2-i, 4-i, 0 ); +} + +void BoardWidget::resizeEvent(QResizeEvent *e) +{ + drawBoard(); + BallWidget::resizeEvent(e); +} + +void BoardWidget::moveEvent(QMoveEvent*) +{ + drawBoard(); +} + +void BoardWidget::paintEvent(QPaintEvent *) +{ + if (renderMode) { + pm = boardPM; + BallWidget::paint(&pm); + } + else + draw(); + bitBlt(this, 0, 0, &pm); +} + + +void drawShadedHexagon(QPainter *p, int x, int y, int r, int lineWidth, + const QColorGroup& g, bool sunken) +{ + int dx=r/2, dy=(r*87)/100; + int y1=y-dy, y2=y+dy; + int i; + + QPen oldPen = p->pen(); + + p->setPen(sunken ? g.midlight() : g.dark()); + + for(i=0; idrawLine( x-i+dx, y-dy, x+2*dx-i, y); + p->drawLine( x+2*dx-i, y, x-i+dx, y+dy); + p->drawLine( x-i+dx, y1+i, x+i-dx, y1+i); + } + + p->setPen(sunken ? g.dark() : g.midlight()); + + for(i=0; idrawLine( x+i-dx, y-dy, x+i-2*dx, y); + p->drawLine( x+i-2*dx, y, x+i-dx, y+dy); + p->drawLine( x-i+dx, y2-i, x+i-dx, y2-i); + } + + p->setPen(oldPen); +} + + +void drawColor(QPainter *p, int x, int y, int r, QColor* c) +{ + QColor w("white"); + QPalette pal(*c); + + QPen oldPen = p->pen(); + QBrush oldBrush = p->brush(); + + p->setBrush(pal.active().dark()); + p->setPen(pal.active().dark()); + p->drawEllipse( x-r - 10,y-r +5, 2*r,2*r); + + p->setBrush(pal.active().mid()); + p->setPen(pal.active().mid()); + p->drawEllipse( x-r,y-r, 2*r,2*r); + + p->setBrush(pal.active().light()); + p->setPen(pal.active().light()); + p->drawEllipse( x-r/3, y-r/3, 4*r/3,4*r/3); + + p->setBrush(w); + p->setPen(w); + p->drawEllipse( x+r/3, y+r/3, r/3,r/3); + + p->setPen(oldPen); + p->setBrush(oldBrush); +} + + +void BoardWidget::drawBoard() +{ + boardPM.resize(width(), height()); + boardPM.fill(this, 0,0); + +#ifndef HAVE_KIR + QColorGroup g = QPalette( *boardColor ).active(); + QColorGroup g2 = QWidget::colorGroup(); + + int boardSize = width() *10/12; + if (boardSize > height()) boardSize = height(); + + QPainter p; + p.begin(&boardPM); + p.setBrush(g2.brush(QColorGroup::Mid)); + + QWMatrix m; + QPoint cp = rect().center(); + m.translate(cp.x(), cp.y()); + m.scale(boardSize/1100.0, boardSize/1000.0); + + m.rotate(0); + + p.setWorldMatrix(m); + + /* draw field */ + + int i,j; + + QPointArray a; + int dx=520 /2, dy=(520 *87)/100; + a.setPoints(6, -dx,-dy, dx,-dy, 2*dx,0, dx,dy, -dx,dy, -2*dx,0 ); + p.drawPolygon(a); + + drawShadedHexagon(&p, 0,0, 505, 1, g, false); + drawShadedHexagon(&p, 0,0, 512, 3, g, true); + drawShadedHexagon(&p, 0,0, 525, 5, g2, true); + +#define xpos(i,j) (495*(2*(i)-(j))/9) +#define ypos(j) (500*19*(j)/100) + + for(j=-4;j<5;j++) + for(i= ((j>0)?j-4:-4) ; i< ((j<0)?5+j:5) ;i++) { + int x=xpos(i,j); + int y=ypos(j); + + drawShadedHexagon(&p, x,y, 50, 2, g, true); + drawShadedHexagon(&p, x,y, 30, 1, g, false); + } + p.end(); +#endif + draw(); +} + +void BoardWidget::renderBalls(bool r) +{ + renderMode=r; + draw(); +} + +void BoardWidget::updateBalls() +{ + int i,j; + + for(j=-4;j<5;j++) + for(i= ((j>0)?j-4:-4) ; i< ((j<0)?5+j:5) ;i++) { + int pos = 60+j*11+i; + int w=field[60+j*11+i]; + + if (w==Board::color1) { + if (positions[pos]->def != n1) { + positions[pos]->def= n1; + startAnimation(pos,1, ANIMATION_FORWARD); + } + else { + stopAnimation(pos); + } + } + else if (w==Board::color1bright) + startAnimation(pos,2,ANIMATION_LOOP); + else if (w==Board::color2) { + if (positions[pos]->def != n2) { + positions[pos]->def= n2; + startAnimation(pos,3,ANIMATION_FORWARD); + } + else { + stopAnimation(pos); + } + } + else if (w==Board::color2bright) + startAnimation(pos,4,ANIMATION_LOOP); + else if (w==Board::free) { + positions[pos]->def= 0; + positions[pos]->actAnimation = 0; + // stopAnimation(pos); + } + } + + for(i=0;i<6;i++) + positions[i]->def= ((14-color1Count)>i && color1Count>0) ? n1:0; + + for(i=6;i<12;i++) + positions[i]->def= ((14-color2Count)>i-6 && color2Count>0) ? n2:0; + +} + +void BoardWidget::draw() +{ + if (boardPM.isNull()) + return; + + pm = boardPM; + + if (renderMode) { + updateBalls(); + repaint(false); + return; + } + + int boardSize = width() *10/12; + if (boardSize > height()) boardSize = height(); + + QPainter p; + p.begin(&pm); + p.setBrush(foregroundColor()); + + QWMatrix m; + QPoint cp = rect().center(); + m.translate(cp.x(), cp.y()); + m.scale(boardSize/1100.0, boardSize/1000.0); + + m.rotate(0); + + p.setWorldMatrix(m); + + /* draw fields */ + + int i,j; + + for(j=-4;j<5;j++) + for(i= ((j>0)?j-4:-4) ; i< ((j<0)?5+j:5) ;i++) { + int x=xpos(i,j); + int y=ypos(j); + int w=field[60+j*11+i]; + + if (w==Board::color1) + drawColor(&p, x,y, 35, redColor ); + else if (w==Board::color1bright) + drawColor(&p, x,y, 35, redHColor ); + else if (w==Board::color2) + drawColor(&p, x,y, 35, yellowColor ); + else if (w==Board::color2bright) + drawColor(&p, x,y, 35, yellowHColor ); + } + + if (color1Count >0) { + /* the outer marks of color1 */ + if (color1Count <12) { + for(i=11; i>8 && i>color1Count ;i--) + drawColor(&p, xpos(12-i,7-i)+55, ypos(7-i), 35, redColor ); + } + for(i=14; i>11 && i>color1Count ;i--) + drawColor(&p, xpos(-6,10-i)+55, ypos(10-i), 35, redColor ); + + /* the outer marks of color2 */ + if (color2Count <12) { + for(i=11; i>8 && i>color2Count ;i--) + drawColor(&p, xpos(i-12,i-7)-55, ypos(i-7), 35, yellowColor); + } + for(i=14; i>11 && i>color2Count ;i--) + drawColor(&p, xpos(6,i-10)-55, ypos(i-10), 35, yellowColor); + } + p.end(); + bitBlt(this, 0, 0, &pm); +} + +/** updatePosition + * + * Update my position with that of the member. + * If is true, draw widget + */ +void BoardWidget::updatePosition(bool updateGUI) +{ + for(int i=0; i2) ? Board::color1bright:Board::free; + opponentNew = (step<2) ? Board::color2 : Board::color2bright; + } + else { + colorNew = (step<2) ? Board::color2 : + (step>2) ? Board::color2bright:Board::free; + opponentNew = (step<2) ? Board::color1 : Board::color1bright; + } + + afterMove = (step == 1) || (step == 4); + + f = m.field; + dir = Board::fieldDiffOfDir(m.direction); + + /* first field */ + field[f] = afterMove ? Board::free : colorNew; + + switch(m.type) { + case Move::out2: /* (c c c o o |) */ + field[f + dir] = colorNew; + field[f + 2*dir] = colorNew; + field[f + 3*dir] = afterMove ? colorNew : opponentNew; + field[f + 4*dir] = opponentNew; + break; + case Move::out1with3: /* (c c c o |) */ + field[f + dir] = colorNew; + field[f + 2*dir] = colorNew; + field[f + 3*dir] = afterMove ? colorNew : opponentNew; + break; + case Move::move3: /* (c c c .) */ + field[f + dir] = colorNew; + field[f + 2*dir] = colorNew; + field[f + 3*dir] = afterMove ? colorNew : Board::free; + break; + case Move::out1with2: /* (c c o |) */ + field[f + dir] = colorNew; + field[f + 2*dir] = afterMove ? colorNew : opponentNew; + break; + case Move::move2: /* (c c .) */ + field[f + dir] = colorNew; + field[f + 2*dir] = afterMove ? colorNew : Board::free; + break; + case Move::push2: /* (c c c o o .) */ + field[f + dir] = colorNew; + field[f + 2*dir] = colorNew; + field[f + 3*dir] = afterMove ? colorNew : opponentNew; + field[f + 4*dir] = opponentNew; + field[f + 5*dir] = afterMove ? opponentNew : Board::free; + break; + case Move::left3: + dir2 = Board::fieldDiffOfDir(m.direction-1); + field[f+dir2] = afterMove ? colorNew : Board::free; + field[f+=dir] = afterMove ? Board::free : colorNew; + field[f+dir2] = afterMove ? colorNew : Board::free; + field[f+=dir] = afterMove ? Board::free : colorNew; + field[f+dir2] = afterMove ? colorNew : Board::free; + break; + case Move::right3: + dir2 = Board::fieldDiffOfDir(m.direction+1); + field[f+dir2] = afterMove ? colorNew : Board::free; + field[f+=dir] = afterMove ? Board::free : colorNew; + field[f+dir2] = afterMove ? colorNew : Board::free; + field[f+=dir] = afterMove ? Board::free : colorNew; + field[f+dir2] = afterMove ? colorNew : Board::free; + break; + case Move::push1with3: /* (c c c o .) => (. c c c o) */ + field[f + dir] = colorNew; + field[f + 2*dir] = colorNew; + field[f + 3*dir] = afterMove ? colorNew : opponentNew; + field[f + 4*dir] = afterMove ? opponentNew : Board::free; + break; + case Move::push1with2: /* (c c o .) => (. c c o) */ + field[f + dir] = colorNew; + field[f + 2*dir] = afterMove ? colorNew : opponentNew; + field[f + 3*dir] = afterMove ? opponentNew : Board::free; + break; + case Move::left2: + dir2 = Board::fieldDiffOfDir(m.direction-1); + field[f+dir2] = afterMove ? colorNew : Board::free; + field[f+=dir] = afterMove ? Board::free : colorNew; + field[f+dir2] = afterMove ? colorNew : Board::free; + break; + case Move::right2: + dir2 = Board::fieldDiffOfDir(m.direction+1); + field[f+dir2] = afterMove ? colorNew : Board::free; + field[f+=dir] = afterMove ? Board::free : colorNew; + field[f+dir2] = afterMove ? colorNew : Board::free; + break; + case Move::move1: /* (c .) => (. c) */ + field[f + dir] = afterMove ? colorNew : Board::free; + break; + default: + break; + } + + if (updateGUI) + draw(); +} + +void BoardWidget::showStart(const Move& m, int step, bool updateGUI) +{ + int f, dir; + int colorNew; + + if (boardOK) { + /* board ok means: board has the normal state before move */ + if (step == 0) + return; /* nothing to be done */ + } + boardOK = (step == 0) ? true:false; + + if (color == Board::color1) + colorNew = (step==0) ? Board::color1 : Board::color1bright; + else + colorNew = (step==0) ? Board::color2 : Board::color2bright; + + f = m.field; + + /* first field */ + field[f] = colorNew; + + switch(m.type) { + case Move::left3: + case Move::right3: + dir = Board::fieldDiffOfDir(m.direction); + field[f + dir] = colorNew; + field[f + 2*dir] = colorNew; + break; + case Move::left2: + case Move::right2: + dir = Board::fieldDiffOfDir(m.direction); + field[f + dir] = colorNew; + default: + break; + } + + if (updateGUI) + draw(); +} + + +void BoardWidget::choseMove(MoveList *pl) +{ + if (!gettingMove && pl != 0) { + pList = pl; + gettingMove = true; + mbDown = false; + actValue = - board.calcEvaluation(); + kdDebug(12011) << "Chose Move..." << endl; + } +} + + +/* returns position of point (xx,yy) + */ +int BoardWidget::positionOf(int xx, int yy) +{ + int boardSize = QMIN( width()*10/12, height() ); + int x = (1000 * (xx- (width()-boardSize)/2)) / boardSize; + int y = (1000 * (yy- (height()-boardSize)/2)) / boardSize; + + /* + kdDebug(12011) << "(" << xx << "," << yy << ") -> (" + << x << "," << y << ")" << endl; + */ + + y = (y-2)/47; + if (y < 2 || y > 18) return 0; + x= ((x+25)/25+ (y-10) )/2; + if (y < 10 && ((x < 2) || (x > 8+y) )) return 0; + if (y >= 10 && ((x < y-8) || (x > 18) )) return 0; + + return 22*y + x; +} + + +bool isBetweenFields(int pos) +{ + bool res = ( ((pos%2) == 1) || ( ((pos/22)%2) == 1) ); + // kdDebug(12011) << "Pos " << pos << " is between fields: " << res << endl; + return res; +} + +int fieldOf(int pos) +{ + int f = 11*(pos/44) + (pos%22)/2; + // kdDebug(12011) << "Field1 of pos " << pos << " is " << f << endl; + return f; +} + +int field2Of(int pos) +{ + int y = pos/22, x = pos%22; + int f2 = 0, f1 = 11*(y/2) + (x/2); + + if ( (y%2) == 0) { + /* exact on row */ + if ( (x%2) != 0) { + /* horizontial between fields */ + f2 = f1+1; + } + } + else { + /* vertical between fields */ + f2 = f1 + ( ((x%2)==0) ? 11:12 ); + } + + // kdDebug(12011) << "Field2 of pos " << pos << " is " << f2 << endl; + return f2; +} + + +/* get direction depending on difference of positions */ +int directionOfPosDiff(int d) +{ + if (d>0) { + return ((d<21) ? Move::Right : + ((d%22) == 0) ? Move::LeftDown : + ((d%23) == 0) ? Move::RightDown : 0); + } + else if (d<0) { + return ((d>-21) ? Move::Left : + ((d%23) == 0) ? Move::LeftUp : + ((d%22) == 0) ? Move::RightUp : 0); + } + return 0; +} + +int directionOfFieldDiff(int d) +{ + if (d>0) { + return ((d<10) ? Move::Right : + ((d%11) == 0) ? Move::LeftDown : + ((d%12) == 0) ? Move::RightDown : 0); + } + else if (d<0) { + return ((d>-10) ? Move::Left : + ((d%12) == 0) ? Move::LeftUp : + ((d%11) == 0) ? Move::RightUp : 0); + } + return 0; +} + +/* Check if is a valid start position for a allowed move + * If yes, set + * , and + */ +bool BoardWidget::isValidStart(int pos, bool midPressed) +{ + bool res = false; + int f1 = fieldOf(pos); + + startField = f1; + + if (isBetweenFields(pos)) { + int f2 = field2Of(pos); + + actMove = Move(f1, directionOfFieldDiff( f2-f1 ), Move::none); + res = pList->isElement(actMove, MoveList::start2); + if (!res) { + startField = f2; + actMove = Move(f2, directionOfFieldDiff( f1-f2 ), Move::none); + res = pList->isElement(actMove, MoveList::start2); + } + startType = MoveList::start2; + return res; + } + + if (midPressed) { + startType = MoveList::start3; + + /* Check all 6 directions */ + for(int dir=1;dir<7;dir++) { + actMove = Move(f1 - Board::fieldDiffOfDir(dir), dir, Move::none ); + if (pList->isElement(actMove, startType)) + return true; + } + /* if we don't find a side move3 fall trough to normal moves... */ + } + + startType = MoveList::start1; + actMove = Move(f1, 0, Move::none); + res = pList->isElement(actMove, startType); + + return res; +} + + +/* Check if is a valid end position for a move + * regarding + * If yes, set + */ +bool BoardWidget::isValidEnd(int pos) +{ + int dir = directionOfPosDiff(pos - startPos); + Move m; + + if (dir == 0) return false; + + switch(startType) { + case MoveList::start1: + m = Move(startField, dir, Move::none); + if (!pList->isElement(m, startType)) + return false; + break; + + case MoveList::start2: + { + int f1 = fieldOf(startPos); + int f2 = field2Of(startPos); + int dir2 = directionOfFieldDiff( f2-f1 ); + int dir3 = directionOfFieldDiff( f1-f2 ); + + switch((dir2-dir+6)%6) { + case 1: + m = Move(f1, dir2, Move::left2); + break; + case 2: + m = Move(f2, dir3, Move::right2); + break; + case 4: + m = Move(f2, dir3, Move::left2); + break; + case 5: + m = Move(f1, dir2, Move::right2); + break; + default: + return false; + } + if (!pList->isElement(m, startType)) + return false; + + break; + } + case MoveList::start3: + { + int rightDir = (dir%6)+1; + m = Move( startField - Board::fieldDiffOfDir(rightDir), rightDir, Move::left3 ); + if (!pList->isElement(m, startType)) { + int leftDir = ((dir-2)%6)+1; + m = Move( startField - Board::fieldDiffOfDir(leftDir), leftDir, Move::right3 ); + if (!pList->isElement(m, startType)) + return false; + } + } + break; + } + + actMove = m; + shownDirection = dir; + return true; +} + + + +void BoardWidget::mousePressEvent( QMouseEvent* pEvent ) +{ + int pos = positionOf( pEvent->x(), pEvent->y() ); + int f = fieldOf(pos); + + if (pEvent->button() == RightButton) { + emit rightButtonPressed(f, pEvent->globalPos()); + return; + } + + if (!gettingMove && !editMode) { + return; + } + mbDown = true; + + + if (editMode) { + editColor = (pEvent->button() == MidButton) ? + Board::color2 : Board::color1; + int newColor = (pEvent->button() == MidButton) ? + Board::color2bright : Board::color1bright; + + if (field[f] == Board::free) { + field[f] = newColor; + } + else if (field[f] == Board::color1) { + if (editColor == Board::color1) { + editColor = Board::free; + newColor = Board::color1bright; + } + field[f] = newColor; + } + else if (field[f] == Board::color2) { + if (editColor == Board::color2) { + editColor = Board::free; + newColor = Board::color2bright; + } + field[f] = newColor; + } + else { + editColor = Board::out; + } + + oldPos = pos; + draw(); + return; + } + + startValid = isValidStart(pos, (pEvent->button() == MidButton)); + kdDebug(12011) << "Start pos " << pos << " is valid: " << startValid << endl; + // actMove.print(); + + if (!startValid) return; + startPos = oldPos = pos; + + showStart(actMove,1); + startShown = true; + + QString tmp; + actValue = - board.calcEvaluation(); + tmp = i18n("Board value: %1").arg(actValue); + emit updateSpy(tmp); +} + + +void BoardWidget::mouseMoveEvent( QMouseEvent* pEvent ) +{ + if ((!gettingMove && !editMode) || !mbDown) return; + + int pos = positionOf( pEvent->x(), pEvent->y() ); + if (pos == oldPos) return; + oldPos = pos; + + if (editMode) { + int f = fieldOf(pos); + if (field[f] != Board::out && field[f] != editColor) { + int newColor = (editColor == Board::color1) ? Board::color1bright : + (editColor == Board::color2) ? Board::color2bright : + (field[f] == Board::color1) ? Board::color1bright : + (field[f] == Board::color2) ? Board::color2bright : field[f]; + field[f] = newColor; + draw(); + } + return; + } + + if (!startValid) { + /* We haven't a valid move yet. Check if we are over a valid start */ + + startValid = isValidStart(pos, (pEvent->button() == MidButton)); + kdDebug(12011) << "Start pos " << pos << " is valid: " << startValid << endl; + // actMove.print(); + + if (!startValid) return; + startPos = oldPos = pos; + + showStart(actMove,1); + startShown = true; + + QString tmp; + actValue = - board.calcEvaluation(); + tmp = i18n("Board value: %1").arg(actValue); + emit updateSpy(tmp); + return; + } + + /* restore board */ + updatePosition(); + startShown = false; + + if (isValidEnd(pos)) { + // actMove.print(); + + board.playMove(actMove); + int v = board.calcEvaluation(); + board.takeBack(); + + QString tmp; + tmp.sprintf("%+d", v-actValue); + QString str = QString("%1 : %2").arg(actMove.name()).arg(tmp); + emit updateSpy(str); + + showMove(actMove,3); + setCursor(*arrow[shownDirection]); + } + else { + QString tmp; + + setCursor(crossCursor); + if (pos == startPos) { + showStart(actMove,1); + startShown = true; + tmp = i18n("Board value: %1").arg(actValue); + } + else + draw(); + emit updateSpy(tmp); + } +} + + +void BoardWidget::mouseReleaseEvent( QMouseEvent* pEvent ) +{ + if (!gettingMove && !editMode) return; + mbDown = false; + + if (editMode) { + int i; + + // printf("Releasing..."); + for(i=0; ix(), pEvent->y() ); + if (isValidEnd(pos)) { + // actMove.print(); + startValid = false; + setCursor(crossCursor); + gettingMove = false; + emit moveChoosen(actMove); + return; + } + + updatePosition(true); + startValid = false; + setCursor(crossCursor); + + QString tmp; + emit updateSpy(tmp); +} + +QSize BoardWidget::sizeHint() const +{ + return QSize(400, 350); +} + +#include "BoardWidget.moc" diff --git a/kenolaba/BoardWidget.h b/kenolaba/BoardWidget.h new file mode 100644 index 00000000..8fc0a317 --- /dev/null +++ b/kenolaba/BoardWidget.h @@ -0,0 +1,116 @@ +#ifndef _BOARDWIDGET_H_ +#define _BOARDWIDGET_H_ + +#include +#include + +#include "Ball.h" +#include "Move.h" +#include "Board.h" + +#ifdef HAVE_KIR +class KIRenderer; +#endif + +class BoardWidget : public BallWidget +{ + Q_OBJECT + + public: + BoardWidget(Board&, QWidget *parent = 0, const char *name = 0); + ~BoardWidget(); + + void createPos(int , int , int , Ball*); + void initBalls(); + void updateBalls(); + + virtual void resizeEvent(QResizeEvent *); + virtual void moveEvent(QMoveEvent *); + virtual void paintEvent(QPaintEvent *); + virtual void mousePressEvent( QMouseEvent* pEvent ); + virtual void mouseReleaseEvent( QMouseEvent* pEvent ); + virtual void mouseMoveEvent( QMouseEvent* pEvent ); + + void renderBalls(bool r); + + void draw(); + + void choseMove(MoveList*); + + /* Show a move with highlighting + * step 0: state before move + * 1: state after move + * 2: remove all marks (for blinking) + * 3: highlight marks (before move) + * 4: highlight marks (after move) + * Always call with step 0 before actual playing the move !! */ + void showMove(const Move& m, int step, bool updateGUI = true); + + /* Only highlight start + * Step 0: original state + * 1: highlight start + * Always call with step 0 before actual playing the move !! */ + void showStart(const Move& m, int step, bool updateGUI = true); + + + /* enable/disable Edit Mode: + * On disabling & valid position it's actually copied to Board + */ + bool setEditMode(bool); + // int validState() { return board->validState(); } + // bool isValid() { return validState()==Board::valid; } + + /* copy actual board position */ + void updatePosition(bool updateGUI = false); + void clearPosition(); + + int getColor1Count() { return color1Count; } + int getColor2Count() { return color2Count; } + + public slots: + void configure(); + void drawBoard(); + + signals: + void moveChoosen(Move&); + void rightButtonPressed(int,const QPoint&); + void updateSpy(QString); + void edited(int); + protected: + virtual QSize sizeHint() const; + + private: + int positionOf(int x, int y); + bool isValidStart(int pos, bool); + bool isValidEnd(int pos); + + QPixmap pm, boardPM; + Board& board; + int actValue; + + bool editMode, renderMode; + int editColor; + + /* copied position */ + int field[Board::AllFields]; + int color1Count, color2Count, color; + bool boardOK; + + /* for getting user Move */ + MoveList *pList; + Move actMove; + bool gettingMove, mbDown, startValid, startShown; + int startPos, actPos, oldPos, shownDirection; + int startField, startField2, actField, oldField, startType; + QColor *boardColor, *redColor, *yellowColor, *redHColor, *yellowHColor; + QCursor *arrowAll, *arrow[7]; + + Ball *n1, *n2, *h1, *h2, *d1, *d2; //, *e; + +#ifdef HAVE_KIR + KIRenderer *m_backRenderer; +#endif +}; + +#endif /* _BOARDWIDGET_H_ */ + diff --git a/kenolaba/ChangeLog b/kenolaba/ChangeLog new file mode 100644 index 00000000..dc3947a5 --- /dev/null +++ b/kenolaba/ChangeLog @@ -0,0 +1,35 @@ +Version 1.01 (24.9.97) + - Internationalisation supported (locale DE supplied) + - Updated HTML documentation (german added !) + +Version 1.02 (16.10.97) + - Improved computers playing (a little slower but much stronger !) + - Toolbar + - Options saved in config file + Ratings too: Change the way computer rates a position + (see Board::calcRating / readRating) + - New option "Spy": See some of the internals of the computers thoughts ! + - Session management + - KStdAccel used for standard key bindings + - Icon/MiniIcon supplied + + +Version 1.03: + * [Robert Williams] Added version.h and ChangeLog + * [Robert Williams] Added -caption "%c" to kenolaba.kdelnk + * [Robert Williams] Added getHelpMenu() + +Version 1.04 (10.6.99) + - Fixed *BIG* bug in a/b search + - Edit mode + - Copy/Paste Positions in nice ASCII over clipboard + - Ball rendering + - Network Mode + +Version 1.05 (16.3.2000) + - Statusbar correction + - Action'ized (Menu + Toolbar), using XML GUI description + +Version 1.05b (24.7.2000) + - Bugfix release, Cleanup for KDE 2.0 + \ No newline at end of file diff --git a/kenolaba/EvalDlg.ui b/kenolaba/EvalDlg.ui new file mode 100644 index 00000000..06931458 --- /dev/null +++ b/kenolaba/EvalDlg.ui @@ -0,0 +1,1760 @@ + +EvalDlg + + + EvalDlg + + + + 0 + 0 + 350 + 388 + + + + Configure Evaluation + + + + unnamed + + + 11 + + + 6 + + + + TabWidget2 + + + + + + moves + + + Moves + + + + unnamed + + + 11 + + + 6 + + + + Layout17 + + + + unnamed + + + 0 + + + 6 + + + + Layout35 + + + + + + unnamed + + + 0 + + + 4 + + + + moveEval1 + + + + 0 + 0 + 0 + 0 + + + + + 30 + 32767 + + + + AlignLeft + + + + + + + PixmapLabel3_2 + + + image0 + + + false + + + + + moveEval2 + + + + 0 + 0 + 0 + 0 + + + + + 30 + 32767 + + + + AlignLeft + + + + + + + PixmapLabel1_2_5 + + + image1 + + + false + + + + + moveEval3 + + + + 0 + 0 + 0 + 0 + + + + + 30 + 32767 + + + + AlignLeft + + + + + + + PixmapLabel3_3 + + + image0 + + + false + + + + + unnamed + + + image1 + + + false + + + + + PixmapLabel1_2_6 + + + image1 + + + false + + + + + PixmapLabel1_2_4 + + + image1 + + + false + + + + + PixmapLabel3 + + + image0 + + + false + + + + + PixmapLabel1_2 + + + image1 + + + false + + + + + PixmapLabel1_3 + + + image1 + + + false + + + + + Spacer2 + + + Horizontal + + + Expanding + + + + + + + TextLabel4_2_2 + + + Push Out + + + AlignTop|AlignLeft + + + + + + + Spacer9_2 + + + Horizontal + + + Expanding + + + + + TextLabel4_2 + + + Push + + + AlignTop|AlignLeft + + + + + + + Layout37 + + + + + + unnamed + + + 0 + + + 4 + + + + PixmapLabel1_2_5_2_3 + + + image1 + + + false + + + + + PixmapLabel3_3_2_3 + + + image0 + + + false + + + + + PixmapLabel3_3_2_2 + + + image0 + + + false + + + + + moveEval4 + + + + 0 + 0 + 0 + 0 + + + + + 30 + 32767 + + + + AlignLeft + + + + + + + moveEval5 + + + + 0 + 0 + 0 + 0 + + + + + 30 + 32767 + + + + AlignLeft + + + + + + + PixmapLabel2_3 + + + image2 + + + false + + + + + PixmapLabel1_2_5_2 + + + image1 + + + false + + + + + PixmapLabel3_3_2 + + + image0 + + + false + + + + + PixmapLabel2_2_2 + + + image2 + + + false + + + + + PixmapLabel1_3_2 + + + image1 + + + false + + + + + Spacer2_2 + + + Horizontal + + + Expanding + + + + + moveEval6 + + + + 0 + 0 + 0 + 0 + + + + + 30 + 32767 + + + + AlignLeft + + + + + + + PixmapLabel1_4 + + + image1 + + + false + + + + + PixmapLabel1_2_2 + + + image1 + + + false + + + + + PixmapLabel2_2 + + + image2 + + + false + + + + + PixmapLabel1_4_3 + + + image1 + + + false + + + + + PixmapLabel1_2_5_2_2 + + + image1 + + + + + PixmapLabel2 + + + image2 + + + false + + + + + PixmapLabel1_4_2 + + + image1 + + + false + + + + + + + Spacer9 + + + Horizontal + + + Expanding + + + + + Layout38 + + + + + + unnamed + + + 0 + + + 4 + + + + PixmapLabel2_4 + + + image2 + + + false + + + + + PixmapLabel1_3_2_2 + + + image1 + + + false + + + + + PixmapLabel1_2_5_2_2_2 + + + image1 + + + + + Spacer2_2_2 + + + Horizontal + + + Expanding + + + + + moveEval7 + + + + 0 + 0 + 0 + 0 + + + + + 30 + 32767 + + + + AlignLeft + + + + + + + PixmapLabel1_2_2_2 + + + image1 + + + false + + + + + PixmapLabel2_2_2_3 + + + image2 + + + false + + + + + moveEval9 + + + + 0 + 0 + 0 + 0 + + + + + 30 + 32767 + + + + AlignLeft + + + + + + + PixmapLabel1_4_2_2 + + + image1 + + + false + + + + + PixmapLabel1_2_5_2_3_3 + + + image1 + + + false + + + + + moveEval8 + + + + 0 + 0 + 0 + 0 + + + + + 30 + 32767 + + + + AlignLeft + + + + + + + PixmapLabel1_4_3_2 + + + image1 + + + false + + + + + PixmapLabel1_4_4 + + + image1 + + + false + + + + + PixmapLabel1_2_5_2_4 + + + image1 + + + false + + + + + PixmapLabel2_3_3 + + + image2 + + + false + + + + + PixmapLabel2_2_3 + + + image2 + + + false + + + + + + + TextLabel4 + + + Normal + + + AlignTop|AlignLeft + + + + + + + + + TextLabel5 + + + + 0 + 20 + + + + For every move possible the given points are added to the Evaluation. + + + WordBreak|AlignTop|AlignLeft + + + + + + + + + Spacer5 + + + Vertical + + + Expanding + + + + + + + position + + + Position + + + + unnamed + + + 11 + + + 6 + + + + Layout63 + + + + unnamed + + + 0 + + + 0 + + + + Spacer12 + + + Horizontal + + + Expanding + + + + + Layout62 + + + + unnamed + + + 0 + + + 6 + + + + TextLabel1_2_3_2 + + + Inner ring 3: + + + + + posEval3 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + posEval5 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + posEval2 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + TextLabel1_2 + + + Outermost ring: + + + + + diffEval2 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + diffEval3 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + posEval4 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + TextLabel1_2_2 + + + Middle position: + + + + + diffEval5 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + posEval1 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + TextLabel1_2_3 + + + Inner ring 2: + + + + + TextLabel3_4 + + + +/- + + + + + TextLabel3_3 + + + +/- + + + + + diffEval4 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + TextLabel3_2 + + + +/- + + + + + TextLabel1_2_2_2 + + + Innermost ring: + + + + + TextLabel3 + + + +/- + + + + + + + Spacer11 + + + Horizontal + + + Expanding + + + + + + + TextLabel6 + + + For every ball, the given points are added to the evaluation depending on the balls position. The bonus for a given position is changed randomly in the +/- range. + + + WordBreak|AlignTop|AlignLeft + + + + + + + + + Spacer4 + + + Vertical + + + Expanding + + + + + + + inarow + + + In-A-Row + + + + unnamed + + + 11 + + + 6 + + + + Layout64 + + + + unnamed + + + 0 + + + 6 + + + + Spacer14 + + + Horizontal + + + Expanding + + + + + Layout42 + + + + unnamed + + + 0 + + + 6 + + + + rowEval3 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + rowEval2 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + rowEval5 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + rowEval4 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + TextLabel1_2_2_3_2 + + + Three in-a-row: + + + + + TextLabel1_2_2_3 + + + Two in-a-row: + + + + + TextLabel1_2_2_3_3 + + + Four in-a-row: + + + + + TextLabel1_2_2_3_4 + + + Five in-a-row: + + + + + + + Spacer13 + + + Horizontal + + + Expanding + + + + + + + TextLabel6_2 + + + For a number of balls In-a-Row, the given points are added to the evaluation + + + WordBreak|AlignTop|AlignLeft + + + + + + + + + Spacer6 + + + Vertical + + + Expanding + + + + + + + count + + + Count + + + + unnamed + + + 11 + + + 6 + + + + Layout66 + + + + unnamed + + + 0 + + + 6 + + + + Spacer16 + + + Horizontal + + + Expanding + + + + + Layout65 + + + + unnamed + + + 0 + + + 6 + + + + countEval2 + + + + 40 + 32767 + + + + AlignLeft + + + + + + + TextLabel1_2_2_3_4_2 + + + 4 Balls more: + + + + + countEval5 + + + + 40 + 32767 + + + + AlignLeft + + + + + + + countEval4 + + + + 40 + 32767 + + + + AlignLeft + + + + + + + TextLabel1_2_2_3_3_2 + + + 3 Balls more: + + + + + TextLabel1_2_2_3_4_2_2 + + + 5 Balls more: + + + + + countEval1 + + + + 40 + 32767 + + + + AlignLeft + + + + + + + TextLabel1_2_2_3_2_2 + + + 2 Balls more: + + + + + countEval3 + + + + 40 + 32767 + + + + AlignLeft + + + + + + + TextLabel1_2_2_3_5 + + + 1 Ball more: + + + + + + + Spacer15 + + + Horizontal + + + Expanding + + + + + + + TextLabel6_2_2 + + + For a difference in the number of balls, the given points are added to the evaluation. A difference of 6 only can be a lost/won game. + + + WordBreak|AlignTop|AlignLeft + + + + + + + + + Spacer6_2 + + + Vertical + + + Expanding + + + + + + + tab + + + Evaluation Schemes + + + + unnamed + + + 11 + + + 6 + + + + evalList + + + + + Layout13 + + + + unnamed + + + 0 + + + 6 + + + + evalSaveAs + + + Save As... + + + + + evalDelete + + + Delete + + + + + + + TextLabel1 + + + Your evaluation scheme, defined in all other tabs of this dialog, can be stored here. + + + WordBreak|AlignTop|AlignLeft + + + + + + + + + + + + Layout13 + + + + unnamed + + + 0 + + + 6 + + + + Spacer2_3 + + + Horizontal + + + Expanding + + + + + TextLabel9 + + + Evaluation of actual position: + + + + + actualEval + + + Flat + + + + + Spacer1 + + + Horizontal + + + Expanding + + + + + + + + + 789cd3d7528808f055d0d2e72a2e492cc94c5648ce482c52d04a29cdcdad8c8eb5ade6523234530022630543251d2e253d856405bffcbc54105b19c856b630b630b43006711341dc343000ab0401651000b3f4a062ca30801003721241002208160309817525825950318810d8108818921058901662d8ecd583fb4a0fe13eb820c4f1e8fe4d448401d8ab204f23850b483431116a9352ad351700a1cd4f38 + + + 789c6dd0410ac2301005d07d4e3124bb20ad4511413c82e2521017e9c4a28b56d0ba10f1ee66664cda6a3fa1cc7f9436496e61bfdb80cdd5bd75ed0501cfee06d63feafa7938ae5f4a170b082b3cf444e90c10b6d7e644b309b39972a83aaaf322d692ea72162b52adaa58bd540a7f9662283c655f33c65144c5884a0a235b2241b14401c5fa44d83744fc3544c6bea1e4cf7c32f92f91f76806fbe3f7d2fe3aecce317aded17b1944bf57ea03b18f6cb9 + + + 789c6dd04d0ac2301005e07d4e3124bb20ad452c05f1088a4b415c24138b2e5a41eb42c4bb9bc9246a208f52e67d94fcb4d6b0df6d40d7e23e99e98280677303ed1ec3f03c1cd72f219b16fce35f7226640508dbeb78a259f959cd43a81aaadda26bb85aaab8c4962b52ed7db8ba54fb3e2c4b51943055d194321456b648118331596b19d9220564fb1163329b921bfa940cffcd047398d68bfb7a72cea8fc7cf4ddf77ca57b14ef5bfc2f59e47b253ec60c6d67 + + + + TabWidget2 + moveEval1 + moveEval2 + moveEval3 + moveEval4 + moveEval5 + moveEval6 + moveEval7 + moveEval8 + moveEval9 + posEval1 + posEval2 + diffEval2 + posEval3 + diffEval3 + posEval4 + diffEval4 + posEval5 + diffEval5 + rowEval2 + rowEval3 + rowEval4 + rowEval5 + countEval1 + countEval2 + countEval3 + countEval4 + countEval5 + evalList + evalDelete + evalSaveAs + + + kseparator.h + + + diff --git a/kenolaba/EvalDlgImpl.cpp b/kenolaba/EvalDlgImpl.cpp new file mode 100644 index 00000000..a5903429 --- /dev/null +++ b/kenolaba/EvalDlgImpl.cpp @@ -0,0 +1,299 @@ +/** + * Some Implementation details for configEval Dialog + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "EvalDlgImpl.h" +#include "Board.h" +#include "EvalScheme.h" + +EvalDlgImpl::EvalDlgImpl(QWidget* parent, Board* board) + :EvalDlg(parent) +{ + _board = board; + _origScheme = board->evalScheme(); + _scheme = new EvalScheme(*_origScheme); + + connect( evalDelete, SIGNAL( clicked() ), this, SLOT( deleteEntry() ) ); + connect( evalSaveAs, SIGNAL( clicked() ), this, SLOT( saveas() ) ); + connect( evalList, SIGNAL( highlighted(int) ), this, SLOT( select(int) ) ); + + KConfig* config = kapp->config(); + config->setGroup("General"); + QStringList list = config->readListEntry("EvalSchemes"); + evalList->insertItem( i18n("Current") ); + evalList->insertItem( i18n("Default") ); + for(int i=0;iinsertItem(list[i]); + + evalList->setSelected(0, TRUE); + + updateWidgets(); + connectEditLines(); +} + +EvalDlgImpl::~EvalDlgImpl() +{ + delete _scheme; +} + + +void EvalDlgImpl::connectEditLines() +{ + connect( moveEval1, SIGNAL(textChanged(const QString&)), this, SLOT(updateMove()) ); + connect( moveEval2, SIGNAL(textChanged(const QString&)), this, SLOT(updateMove()) ); + connect( moveEval3, SIGNAL(textChanged(const QString&)), this, SLOT(updateMove()) ); + connect( moveEval4, SIGNAL(textChanged(const QString&)), this, SLOT(updateMove()) ); + connect( moveEval5, SIGNAL(textChanged(const QString&)), this, SLOT(updateMove()) ); + connect( moveEval6, SIGNAL(textChanged(const QString&)), this, SLOT(updateMove()) ); + connect( moveEval7, SIGNAL(textChanged(const QString&)), this, SLOT(updateMove()) ); + connect( moveEval8, SIGNAL(textChanged(const QString&)), this, SLOT(updateMove()) ); + connect( moveEval9, SIGNAL(textChanged(const QString&)), this, SLOT(updateMove()) ); + connect( posEval1, SIGNAL(textChanged(const QString&)), this, SLOT(updateFields()) ); + connect( posEval2, SIGNAL(textChanged(const QString&)), this, SLOT(updateFields()) ); + connect( posEval3, SIGNAL(textChanged(const QString&)), this, SLOT(updateFields()) ); + connect( posEval4, SIGNAL(textChanged(const QString&)), this, SLOT(updateFields()) ); + connect( posEval5, SIGNAL(textChanged(const QString&)), this, SLOT(updateFields()) ); + connect( diffEval2, SIGNAL(textChanged(const QString&)), this, SLOT(updateFields()) ); + connect( diffEval3, SIGNAL(textChanged(const QString&)), this, SLOT(updateFields()) ); + connect( diffEval4, SIGNAL(textChanged(const QString&)), this, SLOT(updateFields()) ); + connect( diffEval5, SIGNAL(textChanged(const QString&)), this, SLOT(updateFields()) ); + connect( rowEval2, SIGNAL(textChanged(const QString&)), this, SLOT(updateInARow()) ); + connect( rowEval3, SIGNAL(textChanged(const QString&)), this, SLOT(updateInARow()) ); + connect( rowEval4, SIGNAL(textChanged(const QString&)), this, SLOT(updateInARow()) ); + connect( rowEval5, SIGNAL(textChanged(const QString&)), this, SLOT(updateInARow()) ); + connect( countEval1, SIGNAL(textChanged(const QString&)), this, SLOT(updateCount()) ); + connect( countEval2, SIGNAL(textChanged(const QString&)), this, SLOT(updateCount()) ); + connect( countEval3, SIGNAL(textChanged(const QString&)), this, SLOT(updateCount()) ); + connect( countEval4, SIGNAL(textChanged(const QString&)), this, SLOT(updateCount()) ); + connect( countEval5, SIGNAL(textChanged(const QString&)), this, SLOT(updateCount()) ); +} + +void EvalDlgImpl::disconnectEditLines() +{ + moveEval1->disconnect(); + moveEval2->disconnect(); + moveEval3->disconnect(); + moveEval4->disconnect(); + moveEval5->disconnect(); + moveEval6->disconnect(); + moveEval7->disconnect(); + moveEval8->disconnect(); + moveEval9->disconnect(); + posEval1->disconnect(); + posEval2->disconnect(); + posEval3->disconnect(); + posEval4->disconnect(); + posEval5->disconnect(); + diffEval2->disconnect(); + diffEval3->disconnect(); + diffEval4->disconnect(); + diffEval5->disconnect(); + rowEval2->disconnect(); + rowEval3->disconnect(); + rowEval4->disconnect(); + rowEval5->disconnect(); + countEval1->disconnect(); + countEval2->disconnect(); + countEval3->disconnect(); + countEval4->disconnect(); + countEval5->disconnect(); +} + +void EvalDlgImpl::updateWidgets() +{ + // Moves + moveEval1->setText( QString::number(_scheme->moveValue(Move::move1)) ); + moveEval2->setText( QString::number(_scheme->moveValue(Move::move2)) ); + moveEval3->setText( QString::number(_scheme->moveValue(Move::move3)) ); + moveEval4->setText( QString::number(_scheme->moveValue(Move::push1with2)) ); + moveEval5->setText( QString::number(_scheme->moveValue(Move::push1with3)) ); + moveEval6->setText( QString::number(_scheme->moveValue(Move::push2)) ); + moveEval7->setText( QString::number(_scheme->moveValue(Move::out1with2)) ); + moveEval8->setText( QString::number(_scheme->moveValue(Move::out1with3)) ); + moveEval9->setText( QString::number(_scheme->moveValue(Move::out2)) ); + + // Position + posEval1->setText( QString::number(_scheme->ringValue(0)) ); + posEval2->setText( QString::number(_scheme->ringValue(1)) ); + posEval3->setText( QString::number(_scheme->ringValue(2)) ); + posEval4->setText( QString::number(_scheme->ringValue(3)) ); + posEval5->setText( QString::number(_scheme->ringValue(4)) ); + + diffEval2->setText( QString::number(_scheme->ringDiff(1)) ); + diffEval3->setText( QString::number(_scheme->ringDiff(2)) ); + diffEval4->setText( QString::number(_scheme->ringDiff(3)) ); + diffEval5->setText( QString::number(_scheme->ringDiff(4)) ); + + // InARow + rowEval2->setText( QString::number(_scheme->inARowValue(0)) ); + rowEval3->setText( QString::number(_scheme->inARowValue(1)) ); + rowEval4->setText( QString::number(_scheme->inARowValue(2)) ); + rowEval5->setText( QString::number(_scheme->inARowValue(3)) ); + + // Count + countEval1->setText( QString::number(_scheme->stoneValue(1)) ); + countEval2->setText( QString::number(_scheme->stoneValue(2)) ); + countEval3->setText( QString::number(_scheme->stoneValue(3)) ); + countEval4->setText( QString::number(_scheme->stoneValue(4)) ); + countEval5->setText( QString::number(_scheme->stoneValue(5)) ); + + updateEval(); +} + +void EvalDlgImpl::updateEval() +{ + int value; + + // set temporary the new scheme + _board->setEvalScheme(_scheme); + value = - _board->calcEvaluation(); + _board->setEvalScheme(_origScheme); + + kdDebug(12011) << "Updated Eval: " << value << endl; + + if (value<-15999 || value>15999) value=0; + actualEval->display(value); +} + +void EvalDlgImpl::updateMove() +{ + _scheme->setMoveValue(Move::move1, moveEval1->text().toInt()); + _scheme->setMoveValue(Move::move2, moveEval2->text().toInt()); + _scheme->setMoveValue(Move::left2, moveEval2->text().toInt()); + _scheme->setMoveValue(Move::right2, moveEval2->text().toInt()); + _scheme->setMoveValue(Move::move3, moveEval3->text().toInt()); + _scheme->setMoveValue(Move::left3, moveEval3->text().toInt()); + _scheme->setMoveValue(Move::right3, moveEval3->text().toInt()); + + _scheme->setMoveValue(Move::push1with2, moveEval4->text().toInt()); + _scheme->setMoveValue(Move::push1with3, moveEval5->text().toInt()); + _scheme->setMoveValue(Move::push2, moveEval6->text().toInt()); + + _scheme->setMoveValue(Move::out1with2, moveEval7->text().toInt()); + _scheme->setMoveValue(Move::out1with3, moveEval8->text().toInt()); + _scheme->setMoveValue(Move::out2, moveEval9->text().toInt()); + + updateEval(); +} + +void EvalDlgImpl::updateCount() +{ + _scheme->setStoneValue(1, countEval1->text().toInt()); + _scheme->setStoneValue(2, countEval2->text().toInt()); + _scheme->setStoneValue(3, countEval3->text().toInt()); + _scheme->setStoneValue(4, countEval4->text().toInt()); + _scheme->setStoneValue(5, countEval5->text().toInt()); + + updateEval(); +} + +void EvalDlgImpl::updateFields() +{ + _scheme->setRingValue(0, posEval1->text().toInt()); + _scheme->setRingValue(1, posEval2->text().toInt()); + _scheme->setRingValue(2, posEval3->text().toInt()); + _scheme->setRingValue(3, posEval4->text().toInt()); + _scheme->setRingValue(4, posEval5->text().toInt()); + _scheme->setRingDiff(1, diffEval2->text().toInt()); + _scheme->setRingDiff(2, diffEval3->text().toInt()); + _scheme->setRingDiff(3, diffEval4->text().toInt()); + _scheme->setRingDiff(4, diffEval5->text().toInt()); + + updateEval(); +} + +void EvalDlgImpl::updateInARow() +{ + _scheme->setInARowValue(0, rowEval2->text().toInt()); + _scheme->setInARowValue(1, rowEval3->text().toInt()); + _scheme->setInARowValue(2, rowEval4->text().toInt()); + _scheme->setInARowValue(3, rowEval5->text().toInt()); + + updateEval(); +} + + +void EvalDlgImpl::deleteEntry() +{ + int i = evalList->currentItem(); + // You cannot delete Pseudo Items 0 (Current) and 1 (Default) + + if (i>1) { + QString name = evalList->text(i); + evalList->removeItem(i); + + KConfig* config = kapp->config(); + config->setGroup("General"); + QStringList list = config->readListEntry("EvalSchemes"); + list.remove(name); + config->writeEntry("EvalSchemes", list); + config->sync(); + } +} + +void EvalDlgImpl::saveas() +{ + KLineEditDlg dlg(i18n("Name for scheme:"), QString::null, this); + dlg.setCaption(i18n("Save Scheme")); + + if (dlg.exec()) { + QString name=dlg.text(); + KConfig* config = kapp->config(); + config->setGroup("General"); + QStringList list = config->readListEntry("EvalSchemes"); + QListBoxItem *it = evalList->findItem(name); + if (!it) { + evalList->insertItem(name); + it = evalList->findItem(name); + list << name; + config->writeEntry("EvalSchemes", list); + } + evalList->setSelected(it, TRUE); + + EvalScheme savedScheme(*_scheme); + savedScheme.setName(name); + savedScheme.save(config); + config->sync(); + } +} + +void EvalDlgImpl::select(int i) +{ + QString name = evalList->text(i); + + delete _scheme; + _scheme = 0; + + // 2 fixed entries: 0 is Current (origScheme), 1 is Default + + if (i == 0) _scheme = new EvalScheme(*_origScheme); + else if (i==1) { + _scheme = new EvalScheme(name); + } + else { + // read in the Scheme from the config file + _scheme = new EvalScheme(name); + KConfig* config = kapp->config(); + _scheme->read(config); + } + + kdDebug(12011) << "Selected " << name << ", Index " << i << endl; + + disconnectEditLines(); + updateWidgets(); + connectEditLines(); +} +#include "EvalDlgImpl.moc" diff --git a/kenolaba/EvalDlgImpl.h b/kenolaba/EvalDlgImpl.h new file mode 100644 index 00000000..70c903fb --- /dev/null +++ b/kenolaba/EvalDlgImpl.h @@ -0,0 +1,44 @@ +/** + * Some Implementation details for configEval Dialog + * + */ + +#ifndef _EVALDLGIMPL_H_ +#define _EVALDLGIMPL_H_ + +#include "EvalDlg.h" + +class EvalScheme; +class Board; + +class EvalDlgImpl: public EvalDlg +{ + Q_OBJECT + + public: + EvalDlgImpl(QWidget* parent, Board* board); + ~EvalDlgImpl(); + + EvalScheme* evalScheme() { return _scheme; } + + public slots: + void deleteEntry(); + void saveas(); + void select(int i); + void updateCount(); + void updateMove(); + void updateFields(); + void updateInARow(); + + private: + void updateEval(); + void updateWidgets(); + void connectEditLines(); + void disconnectEditLines(); + + EvalScheme *_origScheme, *_scheme; + Board* _board; +}; + +#endif // _EVALDLGIMPL_H_ + diff --git a/kenolaba/EvalScheme.cpp b/kenolaba/EvalScheme.cpp new file mode 100644 index 00000000..18ba5b43 --- /dev/null +++ b/kenolaba/EvalScheme.cpp @@ -0,0 +1,231 @@ +/** + * EvalScheme + * + * Configuration options for a Evaluation Scheme. + * Evaluation Schemes are used for evalution of a Abalone board position + * + * (C) JW, 2000 + */ + +#include +#include + +#include "EvalScheme.h" + +// Default Values +static int defaultRingValue[] = { 45, 35, 25, 10, 0 }; +static int defaultRingDiff[] = { 0, 10, 10, 8, 5 }; +static int defaultStoneValue[]= { 0,-800,-1800,-3000,-4400,-6000 }; +static int defaultMoveValue[Move::typeCount] = { 40,30,30, 15,14,13, + 5,5,5, 2,2,2, 1 }; +static int defaultInARowValue[InARowCounter::inARowCount]= { 2, 5, 4, 3 }; + + +/** + * Constructor: Set Default values + */ +EvalScheme::EvalScheme(QString n) +{ + _name = n; + setDefaults(); +} + + +/** + * Copy Constructor + */ +EvalScheme::EvalScheme(EvalScheme& s) +{ + _name = s._name; + + for(int i=0;i<6;i++) + _stoneValue[i] = s._stoneValue[i]; + + for(int i=0;isetGroup(confSection); + + QStringList list; + QString tmp; + + list = config->readListEntry("StoneValues"); + if (list.count()>0) { + _stoneValue[0] = 0; + for(int i=1;i<6;i++) + _stoneValue[i] = list[i-1].toInt(); + } + + list = config->readListEntry("MoveValues"); + if (list.count()>0) { + for(int i=0;ireadListEntry("InARowValues"); + if (list.count()>0) { + for(int i=0;ireadListEntry("RingValues"); + if (list.count()>0) { + for(int i=0;i<5;i++) + _ringValue[i] = list[i].toInt(); + } + + list = config->readListEntry("RingDiffs"); + if (list.count()>0) { + for(int i=0;i<5;i++) + _ringDiff[i] = list[i].toInt(); + } +} + + +void EvalScheme::save(KConfig* config) +{ + QString confSection = QString("%1 Evaluation Scheme").arg(_name); + config->setGroup(confSection); + + QString entry; + + entry.sprintf("%d,%d,%d,%d,%d", _stoneValue[1], _stoneValue[2], + _stoneValue[3], _stoneValue[4], _stoneValue[5]); + config->writeEntry("StoneValues", entry); + + entry.sprintf("%d", _moveValue[0]); + for(int i=1;iwriteEntry("MoveValues", entry); + + entry.sprintf("%d", _inARowValue[0]); + for(int i=1;iwriteEntry("InARowValues", entry); + + entry.sprintf("%d,%d,%d,%d,%d", _ringValue[0], _ringValue[1], + _ringValue[2], _ringValue[3], _ringValue[4]); + config->writeEntry("RingValues", entry); + + entry.sprintf("%d,%d,%d,%d,%d", _ringDiff[0], _ringDiff[1], + _ringDiff[2], _ringDiff[3], _ringDiff[4]); + config->writeEntry("RingDiffs", entry); +} + +void EvalScheme::setRingValue(int ring, int value) +{ + if (ring >=0 && ring <5) + _ringValue[ring] = value; +} + +void EvalScheme::setRingDiff(int ring, int value) +{ + if (ring >=1 && ring <5) + _ringDiff[ring] = value; +} + +void EvalScheme::setStoneValue(int stoneDiff, int value) +{ + if (stoneDiff>0 && stoneDiff<6) + _stoneValue[stoneDiff] = value; +} + +void EvalScheme::setMoveValue(int type, int value) +{ + if (type>=0 && type=0 && stones=,,,,... (34 values) + * + */ + +EvalScheme* EvalScheme::create(QString scheme) +{ + int pos = scheme.find('='); + if (pos<0) return 0; + + EvalScheme* evalScheme = new EvalScheme( scheme.left(pos) ); + evalScheme->setDefaults(); + + QStringList list = QStringList::split( QString(","), scheme.right(pos+1) ); + + int i=0; + while(i_stoneValue[i+1] = list[i].toInt(); + else if (i<10) + evalScheme->_ringValue[i-5] = list[i].toInt(); + else if (i<15) + evalScheme->_ringDiff[i-10] = list[i].toInt(); + else if (i<15+Move::typeCount) + evalScheme->_moveValue[i-15] = list[i].toInt(); + else if (i<15+Move::typeCount+InARowCounter::inARowCount) + evalScheme->_inARowValue[i-15-Move::typeCount] = list[i].toInt(); + i++; + } + + return evalScheme; +} + +QString EvalScheme::ascii() +{ + QString res; + int i; + + res.sprintf("%s=%d", _name.ascii(), _stoneValue[1]); + for(i=1;i<6;i++) + res += QString(",%1").arg( _stoneValue[i] ); + for(i=0;i + +#include "Move.h" + +class KConfig; + +/* + * The constructor gets a name, and tries to read the scheme + * for the Kenolaba configuration file, if nothing found, use + * default values + */ + +class EvalScheme +{ + public: + EvalScheme(QString); + EvalScheme(EvalScheme&); + ~EvalScheme() {} + + void setDefaults(); + void read(KConfig*); + void save(KConfig*); + + static EvalScheme* create(QString); + QString ascii(); + + void setName(QString n) { _name = n; } + void setRingValue(int ring, int value); + void setRingDiff(int ring, int value); + void setStoneValue(int stoneDiff, int value); + void setMoveValue(int type, int value); + void setInARowValue(int stones, int value); + + QString name() { return _name; } + int ringValue(int r) { return (r>=0 && r<5) ? _ringValue[r] : 0; } + int ringDiff(int r) { return (r>0 && r<5) ? _ringDiff[r] : 0; } + int stoneValue(int s) { return (s>0 && s<6) ? _stoneValue[s] : 0; } + int moveValue(int t) { return (t>=0 && t=0 && s +#include + +#include + +#include + +#include "Move.h" +#include "Board.h" + +const QString nameOfDir(int dir) +{ + dir = dir % 6; + return + (dir == 1) ? i18n("Right") : + (dir == 2) ? i18n("RightDown") : + (dir == 3) ? i18n("LeftDown") : + (dir == 4) ? i18n("Left") : + (dir == 5) ? i18n("LeftUp") : + (dir == 0) ? i18n("RightUp") : QString("??"); +} + +QString nameOfPos(int p) +{ + static char tmp[3]; + tmp[0] = 'A' + (p-12)/11; + tmp[1] = '1' + (p-12)%11; + tmp[2] = 0; + + return QString( tmp ); +} + +QString Move::name() const +{ + QString s,tmp; + + /* sideway moves... */ + if (type == left3 || type == right3) { + int f1, f2, df; + + f1 = f2 = field; + df = 2* Board::fieldDiffOfDir(direction); + if (df > 0) + f2 += df; + else + f1 += df; + + s = nameOfPos( f1 ); + s += '-'; + s += nameOfPos( f2 ); + s+= '/'; + s+= (type == left3) ? nameOfDir(direction-1): nameOfDir(direction+1); + } + else if ( type == left2 || type == right2) { + int f1, f2, df; + + f1 = f2 = field; + df = Board::fieldDiffOfDir(direction); + if (df > 0) + f2 += df; + else + f1 += df; + + s = nameOfPos( f1 ); + s += '-'; + s += nameOfPos( f2 ); + s+= '/'; + s+= (type == left2) ? nameOfDir(direction-1): nameOfDir(direction+1); + } + else if (type == none) { + s = QString("??"); + } + else { + s = nameOfPos( field ); + s += '/'; + s += nameOfDir(direction); + + tmp = (type <3 ) ? i18n("Out") : + (type <6 ) ? i18n("Push") : QString(""); + if (!tmp.isEmpty()) { + s += '/'; + s += tmp; + } + } + return s; +} + +void Move::print() const +{ + printf("%s", name().ascii() ); +} + +MoveTypeCounter::MoveTypeCounter() +{ + init(); +} + +void MoveTypeCounter::init() +{ + for(int i=0;i < Move::typeCount;i++) + count[i] = 0; +} + +int MoveTypeCounter::sum() +{ + int sum = count[0]; + + for(int i=1;i < Move::typeCount;i++) + sum += count[i]; + + return sum; +} + + +InARowCounter::InARowCounter() +{ + init(); +} + +void InARowCounter::init() +{ + for(int i=0;i < inARowCount;i++) + count[i] = 0; +} + +MoveList::MoveList() +{ + clear(); +} + +void MoveList::clear() +{ + int i; + + for(i=0;i= Move::typeCount) return; + if (nextUnused == MaxMoves) return; + + assert( nextUnused < MaxMoves ); + + /* adjust queue */ + if (first[t] == -1) { + first[t] = last[t] = nextUnused; + } + else { + assert( last[t] < nextUnused ); + next[last[t]] = nextUnused; + last[t] = nextUnused; + } + + next[nextUnused] = -1; + move[nextUnused] = m; + nextUnused++; +} + +bool MoveList::isElement(int f) +{ + int i; + + for(i=0; i 0) && (mm.direction != m.direction)) + continue; + + /* if type is supplied it has to match */ + if ((m.type != Move::none) && (m.type != mm.type)) + continue; + + if (m.type == mm.type) { + /* exact match; eventually supply direction */ + m.direction = mm.direction; + if (del) mm.type = Move::none; + return true; + } + + switch(mm.type) { + case Move::left3: + case Move::right3: + if (startType == start3 || startType == all) { + m.type = mm.type; + m.direction = mm.direction; + if (del) mm.type = Move::none; + return true; + } + break; + case Move::left2: + case Move::right2: + if (startType == start2 || startType == all) { + m.type = mm.type; + m.direction = mm.direction; + if (del) mm.type = Move::none; + return true; + } + break; + default: + if (startType == start1 || startType == all) { + /* unexact match: supply type */ + m.type = mm.type; + m.direction = mm.direction; + if (del) mm.type = Move::none; + return true; + } + } + } + return false; +} + + +bool MoveList::getNext(Move& m, int maxType) +{ + if (actualType == Move::typeCount) return false; + + while(1) { + while(actualType < 0 || actual[actualType] == -1) { + actualType++; + if (actualType == Move::typeCount) return false; + actual[actualType] = first[actualType]; + if (actualType > maxType) return false; + } + m = move[actual[actualType]]; + actual[actualType] = next[actual[actualType]]; + if (m.type != Move::none) break; + } + + return true; +} + + diff --git a/kenolaba/Move.h b/kenolaba/Move.h new file mode 100644 index 00000000..1226d969 --- /dev/null +++ b/kenolaba/Move.h @@ -0,0 +1,132 @@ +/* Classes Move, MoveList + * - represents a move in the game of Abalone + * + * Josef Weidendorfer, 28.8.97 +*/ + +#ifndef _MOVE_H_ +#define _MOVE_H_ + +#include + +class Move +{ + public: + + /* Type of move: Moves are searched in this order */ + enum MoveType { out2 = 0, out1with3, out1with2, push2, + push1with3, push1with2, move3, left3, right3, + left2, right2, move2, move1, none }; + enum { typeCount = none }; + + Move() { type = none; } + Move(short f, char d, MoveType t) + { field = f; direction = d, type = t; } + + + bool isOutMove() const + { return type <= out1with2; } + bool isPushMove() const + { return type <= push1with2; } + static int maxOutType() + { return out1with2; } + static int maxPushType() + { return push1with2; } + static int maxMoveType() + { return move1; } + + QString name() const; + + void print() const; + + /* Directions */ + enum { Right=1, RightDown, LeftDown, Left, LeftUp, RightUp }; + + short field; + unsigned char direction; + MoveType type; +}; + +/** + * Class MoveTypeCounter + * + * Used in Board evaluation to count types of board allowed + * in a position + */ +class MoveTypeCounter +{ + public: + MoveTypeCounter(); + ~MoveTypeCounter() {} + + void init(); + void incr(int t) { count[t]++; } + int get(int t) { return count[t]; } + int sum(); + + private: + int count[Move::typeCount]; +}; + +/** + * Class InARowCounter + * + * Used in Board evaluation to count connectiveness + * of some degrees in a position + */ +class InARowCounter +{ + public: + enum InARowType { inARow2 = 0, inARow3, inARow4, inARow5, inARowCount }; + + InARowCounter(); + ~InARowCounter() {} + + void init(); + void incr(int c) { count[c]++; } + int get(int c) { return count[c]; } + + private: + int count[inARowCount]; +}; + + +/* MoveList stores a fixed number of moves (for efficince) + * returns reference of next move ordered according to type + * does nothing if there isn't enough free space + * + * Recommend usage (* means 0 or more times): + * [ clear() ; insert() * ; isElement() * ; getNext() * ] * + */ +class MoveList +{ + public: + MoveList(); + + enum { MaxMoves = 150 }; + + /* for isElement: search for moves starting with 1/2/3 fields */ + enum { all , start1, start2, start3 }; + + void clear(); + void insert(Move); + bool isElement(int f); + bool isElement(Move&, int startType, bool del=false); + void insert(short f, char d, Move::MoveType t) + { insert( Move(f,d,t) ); } + int getLength() + { return nextUnused; } + + bool getNext(Move&,int maxType); /* returns false if no more moves */ + + private: + Move move[MaxMoves]; + int next[MaxMoves]; + int first[Move::typeCount]; + int last[Move::typeCount]; + int actual[Move::typeCount]; + int nextUnused, actualType; +}; + +#endif /* _MOVE_H_ */ + diff --git a/kenolaba/Network.cpp b/kenolaba/Network.cpp new file mode 100644 index 00000000..e12f6b5e --- /dev/null +++ b/kenolaba/Network.cpp @@ -0,0 +1,193 @@ +#include + +#include "Network.h" + +#include +#include +#include +#include +#include +#include +#include + +Listener::Listener(const char* h, int p, struct sockaddr_in s,bool r) +{ + if (h==0) + host[0]=0; + else { + int l = strlen(h); + if (l>99) l=99; + strncpy(host, h, l); + host[l] = 0; + } + port = p; + sin = s; + reachable = r; +} + +Network::Network(int port) +{ + struct sockaddr_in name; + int i,j; + + listeners.setAutoDelete(TRUE); + + fd = ::socket (PF_INET, SOCK_STREAM, 0); + if (fd<0) return; + + for(i = 0; i<5;i++) { + name.sin_family = AF_INET; + name.sin_port = htons (port+i); + name.sin_addr.s_addr = htonl (INADDR_ANY); + if (bind (fd, (struct sockaddr *) &name, sizeof (name)) >= 0) + break; + // printf("...Port %d in use\n", port+i); + } + mySin = name; + // printf("I'm using Port %d\n", port+i); + if (i==5) { + printf("Error in bind to port %d\n", port); + close(fd); + fd = -1; + return; + } + for(j = 0; jreachable) + sendString( l->sin, tmp, len); + } + listeners.clear(); + + delete sn; +} + +void Network::gotConnection() +{ + static char tmp[1024]; + int len=0; + struct sockaddr_in sin; + kde_socklen_t sz = sizeof (sin); + + // printf("GotConnection: "); + int s = accept(fd,(struct sockaddr *)&sin, &sz); + if (s<0) { + printf("Error in accept\n"); + return; + } + while(read(s, tmp+len, 1)==1) len++; + close(s); + tmp[len]=0; len++; + // printf("Got: '%s'\n",tmp); + if (strncmp(tmp,"reg ",4)==0) { + int port = atoi(tmp+4); + sin.sin_port = htons( port ); + Listener *l = new Listener(0,0,sin); + // printf("Reg of 0x%x:%d\n", + // ntohl(sin.sin_addr.s_addr ), ntohs(sin.sin_port)); + listeners.append(l); + return; + } + + if (strncmp(tmp,"unreg ",6)==0) { + int port = atoi(tmp+6); + sin.sin_port = htons( port ); + Listener* l; + for(l=listeners.first(); l!=0; l=listeners.next()) + if (l->sin.sin_addr.s_addr == sin.sin_addr.s_addr && + l->sin.sin_port == sin.sin_port) break; + if (l==0) { + printf("Error: UnReg of 0x%x:%d. Not Found\n", + ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port)); + return; + } + listeners.remove(l); + // printf("UnReg of 0x%x:%d\n", + // ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port)); + return; + } + + if (strncmp(tmp,"pos ",4)==0) { + emit gotPosition(tmp+4); + } +} + +void Network::addListener(const char* host, int port) +{ + struct hostent *hostinfo; + struct sockaddr_in name; + + memset(&name, 0, sizeof(struct sockaddr_in)); + name.sin_family = AF_INET; + name.sin_port = htons (port); + hostinfo = gethostbyname (host); + if (hostinfo == NULL) { + printf ("Error in addListener: Unknown host %s.\n", host); + return; + } + name.sin_addr = *(struct in_addr *) hostinfo->h_addr; + + Listener *l = new Listener(host,port,name); +// printf("Added Listener %s, 0x%x:%d\n", +// host, ntohl(name.sin_addr.s_addr), ntohs(name.sin_port)); + listeners.append(l); + + char tmp[50]; + int len = sprintf(tmp, "reg %d", ntohs(mySin.sin_port)); + + if (!sendString( name, tmp, len)) + listeners.remove(l); +} + +void Network::broadcast(const char* pos) +{ + char tmp[1024]; + int len = sprintf(tmp,"pos %s", pos); + + for(Listener* l=listeners.first(); l!=0; l=listeners.next()) + if (l->reachable) + l->reachable = sendString(l->sin, tmp, len); +} + +bool Network::sendString(struct sockaddr_in sin, char* str, int len) +{ + int s = ::socket (PF_INET, SOCK_STREAM, 0); + if (s<0) { + printf("Error in sendString/socket ??\n"); + return false; + } + if (::connect (s, (struct sockaddr *)&sin, sizeof (sin)) <0) { + printf("Error in sendString/connect to socket 0x%x:%d\n", + ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port) ); + return false; + } + write(s, str, len); + close(s); + // printf("Send '%s' to 0x%x:%d\n", str, + // ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port) ); + return true; +} + +#include "Network.moc" diff --git a/kenolaba/Network.h b/kenolaba/Network.h new file mode 100644 index 00000000..d5251cc6 --- /dev/null +++ b/kenolaba/Network.h @@ -0,0 +1,60 @@ +/* + * Simple Network Support + * Install a listening socket; receive positions on incoming + * connections (incoming positions are treated as if pasted in) + */ + +#ifndef _NETWORK_H_ +#define _NETWORK_H_ + +#include +#include + +#include +#include +#include + +class Listener { + public: + Listener(const char*,int,struct sockaddr_in,bool=true); + + char host[100]; + int port; + struct sockaddr_in sin; + bool reachable; +}; + + +class Network: public QObject +{ + Q_OBJECT + + public: + enum { defaultPort = 23412 }; + + /* install listening TCP socket on port */ + Network(int port = defaultPort); + ~Network(); + + bool isOK() { return (fd>=0); } + void addListener(const char* host, int port); + void broadcast(const char* pos); + + signals: + void gotPosition(const char* pos); + + private slots: + void gotConnection(); + + private: + bool sendString(struct sockaddr_in sin, char* str, int len); + + QPtrList listeners; + struct sockaddr_in mySin; + int fd, myPort; + QSocketNotifier *sn; +}; + +#endif + + diff --git a/kenolaba/README b/kenolaba/README new file mode 100644 index 00000000..6d69911b --- /dev/null +++ b/kenolaba/README @@ -0,0 +1,23 @@ +Another kool game for KDE :) + +Kenolaba is a game like Abalone. You play against the computer on a +board. For rules look at the HTML manual. + +INSTALLATION: +If you find this directory (and README) in a kde package bundled with +other applications, there is nothing to do. By installing the package +Kenolaba should be installed too. + +Otherwise get the kdeapps package and unpack it. +Make the kenolaba/ directory (with this README) a subdirectory of +kdeapps/ and add "kenolaba" to the SUBDIRS variable in the toplevel +Makefile.in of the kdeapps package. By installing the package kdeapps now +Kenolaba should be installed too. + +Have fun... +...and remember: Your computer already is a real thinking machine ! + +Greetings, + +Josef Weidendorfer + diff --git a/kenolaba/Spy.cpp b/kenolaba/Spy.cpp new file mode 100644 index 00000000..07f17702 --- /dev/null +++ b/kenolaba/Spy.cpp @@ -0,0 +1,155 @@ +/* Class Spion + * + * Josef Weidendorfer, 10/97 + */ + + +#include +#include +#include + +#include +#include + +#include "BoardWidget.h" +#include "Spy.h" + +Spy::Spy(Board& b) + :board(b) +{ + int i; + + top = new QVBoxLayout(this, 5); + + QLabel *l = new QLabel(this); + l->setText( i18n("Actual examined position:") ); + l->setFixedHeight( l->sizeHint().height() ); + l->setAlignment( AlignVCenter | AlignLeft ); + top->addWidget( l ); + + QHBoxLayout* b1 = new QHBoxLayout(); + top->addLayout( b1, 10 ); + + for(i=0;iaddLayout( b2 ); + + actBoard[i] = new BoardWidget(board,this); + actLabel[i] = new QLabel(this); + actLabel[i]->setText("---"); + // actLabel[i]->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + actLabel[i]->setAlignment( AlignHCenter | AlignVCenter); + actLabel[i]->setFixedHeight( actLabel[i]->sizeHint().height() ); + + b2->addWidget( actBoard[i] ); + b2->addWidget( actLabel[i] ); + connect( actBoard[i], SIGNAL(mousePressed()), this, SLOT(nextStep()) ); + } + + l = new QLabel(this); + l->setText( i18n("Best move so far:") ); + l->setFixedHeight( l->sizeHint().height() ); + l->setAlignment( AlignVCenter | AlignLeft ); + top->addWidget( l ); + + b1 = new QHBoxLayout(); + top->addLayout( b1, 10 ); + + for(i=0;iaddLayout( b2 ); + + bestBoard[i] = new BoardWidget(board,this); + bestLabel[i] = new QLabel(this); + bestLabel[i]->setText("---"); + // bestLabel[i]->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + bestLabel[i]->setAlignment( AlignHCenter | AlignVCenter); + bestLabel[i]->setFixedHeight( bestLabel[i]->sizeHint().height() ); + + b2->addWidget( bestBoard[i] ); + b2->addWidget( bestLabel[i] ); + connect( bestBoard[i], SIGNAL(mousePressed()), this, SLOT(nextStep()) ); + } + + connect( &board, SIGNAL(update(int,int,Move&,bool)), + this, SLOT(update(int,int,Move&,bool)) ); + connect( &board, SIGNAL(updateBest(int,int,Move&,bool)), + this, SLOT(updateBest(int,int,Move&,bool)) ); + top->activate(); + setCaption(i18n("Spy")); + // KWM::setDecoration(winId(), 2); + resize(500,300); +} + +void Spy::keyPressEvent(QKeyEvent *) +{ + nextStep(); +} + +void Spy::nextStep() +{ + next = true; +} + +void Spy::clearActBoards() +{ + for(int i=0;iclearPosition(); + actBoard[i]->draw(); + actLabel[i]->setText("---"); + } +} + +void Spy::update(int depth, int value, Move& m, bool wait) +{ + next = false; + + if (depth>BoardCount-1) return; + actBoard[depth]->showMove(m,3); + // actBoard[depth]->showMove(m,0,false); + + if (!wait) { + actLabel[depth]->setText("---"); + return; + } + + if (depthupdatePosition(true); + actLabel[depth+1]->setNum(value); + board.takeBack(); + + if (depthclearPosition(); + actBoard[depth+2]->draw(); + } + } + + while(!next) + kapp->processEvents(); +} + +void Spy::updateBest(int depth, int value, Move& m, bool cutoff) +{ + if (depth>BoardCount-1) return; + bestBoard[depth]->showMove(m,3); + // board.showMove(m,0); + + if (depthupdatePosition(true); + + QString tmp; + tmp.setNum(value); + if (cutoff) tmp += " (CutOff)"; + bestLabel[depth+1]->setText(tmp); + board.takeBack(); + } +} + + + + + + +#include "Spy.moc" diff --git a/kenolaba/Spy.h b/kenolaba/Spy.h new file mode 100644 index 00000000..f69d99d3 --- /dev/null +++ b/kenolaba/Spy.h @@ -0,0 +1,46 @@ +/* Class Spion + * + * Josef Weidendorfer, 10/97 + */ + +#ifndef _SPY_H_ +#define _SPY_H_ + + +#include +#include "Board.h" + + +class BoardWidget; +class QLabel; + +class Spy: public QWidget +{ + Q_OBJECT + +public: + Spy(Board&); + ~Spy() {} + + enum { BoardCount = 5 }; + + void clearActBoards(); + + void keyPressEvent(QKeyEvent *e); + +public slots: + void update(int,int,Move&,bool); + void updateBest(int,int,Move&,bool); + void nextStep(); + +private: + bool next; + Board &board; + QBoxLayout *top; + BoardWidget *actBoard[BoardCount], *bestBoard[BoardCount]; + QLabel *actLabel[BoardCount], *bestLabel[BoardCount]; +}; + + + +#endif /* _SPION_H_ */ diff --git a/kenolaba/TODO b/kenolaba/TODO new file mode 100644 index 00000000..baad7913 --- /dev/null +++ b/kenolaba/TODO @@ -0,0 +1,8 @@ +TODO for Abalone > 1.05b + +* "Back" in Network mode +* Computer Character Editor (=Rating values) +* Search Inspector +* Computer playing in own process/thread +* 3 player mode +* OpenGL 3D board diff --git a/kenolaba/bitmaps/Arrow1 b/kenolaba/bitmaps/Arrow1 new file mode 100644 index 00000000..45c2ce41 --- /dev/null +++ b/kenolaba/bitmaps/Arrow1 @@ -0,0 +1,8 @@ +#define Arrow1_width 16 +#define Arrow1_height 16 +#define Arrow1_x_hot 7 +#define Arrow1_y_hot 7 +static unsigned char Arrow1_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x03, 0x00, 0x0f, + 0xfc, 0x3f, 0xfe, 0x7f, 0xfc, 0x3f, 0x00, 0x0f, 0x80, 0x03, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow1Mask b/kenolaba/bitmaps/Arrow1Mask new file mode 100644 index 00000000..2e50b48d --- /dev/null +++ b/kenolaba/bitmaps/Arrow1Mask @@ -0,0 +1,6 @@ +#define Arrow1Mask_width 16 +#define Arrow1Mask_height 16 +static unsigned char Arrow1Mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xc0, 0x03, 0xc0, 0x0f, 0xfc, 0x3f, + 0xfe, 0x7f, 0xff, 0xff, 0xfe, 0x7f, 0xfc, 0x3f, 0xc0, 0x0f, 0xc0, 0x03, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow2 b/kenolaba/bitmaps/Arrow2 new file mode 100644 index 00000000..47487a89 --- /dev/null +++ b/kenolaba/bitmaps/Arrow2 @@ -0,0 +1,8 @@ +#define Arrow2_width 16 +#define Arrow2_height 16 +#define Arrow2_x_hot 7 +#define Arrow2_y_hot 7 +static unsigned char Arrow2_bits[] = { + 0x00, 0x00, 0x10, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf0, 0x00, 0xf0, 0x08, + 0xe0, 0x0d, 0xc0, 0x0f, 0xc0, 0x0f, 0xf0, 0x0f, 0xe0, 0x0f, 0x80, 0x0f, + 0x00, 0x0e, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow2Mask b/kenolaba/bitmaps/Arrow2Mask new file mode 100644 index 00000000..121295e6 --- /dev/null +++ b/kenolaba/bitmaps/Arrow2Mask @@ -0,0 +1,6 @@ +#define Arrow2Mask_width 16 +#define Arrow2Mask_height 16 +static unsigned char Arrow2Mask_bits[] = { + 0x10, 0x00, 0x38, 0x00, 0x7c, 0x00, 0xfc, 0x00, 0xf8, 0x09, 0xf8, 0x1d, + 0xf0, 0x1f, 0xe0, 0x1f, 0xf0, 0x1f, 0xf8, 0x1f, 0xf0, 0x1f, 0xe0, 0x1f, + 0x80, 0x1f, 0x00, 0x1e, 0x00, 0x18, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow3 b/kenolaba/bitmaps/Arrow3 new file mode 100644 index 00000000..b511a017 --- /dev/null +++ b/kenolaba/bitmaps/Arrow3 @@ -0,0 +1,8 @@ +#define Arrow3_width 16 +#define Arrow3_height 16 +#define Arrow3_x_hot 7 +#define Arrow3_y_hot 7 +static unsigned char Arrow3_bits[] = { + 0x00, 0x00, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x0f, 0x80, 0x07, 0x88, 0x07, + 0xd8, 0x03, 0xf8, 0x01, 0xf8, 0x01, 0xf8, 0x07, 0xf8, 0x03, 0xf8, 0x00, + 0x38, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow3Mask b/kenolaba/bitmaps/Arrow3Mask new file mode 100644 index 00000000..f4af9ccb --- /dev/null +++ b/kenolaba/bitmaps/Arrow3Mask @@ -0,0 +1,6 @@ +#define Arrow3Mask_width 16 +#define Arrow3Mask_height 16 +static unsigned char Arrow3Mask_bits[] = { + 0x00, 0x04, 0x00, 0x0e, 0x00, 0x1f, 0x80, 0x1f, 0xc8, 0x0f, 0xdc, 0x0f, + 0xfc, 0x07, 0xfc, 0x03, 0xfc, 0x07, 0xfc, 0x0f, 0xfc, 0x07, 0xfc, 0x03, + 0xfc, 0x00, 0x3c, 0x00, 0x0c, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow4 b/kenolaba/bitmaps/Arrow4 new file mode 100644 index 00000000..dfcdbd82 --- /dev/null +++ b/kenolaba/bitmaps/Arrow4 @@ -0,0 +1,8 @@ +#define Arrow4_width 16 +#define Arrow4_height 16 +#define Arrow4_x_hot 8 +#define Arrow4_y_hot 7 +static unsigned char Arrow4_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x01, 0xf0, 0x00, + 0xfc, 0x3f, 0xfe, 0x7f, 0xfc, 0x3f, 0xf0, 0x00, 0xc0, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow4Mask b/kenolaba/bitmaps/Arrow4Mask new file mode 100644 index 00000000..6c498fb5 --- /dev/null +++ b/kenolaba/bitmaps/Arrow4Mask @@ -0,0 +1,6 @@ +#define Arrow4Mask_width 16 +#define Arrow4Mask_height 16 +static unsigned char Arrow4Mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x03, 0xf0, 0x03, 0xfc, 0x3f, + 0xfe, 0x7f, 0xff, 0xff, 0xfe, 0x7f, 0xfc, 0x3f, 0xf0, 0x03, 0xc0, 0x03, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow5 b/kenolaba/bitmaps/Arrow5 new file mode 100644 index 00000000..72b93adc --- /dev/null +++ b/kenolaba/bitmaps/Arrow5 @@ -0,0 +1,8 @@ +#define Arrow5_width 16 +#define Arrow5_height 16 +#define Arrow5_x_hot 7 +#define Arrow5_y_hot 7 +static unsigned char Arrow5_bits[] = { + 0x00, 0x00, 0x08, 0x00, 0x38, 0x00, 0xf8, 0x00, 0xf8, 0x03, 0xf8, 0x07, + 0xf8, 0x01, 0xf8, 0x01, 0xd8, 0x03, 0x88, 0x07, 0x80, 0x07, 0x00, 0x0f, + 0x00, 0x0e, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow5Mask b/kenolaba/bitmaps/Arrow5Mask new file mode 100644 index 00000000..da77ea00 --- /dev/null +++ b/kenolaba/bitmaps/Arrow5Mask @@ -0,0 +1,6 @@ +#define Arrow5Mask_width 16 +#define Arrow5Mask_height 16 +static unsigned char Arrow5Mask_bits[] = { + 0x0c, 0x00, 0x3c, 0x00, 0xfc, 0x00, 0xfc, 0x03, 0xfc, 0x07, 0xfc, 0x0f, + 0xfc, 0x07, 0xfc, 0x03, 0xfc, 0x07, 0xdc, 0x0f, 0xc8, 0x0f, 0x80, 0x1f, + 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x04, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow6 b/kenolaba/bitmaps/Arrow6 new file mode 100644 index 00000000..159770b4 --- /dev/null +++ b/kenolaba/bitmaps/Arrow6 @@ -0,0 +1,8 @@ +#define Arrow6_width 16 +#define Arrow6_height 16 +#define Arrow6_x_hot 7 +#define Arrow6_y_hot 7 +static unsigned char Arrow6_bits[] = { + 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x80, 0x0f, 0xe0, 0x0f, 0xf0, 0x0f, + 0xc0, 0x0f, 0xc0, 0x0f, 0xe0, 0x0d, 0xf0, 0x08, 0xf0, 0x00, 0x78, 0x00, + 0x38, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow6Mask b/kenolaba/bitmaps/Arrow6Mask new file mode 100644 index 00000000..453ffde2 --- /dev/null +++ b/kenolaba/bitmaps/Arrow6Mask @@ -0,0 +1,6 @@ +#define Arrow6Mask_width 16 +#define Arrow6Mask_height 16 +static unsigned char Arrow6Mask_bits[] = { + 0x00, 0x18, 0x00, 0x1e, 0x80, 0x1f, 0xe0, 0x1f, 0xf0, 0x1f, 0xf8, 0x1f, + 0xf0, 0x1f, 0xe0, 0x1f, 0xf0, 0x1f, 0xf8, 0x1d, 0xf8, 0x09, 0xfc, 0x00, + 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Makefile.am b/kenolaba/bitmaps/Makefile.am new file mode 100644 index 00000000..d25cc3b2 --- /dev/null +++ b/kenolaba/bitmaps/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = Arrow1 Arrow1Mask Arrow2 Arrow2Mask Arrow3 Arrow3Mask Arrow4 Arrow4Mask Arrow5 Arrow5Mask Arrow6 Arrow6Mask diff --git a/kenolaba/hi128-app-kenolaba.png b/kenolaba/hi128-app-kenolaba.png new file mode 100644 index 00000000..d645ff30 Binary files /dev/null and b/kenolaba/hi128-app-kenolaba.png differ diff --git a/kenolaba/hi16-app-kenolaba.png b/kenolaba/hi16-app-kenolaba.png new file mode 100644 index 00000000..44974a0f Binary files /dev/null and b/kenolaba/hi16-app-kenolaba.png differ diff --git a/kenolaba/hi22-app-kenolaba.png b/kenolaba/hi22-app-kenolaba.png new file mode 100644 index 00000000..d5b649f4 Binary files /dev/null and b/kenolaba/hi22-app-kenolaba.png differ diff --git a/kenolaba/hi32-app-kenolaba.png b/kenolaba/hi32-app-kenolaba.png new file mode 100644 index 00000000..9620d54b Binary files /dev/null and b/kenolaba/hi32-app-kenolaba.png differ diff --git a/kenolaba/hi48-app-kenolaba.png b/kenolaba/hi48-app-kenolaba.png new file mode 100644 index 00000000..2952e03e Binary files /dev/null and b/kenolaba/hi48-app-kenolaba.png differ diff --git a/kenolaba/hi64-app-kenolaba.png b/kenolaba/hi64-app-kenolaba.png new file mode 100644 index 00000000..9e5aa438 Binary files /dev/null and b/kenolaba/hi64-app-kenolaba.png differ diff --git a/kenolaba/kenolaba.cpp b/kenolaba/kenolaba.cpp new file mode 100644 index 00000000..e59aa9d7 --- /dev/null +++ b/kenolaba/kenolaba.cpp @@ -0,0 +1,71 @@ +/* Start point */ + +#include + +#include +#include +#include +#include + +#include + +#include "version.h" +#include "AbTop.h" + + +static const char description[] = + I18N_NOOP("Board game inspired by Abalone"); + +static KCmdLineOptions options[] = +{ + { "h", 0, 0}, + { "host ", I18N_NOOP("Use 'host' for network game"), 0 }, + { "p", 0, 0}, + { "port ", I18N_NOOP("Use 'port' for network game"), 0 }, + KCmdLineLastOption +}; + +AbTop *create(KCmdLineArgs *args) +{ + AbTop* top = new AbTop; + if (args->isSet("port")) + top->netPort( args->getOption("port").toInt() ); + if (args->isSet("host")) + top->netHost( args->getOption("host").data() ); + top->readConfig(); + return top; +} + +int main(int argc, char *argv[]) +{ + KAboutData aboutData( "kenolaba", I18N_NOOP("Kenolaba"), + KENOLABA_VERSION, description, KAboutData::License_GPL, + "(c) 1997-2000, Josef Weidendorfer"); + aboutData.addAuthor("Josef Weidendorfer",0, "Josef.Weidendorfer@gmx.de"); + aboutData.addAuthor("Robert Williams"); + KCmdLineArgs::init( argc, argv, &aboutData ); + KCmdLineArgs::addCmdLineOptions( options ); // Add our own options. + + KApplication app; + KGlobal::locale()->insertCatalogue("libkdegames"); + + /* command line handling */ + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + /* session management */ + if ( app.isRestored() ) { + uint n = 1; + while ( KMainWindow::canBeRestored(n) ) { + AbTop *top = create(args); + top->restore(n); + n++; + } + } else { + AbTop *top = create(args); + app.setMainWidget(top); + top->show(); + } + args->clear(); + return app.exec(); +} + diff --git a/kenolaba/kenolaba.desktop b/kenolaba/kenolaba.desktop new file mode 100644 index 00000000..add3fdda --- /dev/null +++ b/kenolaba/kenolaba.desktop @@ -0,0 +1,64 @@ +[Desktop Entry] +Name=Kenolaba +Name[ar]=لعبة Kenolaba +Name[be]=Ðбалон +Name[bn]=কেনোলাবা +Name[hi]=के-नोलाबा +Name[ne]=केनोलाबा +Name[pa]=ਕੀਨੋਲਾਬਾ +Name[ta]=கெனோபà¯à®³à®¾ +Name[tg]=Кенолаба +Name[zh_TW]=Kenolaba 互推 +Name[zu]=I-Kenolaba +Icon=kenolaba +Type=Application +Exec=kenolaba %i %m -caption "%c" +DocPath=kenolaba/index.html +GenericName=Abalone-like Board Game +GenericName[be]=ÐаÑÑ‚Ð¾Ð»ÑŒÐ½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ Ñ‚Ñ‹Ð¿Ñƒ Ðбалон +GenericName[bg]=ЛогичеÑка игра +GenericName[bn]=অà§à¦¯à¦¾à¦¬à¦¾à¦²à¦¨-জাতীয় ছকভিতà§à¦¤à¦¿à¦• খেলা +GenericName[br]=Ur c'hoari taolenn a seurt gant Abalone +GenericName[bs]=Igra nalik na Abalone +GenericName[ca]=Jocs de taula similar a l'Abalone +GenericName[cs]=Deskové hry podobné Abalone +GenericName[cy]=Gêm Fwrdd sy'n debyg i Abalone +GenericName[da]=Abalone-lignende brætspil +GenericName[de]=Abalone-ähnliches Brettspiel +GenericName[el]=ΕπιτÏαπέζιο παιχνίδι παÏόμοιο με το Abalone +GenericName[eo]=Abalone-simila bretludo +GenericName[es]=Juegos de tablero similar a Abalone +GenericName[et]=Abalone moodi lauamäng +GenericName[eu]=Abalone bezalako mahai-jokoa +GenericName[fa]=بازی Abalone-like Board +GenericName[fi]=Abalone-tyylinen lautapeli +GenericName[fr]=Jeu de plateau dans le style d'Abalone +GenericName[he]=חיקוי Abalon, משחק לוח +GenericName[hr]=Igra na ploÄi poput Abalonea +GenericName[hu]=Abalone-szerű táblás +GenericName[is]=Leikur sem líkist Abalone +GenericName[it]=Gioco da tavolo simile ad Abalone +GenericName[ja]=Abalone ã®ã‚ˆã†ãªãƒœãƒ¼ãƒ‰ã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​ក្ážáž¶ážšâ€‹ážŠáž¼áž… Abalone +GenericName[lv]=Abalone lÄ«dzÄ«ga galda spÄ“le +GenericName[mk]=Игра на табла Ñлична на Abalone +GenericName[nb]=Abalone-lignende brettspill +GenericName[nds]=Abalone-liek Brettspeel +GenericName[ne]=à¤à¤¬à¤¾à¤²à¥‹à¤¨ जसà¥à¤¤à¥ˆ बोरà¥à¤¡ खेल +GenericName[nl]=Abalone-achtig bordspel +GenericName[nn]=Abalone-liknande brettspel +GenericName[pl]=Gra planszowa typu Abalone +GenericName[pt]=Jogo de Tabuleiro tipo Abalone +GenericName[pt_BR]=Jogo parecido com Abalone +GenericName[ru]=Ðбалоне +GenericName[se]=Abalone-lágan duolbbášspeallu +GenericName[sk]=Stolová hra typu Abalone +GenericName[sl]=PloÅ¡Äadna igra, podobna Abalone +GenericName[sr]=Игра на табли налик на Abalone +GenericName[sr@Latn]=Igra na tabli nalik na Abalone +GenericName[sv]=Abalone-liknande brädspel +GenericName[uk]=Гра на дошці подібна до гри Abalone +GenericName[zh_TW]=é¡žä¼¼ Abalone 的棋盤éŠæˆ² +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;BoardGame; diff --git a/kenolaba/kenolabaui.rc b/kenolaba/kenolabaui.rc new file mode 100644 index 00000000..cf8c1626 --- /dev/null +++ b/kenolaba/kenolabaui.rc @@ -0,0 +1,53 @@ + + + + + &Game + + + &Move + + + + + &Edit + + + + + + &Settings + + + + + + + + + &Settings + + + + + + + + + + + + + + Main Toolbar + + + + + + + + + + + diff --git a/kenolaba/toolbar/Makefile.am b/kenolaba/toolbar/Makefile.am new file mode 100644 index 00000000..5fdccbb7 --- /dev/null +++ b/kenolaba/toolbar/Makefile.am @@ -0,0 +1,7 @@ +tb_DATA = new.xpm stop.xpm edit.xpm hint.xpm undo.xpm \ + redball.xpm yellowball.xpm noball.xpm warning.xpm ok.xpm \ + spy0.xpm spy1.xpm spy2.xpm spy3.xpm network.xpm +tbdir = $(kde_datadir)/kenolaba/pics + +EXTRA_DIST = $(tb_DATA) + diff --git a/kenolaba/toolbar/edit.xpm b/kenolaba/toolbar/edit.xpm new file mode 100644 index 00000000..20412f2e --- /dev/null +++ b/kenolaba/toolbar/edit.xpm @@ -0,0 +1,38 @@ +/* XPM */ +static char*ab[]={ +"22 22 13 1", +"j c #ffa858", +"d c #c0c0c0", +"c c #d6cdbb", +"g c #ffc0c0", +"h c #c05800", +"# c #000000", +"k c #ffdca8", +". c None", +"e c #c00000", +"b c #a0a0a4", +"a c #dcdcdc", +"i c #ffffff", +"f c #ff0000", +"......................", +"................#aa...", +"...............#aa#b..", +".............cc#aa#bc.", +"......#########aa#bb..", +".....#dddddddd#aa#bc..", +".....#dededed#aa#bb...", +"....#ddfgfgfg#aa#bcc..", +"....#ddddddd####bb.cc.", +"...#dddddde#aa##bc....", +"...#dddhdd#iia##b.....", +"..#ddddjkd#iab##b#c...", +"..#dddddd#iab##bb#c...", +"..c#ddhdd#ab##bb#c....", +"...#ddjk#ib##bbi#c....", +"...c#dd####bbbi#c.....", +"....#ddhbbbbddi#c.....", +"....c#djkjkjki#c......", +".....#iiiiiiii#c......", +".....c########c.......", +"......................", +"......................"}; diff --git a/kenolaba/toolbar/help.xpm b/kenolaba/toolbar/help.xpm new file mode 100644 index 00000000..71348f4d --- /dev/null +++ b/kenolaba/toolbar/help.xpm @@ -0,0 +1,29 @@ +/* XPM */ +static char * help_xpm[] = { +"22 22 4 1", +" c None", +". c #000000", +"X c #000086", +"o c #868286", +" ", +" ", +" ", +" ", +" . XXXXXo ", +" .. XX oXXo ", +" ... XXo XXX ", +" .... XXo XXX ", +" ..... oXX oXXo ", +" ...... XXo ", +" ....... XX ", +" ........ XXo ", +" ..... XXo ", +" .. .. ", +" . .. XXX ", +" .. XXX ", +" .. ", +" .. ", +" ", +" ", +" ", +" "}; diff --git a/kenolaba/toolbar/hint.xpm b/kenolaba/toolbar/hint.xpm new file mode 100644 index 00000000..399c7c07 --- /dev/null +++ b/kenolaba/toolbar/hint.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * hint_xpm[] = { +"22 22 6 1", +" c None", +". c #000086", +"X c #868286", +"o c black", +"O c red", +"+ c yellow", +" ", +" ", +" .....X ", +" .. X..X ", +" ..X ... ", +" ..X ... ", +" X.. X..X ", +" ..X ", +" .. ", +" oooo ..X ", +" oooooooo ..X ", +" ooOOOOoooo ", +" oOOo oooo ... ", +" oOOo oo oooo... ", +" oOo oooo o+o ", +" oOo oooo o+o ", +" oooo oo o++o ", +" oooo o++o ", +" ooooo+++oo ", +" oooooooo ", +" oooo ", +" "}; diff --git a/kenolaba/toolbar/network.xpm b/kenolaba/toolbar/network.xpm new file mode 100644 index 00000000..0878c2bb --- /dev/null +++ b/kenolaba/toolbar/network.xpm @@ -0,0 +1,35 @@ +/* XPM */ +static char*net[]={ +"22 22 10 1", +". c None", +"b c #808080", +"f c #a0a0a0", +"a c #c3c3c3", +"# c #000000", +"e c #0000c0", +"h c #ffff00", +"d c #585858", +"c c #ffffff", +"g c #ff0000", +"......................", +"......................", +".....#######..........", +"...##aaaabbb##........", +"...#ccaaaaabbb##......", +"...#ddccaaaaabbb#.....", +"...#eeddccaaabbbb#....", +"...#eeeeddcaadbbb#....", +"...#eeeeeecbbdbbb#....", +"...#eeeeeecbbdbbb#....", +"...#aeeeeecbbdbbb#....", +"....#aaeeecbbdbb#.....", +"....###aaecbb###......", +"....#aa##aa##dd#......", +".....##aa##dd##.......", +"..###..##ad##..d##d...", +".##aa##..##.#.##aa##..", +".#aaaaa##..#.#fgagaf#.", +"..##daaaa##..#aaaaaa##", +"....##daa#...#ahaaha#.", +"......###.....##ah##..", +"...............d##d..."}; diff --git a/kenolaba/toolbar/new.xpm b/kenolaba/toolbar/new.xpm new file mode 100644 index 00000000..e5aa03fc --- /dev/null +++ b/kenolaba/toolbar/new.xpm @@ -0,0 +1,36 @@ +/* XPM */ +static char*ab[]={ +"22 22 11 1", +"h c #ffa858", +"d c #d6cdbb", +"b c #c0c0c0", +"f c #ffc0c0", +"g c #c05800", +"# c #000000", +"i c #ffdca8", +". c None", +"c c #c00000", +"a c #ffffff", +"e c #ff0000", +"......................", +"................#a....", +"................#a....", +".............#a.#a.#a.", +"......#######.#a#a#a..", +".....#bbbbbbbb.#.#....", +".....#bcbcbcba.d......", +"....#bbefefefb#a#a#a..", +"....#bbbbbbbb#a.#a.#a.", +"...#bbbbbbcbcbb.#a....", +"...#bbbgbbefefba#a....", +"..#bbbbhibbbbbbba#d...", +"..#bbbbbbbbbbcbba#d...", +"..d#bbgbbbgbbefa#d....", +"...#bbhibbhibbba#d....", +"...d#bbbbbbbbba#d.....", +"....#bbgbgbgbba#d.....", +"....d#bhihihia#d......", +".....#aaaaaaaa#d......", +".....d########d.......", +"......................", +"......................"}; diff --git a/kenolaba/toolbar/noball.xpm b/kenolaba/toolbar/noball.xpm new file mode 100644 index 00000000..14673de6 --- /dev/null +++ b/kenolaba/toolbar/noball.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char*noball[]={ +"16 16 3 1", +"# c #808080", +". c None", +"a c #ffffff", +".....#####......", +"...#########....", +"..###aaaaa###...", +".###a.....a###a.", +".##a........##a.", +"##a..........##a", +"##a..........##a", +"##a..........##a", +"##a..........##a", +"##a..........##a", +".##a........##a.", +".###.......###a.", +"..###.....###a..", +"...#########a...", +"...aa#####aa....", +".....aaaaa......"}; diff --git a/kenolaba/toolbar/ok.xpm b/kenolaba/toolbar/ok.xpm new file mode 100644 index 00000000..290d4657 --- /dev/null +++ b/kenolaba/toolbar/ok.xpm @@ -0,0 +1,25 @@ +/* XPM */ +static char*ok[]={ +"16 16 6 1", +"d c #00ff00", +"b c #004000", +"# c #c0c0ff", +"a c #000000", +". c None", +"c c #ffffff", +".....#####......", +"...#######......", +"..#######.abbc..", +".#######.abbbc..", +".######.abbdbc..", +"#######.abdbc.#.", +"######.abdbc.##.", +"##.###.abbc.###.", +"#.ac#.abdc.####.", +"#abc..abbc.####.", +"..bbcabbc.####..", +".#.bcbbc.#####..", +"...abbc.#####...", +"....bbc#####....", +"....ac.###......", +"................"}; diff --git a/kenolaba/toolbar/redball.xpm b/kenolaba/toolbar/redball.xpm new file mode 100644 index 00000000..24450be5 --- /dev/null +++ b/kenolaba/toolbar/redball.xpm @@ -0,0 +1,25 @@ +/* XPM */ +static char*redball[]={ +"16 16 6 1", +"# c #000000", +"a c #400000", +"b c #800000", +". c None", +"d c #ffffff", +"c c #ff0000", +".....#####......", +"...##aaaaa##....", +"..##aabbbbb##...", +".##aabbbbbbb##..", +".#aabbbbbbbbb#..", +"#aabbbbbbbbbbb#.", +"#aabbbbbcccbbb#.", +"#aabbbbcccccbb#.", +"#aabbbcccccccb#.", +"#aabbbccccdccb#.", +".#aabbcccdddc#..", +".##aabbcccdc##..", +"..##aabbccc##...", +"...##aaaaa##....", +".....#####......", +"................"}; diff --git a/kenolaba/toolbar/spy0.xpm b/kenolaba/toolbar/spy0.xpm new file mode 100644 index 00000000..a6cbb1c5 --- /dev/null +++ b/kenolaba/toolbar/spy0.xpm @@ -0,0 +1,39 @@ +/* XPM */ +static char*spy0[]={ +"22 22 14 1", +"a c #808080", +"j c #ffa858", +"l c #d6cdbb", +"c c #c0c0c0", +"g c #ffc0c0", +"i c #c05800", +"# c #000000", +". c None", +"k c #ffdca8", +"d c #c00000", +"h c #585858", +"e c #dcdcdc", +"b c #ffffff", +"f c #ff0000", +"......................", +"...........####.......", +"..........#....#......", +".........#.aaaa.#.....", +"......####baaaa.#b....", +".....#ccc#baaaa.#b....", +".....#cdc#eaaaa.#b....", +"....#ccfgf#ebbe#b.....", +"....#cccccc#####h.....", +"...#ccccccdcdcc.hb....", +"...#ccciccfgfgc.#h....", +"..#ccccjkccccccc.hb...", +"..#ccccccccccdcc.#h...", +"..l#cciccciccfgb#.hb..", +"...#ccjkccjkcccb#..h..", +"...l#cccccccccb#l..h..", +"....#cciciciccb#l.....", +"....l#cjkjkjkb#l......", +".....#bbbbbbbb#l......", +".....l########l.......", +"......................", +"......................"}; diff --git a/kenolaba/toolbar/spy1.xpm b/kenolaba/toolbar/spy1.xpm new file mode 100644 index 00000000..24baa2ba --- /dev/null +++ b/kenolaba/toolbar/spy1.xpm @@ -0,0 +1,38 @@ +/* XPM */ +static char*spy1[]={ +"22 22 13 1", +"i c #ffa858", +"k c #d6cdbb", +"e c #c0c0c0", +"g c #ffc0c0", +"h c #c05800", +"# c #000000", +". c None", +"j c #ffdca8", +"f c #c00000", +"d c #a0a0a4", +"a c #dcdcdc", +"b c #ffffff", +"c c #ff0000", +"......................", +"...........####.......", +"..........#abba#......", +".........#ac##ca#.....", +"......####bdccbb#b....", +".....#eee#bbccbb#b....", +".....#efe#acbbca#b....", +"....#eecgc#abba#bb....", +"....#eeeeee######b....", +"...#eeeeeefefee##bb...", +"...#eeeheecgcge###b...", +"..#eeeeijeeeeeee##b...", +"..#eeeeeeeeeefee###b..", +"..k#eeheeeheecgbb#....", +"...#eeijeeijeeebb.....", +"...k#eeeeeeeeeb#k.....", +"....#eeheheheeb#k.....", +"....k#eijijijb#k......", +".....#bbbbbbbb#k......", +".....k########k.......", +"......................", +"......................"}; diff --git a/kenolaba/toolbar/spy2.xpm b/kenolaba/toolbar/spy2.xpm new file mode 100644 index 00000000..cd2cb150 --- /dev/null +++ b/kenolaba/toolbar/spy2.xpm @@ -0,0 +1,40 @@ +/* XPM */ +static char*spy2[]={ +"22 22 15 1", +"j c #ffa858", +"g c #c3c3c3", +"l c #d6cdbb", +"e c #c0c0c0", +"m c #ffc0c0", +"i c #c05800", +"# c #000000", +". c None", +"h c #800000", +"k c #ffdca8", +"f c #c00000", +"d c #a0a0a4", +"a c #dcdcdc", +"b c #ffffff", +"c c #ff0000", +"......................", +"...........####.......", +".........##abba##.....", +".........#ac##ca#.....", +"......####bdccbb##....", +".....#ee#b#bccb#b#....", +".....#ef#gachhcab#....", +"....#eec#bhhhhhhb#....", +"....#eee##ehhhhb##....", +"...#eeeee#gggbgg#bb...", +"...#eeeie##bbbb###b...", +"..#eeeejkee#######bb..", +"..#eeeeeeeeeefee###b..", +"..l#eeieeeieecmbb#....", +"...#eejkeejkeeebb.....", +"...l#eeeeeeeeeb#l.....", +"....#eeieieieeb#l.....", +"....l#ejkjkjkb#l......", +".....#bbbbbbbb#l......", +".....l########l.......", +"......................", +"......................"}; diff --git a/kenolaba/toolbar/spy3.xpm b/kenolaba/toolbar/spy3.xpm new file mode 100644 index 00000000..b19cbdcc --- /dev/null +++ b/kenolaba/toolbar/spy3.xpm @@ -0,0 +1,38 @@ +/* XPM */ +static char*spy3[]={ +"22 22 13 1", +"j c #ffa858", +"d c #c3c3c3", +"h c #d6cdbb", +"f c #c0c0c0", +"i c #c05800", +"# c #000000", +". c None", +"g c #800000", +"k c #ffdca8", +"e c #a0a0a4", +"a c #dcdcdc", +"b c #ffffff", +"c c #ff0000", +"...........####.......", +".........########.....", +"........###abba###....", +".......###ac##ca###...", +"......###dbeccbbd##...", +".....#f##b#cccc#b##...", +".....###ddacggcabd##..", +"....#f##dbggggggbd##..", +"....#f##d###gg###d##..", +"...#ff##dd#cccc#dd##..", +"...#ff###ddbccbdd##...", +"..#ffff###dddddd###...", +"..#ffffd####dd####....", +"..h#ffifd######b#.....", +"...#ffjkfdd######.....", +"...h#fffffffffb#h.....", +"....#ffifififfb#h.....", +"....h#fjkjkjkb#h......", +".....#bbbbbbbb#h......", +".....h########h.......", +"......................", +"......................"}; diff --git a/kenolaba/toolbar/stop.xpm b/kenolaba/toolbar/stop.xpm new file mode 100644 index 00000000..c8e641c6 --- /dev/null +++ b/kenolaba/toolbar/stop.xpm @@ -0,0 +1,30 @@ +/* XPM */ +static char * ab_stop_xpm[] = { +"22 22 5 1", +" c none", +". c black", +"X c white", +"o c red", +"O c #820782078207", +" ", +" ", +" ", +" ....... ", +" ......... ", +" ..XXXXXX... ", +" ..X o o o ... ", +" ..X o o o o ... ", +" ..Xo o o o oO.. ", +" ..X o o o o O.. ", +" ..Xo o o o oO.. ", +" ..X o o o o O.. ", +" ..Xo o o o oO.. ", +" ... o o o o O.. ", +" ... o o o O.. ", +" ...OOOOOO.. ", +" ......... ", +" ....... ", +" ", +" ", +" ", +" "}; diff --git a/kenolaba/toolbar/undo.xpm b/kenolaba/toolbar/undo.xpm new file mode 100644 index 00000000..9b002026 --- /dev/null +++ b/kenolaba/toolbar/undo.xpm @@ -0,0 +1,27 @@ +/* XPM */ +static char*undo[]={ +"22 22 2 1", +"# c #000080", +". c None", +"......................", +"......................", +"......................", +"......................", +"......................", +"......................", +"......................", +"..........######......", +".........########.....", +"........####....##....", +"........###...........", +".......###............", +".....#######..........", +"......#####...........", +".......###............", +"........#.............", +"......................", +"......................", +"......................", +"......................", +"......................", +"......................"}; diff --git a/kenolaba/toolbar/warning.xpm b/kenolaba/toolbar/warning.xpm new file mode 100644 index 00000000..b2674f2a --- /dev/null +++ b/kenolaba/toolbar/warning.xpm @@ -0,0 +1,26 @@ +/* XPM */ +static char*warning[]={ +"16 16 7 1", +"e c #d6cdbb", +"# c #c0c0ff", +"a c #000000", +". c None", +"c c #400000", +"b c #ffffff", +"d c #ff0000", +"................", +"...##.aaab.#....", +"..##.accccb.#...", +".###.accdcb.##..", +".###.accdcb.##..", +"####..adcb.####.", +"#####.accb.####.", +"######.ab.#####.", +"######.ab.#####.", +"######.b.######.", +".######..#####..", +".#####.ab.####..", +"..###.adcb.##...", +"...###.cbe##....", +".....#.be#......", +"................"}; diff --git a/kenolaba/toolbar/yellowball.xpm b/kenolaba/toolbar/yellowball.xpm new file mode 100644 index 00000000..fc986c52 --- /dev/null +++ b/kenolaba/toolbar/yellowball.xpm @@ -0,0 +1,25 @@ +/* XPM */ +static char*yellowball[]={ +"16 16 6 1", +"b c #c0c000", +"a c #808000", +"# c #000000", +"c c #ffff00", +". c None", +"d c #ffffff", +".....#####......", +"...##aaaaa##....", +"..##aaaaaaa##...", +".##aaaabbbaa##..", +".#aaaabbbbbaa#..", +"#aaaabbbbbbbaa#.", +"#aaabbbbbbbbba#.", +"#aaabbbccccbba#.", +"#aaabbbcccccba#.", +"#aaaabbcccdcaa#.", +".#aaaabccddda#..", +".##aaaabccdc##..", +"..##aaaaaaa##...", +"...##aaaaa##....", +".....#####......", +"................"}; diff --git a/kenolaba/version.h b/kenolaba/version.h new file mode 100644 index 00000000..90e8ddd1 --- /dev/null +++ b/kenolaba/version.h @@ -0,0 +1 @@ +#define KENOLABA_VERSION "1.06b" diff --git a/kfouleggs/CHANGELOG b/kfouleggs/CHANGELOG new file mode 100644 index 00000000..d7e56fd2 --- /dev/null +++ b/kfouleggs/CHANGELOG @@ -0,0 +1 @@ +see "ksirtet" CHANGELOG diff --git a/kfouleggs/LICENSE b/kfouleggs/LICENSE new file mode 100644 index 00000000..621e9325 --- /dev/null +++ b/kfouleggs/LICENSE @@ -0,0 +1,18 @@ +KFOULEGGS +--------- +Copyright (c) 1999-2004 Nicolas HADACEK (hadacek@kde.org) + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. diff --git a/kfouleggs/Makefile.am b/kfouleggs/Makefile.am new file mode 100644 index 00000000..a47789a4 --- /dev/null +++ b/kfouleggs/Makefile.am @@ -0,0 +1,75 @@ +INCLUDES = -I$(top_builddir)/libksirtet -I$(top_srcdir)/libksirtet -I$(top_srcdir)/libksirtet/base -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) + +SUBDIRS = pics + +KDE_CXXFLAGS = $(KDE_USE_FPIE) + +bin_PROGRAMS = kfouleggs +kfouleggs_LDADD = $(top_builddir)/libksirtet/common/libksirtetcommon.la +kfouleggs_LDFLAGS = $(KDE_USE_PIE) $(all_libraries) $(KDE_RPATH) +kfouleggs_SOURCES = piece.cpp board.cpp ai.cpp field.cpp main.cpp prefs.kcfgc +METASOURCES = board.moc ai.moc field.moc main.moc + +rcdir = $(kde_datadir)/kfouleggs +rc_DATA = kfouleggsui.rc + +xdg_apps_DATA = kfouleggs.desktop +kde_kcfg_DATA = kfouleggs.kcfg + +appdatadir = $(kde_datadir)/kfouleggs +appdata_DATA = eventsrc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kfouleggs.pot + +# for system-wide highscore file +DESTBIN = $(DESTDIR)$(bindir)/$(bin_PROGRAMS) +DESTHIGHSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY) +DESTSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY)/$(bin_PROGRAMS).scores + +install-data-local: + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && echo "********************************************************" \ + && echo "" \ + && echo "This game is installed sgid \"games\" to use the" \ + && echo "system-wide highscore file (in "$(HIGHSCORE_DIRECTORY)")." \ + && echo "" \ + && echo "If the system-wide highscore file does not exist, it is" \ + && echo "created with the correct ownership and permissions. See the" \ + && echo "INSTALL file in \"kdegames/libkdegames/highscore\" for details." \ + && echo "" \ + && echo "********************************************************" \ + ) || true + +install-exec-hook: + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((chown $(highscore_user):$(highscore_group) $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((mkdir -p $(DESTHIGHSCORES) && chown $(highscore_user):$(highscore_group) $(DESTHIGHSCORES) \ + && chmod 750 $(DESTHIGHSCORES)) \ + || echo "Error: Could not create the highscore directory with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((chown $(highscore_user):$(highscore_group) $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test ${setgid} = true \ + && ((chmod 2755 $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((touch $(DESTSCORES) && chown $(highscore_user):$(highscore_group) $(DESTSCORES) \ + && chmod 0660 $(DESTSCORES)) \ + || echo "Error: Could not create system-wide highscore file with correct permissions !!" \ + )) || true + +field.o: ../libksirtet/common/commonprefs.h ../libksirtet/base/baseprefs.h +main.o: ../libksirtet/common/commonprefs.h ../libksirtet/base/baseprefs.h +prefs.o: ../libksirtet/common/commonprefs.h ../libksirtet/base/baseprefs.h + diff --git a/kfouleggs/README b/kfouleggs/README new file mode 100644 index 00000000..46ffcb4c --- /dev/null +++ b/kfouleggs/README @@ -0,0 +1,14 @@ +KFOULEGGS : a puyopuyo-alike game +--------------------------------- +Copyright (c) 1999-2004 Nicolas HADACEK (hadacek@kde.org) +Distributed under the GNU General Public License + + +KFoulEggs is an adaptation of the also well-known (at least in Japan) +Puyo-Puyo game. It provides multiplayers functionality and "artificial +intelligence" player. + +The code links to the ksirtet libraries. + + +Enjoy ! diff --git a/kfouleggs/TODO b/kfouleggs/TODO new file mode 100644 index 00000000..b999a6fe --- /dev/null +++ b/kfouleggs/TODO @@ -0,0 +1,3 @@ +see also the TODO list for libksirtet + + * better (a -very- simple AI is already quite annoying :) and varied AIs diff --git a/kfouleggs/ai.cpp b/kfouleggs/ai.cpp new file mode 100644 index 00000000..ee8a6c4b --- /dev/null +++ b/kfouleggs/ai.cpp @@ -0,0 +1,40 @@ +#include "ai.h" +#include "ai.moc" + +#include + +#include "board.h" + + +const AI::Data FEAI::DATA[] = { + { "OccupiedLines", I18N_NOOP("Occupied lines:"), 0, + false, nbOccupiedLines }, + { "Spaces", I18N_NOOP("Number of spaces:"), I18N_NOOP("Number of spaces under mean height"), + false, nbSpaces }, + { "Peak2Peak", I18N_NOOP("Peak-to-peak distance:"), 0, + false, peakToPeak }, + { "MeanHeight", I18N_NOOP("Mean height:"), 0, + false, mean }, + { "RemovedEggs", I18N_NOOP("Number of removed eggs:"), 0, + false, nbRemoved }, + { "Puyos", I18N_NOOP("Number of puyos:"), 0, + true, nbPuyos }, + { "ChainedPuyos", I18N_NOOP("Number of chained puyos:"), 0, + true, nbChained }, + LastData +}; + +FEAI::FEAI() +: AI(0, 200, DATA) +{} + +double FEAI::nbPuyos(const Board &main, const Board ¤t) +{ + return static_cast(current).nbPuyos() + - static_cast(main).nbPuyos(); +} + +double FEAI::nbChained(const Board &, const Board ¤t) +{ + return static_cast(current).chained(); +} diff --git a/kfouleggs/ai.h b/kfouleggs/ai.h new file mode 100644 index 00000000..86e8f5b2 --- /dev/null +++ b/kfouleggs/ai.h @@ -0,0 +1,21 @@ +#ifndef FE_AI_H +#define FE_AI_H + +#include "common/ai.h" + + +class FEAI : public AI +{ + Q_OBJECT + + public: + FEAI(); + + private: + static const Data DATA[]; + + static double nbPuyos(const Board &, const Board &); + static double nbChained(const Board &, const Board &); +}; + +#endif diff --git a/kfouleggs/board.cpp b/kfouleggs/board.cpp new file mode 100644 index 00000000..836c7e1e --- /dev/null +++ b/kfouleggs/board.cpp @@ -0,0 +1,161 @@ +#include "board.h" +#include "board.moc" + +#include + +#include "common/misc_ui.h" +#include "piece.h" + + +using namespace KGrid2D; + +FEBoard::FEBoard(bool graphic, QWidget *parent) +: Board(graphic, new GiftPool(parent), parent), + _field(matrix().width(), matrix().height()), _chainedPuyos(4) +{ + init(); +} + +void FEBoard::init() +{ + _nbPuyos = 0; + _chained = 0; + _giftRest = 0; + _lastChained = 0; + for (uint i=0; i<4; i++) { + _chainedPuyos[i] = 0; + _lastChained += (2<(g)._nbPuyos; +} + +void FEBoard::start(const GTInitData &data) +{ + init(); + Board::start(data); +} + +void FEBoard::computeInfos() +{ + Board::computeInfos(); + if ( graphic() ) computeNeighbours(); +} + +bool FEBoard::afterGlue(bool doAll, bool first) +{ + return !doFall(doAll, first, false); +} + +void FEBoard::removeBlock(const Coord &c) +{ + Board::removeBlock(c); + + // remove surrounding garbage + CoordList list = matrix().neighbours(c, true, true); + for (CoordList::const_iterator i = list.begin(); i!=list.end(); ++i) + if ( matrix()[*i]!=0 && matrix()[*i]->isGarbage() ) + Board::removeBlock(*i); +} + +bool FEBoard::toBeRemoved(const Coord &c) const +{ + return ( _field[c]>=4 ); +} + +bool FEBoard::toFall(const Coord &c) const +{ + Coord under = c - Coord(0, 1); + return ( matrix()[under]==0 ); +} + +void FEBoard::remove() +{ + Board::remove(); + + // score calculation from another game + // not sure it is the "official" way + uint nbPuyos = _groups.size(); // number of group detroyed + uint nbEggs = 0; // number of eggs destroyed + for (uint k=0; k<_groups.size(); k++) nbEggs += _groups[k]; + + uint bonus = nbEggs - 3; // more than 4 since we are here ! + if ( nbEggs==11 ) bonus += 2; + if ( nbPuyos>=2 ) bonus += 3 * (1 << (nbPuyos-2)); // 3 * 2^(nb-2) + if ( _chained>=1 ) bonus += 1 << (_chained+2); // 2^(chained+2) + uint dscore = 10 * nbPuyos * bonus; + + uint i = kMin(_chained, (uint)3); + _chainedPuyos[i] += nbPuyos; + _lastChained = 2 << i; + _chained++; + _giftRest += dscore; + _nbPuyos += nbPuyos; + updateRemoved(nbRemoved() + nbEggs); + updateScore(score() + dscore); + + updateLevel(); +} + +Board::AfterRemoveResult FEBoard::afterRemove(bool doAll, bool first) +{ + Board::AfterRemoveResult res = Board::afterRemove(doAll, first); + if ( res==Done && needRemoving() ) return NeedRemoving; + return res; +} + +bool FEBoard::needRemoving() +{ + _groups = findGroups(_field, 4); + if ( _groups.size()==0 ) _chained = 0; + return _groups.size(); +} + +/*****************************************************************************/ +// Multiplayers methods +uint FEBoard::gift() +{ + uint n = _giftRest / 60; + _giftRest = _giftRest % 60; + return n; +} + +bool FEBoard::putGift(uint n) +{ + QMemArray free(matrix().width()); + + // garbage blocks are put randomly on conlumns with more than 5 free lines. + uint nbFree = 0; + for (uint i=0; i=(int)matrix().height()-5 ) free[i] = false; + else { + free[i] = true; + nbFree++; + } + } + uint nb = kMin(nbFree, n); + while (nbFree && nb) { + uint k = (uint)randomGarbage.getLong(nbFree); + uint l = 0; + for (uint i=0; igarbageBlock(); + gb->sprite()->show(); + Coord c(i, matrix().height()-1); + setBlock(c, gb); + free[i] = false; + nbFree--; + nb--; + break; + } + l++; + } + } + return true; +} diff --git a/kfouleggs/board.h b/kfouleggs/board.h new file mode 100644 index 00000000..e0889b3b --- /dev/null +++ b/kfouleggs/board.h @@ -0,0 +1,44 @@ +#ifndef FE_BOARD_H +#define FE_BOARD_H + +#include "common/board.h" + + +class FEBoard : public Board +{ + Q_OBJECT + public: + FEBoard(bool graphic, QWidget *parent); + void copy(const GenericTetris &); + + void start(const GTInitData &); + + uint nbPuyos() const { return _nbPuyos; } + uint chained() const { return _chained; } + uint nbChainedPuyos(uint i) const { return _chainedPuyos[i]; } + uint lastChained() const { return _lastChained; } + + private: + // standard methods + bool afterGlue(bool doAll, bool first); + AfterRemoveResult afterRemove(bool doAll, bool first); + bool afterGift(bool first) { return !doFall(false, first, true); } + void removeBlock(const KGrid2D::Coord &); + bool toBeRemoved(const KGrid2D::Coord &) const; + bool toFall(const KGrid2D::Coord &) const; + void remove(); + bool needRemoving(); + void computeInfos(); + + void init(); + + // Multiplayers methods + uint gift(); + bool putGift(uint); + + KGrid2D::Square _field; + QMemArray _groups, _chainedPuyos; + uint _nbPuyos, _chained, _giftRest, _lastChained; +}; + +#endif diff --git a/kfouleggs/eventsrc b/kfouleggs/eventsrc new file mode 100644 index 00000000..11663f03 --- /dev/null +++ b/kfouleggs/eventsrc @@ -0,0 +1,358 @@ +[!Global!] +IconName=kfouleggs +Comment=KFoulEggs +Comment[ar]=لعبة البيضات المعÙنة (KFoulEggs) +Comment[be]=Ð¤Ð°Ñ‚Ð°Ð»ÑŒÐ½Ñ‹Ñ Ñйкі +Comment[bn]=কে-ফাউলà¦à¦— +Comment[cs]=Zkažená vejce +Comment[hi]=के-फाउलà¤à¤—à¥à¤¸ +Comment[hr]=KPokvarena jaja +Comment[is]=Fúlegg +Comment[ne]=केडीई फल à¤à¤— +Comment[pt_BR]=KOvos Sujos +Comment[sv]=Kfouleggs +Comment[ta]=கேதவறான à®®à¯à®Ÿà¯à®Ÿà¯ˆà®•à®³à¯ +Comment[tg]=KТухмҳои Ðфтанда + +[removed] +Name=Blocks removed +Name[ar]=لقد أزيل الطوب +Name[be]=Блокі Ð²Ñ‹Ð´Ð°Ð»ÐµÐ½Ñ‹Ñ +Name[bg]=Премахнати Ñа блокове +Name[bn]=গà§à¦Ÿà¦¿ সরিয়ে ফেলা হয়েছে +Name[bs]=Uklonjeni blokovi +Name[ca]=Blocs eliminats +Name[cs]=Bloky odstranÄ›ny +Name[cy]=Gwaredwyd blociau +Name[da]=Blokke fjernet +Name[de]=Entfernte Klötzchen +Name[el]=Οι γÏαμμές αφαιÏέθηκαν +Name[eo]=Blokoj forigitaj +Name[es]=Bloques eliminados +Name[et]=Eemaldatud blokid +Name[eu]=Kendutako blokeak +Name[fa]=بلوکها حذ٠شدند +Name[fi]=Palikoita poistettu +Name[fr]=Blocs supprimés +Name[ga]=Bloic bainte +Name[gl]=Bloques eliminados +Name[he]=×‘×œ×•×§×™× ×©×”×•×¡×¨×• +Name[hi]=पिणà¥à¤¡ हटाठ+Name[hr]=Uklonjeni blokovi +Name[hu]=Blokk eltávolítva +Name[is]=Kubbar fjarlægðir +Name[it]=Blocchi rimossi +Name[ja]=ブロックを消ã—㟠+Name[km]=ដុំ​ដែល​បាន​យក​ចáŸáž‰ +Name[lt]=Blokai panaikinti +Name[lv]=Noņemti bloki +Name[mk]=ОтÑтранети Ñе блокови +Name[nb]=Blokker fjernet +Name[nds]=Wegdaan Steen +Name[ne]=खणà¥à¤¡ हटाइयो +Name[nl]=Blokken verwijderd +Name[nn]=Blokker fjerna +Name[pa]=ਬਲਾਕ ਹਟਾਠ+Name[pl]=UsuniÄ™cie bloków +Name[pt]=Blocos removidos +Name[pt_BR]=Blocos removidos +Name[ro]=Blocuri eliminate +Name[ru]=Блок удалён +Name[se]=Bihtát leat eretváldon +Name[sk]=Bloky odstránené +Name[sl]=Odstranjeni bloki +Name[sr]=Уклоњени блокови +Name[sr@Latn]=Uklonjeni blokovi +Name[sv]=Block borttagna +Name[ta]=கடà¯à®Ÿà®™à¯à®•à®³à¯ நீகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Name[tg]=БаÑтаҳо ҷойивазкарда шудаанд +Name[tr]=Silinen bloklar +Name[uk]=Блоків вилучено +Name[zh_CN]=消去的å—æ•° +Name[zh_TW]=移除的方塊 +Comment=Blocks removed +Comment[be]=Блокі Ð²Ñ‹Ð´Ð°Ð»ÐµÐ½Ñ‹Ñ +Comment[bg]=Премахнати Ñа блокове +Comment[bn]=গà§à¦Ÿà¦¿ সরিয়ে ফেলা হয়েছে +Comment[bs]=Uklonjeni blokovi +Comment[ca]=Blocs eliminats +Comment[cs]=Bloky odstranÄ›ny +Comment[cy]=Gwaredwyd blociau +Comment[da]=Blokke fjernet +Comment[de]=Entfernte Klötzchen +Comment[el]=Οι γÏαμμές αφαιÏέθηκαν +Comment[eo]=Blokoj forigitaj +Comment[es]=Bloques eliminados +Comment[et]=Eemaldatud blokid +Comment[eu]=Kendutako blokeak +Comment[fa]=بلوکها حذ٠شدند +Comment[fi]=Palikoita poistettu +Comment[fr]=Blocs supprimés +Comment[ga]=Bloic bainte +Comment[gl]=Bloques eliminados +Comment[he]=×‘×œ×•×§×™× ×©×”×•×¡×¨×• +Comment[hi]=पिणà¥à¤¡ हटाठ+Comment[hr]=Uklonjeni blokovi +Comment[hu]=Blokk eltávolítva +Comment[is]=Kubbar fjarlægðir +Comment[it]=Blocchi rimossi +Comment[ja]=ブロック消ã—㟠+Comment[km]=ដុំ​ដែល​បាន​យក​ចáŸáž‰ +Comment[lt]=Blokai panaikinti +Comment[lv]=Noņemti bloki +Comment[mk]=ОтÑтранети Ñе блокови +Comment[nb]=Blokker fjernet +Comment[nds]=Wegdaan Steen +Comment[ne]=खणà¥à¤¡ हटाइयो +Comment[nl]=Blokken verwijderd +Comment[nn]=Blokker fjerna +Comment[pa]=ਬਲਾਕ ਹਟਾਠ+Comment[pl]=UsuniÄ™cie bloków +Comment[pt]=Blocos removidos +Comment[pt_BR]=Blocos removidos +Comment[ro]=Blocuri eliminate +Comment[ru]=Блок удалён +Comment[se]=Bihtát leat eretváldon +Comment[sk]=Bloky odstránené +Comment[sl]=Odstranjeni bloki +Comment[sr]=Уклоњени блокови +Comment[sr@Latn]=Uklonjeni blokovi +Comment[sv]=Block borttagna +Comment[ta]=தடைகள௠நீகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=БаÑтаҳо ҷойивазкарда шудаанд +Comment[tr]=Silinen bloklar +Comment[uk]=Блоків вилучено +Comment[zh_CN]=消去的å—æ•° +Comment[zh_TW]=移除的方塊 +default_presentation=0 + +[game over] +Name=Game Over +Name[af]=Speletjie Bo +Name[ar]=اللعبة انتهت +Name[az]=Oyun Qurtardı +Name[be]=Канец гульні +Name[bg]=Край на играта +Name[bn]=খেল খতম +Name[br]=Echu an abadenn +Name[bs]=Igra zavrÅ¡ena +Name[ca]=Fi de la partida +Name[cs]=Konec hry +Name[cy]=Gêm Drosodd +Name[da]=Spillet forbi +Name[de]=Spiel beendet +Name[el]=Τέλος Ï€Î±Î¹Ï‡Î½Î¹Î´Î¹Î¿Ï +Name[eo]=Ludo finita +Name[es]=Fin de la partida +Name[et]=Mäng läbi +Name[eu]=Jokoa amaitu da +Name[fa]=بازی تمام شد +Name[fi]=Peli loppu +Name[fr]=Fin de la partie +Name[ga]=Cluiche Thart +Name[gl]=Fin do Xogo +Name[he]=×¡×™×•× ×ž×©×—×§ +Name[hi]=खेल ख़तà¥à¤® +Name[hr]=Igra je zavrÅ¡ena +Name[hu]=Vége a játéknak +Name[id]=permainan berakhir +Name[is]=Leik lokið +Name[it]=Gioco terminato +Name[ja]=ゲームオーãƒãƒ¼ +Name[km]=ល្បែង​ចប់ +Name[ko]=SameGame +Name[lt]=Žaidimas baigtas +Name[lv]=SpÄ“les beigas +Name[mk]=Играта заврши +Name[mt]=Il-Logħba SpiÄ‹Ä‹at +Name[nb]=Spillet er slutt +Name[nds]=Speel vörbi +Name[ne]=खेल समापà¥à¤¤ +Name[nl]=Spel is afgelopen +Name[nn]=Spelet er slutt +Name[nso]=Papadi e Fedile +Name[pa]=ਖੇਡ ਖਤਮ +Name[pl]=Koniec gry +Name[pt]=Fim do jogo +Name[pt_BR]=Fim do jogo +Name[ro]=Joc terminat +Name[ru]=Конец игры +Name[se]=Speallu nogai +Name[sk]=Koniec hry +Name[sl]=Konec igre +Name[sr]=Крај игре +Name[sr@Latn]=Kraj igre +Name[sv]=Spelet är slut +Name[ta]=ஆடà¯à®Ÿà®®à¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯ +Name[tg]=Бозӣ ба итмом раÑид +Name[th]=จบเà¸à¸¡ +Name[tr]=Oyun Bitti +Name[uk]=Гру завершено +Name[uz]=OÊ»yin tugadi +Name[uz@cyrillic]=Ўйин тугади +Name[ven]=Muthambo wo Fhela +Name[vi]=Game kết thúc +Name[wa]=Li djeu est houte +Name[xh]=Uphelile Umdlalo +Name[zh_CN]=游æˆç»“æŸ +Name[zh_TW]=éŠæˆ²çµæŸ +Name[zu]=Umdlalo uphelile +Comment=Game over +Comment[be]=Канец гульні +Comment[bg]=Край на играта +Comment[bn]=খেল খতম +Comment[br]=Echu an abadenn +Comment[bs]=Kraj igre +Comment[ca]=Fi de la partida +Comment[cs]=Hra skonÄena +Comment[cy]=Gêm drosodd +Comment[da]=Spil forbi +Comment[de]=Das Spiel ist vorbei +Comment[el]=Τέλος Ï€Î±Î¹Ï‡Î½Î¹Î´Î¹Î¿Ï +Comment[eo]=Ludo finita +Comment[es]=Fin de la partida +Comment[et]=Mäng läbi +Comment[eu]=Jokoa amaitu da +Comment[fa]=بازی تمام شد +Comment[fi]=Peli loppui +Comment[fr]=Fin de la partie +Comment[ga]=Cluiche thart +Comment[he]=×¡×™×•× ×ž×©×—×§ +Comment[hr]=Kraj igre +Comment[hu]=Vége a játéknak +Comment[is]=Leik lokið +Comment[it]=Gioco terminato +Comment[ja]=ゲームオーãƒãƒ¼ +Comment[km]=ល្បែង​ចប់ +Comment[lt]=Žaidimas baigtas +Comment[lv]=SpÄ“le beigusies +Comment[mk]=Играта заврши +Comment[nb]=Spillet er slutt +Comment[nds]=Speel vörbi +Comment[ne]=खेल समापà¥à¤¤ +Comment[nl]=Het spel is afgelopen +Comment[nn]=Spelet er slutt +Comment[pa]=ਖੇਡ ਖਤਮ +Comment[pl]=Koniec gry +Comment[pt]=Fim do jogo +Comment[pt_BR]=Fim do Jogo +Comment[ru]=Конец игры +Comment[se]=Speallu nogai +Comment[sk]=Koniec hry +Comment[sl]=Konec igre +Comment[sr]=Крај игре +Comment[sr@Latn]=Kraj igre +Comment[sv]=Spelet slut +Comment[ta]=ஆடà¯à®Ÿà®®à¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯ +Comment[tr]=Oyun bitti +Comment[uk]=Кінець гри +Comment[wa]=Li djeu est houte +Comment[zh_CN]=游æˆç»“æŸ +Comment[zh_TW]=éŠæˆ²çµæŸ +default_presentation=0 + +[glued] +Name=Piece glued +Name[ar]=لقد ألصقت القطعة +Name[be]=Кавалачак зліпнуўÑÑ +Name[bg]=Залепени Ñа парчета +Name[bn]=গà§à¦Ÿà¦¿ আটকে আছে +Name[bs]=Komad spojen +Name[ca]=Peça enganxada +Name[cs]=Blok spojen +Name[cy]=Darn wedi ei gludo +Name[da]=Brik klæbet fast +Name[de]=Zusammengefügte Klötzchen +Name[el]=Το κομμάτι κολλήθηκε +Name[eo]=Peco gluita +Name[es]=Ficha pegada +Name[et]=Kleepunud klots +Name[eu]=Pieza itsatsita +Name[fa]=قطعه چسبید +Name[fi]=Pala liimattu +Name[fr]=Morceau collé +Name[gl]=Peza pegada +Name[he]=חתיכה מודבקת +Name[hi]=टà¥à¤•à¤¡à¤¼à¥‡ चिपकाठ+Name[hr]=Zalijepljeni dio +Name[hu]=Elem lekötve +Name[is]=Hlutur límdur +Name[it]=Pezzo incollato +Name[ja]=ピースをãã£ã¤ã‘㟠+Name[km]=បំណែក​បាន​បិទ +Name[lt]=Dalis įklijuota +Name[lv]=PielÄ«mÄ“ts gabals +Name[mk]=Залепено е парче +Name[nb]=Brikke festet +Name[nds]=Steen tosamenbackt +Name[ne]=टाà¤à¤¸à¤¿à¤à¤•à¥‹ टà¥à¤•à¥à¤°à¤¾ +Name[nl]=Samengevoegde stukken +Name[nn]=Brikke festa +Name[pl]=Sklejenie elementów +Name[pt]=Peça colada +Name[pt_BR]=Peça colada +Name[ru]=Слипание Ñиц +Name[se]=Bihttá lea liibmejuvvon +Name[sk]=Kus zlepený +Name[sl]=Kos prilepljen +Name[sr]=Залепљено парче +Name[sr@Latn]=Zalepljeno parÄe +Name[sv]=Pjäs fastsatt +Name[ta]= தà¯à®£à¯à®Ÿà¯ கிலூயிட௠+Name[tg]=ҚиÑмҳои чаÑбанда +Name[tr]=Yapıştırılan parçalar +Name[uk]=Фігура приклеєна +Name[zh_CN]=粘连的å—æ•° +Name[zh_TW]=é»è‘—çš„å°ç¢Žç‰‡ +Comment=Piece glued +Comment[ar]=لقد ألصقت القطعة +Comment[be]=Кавалачак зліпнуўÑÑ +Comment[bg]=Залепени Ñа парчета +Comment[bn]=গà§à¦Ÿà¦¿ আটকে আছে +Comment[bs]=Komad spojen +Comment[ca]=Peça enganxada +Comment[cs]=Blok spojen +Comment[cy]=Darn wedi ei gludo +Comment[da]=Brik klæbet fast +Comment[de]=Zusammengefügte Klötzchen +Comment[el]=Το κομμάτι κολλήθηκε +Comment[eo]=Peco gluita +Comment[es]=Ficha pegada +Comment[et]=Paikapandud klots +Comment[eu]=Pieza itsatsita +Comment[fa]=قطعه چسبید +Comment[fi]=Pala liimattu +Comment[fr]=Morceau collé +Comment[gl]=Peza pegada +Comment[he]=חתיכה מודבקת +Comment[hi]=टà¥à¤•à¤¡à¤¼à¥‡ चिपकाठ+Comment[hr]=Zalijepljeni dio +Comment[hu]=Elem lekötve +Comment[is]=Hlutur límdur +Comment[it]=Pezzo incollato +Comment[ja]=ピースをãã£ã¤ã‘㟠+Comment[km]=បំណែក​បាន​បិទ +Comment[lt]=Dalis įklijuota +Comment[lv]=Gabals ir pielÄ«mÄ“ts +Comment[mk]=Залепено е парче +Comment[nb]=Brikke festet +Comment[nds]=Steen tosamenbackt +Comment[ne]=टाà¤à¤¸à¤¿à¤à¤•à¥‹ टà¥à¤•à¥à¤°à¤¾ +Comment[nl]=Samengevoegde stukken +Comment[nn]=Brikke festa +Comment[pl]=Sklejenie elementu +Comment[pt]=Peça colada +Comment[pt_BR]=Peça colada +Comment[ru]=Слипание Ñиц +Comment[se]=Bihttá lea liibmejuvvon +Comment[sk]=Kus zlepený +Comment[sl]=Kos prilepljen +Comment[sr]=Залепљено парче +Comment[sr@Latn]=Zalepljeno parÄe +Comment[sv]=Pjäs fastsatt +Comment[ta]=தà¯à®£à¯à®Ÿà¯ ஒடà¯à®Ÿà®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=ҚиÑмҳои чаÑбанда +Comment[tr]=Parça yapıştırıldı +Comment[uk]=Фігура приклеєна +Comment[zh_CN]=粘连的å—æ•° +default_presentation=0 diff --git a/kfouleggs/field.cpp b/kfouleggs/field.cpp new file mode 100644 index 00000000..9d7abe85 --- /dev/null +++ b/kfouleggs/field.cpp @@ -0,0 +1,61 @@ +#include "field.h" +#include "field.moc" + +#include + +#include +#include + +#include "common/commonprefs.h" +#include "board.h" + + +FEField::FEField(QWidget *parent) + : Field(parent) +{ + Board *b = static_cast(board); + QWhatsThis::add(b->giftPool(), i18n("Display the amount of foul eggs sent by your opponent.")); +} + +void FEField::removedUpdated() +{ + Field::removedUpdated(); + const FEBoard *feb = static_cast(board); + KGameLCD *lcd = static_cast(removedList->lcd(0)); + lcd->displayInt(feb->nbPuyos()); + if ( feb->nbPuyos() ) lcd->highlight(); + if ( CommonPrefs::showDetailedRemoved() ) + for (uint i=0; i<4; i++) { + if ( !(feb->lastChained() & 2<(removedList->lcd(i+1)); + lcd->displayInt(feb->nbChainedPuyos(i)); + if ( feb->nbChainedPuyos(i) ) lcd->highlight(); + } +} + +void FEField::settingsChanged() +{ + Field::settingsChanged(); + + removedList->clear(); + KGameLCD *lcd = new KGameLCD(6, removedList); + removedList->append(i18n("Total:"), lcd); + uint nb = static_cast(board)->nbPuyos(); + lcd->displayInt(nb); + lcd->show(); + + if ( CommonPrefs::showDetailedRemoved() ) { + QWhatsThis::add(removedList, + i18n("Display the number of removed groups (\"puyos\") classified by the number of chained removal.")); + for (uint i=0; i<4; i++) { + KGameLCD *lcd = new KGameLCD(6, removedList); + QString s = (i==3 ? ">3" : QString::number(i)); + removedList->append(s, lcd); + uint nb = static_cast(board)->nbChainedPuyos(i); + lcd->displayInt(nb); + lcd->show(); + } + } else + QWhatsThis::add(removedList, + i18n("Display the number of removed groups (\"puyos\").")); +} diff --git a/kfouleggs/field.h b/kfouleggs/field.h new file mode 100644 index 00000000..df7812f2 --- /dev/null +++ b/kfouleggs/field.h @@ -0,0 +1,19 @@ +#ifndef FE_FIELD_H +#define FE_FIELD_H + +#include "common/field.h" +#include "common/misc_ui.h" + + +class FEField : public Field +{ + Q_OBJECT + public: + FEField(QWidget *parent); + + private slots: + virtual void removedUpdated(); + void settingsChanged(); +}; + +#endif diff --git a/kfouleggs/kfouleggs.desktop b/kfouleggs/kfouleggs.desktop new file mode 100644 index 00000000..d02296d3 --- /dev/null +++ b/kfouleggs/kfouleggs.desktop @@ -0,0 +1,69 @@ +[Desktop Entry] +Name=KFoulEggs +Name[af]=K-vrot-eier +Name[ar]=لعبة البيضات المعÙنة (KFoulEggs) +Name[be]=Ð¤Ð°Ñ‚Ð°Ð»ÑŒÐ½Ñ‹Ñ Ñйкі +Name[bn]=কে-ফাউলà¦à¦— +Name[hi]=के-फाउलà¤à¤—à¥à¤¸ +Name[hr]=KPokvarena jaja +Name[is]=Fúlegg +Name[ne]=के फल à¤à¤— +Name[pl]=Foul Eggs +Name[pt_BR]=KOvos Sujos +Name[sv]=Kfouleggs +Name[ta]=கேதவறான à®®à¯à®Ÿà¯à®Ÿà¯ˆà®•à®³à¯ +Name[tg]=KТухмҳои Ðфтанда +Name[tr]=Çürük Yumurtalar +Icon=kfouleggs +Exec=kfouleggs -caption "%c" %i %m +Type=Application +DocPath=kfouleggs/index.html +GenericName=Japanese PuyoPuyo-like Game +GenericName[be]=ЯпонÑкі Ñ‚ÑÑ‚Ñ€Ñ‹Ñ +GenericName[bg]=ЯпонÑки Ñ‚ÐµÑ‚Ñ€Ð¸Ñ +GenericName[bn]=জাপানি পà§à¦‡à§Ÿà§‹à¦ªà§à¦‡à§Ÿà§‹-জাতীয় খেলা +GenericName[br]=C'hoari doare PuyoPuyo japaneg +GenericName[bs]=Japanska igra nalik na PuyoPuyo +GenericName[ca]=Jocs similar al PuyoPuyo japonès +GenericName[cs]=Japonská hra podobná PuyoPuyo +GenericName[cy]=Gêm Fwrdd sy'n debyg i PuyoPuyo Siapanaidd +GenericName[da]=Japansk PuyoPuyo-lignende spil +GenericName[de]=Puyo-Puyo-ähnliches Brettspiel +GenericName[el]=Ιαπωνικό παιχνίδι παÏόμοιο με το PuyoPuyo +GenericName[eo]=Japana PuyoPuyo-simila bretludo +GenericName[es]=Juego japonés similar al PuyoPuyo +GenericName[et]=Jaapani PuyoPuyo moodi mäng +GenericName[eu]=PuyoPuyo bezalaeko joko japoniarra +GenericName[fa]=بازی شبیه PuyoPuyo ژاپنی +GenericName[fi]=Japanilainen PuyoPuyo-tyylinen peli +GenericName[fr]=Jeu japonais dans le style de PuyoPuyo +GenericName[he]=חיקוי PuyoPuyo יפני +GenericName[hr]=Igra poput japanskog PuyoPuyo +GenericName[hu]=PuyoPuyo-szerű játék +GenericName[is]=Leikur sem líkist Japönskum PuyoPuyo leik +GenericName[it]=Gioco simile al giapponese PuyoPuyo +GenericName[ja]=プヨプヨã®ã‚ˆã†ãªãƒœãƒ¼ãƒ‰ã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​ដូច PuyoPuyo របស់​ជប៉ុន +GenericName[lv]=SpÄ“le lÄ«dzÄ«ga JapÄņu PuyoPuyo +GenericName[mk]=Игри на Ñлична на јапонÑката Пујо-пујо +GenericName[nb]=Japansk PuyoPuyo-lignende spill +GenericName[nds]=Japaansch Puyo-Puyo-liek Brettspeel +GenericName[ne]=जापानी पà¥à¤¯à¥‹à¤ªà¥à¤¯à¥‹ जसà¥à¤¤à¥‹ खेल +GenericName[nl]=Japans PuyoPuyo-achtig spel +GenericName[nn]=Japansk PuyoPuyo-liknande spel +GenericName[pl]=Gra typu PuyoPuyo +GenericName[pt]=Jogo tipo PuyoPuyo +GenericName[pt_BR]=Jogo parecido com PuyoPuyo japonês +GenericName[ru]=Роковые Ñйца +GenericName[se]=JapanalaÅ¡ PuyoPuyo-lágan speallu +GenericName[sk]=Japonská hra typu PuyoPuyo +GenericName[sl]=Igra, podobna japonskemu PuyoPuyo +GenericName[sr]=Игра налик на јапанÑки PuyoPuyo +GenericName[sr@Latn]=Igra nalik na japanski PuyoPuyo +GenericName[sv]=Japanskt PuyoPuyo-liknande spel +GenericName[ta]=ஜபà¯à®ªà®¾à®©à®¿à®¯ பà¯à®¯à¯‹-பà¯à®¯à¯‹ போனà¯à®± விளையாடà¯à®Ÿà¯ +GenericName[uk]=Варіант тетріÑу Пуйо-Пуйо +GenericName[zh_TW]=類似日本 PuyoPuyo éŠæˆ² +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;ArcadeGame; diff --git a/kfouleggs/kfouleggs.kcfg b/kfouleggs/kfouleggs.kcfg new file mode 100644 index 00000000..685e29e1 --- /dev/null +++ b/kfouleggs/kfouleggs.kcfg @@ -0,0 +1,60 @@ + + + + + + -2.0 + -100.0 + 0.0 + + + + 0.0 + -100.0 + 0.0 + + + + 0.0 + -100.0 + 0.0 + + + + 0.0 + -100.0 + 0.0 + + + + 20.0 + 0.0 + 100.0 + + + + 50.0 + 0.0 + 100.0 + + + 0 + 0 + 10 + + + + 80.0 + 0.0 + 100.0 + + + 0 + 0 + 10 + + + diff --git a/kfouleggs/kfouleggsui.rc b/kfouleggs/kfouleggsui.rc new file mode 100644 index 00000000..f4fb0d59 --- /dev/null +++ b/kfouleggs/kfouleggsui.rc @@ -0,0 +1,27 @@ + + + + + + &Multiplayer + + + + + + + + + + + + + + + + + + + + + diff --git a/kfouleggs/main.cpp b/kfouleggs/main.cpp new file mode 100644 index 00000000..4fbee4c7 --- /dev/null +++ b/kfouleggs/main.cpp @@ -0,0 +1,92 @@ +#include "main.h" +#include "main.moc" + +#include +#include +#include + +#include "common/inter.h" +#include "common/highscores.h" +#include "prefs.h" +#include "piece.h" + +//----------------------------------------------------------------------------- +const MPGameInfo MP_GAME_INFO = { + "004", // multiplayer id (increase when incompatible changes are made) + 4, // max nb local games + 500, // interval + true, // IA allowed + 0, 0 // no setting slots +}; + +const MainData MAIN_DATA = { + "kfouleggs", + I18N_NOOP("KFoulEggs"), + I18N_NOOP("KFoulEggs is an adaptation of the well-known\n(at least in " + "Japan) PuyoPuyo game"), + "http://kfouleggs.sourceforge.net/", + I18N_NOOP("Puyos"), + "2.1.11", + "2.1.11 (12 September 2004)" +}; + +const uint HISTOGRAM_SIZE = 8; +const uint HISTOGRAM[HISTOGRAM_SIZE] = { + 1, 5000, 10000, 20000, 50000, 100000, 200000, 300000 +}; + +const BaseBoardInfo BASE_BOARD_INFO = { + 6, 15, // width - height + true, // with pieces + + 150, // before remove time + 10, // after removed time + 3, // nb toggles + 5, // nb partial fall stages + + 0, // nb arcade stages + + HISTOGRAM, HISTOGRAM_SIZE, false, // score is not bound +}; + +const CommonBoardInfo COMMON_BOARD_INFO = { + 1000, // base time + 10, // drop down time + 10, // before glue time + 10, // after glue time + 10, // after gift time + 3, // nb bump stages + + 100, // nb removed to level + 5, 6, 800, 2000 // nb leds, max to send, shower timeout, pool timeout +}; + +FEFactory::FEFactory() + : CommonFactory(MAIN_DATA, BASE_BOARD_INFO, COMMON_BOARD_INFO) +{} + +BaseInterface *FEFactory::createInterface(QWidget *parent) +{ + return new Interface(MP_GAME_INFO, parent); +} + + +//----------------------------------------------------------------------------- +int main(int argc, char **argv) +{ + KHighscore::init(MAIN_DATA.appName); + FEFactory fef; + fef.init(argc, argv); + + FEPieceInfo pieceInfo; + CommonHighscores highscores; + (void) Prefs::self(); // Create preferences + + if ( kapp->isRestored() ) RESTORE(FEMainWindow) + else { + FEMainWindow *mw = new FEMainWindow; + kapp->setMainWidget(mw); + mw->show(); + } + return kapp->exec(); +} diff --git a/kfouleggs/main.h b/kfouleggs/main.h new file mode 100644 index 00000000..5831d546 --- /dev/null +++ b/kfouleggs/main.h @@ -0,0 +1,34 @@ +#ifndef FE_MAIN_H +#define FE_MAIN_H + +#include "common/main.h" +#include "common/factory.h" +#include "board.h" +#include "ai.h" +#include "field.h" + + +//----------------------------------------------------------------------------- +class FEFactory : public CommonFactory +{ + public: + FEFactory(); + + protected: + virtual BaseBoard *createBoard(bool graphic, QWidget *parent) + { return new FEBoard(graphic, parent); } + virtual BaseField *createField(QWidget *parent) + { return new FEField(parent); } + virtual BaseInterface *createInterface(QWidget *parent); + virtual AI *createAI() { return new FEAI; } +}; + +//----------------------------------------------------------------------------- +class FEMainWindow : public MainWindow +{ + Q_OBJECT + public: + FEMainWindow() { init(); } +}; + +#endif diff --git a/kfouleggs/pics/Makefile.am b/kfouleggs/pics/Makefile.am new file mode 100644 index 00000000..c471f62b --- /dev/null +++ b/kfouleggs/pics/Makefile.am @@ -0,0 +1,3 @@ + +KDE_ICON= AUTO + diff --git a/kfouleggs/pics/hi128-app-kfouleggs.png b/kfouleggs/pics/hi128-app-kfouleggs.png new file mode 100644 index 00000000..a9129858 Binary files /dev/null and b/kfouleggs/pics/hi128-app-kfouleggs.png differ diff --git a/kfouleggs/pics/hi16-app-kfouleggs.png b/kfouleggs/pics/hi16-app-kfouleggs.png new file mode 100644 index 00000000..ce66ca75 Binary files /dev/null and b/kfouleggs/pics/hi16-app-kfouleggs.png differ diff --git a/kfouleggs/pics/hi22-app-kfouleggs.png b/kfouleggs/pics/hi22-app-kfouleggs.png new file mode 100644 index 00000000..28713198 Binary files /dev/null and b/kfouleggs/pics/hi22-app-kfouleggs.png differ diff --git a/kfouleggs/pics/hi32-app-kfouleggs.png b/kfouleggs/pics/hi32-app-kfouleggs.png new file mode 100644 index 00000000..b7286d49 Binary files /dev/null and b/kfouleggs/pics/hi32-app-kfouleggs.png differ diff --git a/kfouleggs/pics/hi48-app-kfouleggs.png b/kfouleggs/pics/hi48-app-kfouleggs.png new file mode 100644 index 00000000..c6b5fc5a Binary files /dev/null and b/kfouleggs/pics/hi48-app-kfouleggs.png differ diff --git a/kfouleggs/pics/hi64-app-kfouleggs.png b/kfouleggs/pics/hi64-app-kfouleggs.png new file mode 100644 index 00000000..5c4ff35a Binary files /dev/null and b/kfouleggs/pics/hi64-app-kfouleggs.png differ diff --git a/kfouleggs/piece.cpp b/kfouleggs/piece.cpp new file mode 100644 index 00000000..3c676f1c --- /dev/null +++ b/kfouleggs/piece.cpp @@ -0,0 +1,90 @@ +#include "piece.h" + +#include + +#include +#include + +#include + +#include "base/board.h" + + +const FEPieceInfo::Form FEPieceInfo::FORM = { + {{ 0, 0}, {-1, 0}, { 0, 0}, { 0, -1}}, + {{ 0, -1}, {-1, -1}, {-1, 0}, {-1, -1}} +}; + +const char *FEPieceInfo::DEFAULT_COLORS[NB_NORM_BLOCK_TYPES + 1] = { + "#64C864", "#64C8C8", "#C86464", "#C864C8", "#C8C8C8" +}; + +QColor FEPieceInfo::defaultColor(uint i) const +{ + if ( i>=nbColors() ) return QColor(); + return QColor(DEFAULT_COLORS[i]); +} + +QString FEPieceInfo::colorLabel(uint i) const +{ + return (i==NB_NORM_BLOCK_TYPES ? i18n("Garbage color:") + : i18n("Color #%1:").arg(i+1)); +} + +void FEPieceInfo::draw(QPixmap *pixmap, uint blockType, uint, + bool lighted) const +{ + QColor col = color(blockType); + if (lighted) col = col.light(); + pixmap->fill(col); +} + +void FEPieceInfo::setMask(QPixmap *pixmap, uint blockMode) const +{ + Q_ASSERT( pixmap->width()==pixmap->height() ); // drawing code assumes that + QBitmap bitmap(pixmap->size(), true); + QPainter p(&bitmap); + p.setBrush(Qt::color1); + p.setPen( QPen(Qt::NoPen) ); + + // base circle + int w = pixmap->width(); + int d = (int)((sqrt(2)-2./3)*w); + QRect cr = QRect(0, 0, d, d); + cr.moveCenter(QPoint(w/2, w/2)); + p.drawEllipse(cr); + + if (blockMode) { + int a = (int)(w/(3.*sqrt(2))); + int ra = 2*w/3+1; + cr = QRect(0, 0, ra, ra); + + // first drawing with color1 + if ( blockMode & BaseBoard::Up ) p.drawRect( 0, 0, w, a); + if ( blockMode & BaseBoard::Right ) p.drawRect(w-a+1, 0, a, w); + if ( blockMode & BaseBoard::Down ) p.drawRect( 0, w-a+1, w, a); + if ( blockMode & BaseBoard::Left ) p.drawRect( 0, 0, a, w); + + // second drawing with color0 + p.setBrush(Qt::color0); + if ( (blockMode & BaseBoard::Up) || (blockMode & BaseBoard::Left) ) { + cr.moveCenter(QPoint(0, 0)); + p.drawEllipse(cr); + } + if ( (blockMode & BaseBoard::Right) || (blockMode & BaseBoard::Up) ) { + cr.moveCenter(QPoint(w-1, 0)); + p.drawEllipse(cr); + } + if ( (blockMode & BaseBoard::Down) || (blockMode & BaseBoard::Right) ){ + cr.moveCenter(QPoint(w-1, w-1)); + p.drawEllipse(cr); + } + if ( (blockMode & BaseBoard::Left) || (blockMode & BaseBoard::Down) ) { + cr.moveCenter(QPoint(0, w-1)); + p.drawEllipse(cr); + } + } + + p.end(); + pixmap->setMask(bitmap); +} diff --git a/kfouleggs/piece.h b/kfouleggs/piece.h new file mode 100644 index 00000000..ba90e902 --- /dev/null +++ b/kfouleggs/piece.h @@ -0,0 +1,50 @@ +#ifndef PIECE_H +#define PIECE_H + +#include "base/piece.h" + + +class FEPieceInfo : public GPieceInfo +{ + public: + FEPieceInfo() {} + + virtual uint nbBlocks() const { return NB_BLOCKS; } + virtual uint nbForms() const { return 1; } + virtual uint nbTypes() const + { return NB_NORM_BLOCK_TYPES * NB_NORM_BLOCK_TYPES; } + + virtual const int *i(uint, uint rot) const { return FORM.i[rot]; } + virtual const int *j(uint, uint rot) const { return FORM.j[rot]; } + virtual uint value(uint type, uint n) const + { return (n%2 ? type/4 : type%4); } + virtual uint form(uint) const { return 0; } + virtual uint nbConfigurations(uint type) const + { return ((type%4)==(type/4) ? 2 : 4);} + + virtual uint nbNormalBlockTypes() const { return NB_NORM_BLOCK_TYPES; } + virtual uint nbGarbageBlockTypes() const { return 1; } + virtual uint nbBlockModes() const { return NB_BLOCK_MODES; } + + virtual uint nbColors() const { return NB_NORM_BLOCK_TYPES + 1; } + virtual QString colorLabel(uint i) const; + virtual QColor defaultColor(uint i) const; + + private: + void draw(QPixmap *, uint blockType, uint blockMode, bool lighted) const; + void setMask(QPixmap *, uint blockMode) const; + + enum { NB_BLOCKS = 2, + NB_NORM_BLOCK_TYPES = 4, + NB_BLOCK_MODES = 1+4+6+4+1 }; // all possible connections + + struct Form { + int i[4][NB_BLOCKS]; + int j[4][NB_BLOCKS]; + }; + static const Form FORM; + + static const char *DEFAULT_COLORS[NB_NORM_BLOCK_TYPES + 1]; +}; + +#endif diff --git a/kfouleggs/prefs.kcfgc b/kfouleggs/prefs.kcfgc new file mode 100644 index 00000000..cf32e226 --- /dev/null +++ b/kfouleggs/prefs.kcfgc @@ -0,0 +1,9 @@ +# Code generation options for kconfig_compiler +File=kfouleggs.kcfg +IncludeFiles=common/commonprefs.h +Inherits=CommonPrefs +ClassName=Prefs +Singleton=true +#Mutators=true +#CustomAdditions=true +#Mutators=true diff --git a/kgoldrunner/AUTHORS b/kgoldrunner/AUTHORS new file mode 100644 index 00000000..9d51d2b2 --- /dev/null +++ b/kgoldrunner/AUTHORS @@ -0,0 +1 @@ +Ian Wadham and Marco Krüger diff --git a/kgoldrunner/BUGS b/kgoldrunner/BUGS new file mode 100644 index 00000000..c866690d --- /dev/null +++ b/kgoldrunner/BUGS @@ -0,0 +1,38 @@ +KGoldrunner v2.0 - Known Problems +--------------------------------- + +1. Development of the KDE 1 version of KGoldrunner has been discontinued. + There is still a tarball for it on "apps.kde.com" under "kgoldrunner". + +2. Now that KGoldrunner v2.0 has been re-written as a KDE 3 application + it is no longer portable to non-KDE systems, however a portable + (non-KDE) version of KGoldrunner, based on Qt 3, has been written + and is currently under test. + +3. KGoldrunner 2.0 is not designed to work with KDE 2. It has been tested with + KDE 3.1.1 and Qt 3.1.1. + +4. In KGoldrunner 2.0, the KDE library produces the following error message + occasionally when KDialogbase or KMessageBox is run: + + WARNING: KDE detected X Error: BadMatch (invalid parameter attributes) + \x08 Major opcode: * + + This apppears to be harmless, i.e. all the desired dialog entries are + received by the application and the messages appear on the screen. One + way the X error message can be forced to appear is to run "exec()" twice on + one dialog object, e.g, as in a data-entry validation loop: + + while (dialogName->exec() == QDialog::Accepted) { + if validation fails + issue error message; + continue; + else + break; + } + + Maybe this is a bug in KDE 3.1.1 and maybe it is already fixed in later KDE. + It happens if you do "exec()" twice on an empty KDialogbase object (i.e. one + with just OK and Cancel buttons and a caption), so I don't think KGoldrunner + is responsible. Also, the "while ... exec()" loop works fine with QDialog + objects and always has. diff --git a/kgoldrunner/COPYING b/kgoldrunner/COPYING new file mode 100644 index 00000000..0b84a43f --- /dev/null +++ b/kgoldrunner/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/kgoldrunner/ChangeLog b/kgoldrunner/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/kgoldrunner/Doxyfile b/kgoldrunner/Doxyfile new file mode 100644 index 00000000..e76fadd7 --- /dev/null +++ b/kgoldrunner/Doxyfile @@ -0,0 +1,232 @@ +# Doxyfile 1.3.2-Gideon + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = kgoldrunner +PROJECT_NUMBER = 0.1 +OUTPUT_DIRECTORY = +OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = NO +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +SHORT_NAMES = NO +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +TAB_SIZE = 8 +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ALIASES = +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +SHOW_USED_FILES = YES +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = /home/ianw/KGR_2/kgoldrunner +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.C \ + *.H \ + *.tlh \ + *.diff \ + *.patch \ + *.moc \ + *.xpm +RECURSIVE = yes +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = YES +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = yes +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 1000 +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO +CGI_NAME = search.cgi +CGI_URL = +DOC_URL = +DOC_ABSPATH = +BIN_ABSPATH = /usr/local/bin/ +EXT_DOC_PATHS = diff --git a/kgoldrunner/INSTALL b/kgoldrunner/INSTALL new file mode 100644 index 00000000..aed967ab --- /dev/null +++ b/kgoldrunner/INSTALL @@ -0,0 +1,197 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile, install and uninstall this package is: + + 0. Make sure you uncompress the correct release file for your system. + File "kgoldrunner-2.0.tar.gz" is for use with Qt 3.1.1 library + and KDE 3.1.1 desk top. Also, if you want to do step 3 (install), + make sure you are logged in as "root", the superuser, or use `su root'. + + Some earlier releases, on "http://apps.kde.com", are file + "kgoldrunner-1.0-kde1.tar.gz", for use with Qt 1.x library and + KDE 1.x desk top, and "kgoldrunner-1.0-kde2.tar.gz", for Qt 2.x + library and KDE 2.x desk top. + + 1. `cd' to the top directory containing the file "configure.in" and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Type `make install' to install program, data files and documentation + in the KDE desktop "Games/Arcade" area (working as "root"). + + 4. Log out of or "exit" from "root", then, logged in as yourself, + type `kgoldrunner' to start the game. It will then check that all + the files it requires are in the right places and will report any + problems. For further details on installation, see the online help + documentation section on "Installation" or see the README file if + installation has failed and you cannot run `kgoldrunner'. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 6. To uninstall the package, type `make uninstall' (working as "root"). + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff --git a/kgoldrunner/Makefile.am b/kgoldrunner/Makefile.am new file mode 100644 index 00000000..48d41ca5 --- /dev/null +++ b/kgoldrunner/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src gamedata diff --git a/kgoldrunner/NEWS b/kgoldrunner/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/kgoldrunner/README b/kgoldrunner/README new file mode 100644 index 00000000..0bca08a4 --- /dev/null +++ b/kgoldrunner/README @@ -0,0 +1,36 @@ +KGoldrunner v2.0 - README + +Ian Wadham +Marco Krüger +---------------------------------------------------------------------- + +KGoldrunner is a game in which you, the hero, have to run through a maze +of ladders, bricks, concrete and climbing bars to collect gold nuggets, +while being pursued by enemies. When you have collected all the gold, +you can move to the next level of the game by climbing to the top of the +playing area. This version has several games (sets of levels) of +varying difficulty and includes a Tutorial game. It also has an editor +you can use to make up your own games or vary the released games. You +use the mouse to guide the hero and dig holes in bricks. + +KGoldrunner is based on the Lode Runner (TM) game written in the USA +by Doug Smith in 1983 for the Apple II and Commodore 64 computers. + +See the file INSTALL for instructions on how to compile, build and +install KGoldrunner. + +The source code and other files are essentially the same for KDE2/Qt2 +and KDE3/Qt3, depending on the -DQT3 compiler flag, but the configuration +and make files are quite different. WARNING: This version has been built, +compiled and tested ONLY with KDE 3.1.1 and Qt 3.1.1. + +Just in case you cannot build and install KGoldrunner ... +--------------------------------------------------------- + +The sub-directory "kgoldrunner/docs/en" contains the KGoldrunner User's +Guide in English, which is made available via online help in KGoldrunner +when installed, but you can read it directly with your browser if you have +not yet installed KGoldrunner. It contains details of how to play the game, +including screen shots. The Installation and Technical Details sections +may be useful if you are having difficulty installing KGoldrunner. + diff --git a/kgoldrunner/TODO b/kgoldrunner/TODO new file mode 100644 index 00000000..e9c961e9 --- /dev/null +++ b/kgoldrunner/TODO @@ -0,0 +1,34 @@ +KGoldrunner v2.0 - TODO +----------------------- + +Some ideas for the future:- + + 1. Port KGoldrunner to other O/S's, such as MS Windows and Mac. + Although KGoldrunner v2.0, as released with the KDE desktop, is + a KDE-based application and is not portable, KGoldrunner v2.0-qt, + as configured for Qt 3.x library and compiled with -DKGR_PORTABLE, + has very small dependence on the KDE desktop (i.e. locations of + directories only). Because Qt 3 is intended to be portable to + MS Windows and Mac, so should KGoldrunner v2.0-qt be. However Qt + development licences are NOT free on non-UNIX type O/S's. + + 2. Add sounds for hero actions such as falling, collecting a nugget, + getting killed and completing a level. + + 3. Design and program extra game objects, such as movable bricks, + water, teleportation exits and aquatic enemies. + + Movable bricks would be pushed up or sideways or would fall into + an empty space, to change the maze layout. As with fall-through + bricks, they should look just like ordinary bricks. + + With water, the hero could jump in and swim across the surface, at + a slower speed than on land, but the land enemies would not follow + him. If he has to dive below the surface, he can only hold his + breath for a limited time before he dies. + + Teleportation exits would be connected in pairs. If you go through + one exit, you reappear at the other and vice-versa. + + Aquatic enemies would just make life more difficult for the hero + in the water. Maybe there could be amphibian enemies too. diff --git a/kgoldrunner/VERSION b/kgoldrunner/VERSION new file mode 100644 index 00000000..e3b297a4 --- /dev/null +++ b/kgoldrunner/VERSION @@ -0,0 +1 @@ +KGoldrunner v2.0-kde3 diff --git a/kgoldrunner/gamedata/Makefile.am b/kgoldrunner/gamedata/Makefile.am new file mode 100644 index 00000000..a7fcca1e --- /dev/null +++ b/kgoldrunner/gamedata/Makefile.am @@ -0,0 +1,13 @@ +gamedatadir = $(kde_datadir)/kgoldrunner/system +gamedata_DATA = games.dat hi_level.dat hi_plws.dat hi_plwv.dat hi_wad.dat + +# Untar the file levels.tar and install levels/*.grl files. The "chown" and +# "chmod" ensure that installer owns files: even if installer is "root". The +# "|| true" commands make everything succeed when the installer is NOT "root". +install-data-local: + $(mkinstalldirs) $(DESTDIR)$(gamedatadir) + pwd=`cd $(srcdir) && pwd` ;\ + cd $(DESTDIR)$(gamedatadir) && tar -xf $$pwd/levels.tar ;\ + chown -R root:root levels 2> /dev/null || true ;\ + chmod 644 levels/* 2> /dev/null || true + diff --git a/kgoldrunner/gamedata/games.dat b/kgoldrunner/gamedata/games.dat new file mode 100644 index 00000000..0834f587 --- /dev/null +++ b/kgoldrunner/gamedata/games.dat @@ -0,0 +1,14 @@ +100 T plws Initiation +These 100 levels make an excellent introductory game, as well as a good opportunity for experts to build up high scores. They were composed by Peter Wadham and use traditional playing rules.\n\nThe last few levels are very hard, but if you are looking for even more of a challenge, have a go at 'Vengeance of Peter W' .... ;-) .... +022 T wad Challenge +These tricky little levels were composed by Peter, Simon, Genevieve and their father Ian Wadham. They use traditional playing rules. Enjoy! .... ;-) .... +020 T plwv Vengeance of Peter W +Gooood luck !!\nMwarrhh hwwarrrr haarrrr !!! +018 K level KGoldrunner +These levels were composed by Marco Krüger, the original author of the KGoldrunner program, and some of his friends and contributors. They use KGoldrunner rules. The enemies run fast and have an aggressive search strategy. Enjoy! .... :-) .... +015 K sot State Of Terror +Just when you thought it was safe ... fifteen levels of pure terror, using those quirky-yet-fast KGoldrunner rules.\n\nComposed by Stuart Popejoy - spopejoy@panix.com +007 T tute Tutorial +This tutorial is a collection of easy levels that teaches you the rules of KGoldrunner and helps you develop the skills you need to get started. Each level has a brief explanation, then you play .....\n\nWhen you move on to play more advanced levels, you will find that KGoldrunner combines action, strategy and puzzle solving --- all in one game. +005 T tutea Advanced Tutorial +This tutorial is preparation for some of the things you might find in the middle levels of the 'Initiation' game. Enjoy .... diff --git a/kgoldrunner/gamedata/hi_level.dat b/kgoldrunner/gamedata/hi_level.dat new file mode 100644 index 00000000..74f042df Binary files /dev/null and b/kgoldrunner/gamedata/hi_level.dat differ diff --git a/kgoldrunner/gamedata/hi_plws.dat b/kgoldrunner/gamedata/hi_plws.dat new file mode 100644 index 00000000..6f62932f Binary files /dev/null and b/kgoldrunner/gamedata/hi_plws.dat differ diff --git a/kgoldrunner/gamedata/hi_plwv.dat b/kgoldrunner/gamedata/hi_plwv.dat new file mode 100644 index 00000000..46b0d30f Binary files /dev/null and b/kgoldrunner/gamedata/hi_plwv.dat differ diff --git a/kgoldrunner/gamedata/hi_wad.dat b/kgoldrunner/gamedata/hi_wad.dat new file mode 100644 index 00000000..5e3779da Binary files /dev/null and b/kgoldrunner/gamedata/hi_wad.dat differ diff --git a/kgoldrunner/gamedata/levels.tar b/kgoldrunner/gamedata/levels.tar new file mode 100644 index 00000000..851ff50e Binary files /dev/null and b/kgoldrunner/gamedata/levels.tar differ diff --git a/kgoldrunner/kgoldrunner.kdevelop b/kgoldrunner/kgoldrunner.kdevelop new file mode 100644 index 00000000..8dcc793b --- /dev/null +++ b/kgoldrunner/kgoldrunner.kdevelop @@ -0,0 +1,142 @@ + + + + Ian Wadham + ianw2@optusnet.com.au + 1.0.4-kde3 + KDevKDEAutoProject + C++ + + C++ + Code + Qt + KDE + + . + false + + + + + + src/kgoldrunner + default + + + src/kgoldrunner + + false + true + + + + + + + + false + 1 + false + + + + + + + + + + + + + false + *.o,*.lo,CVS + + + + + gtk + gnustep + python + php + perl + + + /home/ianw/KGR_2/kgoldrunner/html/ + /home/ianw/KGR_2/kgoldrunner/html/ + + + + + libtool + + + true + false + false + true + + + false + true + + + + + + + + + + + + -f + + + + -dP + -f + -C -d -P + -u3 -p + + + + + + false + + + .h + .cpp + true + + + + true + 2 + + + false + false + + kgoldrunner + 1.0.4-kde3 + + + + + + + + + false + false + false + 0 + false + false + false + false + + + diff --git a/kgoldrunner/kgoldrunner.lsm b/kgoldrunner/kgoldrunner.lsm new file mode 100644 index 00000000..b0a6746d --- /dev/null +++ b/kgoldrunner/kgoldrunner.lsm @@ -0,0 +1,24 @@ +Begin3 +Title: KGoldrunner +Version: 2.0 +Entered-date: 12AUG2003 +Description: KGoldrunner is an action game where the hero runs through a + maze and dodges enemies. You must guide him with the mouse + and collect all the gold nuggets, then you can climb up into + the next level. Your only weapon is to dig holes in the + floor and trap the enemies. + + KGoldrunner combines action, strategy and puzzle solving + --- all in one game. It is based on Lode Runner (TM), an + all-time classic computer game. It has a Tutorial and + over 100 levels: some easy, some very hard. +Keywords: arcade game Lode Runner KGoldrunner Loderunner Goldrunner +Author: ianw2@optusnet.com.au (Ian Wadham) + grisuji@gmx.de (Marco Krüger) +Maintained-by: ianw2@optusnet.com.au (Ian Wadham) +Primary-site: +Alternate-site: +Original-site: +Platforms: Linux with KDE 3 Desktop +Copying-policy: GPL +End diff --git a/kgoldrunner/src/KGoldrunner.desktop b/kgoldrunner/src/KGoldrunner.desktop new file mode 100644 index 00000000..6e22384a --- /dev/null +++ b/kgoldrunner/src/KGoldrunner.desktop @@ -0,0 +1,122 @@ +# KDE Config File +[Desktop Entry] +Name=KGoldrunner +Name[ar]=لعبة البحث عن الذهب (KGoldrunner) +Name[be]=Ð—Ð°Ð»Ð°Ñ‚Ð°Ñ Ð»Ñ–Ñ…Ð°Ð¼Ð°Ð½ÐºÐ° +Name[bn]=কে-গোলà§à¦¡à¦°à¦¾à¦¨à¦¾à¦° +Name[fr]=KGoldRunner +Name[hi]=के-गोलà¥à¤¡à¤°à¤¨à¤° +Name[hr]=KPotraga za zlatom +Name[ne]=केडीई गोलà¥à¤¡ रनर +Name[pa]=ਕੇ-ਗੋਲਡ ਰਨਰ +Name[sv]=Kgoldrunner +Name[ta]=கேதஙà¯à®•à®®à¯ ஓடà¯à®ªà®µà®°à¯ +Name[tg]=KÒ¶Ó¯Ñндаи тилло +Name[uk]=Золотошукач +Name[zh_TW]=KGoldrunner 撿金塊 +GenericName=Action & Puzzle Solving Game +GenericName[be]=ÐÐºÑ‚Ñ‹ÑžÐ½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ Ð· галаваломкамі +GenericName[bg]=Бегач за злато +GenericName[bn]=অà§à¦¯à¦¾à¦•à¦¶à¦¨ আর ধাà¦à¦§à¦¾à¦ সমাধানের খেলা +GenericName[bs]=Igra akcije i rjeÅ¡avanja zagonetki +GenericName[ca]=Joc d'acció i resolució de trencaclosques +GenericName[cs]=ŘeÅ¡ení hádanek a akce +GenericName[cy]=Gêm Gynhyrfus o Ddatrys Posau +GenericName[da]=Handlings & mysteriums løsningsspil +GenericName[de]=Spiel zum Rätsellösen +GenericName[el]=Παιχνίδι δÏάσης και επίλυσης παζλ +GenericName[eo]=Agada kaj enigmosolva ludo +GenericName[es]=Juego de acción y resolución de rompecabezas +GenericName[et]=Põnev mõistatuste lahendamise mäng +GenericName[eu]=Ekintza eta puzzleen ebazpen jokoa +GenericName[fa]=بازی کنشی Ùˆ حل معما +GenericName[fi]=Toiminta- & palikkapeli +GenericName[fr]=Jeu de résolution de puzzle et d'actions +GenericName[he]=משחק ×קשן ופתרון חידות +GenericName[hr]=Igra akcije i rjeÅ¡avanja zagonetki +GenericName[hu]=Ãœgyességi és logikai +GenericName[is]=Leysa þrautir og læti +GenericName[it]=Gioco di azione e rompicapo +GenericName[ja]=アクションã¨è¬Žè§£ãゲーム +GenericName[km]=សកម្មភាព Puzzle ដោះ​ស្រាយ​ស្បែង +GenericName[ko]=ì•¡ì…˜ ë° í¼ì¦ 풀기 게임 +GenericName[lt]=Veiskmo ir dÄ—lionių žaidimas +GenericName[lv]=DarbÄ«bas un mÄ«klu atrisinÄÅ¡anas spÄ“le +GenericName[mk]=Игра Ñо акција и решавање Ñложувалка +GenericName[nb]=Handlingsfylt grublespill +GenericName[nds]=Akschoon- un Radelsspeel +GenericName[ne]=काम र पजल समाधान गरà¥à¤¨à¥‡ खेल +GenericName[nl]=Actie- en puzzelspel +GenericName[nn]=Handlingsfylt grublespel +GenericName[pl]=UkÅ‚adanka zrÄ™cznoÅ›ciowa +GenericName[pt]=Jogo de Acção e Puzzles +GenericName[pt_BR]=Jogo de ação e quebra-cabeça +GenericName[ru]=ЗолотоиÑкатель +GenericName[sk]=AkÄná a skladaÄková hra +GenericName[sl]=Igra akcije in reÅ¡evanja ugank +GenericName[sr]=Ðкциона игра Ñа решавањем загонетки +GenericName[sr@Latn]=Akciona igra sa reÅ¡avanjem zagonetki +GenericName[sv]=Action och pussellösningsspel +GenericName[ta]=செயல௠& பà¯à®¤à®¿à®°à¯ விடà¯à®µà®¿à®•à¯à®•à¯à®®à¯ விளையாடà¯à®Ÿà¯ +GenericName[uk]=Тактичка гра-головоломка +GenericName[zh_CN]=åŠ¨ä½œä¸Žè§£è¿·æ¸¸æˆ +GenericName[zh_TW]=動作與解謎éŠæˆ² +Type=Application +Exec=kgoldrunner +Icon=kgoldrunner +DocPath=kgoldrunner/index.html +Comment=A game of action and puzzle-solving +Comment[ar]=لعبة حركة وحلَ ألغاز +Comment[be]=ÐÐºÑ‚Ñ‹ÑžÐ½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ Ð· галаваломкамі +Comment[bg]=Екшън игра Ñ Ñ€ÐµÑˆÐ°Ð²Ð°Ð½Ðµ на главоблъÑканици +Comment[bn]=ধাà¦à¦§à¦¾à¦ ও অà§à¦¯à¦¾à¦•à¦¶à¦¨ মেশানো à¦à¦• দারà§à¦£ খেলা +Comment[bs]=Akciona igra sa rjeÅ¡avanjem zagonetke +Comment[ca]=Un joc d'acció i solució de trencaclosques +Comment[cs]=AkÄní hra s Å™eÅ¡ením hádanek +Comment[cy]=Gêm gynhyrfus o ddatrys posau +Comment[da]=Et spil med handling og problemløsning +Comment[de]=Aktionsreiches Spiel für Rätsellöser +Comment[el]=Ένα παιχνίδι δÏάσης και λÏσης γÏίφων +Comment[es]=Un juego de acción y resolución de rompecabezas +Comment[et]=Põnev mõistatuste lahendamise mäng +Comment[eu]=Ekintzarako eta puzzleen ebazpenerako jokoa +Comment[fa]=یک بازی کنشی Ùˆ حل معما +Comment[fi]=Toiminta- ja pulmanratkaisupeli +Comment[fr]=Jeu d'action et de résolution d'énigmes +Comment[gl]=Un xogo de acción e resolución de puzzles +Comment[he]=משחק ×קשן ופתרון חידות +Comment[hi]=पहेलियाठहल करने और सकà¥à¤°à¤¿à¤¯à¤¤à¤¾ का à¤à¤• खेल +Comment[hr]=Igra akcije i rjeÅ¡avanja zagonetki +Comment[hu]=Ãœgyességi játék rejtvényekkel tarkítva +Comment[is]=Leikur sem reynir á hraða og kænsku +Comment[it]=Un gioco d'azione e un rompicapo +Comment[ja]=アクションã¨è¬Žè§£ãゲーム +Comment[km]=ល្បែង​នៃ​សកម្មភាព និង​ការ​ដោះ​ស្រាយ puzzle +Comment[ko]=ì•¡ì…˜ ë° í¼ì¦ 풀기 게임 +Comment[lt]=Veiksmo ir galvosÅ«kių žaidimas +Comment[lv]=DarbÄ«bas un loÄ£ikas spÄ“le +Comment[mk]=Игра Ñо акција и решавање на загатки +Comment[nb]=Et handlingsfylt spill med innlagte gÃ¥ter +Comment[nds]=Een Speel mit veel Akschoon un Radels +Comment[ne]=काम र पजल समाधान गरà¥à¤¨à¥‡ खेल +Comment[nl]=Een spel met veel actie en puzzels +Comment[nn]=Eit handlingsfylt spel med innlagde gÃ¥ter +Comment[pl]=Gra akcji i rozwiÄ…zywania ukÅ‚adanek +Comment[pt]=Um jogo de acção e resolução de quebra-cabeças +Comment[pt_BR]=Um jogo de ação e quebra-cabeça +Comment[ro]=Un joc de acÅ£iune ÅŸi rezolvare de puzzle +Comment[ru]=Решай головоломки на ходу +Comment[sk]=Kombinovaná akÄná a logická hra +Comment[sl]=Igra akcije in reÅ¡evanja ugank +Comment[sr]=Игра акције и решавања загонетки +Comment[sr@Latn]=Igra akcije i reÅ¡avanja zagonetki +Comment[sv]=Ett spel med action och pussellösning +Comment[ta]=செயலà¯à®ªà®¾à®Ÿà¯ மறà¯à®±à¯à®®à¯ பà¯à®¤à®¿à®°à¯ விடà¯à®µà®¿à®•à¯à®•à¯à®®à¯ விளையாடà¯à®Ÿà¯ +Comment[tg]=Бозии амалиёт ва ҳалли муаммоҳо +Comment[tr]=Aksiyon ve bulmaca çözümlerini barındıran bir oyun +Comment[uk]=Ð’Ð¸Ñ€Ñ–ÑˆÐµÐ½Ð½Ñ Ð³Ð¾Ð»Ð¾Ð²Ð¾Ð»Ð¾Ð¼Ð¾Ðº на ходу +Comment[zh_CN]=åŠ¨ä½œä¸Žè§£è¿·æ¸¸æˆ +Comment[zh_TW]= 一個動作和解謎的éŠæˆ² +Comment[zu]=Umdlalo womnyakazo kanye neyokuxazulula i puzzle +Terminal=false +Categories=Qt;KDE;Game;ArcadeGame; diff --git a/kgoldrunner/src/Makefile.am b/kgoldrunner/src/Makefile.am new file mode 100644 index 00000000..b243765b --- /dev/null +++ b/kgoldrunner/src/Makefile.am @@ -0,0 +1,42 @@ +## Makefile.am for kgoldrunner + +# this is the program that gets installed. it's name is used for all +# of the other Makefile.am variables +bin_PROGRAMS = kgoldrunner + +# set the include path for X, qt and KDE +INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes) + +# the library search path. +kgoldrunner_LDFLAGS = $(KDE_RPATH) $(all_libraries) + +# the libraries to link against. +kgoldrunner_LDADD = $(LIB_KDEGAMES) $(LIB_KFILE) $(LIB_KDEPRINT) +kgoldrunner_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +# which sources should be compiled for kgoldrunner +kgoldrunner_SOURCES = kgoldrunner.cpp kgrcanvas.cpp kgrfigure.cpp kgrgame.cpp kgrobject.cpp main.cpp kgrdialog.cpp + +# these are the headers for your project +noinst_HEADERS = enemy1.xpm enemy2.xpm hero.xpm kgoldrunner.h kgrcanvas.h kgrconsts.h kgrdialog.h kgrfigure.h kgrgame.h kgrobject.h kgraphics.h + +# client stuff + + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kgoldrunner.pot + +KDE_ICON = kgoldrunner + +# this is where the kdelnk file will go +xdg_apps_DATA = KGoldrunner.desktop + +# this is where the XML-GUI resource file goes +rcdir = $(kde_datadir)/kgoldrunner +rc_DATA = kgoldrunnerui.rc + +AM_CXXFLAGS = -DQT3 + diff --git a/kgoldrunner/src/data_messages.cpp b/kgoldrunner/src/data_messages.cpp new file mode 100644 index 00000000..838db2a8 --- /dev/null +++ b/kgoldrunner/src/data_messages.cpp @@ -0,0 +1,608 @@ +/* This is a dummy *.cpp program that contains strings extracted from + KGoldrunner game data files. It is not intended to be compiled or + executed. Its only function is to hold the strings in a place where + they will be re-formatted into a .pot file for the translators. +*/ + +#include + +dont_call_me() { + +i18n("TRANSLATORS: Please see the notes in the data_messages.cpp file."); + +// NOTES: Strings in this file originate from KGoldrunner DATA files, +// as installed in $KDEDIR/share/apps/kgoldrunner/system. They are +// names and descriptions of games and names and hints for levels. The +// C++ comments indicate which data file the strings came from. You +// may sometimes need to look at the corresponding game or level graphics +// to work out a good translation (e.g. Initiation 25, Mongolian Horde). + +// To ease the burden on translators, this file does not include all the +// data messages. For example, the hints on championship games are not +// included. A player that good should not need a hint and maybe has no +// need of a translation ... :-) The most urgent translations would be +// names and hints for the Tutorial games, then the game names and +// descriptions (as appearing in the main dialog box) and then the other +// level names and hints. + +// From levels/tute001.grl (in levels.tar). +i18n("Hi !!"); +i18n("Hi ! Welcome to KGoldrunner ! The idea of the game is to pick up all the gold nuggets, then climb to the top of the playing area and move up to the next level. A hidden ladder will appear as you collect the last nugget." +"\n" +"\nThe hero (the green figure) is your deputy. To collect the nuggets, just point the mouse where you want him to go. At first gravity takes over and he falls ..." +); + +// From levels/tute002.grl (in levels.tar). +i18n("Navigation"); +i18n("This is an exercise in moving around. Follow the track of gold nuggets until the ladder pops up at the right. The hero can only follow the mouse along simple paths (like _ | L or U), so be careful not to get too far ahead of him." +"\n" +"\nDANGER: Try not to fall off the ladder or bar into the concrete pit at the bottom right. If you do get trapped there, the only way out is to kill the hero (press key Q for quit) and start the level again." +); + +// From levels/tute003.grl (in levels.tar). +i18n("Digging"); +i18n("Now you have to dig to get the gold! Just use the left and right mouse buttons to dig left or right of the hero's position. The hero can then jump into and through the hole he has dug. He can also dig several holes in a row and run sideways through the dug holes. Be careful though. After a while the holes close up and you can get trapped and killed." +"\n" +"\nIn the third box down, you have to dig two holes, jump in and quickly dig one more, to get through two layers. On the right, you have to dig three, then two then one to get through. There are also two little puzzles to work out along the way. Good luck!" +"\n" +"\nBy the way, you can dig through brick, but not concrete." +); + +// From levels/tute004.grl (in levels.tar). +i18n("You Have ENEMIES !!!"); +i18n("Well, it's been nice and easy up to now, but the game would be no fun without enemies. They are after the gold too: worse still they are after you! You die if they catch you, but maybe you will have a few lives left and can start again." +"\n" +"\nYou can handle enemies by running away, digging a hole or luring them into part of the playing area where they get stranded." +"\n" +"\nIf an enemy falls into a hole, he gives up any gold he is carrying, then gets stuck in the hole for a time and climbs out. If the hole closes while he is in it, he dies and reappears somewhere else on the screen. You can deliberately kill enemies by digging several holes in a row." +"\n" +"\nMore importantly, you can run over an enemy's head. You must do that right at the start of this level. Dig a hole, trap the enemy, wait for him to fall all the way in, then run over him, with the other enemy in hot pursuit..." +); + +// From levels/tute005.grl (in levels.tar). +i18n("Bars"); +i18n("You can move horizontally along bars (or poles), but if you move down you will let go and fall .... Also, note that you can collect gold by falling onto it." +); + +// From levels/tute006.grl (in levels.tar). +i18n("False Bricks"); +i18n("Some of the bricks in this level are not what they seem. If you walk onto them you fall through. The enemies fall through them too." +); + +// From levels/tute007.grl (in levels.tar). +i18n("Bye ......."); +i18n("This is just a nice easy level to finish up with. Have fun with the other games in KGoldrunner." +"\n" +"\nNote that there is a game Editor in which you and your friends can make up levels and challenge each other. There is also a KGoldrunner Handbook in the Help Menu, which contains more detail than this Tutorial." +"\n" +"\nIt's been a pleasure showing you around. Bye !!!" +); + +// From levels/tutea001.grl (in levels.tar). +i18n("Bars and Ladders"); +i18n("There's nowhere to dig, so you must dodge the enemies and avoid falling to the concrete at the wrong time. Try to keep the enemies together." +"\n" +"\nIf an enemy has a gold outline, he is holding a nugget. He might drop it as he runs over the concrete or maybe at the top of a ladder ...... patience, patience !!" +); + +// From levels/tutea002.grl (in levels.tar). +i18n("To kill ....."); +i18n("You cannot get up to the gold, so you must get the enemies to bring it down to you. But how do you get them to keep going back?" +"\n" +"\nIf you are feeling stressed, you can hit the P or Esc key and take a break. Also, you can use the Settings menu to slow down the action." +); + +// From levels/tutea003.grl (in levels.tar). +i18n("... Or not to kill?"); +i18n("It is best not to kill the enemy. Try it and you will find out why ... Heh, heh, heh !! ... ;-)" +"\n" +"\nIf you do kill him unintentionally, before you have collected the gold at the top left, you can still finish the level by digging away the side of the pit he is in." +); + +// From levels/tutea004.grl (in levels.tar). +i18n("Traps"); +i18n("Some of the bricks here are not what they seem. They are known as false bricks, fall-through bricks or traps. If you try to walk on them, you fall through. If the enemies walk on them, they can descend on you without warning. Sometimes you have to fall through a brick to get some gold." +"\n" +"\nAt the start, jump into the concrete pit, dig and drop through. That will get one enemy tied up for a while .... :-)" +); + +// From levels/tutea005.grl (in levels.tar). +i18n("Bye ......."); +i18n("This is just a nice easy level to finish up with. Have fun with the other games in KGoldrunner." +"\n" +"\nNote that there is a game Editor in which you and your friends can make up levels and challenge each other. There is also a KGoldrunner Handbook in the Help Menu, which contains more detail than this Tutorial." +"\n" +"\nIt's been a pleasure showing you around. Bye !!!" +); + +// From levels/level000.grl (in levels.tar). + +// From levels/level001.grl (in levels.tar). + +// From levels/level002.grl (in levels.tar). + +// From levels/level003.grl (in levels.tar). + +// From levels/level004.grl (in levels.tar). + +// From levels/level005.grl (in levels.tar). + +// From levels/level006.grl (in levels.tar). + +// From levels/level007.grl (in levels.tar). + +// From levels/level008.grl (in levels.tar). + +// From levels/level009.grl (in levels.tar). + +// From levels/level010.grl (in levels.tar). + +// From levels/level011.grl (in levels.tar). + +// From levels/level012.grl (in levels.tar). + +// From levels/level013.grl (in levels.tar). + +// From levels/level014.grl (in levels.tar). + +// From levels/level015.grl (in levels.tar). + +// From levels/level016.grl (in levels.tar). + +// From levels/level017.grl (in levels.tar). + +// From levels/level018.grl (in levels.tar). + +// From levels/plws001.grl (in levels.tar). +i18n("Don't Panic"); + +// From levels/plws002.grl (in levels.tar). +i18n("Lust for Gold"); +i18n("When you kill the enemies you can trap them permanently in the pit at top right." +); + +// From levels/plws003.grl (in levels.tar). +i18n("Ladders? Trust me !"); + +// From levels/plws004.grl (in levels.tar). +i18n("Drop In and Say Hello"); + +// From levels/plws005.grl (in levels.tar). +i18n("The Mask"); + +// From levels/plws006.grl (in levels.tar). +i18n("Check for Traps"); + +// From levels/plws007.grl (in levels.tar). +i18n("Take It Easy !"); + +// From levels/plws008.grl (in levels.tar). +i18n("Fall on a Fortune"); + +// From levels/plws009.grl (in levels.tar). +i18n("The Lattice"); + +// From levels/plws010.grl (in levels.tar). +i18n("Shower of Gold"); + +// From levels/plws011.grl (in levels.tar). +i18n("The Foundry"); + +// From levels/plws012.grl (in levels.tar). +i18n("Soft Landings"); + +// From levels/plws013.grl (in levels.tar). +i18n("Unlucky for Some"); + +// From levels/plws014.grl (in levels.tar). +i18n("The Balance"); + +// From levels/plws015.grl (in levels.tar). +i18n("Gold Bars"); + +// From levels/plws016.grl (in levels.tar). +i18n("Hard Row to Hoe"); + +// From levels/plws017.grl (in levels.tar). +i18n("Golden Maze"); + +// From levels/plws018.grl (in levels.tar). +i18n("Delayed Trap"); + +// From levels/plws019.grl (in levels.tar). +i18n("Nowhere to Hide"); + +// From levels/plws020.grl (in levels.tar). +i18n("Watch the Centre"); + +// From levels/plws021.grl (in levels.tar). +i18n("Where to Dig?"); + +// From levels/plws022.grl (in levels.tar). +i18n("Easy Stages"); + +// From levels/plws023.grl (in levels.tar). +i18n("Gold Mesh"); + +// From levels/plws024.grl (in levels.tar). +i18n("Acrobat"); + +// From levels/plws025.grl (in levels.tar). +i18n("Mongolian Horde"); + +// From levels/plws026.grl (in levels.tar). +i18n("Rocky Terrain"); + +// From levels/plws027.grl (in levels.tar). +i18n("Down the Chimney"); + +// From levels/plws028.grl (in levels.tar). +i18n("Space Invader"); + +// From levels/plws029.grl (in levels.tar). +i18n("Winding Road"); + +// From levels/plws030.grl (in levels.tar). +i18n("Light My Fire"); + +// From levels/plws031.grl (in levels.tar). +i18n("Cockroach"); + +// From levels/plws032.grl (in levels.tar). +i18n("The Runaround"); + +// From levels/plws033.grl (in levels.tar). +i18n("Speedy"); + +// From levels/plws034.grl (in levels.tar). +i18n("Dig Deep"); + +// From levels/plws035.grl (in levels.tar). +i18n("Zig Zag"); + +// From levels/plws036.grl (in levels.tar). +i18n("Free Fall"); + +// From levels/plws037.grl (in levels.tar). +i18n("Forgotten Gold"); + +// From levels/plws038.grl (in levels.tar). +i18n("Two of Diamonds"); + +// From levels/plws039.grl (in levels.tar). +i18n("Suicide Jump"); + +// From levels/plws040.grl (in levels.tar). +i18n("Easy Access"); + +// From levels/plws041.grl (in levels.tar). +i18n("Gold Braid"); + +// From levels/plws042.grl (in levels.tar). +i18n("Cat's Eyes"); + +// From levels/plws043.grl (in levels.tar). +i18n("Keep 'em Coming"); + +// From levels/plws044.grl (in levels.tar). +i18n("The Funnel"); + +// From levels/plws045.grl (in levels.tar). +i18n("Lattice Maze"); + +// From levels/plws046.grl (in levels.tar). +i18n("Hard Work for Poor Pay"); + +// From levels/plws047.grl (in levels.tar). +i18n("Forked Ladders"); + +// From levels/plws048.grl (in levels.tar). +i18n("Snowing Gold"); + +// From levels/plws049.grl (in levels.tar). +i18n("Left or Right?"); + +// From levels/plws050.grl (in levels.tar). +i18n("Houndstooth"); + +// From levels/plws051.grl (in levels.tar). +i18n("Five Levels"); + +// From levels/plws052.grl (in levels.tar). +i18n("Pitfalls"); + +// From levels/plws053.grl (in levels.tar). +i18n("Get IN There !!"); + +// From levels/plws054.grl (in levels.tar). +i18n("A Steady Climb"); + +// From levels/plws055.grl (in levels.tar). +i18n("Fall-through Lattice"); + +// From levels/plws056.grl (in levels.tar). +i18n("Get me OUT of Here !!"); + +// From levels/plws057.grl (in levels.tar). +i18n("Empty Cellar"); + +// From levels/plws058.grl (in levels.tar). +i18n("The Rose"); + +// From levels/plws059.grl (in levels.tar). +i18n("Lotus Puzzle"); + +// From levels/plws060.grl (in levels.tar). +i18n("Long Drop"); + +// From levels/plws061.grl (in levels.tar). +i18n("Party On !!!"); + +// From levels/plws062.grl (in levels.tar). +i18n("Cross-stitch"); + +// From levels/plws063.grl (in levels.tar). +i18n("Can't Get Up There"); + +// From levels/plws064.grl (in levels.tar). +i18n("They're Everywhere !!!"); + +// From levels/plws065.grl (in levels.tar). +i18n("Rooftops"); + +// From levels/plws066.grl (in levels.tar). +i18n("Tricky Traps"); + +// From levels/plws067.grl (in levels.tar). +i18n("Make Them Work for You"); + +// From levels/plws068.grl (in levels.tar). +i18n("Get Going !!"); + +// From levels/plws069.grl (in levels.tar). +i18n("Three Chimneys"); + +// From levels/plws070.grl (in levels.tar). +i18n("The Archway"); + +// From levels/plws071.grl (in levels.tar). +i18n("Starwave"); + +// From levels/plws072.grl (in levels.tar). +i18n("Amazing Finish"); + +// From levels/plws073.grl (in levels.tar). +i18n("Overcrowding"); + +// From levels/plws074.grl (in levels.tar). +i18n("Pillars"); + +// From levels/plws075.grl (in levels.tar). +i18n("Hopeful Descent"); + +// From levels/plws076.grl (in levels.tar). +i18n("The Rack"); + +// From levels/plws077.grl (in levels.tar). +i18n("Twists and Turns"); + +// From levels/plws078.grl (in levels.tar). +i18n("The Saucer"); + +// From levels/plws079.grl (in levels.tar). +i18n("The Dotted Line"); + +// From levels/plws080.grl (in levels.tar). +i18n("Don't Look Down (1)"); + +// From levels/plws081.grl (in levels.tar). +i18n("Getting Started"); + +// From levels/plws082.grl (in levels.tar). +i18n("Digging Hassle"); + +// From levels/plws083.grl (in levels.tar). +i18n("Easy Middle"); + +// From levels/plws084.grl (in levels.tar). +i18n("Don't Look Down (2)"); + +// From levels/plws085.grl (in levels.tar). +i18n("Which Way?"); + +// From levels/plws086.grl (in levels.tar). +i18n("Don't Look Down (3)"); + +// From levels/plws087.grl (in levels.tar). +i18n("Drop ???"); + +// From levels/plws088.grl (in levels.tar). +i18n("Help !!!"); + +// From levels/plws089.grl (in levels.tar). +i18n("Yorick's Skull"); + +// From levels/plws090.grl (in levels.tar). +i18n("No Mercy"); + +// From levels/plws091.grl (in levels.tar). +i18n("Gold Sandwich"); + +// From levels/plws092.grl (in levels.tar). +i18n("Golden Curtain"); + +// From levels/plws093.grl (in levels.tar). +i18n("Are you spider or fly?"); + +// From levels/plws094.grl (in levels.tar). +i18n("Funny?"); + +// From levels/plws095.grl (in levels.tar). +i18n("Hard Landings"); + +// From levels/plws096.grl (in levels.tar). +i18n("Golden Tower"); + +// From levels/plws097.grl (in levels.tar). +// i18n("Das Boot"); // Not translated: it's the name of a German movie. +i18n("This level is named after the famous German submarine war film and dedicated to Marco Krüger of Berlin, the original author of KGoldrunner." +"\n" +"\nJust one small hint .... if you stand on the right hand end of the boat you can get the enemy to fall towards you .... the rest is up to you !!!!" +); + +// From levels/plws098.grl (in levels.tar). +i18n("Quick ! RUN !!!"); + +// From levels/plws099.grl (in levels.tar). +i18n("Surprise Ending"); + +// From levels/plws100.grl (in levels.tar). +i18n("Diagonal Disaster"); + +// From levels/wad001.grl (in levels.tar). +i18n("Easy Start"); + +// From levels/wad002.grl (in levels.tar). +i18n("Mobile Bricks"); + +// From levels/wad003.grl (in levels.tar). +i18n("The Big Haul"); + +// From levels/wad004.grl (in levels.tar). +i18n("Quick Off The Mark"); + +// From levels/wad005.grl (in levels.tar). +i18n("Who Needs Enemies?"); + +// From levels/wad006.grl (in levels.tar). +i18n("Asymmetrical"); + +// From levels/wad007.grl (in levels.tar). +i18n("Goldrunner Prophecy"); + +// From levels/wad008.grl (in levels.tar). +i18n("The Rosette"); + +// From levels/wad009.grl (in levels.tar). +i18n("He's Got the Gold"); + +// From levels/wad010.grl (in levels.tar). +i18n("Towers of Gold"); + +// From levels/wad011.grl (in levels.tar). +i18n("The Box"); + +// From levels/wad012.grl (in levels.tar). +i18n("Delayed Drop"); + +// From levels/wad013.grl (in levels.tar). +i18n("Maze of Ladders"); + +// From levels/wad014.grl (in levels.tar). +i18n("Ride 'em Down"); + +// From levels/wad015.grl (in levels.tar). +i18n("Hair's Breadth Timing"); + +// From levels/wad016.grl (in levels.tar). +i18n("The Three Musketeers"); + +// From levels/wad017.grl (in levels.tar). +i18n("Rat Trap"); + +// From levels/wad018.grl (in levels.tar). +i18n("Head Case"); + +// From levels/wad019.grl (in levels.tar). +i18n("Under the Stairs"); + +// From levels/wad020.grl (in levels.tar). +i18n("Bertie Beetle"); + +// From levels/wad021.grl (in levels.tar). +i18n("Short Circuit"); + +// From levels/wad022.grl (in levels.tar). +i18n("Synchronised Running"); + +// From levels/plwv001.grl (in levels.tar). +i18n("Impossible?"); + +// From levels/plwv002.grl (in levels.tar). +i18n("The Runaround"); + +// From levels/plwv003.grl (in levels.tar). +i18n("Short Cut?"); + +// From levels/plwv004.grl (in levels.tar). +i18n("Sky Walker"); + +// From levels/plwv005.grl (in levels.tar). +i18n("The Vault"); + +// From levels/plwv006.grl (in levels.tar). +i18n("Patchwork Quilt"); + +// From levels/plwv007.grl (in levels.tar). +i18n("Do You Need Him?"); + +// From levels/plwv008.grl (in levels.tar). +i18n("Stuck in Storage"); + +// From levels/plwv009.grl (in levels.tar). +i18n("So Far for So Little"); + +// From levels/plwv010.grl (in levels.tar). +i18n("Pharaoh's Tomb"); + +// From levels/plwv011.grl (in levels.tar). +i18n("Entangled"); + +// From levels/plwv012.grl (in levels.tar). +i18n("Flying Tower"); + +// From levels/plwv013.grl (in levels.tar). +i18n("Pot Hole"); + +// From levels/plwv014.grl (in levels.tar). +i18n("Sticky Ladders"); + +// From levels/plwv015.grl (in levels.tar). +i18n("The Laboratory"); + +// From levels/plwv016.grl (in levels.tar). +i18n("Pete likes Ladders"); + +// From levels/plwv017.grl (in levels.tar). +i18n("Where's the Roof?"); + +// From levels/plwv018.grl (in levels.tar). +i18n("Ninja Style"); + +// From levels/plwv019.grl (in levels.tar). +i18n("Cooperation?"); + +// From levels/plwv020.grl (in levels.tar). +i18n("Triple Trap"); + +// The following messages are titles and descriptions of games, +// which have been extracted from the file gamedata/games.dat. + +i18n("Initiation"); +i18n("These 100 levels make an excellent introductory game, as well as a good opportunity for experts to build up high scores. They were composed by Peter Wadham and use traditional playing rules.\n\nThe last few levels are very hard, but if you are looking for even more of a challenge, have a go at 'Vengeance of Peter W' .... ;-) ...."); + +i18n("Challenge"); +i18n("These tricky little levels were composed by Peter, Simon, Genevieve and their father Ian Wadham. They use traditional playing rules. Enjoy! .... ;-) ...."); + +i18n("Vengeance of Peter W"); +i18n("Gooood luck !!\nMwarrhh hwwarrrr haarrrr !!!"); + +i18n("KGoldrunner"); +i18n("These levels were composed by Marco Krüger, the original author of the KGoldrunner program, and some of his friends and contributors. They use KGoldrunner rules. The enemies run fast and have an aggressive search strategy. Enjoy! .... :-) ...."); + +i18n("Tutorial"); +i18n("This tutorial is a collection of easy levels that teaches you the rules of KGoldrunner and helps you develop the skills you need to get started. Each level has a brief explanation, then you play .....\n\nWhen you move on to play more advanced levels, you will find that KGoldrunner combines action, strategy and puzzle solving --- all in one game."); + +i18n("Advanced Tutorial"); +i18n("This tutorial is preparation for some of the things you might find in the middle levels of the 'Initiation' game. Enjoy ...."); +} diff --git a/kgoldrunner/src/enemy1.xpm b/kgoldrunner/src/enemy1.xpm new file mode 100644 index 00000000..a494edb7 --- /dev/null +++ b/kgoldrunner/src/enemy1.xpm @@ -0,0 +1,25 @@ +/* XPM */ +static const char * enemy1_xpm[] = { +"320 16 6 1", +" c None", +". c #00FFFF", +"+ c #008080", +"@ c #0000FF", +"# c #000080", +"$ c #000000", +" .++ .++ ", +" .+@ +## +## +## .+@ .+@ +## .@@ +## .@@ +## .++ .+@ .+@ +@# ++# ", +" .@@# +## +## +### .@@ .@@ +## ++@@ +## .@@ +### .@@ .@@.++@@ .+++@.@@ .@@ .@@ ", +" +++## .++@@ +++## .++@@ ++++# .++@@ ++++# .++@@ ++@@#.++@.@@@ .@@ ++@@ .++@++@@ .@@ .@@@.++@++@@# .@@ .@@#.++@ .@@# .@@ .@@++@@# ++@@++@@ ++# +++# .@@ .@@ +++# ++# ", +" .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ ++@@.@@++@# .@++++ ++@@++@+@@# .@@++@ ++@+@@+.@@# .++@@@ ++@+@@+.@@# .+@.@@ +++@+@@ ++ ++ .@@+++# .@@# .@@@++@# ++@#.@@@ ++@@ ", +" ++@@## +++.@@@# ++@@## .@@@# +#+@@# +@@@@#++ +#+@@# ++@@@ ++@@@+@@# .@.@@@ ++@@@@@@# .@@@@@ ++@+@@@@# +@@+@@ ++@+@@@@# +@@.@@ ++@@@+++ .+.@@@@+ .@@@+++@@+@@# ++@@@@#+++@@@ ", +" +++#+@@# .@@@@@@@@@++@ ++++@@@@@## ++@ +++@@@@@@ ++@@++++ .+++@@@@@@@@@ .++ +++@@@@@@## .@@@@@@@# ++@@@@ ..+@ ++@@@@ .@@+ .@@@@# .++@ .@@@@# .@@@ .@@@+@@ .@@@@@@ #@@@@@@@@@ .@@@@@@@@# ", +"+++@@@@@@@+ ++@@ .@+@@@@@+@@@# .@@@#+@@@@@+@@@ .@@+@@@+@@+#.+++ .@@@@@@@++# +@@@+@@@@@+@@ .@@+@@@@@@#@@@@ +++@@@@@+@@@ .@@@# .@@+++ .@@@# .@@@++ ++@@@ +++@@@ ++@@@ ++.@@@ .@@@@@.# ++.@@@@@ #+@@@@ .@@@@# ", +".@@##+@@@@@+@@@# ##+@@@@+## .@@ .@@@@@@@ .@@++@@++@@@+@@@+@@@@@@##+@@ ##++@@@@## .@@++@@@ .@@ .@@@+.@++@@@ .@@@# ++@@@@@ .@@@# ++@@@@@ ++@@@ .@@@@@@ ++@@@ .@@.@@@# .+.@@@@@ .@@@@@@@ .@@@# ++@@@ ", +".@@ .@@@+@@@# .@@@@ ### ++@@@ +++@@@@$+## #@@@@+@@@ .@@ .@@@@ .@@@# +## +## .@@@@@# ++@@+@@@@ .@.### ++@@@@@@@ .@@### .+@@+@@@# +##.@@@ .+@@+@@@# +##.@@@# .@@@@@@@ .@@@@@@@ +++@@@@ .@@@@## ", +" .@@@++## .@@@+ .@@@@ .@@@+ ++++@@@@ ++@@@@ .@@@@ .@@@@ .@@@ ++@@ ++@@ .@@@ ++@@ ++@@ .@@# .@@@ .@@@ .@@# .@@@ .@@@ .@@.@@@@ .@@@.@@@ ++@@@@@@@@ .@@@++@@@# ", +" .@@@@@@@@ +++@@@@@ .++@ +@@@@@# +++@@@@@ .@@@@@@@@ .@@@@@++ ++@@@@+ .++@ .@@@@@#++ ++@@ .@@@ .@@@ ++@@ .@@@ .@@@ .@@@ .@@# .@@@ .@@@ .@@# .@@@ .@@ ++@@ .@@# .@@ .@@# .@@ .@@ ++@@ ", +" .@@@ ++@@ .@@@@@+@@@@ +@@@@@@@+@@@ .@@@@@+@@@ .@@# .@@@ .@@@+@@@@@@ .@@@+@@+@@@# .@@@@@@@@@ ++@@# .@@ .@+@@ ++@@# +@@ .+.@@ +@@ ++@@# .@.+@ +@@ ++@@# .@.+@ ### .@@# ++@@ +## ++@@# .@@ .@@ ++@@# ", +" ++@@ .@@# +@####+#+@@@ #@@@@ .@@# +@####.@@@ ++@@ .@@# .@@@#+####@@ ++@@ @@@@# .@@@####@@ .@@# .@@ .@@# .@@ ++@@ .@@ ++@@ .@@ ++@@ .@@# +@@# .@@ .@@ ++@@ ", +"++@@# +## +@@# ++@@# .@@@ ### ++@@# +@@# ++@@# .@@@ .@@# ++@@ .@@ .@@ ", +".@@# ++@@ ++@@@ ++@@ .@@# .@@@# +@@@ +@@# +@# ++# "}; diff --git a/kgoldrunner/src/enemy2.xpm b/kgoldrunner/src/enemy2.xpm new file mode 100644 index 00000000..7de334d0 --- /dev/null +++ b/kgoldrunner/src/enemy2.xpm @@ -0,0 +1,26 @@ +/* XPM */ +static const char * enemy2_xpm[] = { +"320 16 7 1", +" c None", +". c #FFFF00", +"+ c #808080", +"@ c #0000FF", +"# c #808000", +"$ c #000080", +"% c #000000", +" .++ .++ ", +" .+@ #$$ #$$ #$$ .+@ .+@ #$$ .@@ #$$ .@@ #$$ .++ .+@ .+@ #@$ #+$ ", +" .@@$ #$$ #$$ #$$$ .@@ .@@ #$$ #+@@ #$$ .@@ #$$$ .@@ .@@.++@@ .+++@.@@ .@@ .@@ ", +" ###$$ .++@@ ###$$ .++@@ ####$ .++@@ ####$ .++@@ #+@@$.++@.@@@ .@@ #+@@ .++@#+@@ .@@ .@@@.++@#+@@$ .@@ .@@$.++@ .@@$ .@@ .@@++@@$ #+@@++@@ ##$ ###$ .@@ .@@ ###$ ##$ ", +" .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ #+@@.@@++@$ .@++++ #+@@++@+@@$ .@@++@ #+@+@@+.@@$ .++@@@ #+@+@@+.@@$ .+@.@@ #++@+@@ ## ## .@@+++$ .@@$ .@@@#+@$ #+@$.@@@ #+@@ ", +" #+@@$$ ###.@@@$ #+@@$$ .@@@$ #$+@@$ #@@@@$## #$+@@$ #+@@@ #+@@@+@@$ .@.@@@ #+@@@@@@$ .@@@@@ #+@+@@@@$ +@@+@@ #+@+@@@@$ +@@.@@ #+@@@+++ .+.@@@@# .@@@###@@+@@$ #+@@@@$##+@@@ ", +" ###$+@@$ .@@@@@@@@@#+@ ##++@@@@@$$ #+@ #++@@@@@@ #+@@#### .+#+@@@@@@@@@ .+# ##+@@@@@@$$ .@@@@@@@$ #+@@@@ ..+@ #+@@@@ .@@+ .@@@@$ .++@ .@@@@$ .@@@ .@@@+@@ .@@@@@@ $@@@@@@@@@ .@@@@@@@@$ ", +"#++@@@@@@@+ #+@@ .@+@@@@@+@@@$ .@@@$+@@@@@+@@@ .@@+@@@+@@#$.++# .@@@@@@@++$ #@@@+@@@@@+@@ .@@+@@@@@@$@@@@ ##+@@@@@+@@@ .@@@$ .@@+## .@@@$ .@@@## #+@@@ ##+@@@ #+@@@ ##.@@@ .@@@@@.$ #+.@@@@@ $+@@@@ .@@@@$ ", +".@@$$+@@@@@+@@@$ $$+@@@@#$$ .@@ .@@@@@@@ .@@++@@#+@@@#@@@+@@@@@@$$+@@ $$#+@@@@$$ .@@++@@@ .@@ .@@@#.@++@@@ .@@@$ #+@@@@@ .@@@$ #+@@@@@ #+@@@ .@@@@@@ #+@@@ .@@.@@@$ .+.@@@@@ .@@@@@@@ .@@@$ #+@@@ ", +".@@ .@@@+@@@$ .@@@@ $$$ #+@@@ #++@@@@%#$$ $@@@@+@@@ .@@ .@@@@ .@@@$ #$$ #$$%.@@@@@$ #+@@+@@@@ .@.$$$ #+@@@@@@@ .@@$$$ .+@@+@@@$ #$$.@@@ .+@@+@@@$ #$$.@@@$ .@@@@@@@ .@@@@@@@ ##+@@@@ .@@@@$$ ", +" .@@@+#$$ .@@@+ .@@@@ .@@@+ ###+@@@@ #+@@@@ .@@@@ .@@@@ .@@@ #+@@ #+@@ .@@@ #+@@ #+@@ .@@$ .@@@ .@@@ .@@$ .@@@ .@@@ .@@.@@@@ .@@@.@@@ #+@@@@@@@@ .@@@++@@@$ ", +" .@@@@@@@@ ##+@@@@@ .++@ #@@@@@$ ##+@@@@@ .@@@@@@@@ .@@@@@## #+@@@@# .++@ .@@@@@$## #+@@ .@@@ .@@@ #+@@ .@@@ .@@@ .@@@ .@@$ .@@@ .@@@ .@@$ .@@@ .@@ #+@@ .@@$ .@@ .@@$ .@@ .@@ #+@@ ", +" .@@@ #+@@ .@@@@@+@@@@ #@@@@@@@+@@@ .@@@@@+@@@ .@@$ .@@@ .@@@+@@@@@@ .@@@+@@+@@@$ .@@@@@@@@@ #+@@$ .@@ .@+@@ #+@@$ +@@ .+.@@ +@@ #+@@$ .@.+@ +@@ #+@@$ .@.+@ $$$ .@@$ #+@@ #$$ #+@@$ .@@ .@@ #+@@$ ", +" #+@@ .@@$ +@$$$$#$+@@@ $@@@@ .@@$ +@$$$$.@@@ #+@@ .@@$ .@@@$#$$$$@@ #+@@ @@@@$ .@@@$$$$@@ .@@$ .@@ .@@$ .@@ #+@@ .@@ #+@@ .@@ #+@@ .@@$ +@@$ .@@ .@@ #+@@ ", +"#+@@$ #$$ #@@$ #+@@$ .@@@ $$$ #+@@$ #@@$ #+@@$ .@@@ .@@$ #+@@ .@@ .@@ ", +".@@$ #+@@ #+@@@ #+@@ .@@$ .@@@$ #@@@ +@@$ #@$ #+$ "}; diff --git a/kgoldrunner/src/hero.xpm b/kgoldrunner/src/hero.xpm new file mode 100644 index 00000000..6cfa2e2c --- /dev/null +++ b/kgoldrunner/src/hero.xpm @@ -0,0 +1,26 @@ +/* XPM */ +static const char * hero_xpm[] = { +"320 16 7 1", +" c None", +". c #00FF00", +"+ c #00C000", +"@ c #008000", +"# c #004000", +"$ c #000000", +"% c #303030", +" .++ .++ ", +" .+@ @## @## @## .+@ .+@ @## .@@ @## .@@ @## .++ .+@ .+@ @@# @+# ", +" .@@# @## @## @### .@@ .@@ @## @+@@ @## .@@ @### .@@ .@@.++@@ .+++@.@@ .@@ .@@ ", +" @@@## .++@@ @@@## .++@@ @@@@# .++@@ @@@@# .++@@ @+@@#.++@.@@@ .@@ @+@@ .++@@+@@ .@@ .@@@.++@@+@@# .@@ .@@#.++@ .@@# .@@ .@@++@@# @+@@++@@ @@# @@@# .@@ .@@ @@@# @@# ", +" .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ @+@@.@@++@# .@++++ @+@@++@+@@# .@@++@ @+@+@@+.@@# .++@@@ @+@+@@+.@@# .+@.@@ @++@+@@ @@ @@ .@@+++# .@@# .@@@@+@# @+@#.@@@ @+@@ ", +" @+@@## @@@.@@@# @+@@## .@@@# @#+@@# @@@@@#@@ @#+@@# @+@@@ @+@@@+@@# .@.@@@ @+@@@@@@# .@@@@@ @+@+@@@@# +@@+@@ @+@+@@@@# +@@.@@ @+@@@+++ .+.@@@@@ .@@@@@@@@+@@# @+@@@@#@@+@@@ ", +" @@@#+@@# .@@@@@@@@@@+@ @@++@@@@@## @+@ @++@@@@@@ @+@@@@@@ .+@+@@@@@@@@@ .+@ @@+@@@@@@## .@@@@@@@# @+@@@@ ..+@ @+@@@@ .@@+ .@@@@# .++@ .@@@@# .@@@ .@@@+@@ .@@@@@@ #@@@@@@@@@ .@@@@@@@@# ", +"@++@@@@@@@+ @+@@ .@+@@@@@+@@@# .@@@#+@@@@@+@@@ .@@+@@@+@@@#.++@ .@@@@@@@++# @@@@+@@@@@+@@ .@@+@@@@@@#@@@@ @@+@@@@@+@@@ .@@@# .@@+@@ .@@@# .@@@@@ @+@@@ @++@@@ @+@@@ @..@@@ .@@@@@.# @+.@@@@@ #+@@@@ .@@@@# ", +".@@##+@@@@@+@@@# ##+@@@@@## .@@ .@@@@@@@ .@@++@@@+@@@@@@@+@@@@@@##+@@ ##@+@@@@## .@@++@@@ .@@ .@@@@.@++@@@ .@@@# @+@@@@@ .@@@# @+@@@@@ @+@@@ .@@@+@@ @+@@@ .@@.@@@# .+.@@@@@ .@@@@@@@ .@@@# @+@@@ ", +".@@ .@@@+@@@# .@@@@ ### @+@@@ @++@@@@$@## #@@@@+@@@ .@@ .@@@@ .@@@# @## @##$.@@@@@# @+@@+@@@@ .@.### @+@@@@@@@ .@@### .+@@+@@@# @##.@@@ .+@@+@@@# @# .@@@# .@@@@@@@ .@@@@@@@ @@+@@@@ .@@@@## ", +" .@@@+@## .@@@+ .@@@@ .@@@+ @@@+@@@@ @+@@@@ .@@@@ .@@@@ .@@@ @+@@ @+@@ .@@@ @+@@ @+@@ .@@% .@@@ .@@@ .@@% .@@@ .@@@ .@@.@@@@ .@@@.@@@ @+@@@@@@@@ .@@@++@@@# ", +" .@@@@@@@@ @@+@@@@@ .++@ @@@@@@# @@+@@@@@ .@@@@@@@@ .@@@@@@@ @+@@@@@ .++@ .@@@@@#@@ @+@@ .@@@ .@@@ @+@@ .@@@ .@@@ .@@@ .@@# .@@@ .@@@ .@@# .@@@ .@@ @+@@ .@@# .@@ .@@# .@@ .@@ @+@@ ", +" .@@@ @+@@ .@@@@@+@@@@ @@@@@@@@+@@@ .@@@@@+@@@ .@@# .@@@ .@@@+@@@@@@ .@@@+@@+@@@# .@@@@@@@@@ @+@@# .@@ .@+@@ @+@@# +@@ .+.@@ +@@ @+@@# .@.+@ +@@ @+@@# .@.+@ ### .@@# @+@@ @## @+@@# .@@ .@@ @+@@# ", +" @+@@ .@@# +@#%##@#+@@@ #@@@@ .@@# +@#%##.@@@ @+@@ .@@# .@@@#@####@@ @+@@ @@@@# .@@@####@@ .@@# .@@ .@@# .@@ @+@@ .@@ @+@@ .@@ @+@@ .@@# +@@# .@@ .@@ @+@@ ", +"@+@@# @## @@@# @+@@# .@@@ ### @+@@# @@@% @+@@# .@@@ .@@# @+@@ .@@ .@@ ", +".@@# @+@@ @+@@@ @+@@ .@@# .@@@# @@@@ +@@# @@# @+# "}; diff --git a/kgoldrunner/src/hi128-app-kgoldrunner.png b/kgoldrunner/src/hi128-app-kgoldrunner.png new file mode 100644 index 00000000..d47375a8 Binary files /dev/null and b/kgoldrunner/src/hi128-app-kgoldrunner.png differ diff --git a/kgoldrunner/src/hi16-app-kgoldrunner.png b/kgoldrunner/src/hi16-app-kgoldrunner.png new file mode 100644 index 00000000..ae7d42a6 Binary files /dev/null and b/kgoldrunner/src/hi16-app-kgoldrunner.png differ diff --git a/kgoldrunner/src/hi22-app-kgoldrunner.png b/kgoldrunner/src/hi22-app-kgoldrunner.png new file mode 100644 index 00000000..460d7cdd Binary files /dev/null and b/kgoldrunner/src/hi22-app-kgoldrunner.png differ diff --git a/kgoldrunner/src/hi32-app-kgoldrunner.png b/kgoldrunner/src/hi32-app-kgoldrunner.png new file mode 100644 index 00000000..1b72581a Binary files /dev/null and b/kgoldrunner/src/hi32-app-kgoldrunner.png differ diff --git a/kgoldrunner/src/hi48-app-kgoldrunner.png b/kgoldrunner/src/hi48-app-kgoldrunner.png new file mode 100644 index 00000000..6ae5f636 Binary files /dev/null and b/kgoldrunner/src/hi48-app-kgoldrunner.png differ diff --git a/kgoldrunner/src/hi64-app-kgoldrunner.png b/kgoldrunner/src/hi64-app-kgoldrunner.png new file mode 100644 index 00000000..35172e71 Binary files /dev/null and b/kgoldrunner/src/hi64-app-kgoldrunner.png differ diff --git a/kgoldrunner/src/kgoldrunner.cpp b/kgoldrunner/src/kgoldrunner.cpp new file mode 100644 index 00000000..6e8cfb3c --- /dev/null +++ b/kgoldrunner/src/kgoldrunner.cpp @@ -0,0 +1,1097 @@ +/* + * Copyright (C) 2003 Ian Wadham and Marco Krüger + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "kgrconsts.h" +#include "kgrobject.h" +#include "kgrfigure.h" +#include "kgrcanvas.h" +#include "kgrdialog.h" +#include "kgrgame.h" +#include "kgoldrunner.h" + +KGoldrunner::KGoldrunner() + : KMainWindow (0, "KGoldrunner"), + view (new KGrCanvas (this)) +{ +/******************************************************************************/ +/************* FIND WHERE THE GAMES DATA AND HANDBOOK SHOULD BE *************/ +/******************************************************************************/ + + // Avoid "saveOK()" check if an error-exit occurs during the file checks. + startupOK = TRUE; + + // Get directory paths for the system levels, user levels and manual. + if (! getDirectories()) { + fprintf (stderr, "getDirectories() FAILED\n"); + startupOK = FALSE; + return; // If games directory not found, abort. + } + + // This message is to help diagnose distribution or installation problems. + printf + ("The games data and handbook should be in the following locations:\n"); + printf ("System games: %s\nUser data: %s\nHandbook: %s\n", + systemDataDir.myStr(), userDataDir.myStr(), systemHTMLDir.myStr()); + +/******************************************************************************/ +/************************ SET PLAYFIELD AND GAME DATA ***********************/ +/******************************************************************************/ + + game = new KGrGame (view, systemDataDir, userDataDir); + + // Initialise the collections of levels (i.e. the list of games). + if (! game->initCollections()) { + startupOK = FALSE; + return; // If no game files, abort. + } + + hero = game->getHero(); // Get a pointer to the hero. + +/******************************************************************************/ +/************************* SET UP THE USER INTERFACE ************************/ +/******************************************************************************/ + + // Get catalog for translation + KGlobal::locale()->insertCatalogue("libkdegames"); + + // Tell the KMainWindow that this is the main widget + setCentralWidget (view); + + // Set up our actions (menu, toolbar and keystrokes) ... + setupActions(); + + // and a status bar. + initStatusBar(); + + // Connect the game actions to the menu and toolbar displays. + connect(game, SIGNAL (setEditMenu (bool)), SLOT (setEditMenu (bool))); + connect(game, SIGNAL (markRuleType (char)), SLOT (markRuleType (char))); + connect(game, SIGNAL (hintAvailable(bool)), SLOT (adjustHintAction(bool))); + connect(game, SIGNAL (defaultEditObj()), SLOT (defaultEditObj())); + + // Apply the saved mainwindow settings, if any, and ask the mainwindow + // to automatically save settings if changed: window size, toolbar + // position, icon size, etc. + setAutoSaveSettings(); + +#ifdef QT3 + // Base size of playing-area and widgets on the monitor resolution. + int dw = KApplication::desktop()->width(); + if (dw > 800) { // More than 800x600. + view->changeSize (+1); // Scale 1.25:1. + } + if (dw > 1024) { // More than 1024x768. + view->changeSize (+1); + view->changeSize (+1); // Scale 1.75:1. + setUsesBigPixmaps (TRUE); // Use big toolbar buttons. + } + view->setBaseScale(); // Set scale for level-names. +#endif + setFixedSize (view->size()); + + makeEditToolbar(); // Uses pixmaps from "view". + editToolbar->hide(); + setDockEnabled (DockBottom, FALSE); + setDockEnabled (DockLeft, FALSE); + setDockEnabled (DockRight, FALSE); + + // Make it impossible to turn off the editor toolbar. + // Accidentally hiding it would make editing impossible. + setDockMenuEnabled (FALSE); + + // Set mouse control of the hero as the default. + game->setMouseMode (TRUE); + + // Paint the main widget (title, menu, status bar, blank playfield). + show(); + + // Force the main widget to appear before the "Start Game" dialog does. + qApp->processEvents(); + + // Call the "Start Game" function and pop up the "Start Game" dialog. + game->startLevelOne(); +} + +KGoldrunner::~KGoldrunner() +{ + delete editToolbar; +} + +void KGoldrunner::setupActions() +{ + /**************************************************************************/ + /****************************** GAME MENU ******************************/ + /**************************************************************************/ + + // New Game... + // Load Saved Game... + // Play Any Level... + // Play Next Level... + // Tutorial + // -------------------------- + + KAction * newAction = KStdGameAction:: + gameNew ( + game, + SLOT(startLevelOne()), actionCollection()); + newAction-> setText (i18n("&New Game...")); + KAction * loadGame = KStdGameAction:: + load ( + game, SLOT(loadGame()), actionCollection()); + loadGame-> setText (i18n("&Load Saved Game...")); + (void) new KAction ( + i18n("&Play Any Level..."), + 0, + game, SLOT(startAnyLevel()), actionCollection(), + "play_any"); + (void) new KAction ( + i18n("Play &Next Level..."), + 0, + game, + SLOT(startNextLevel()), actionCollection(), + "play_next"); + + // Save Game... + // Save Edits... (extra copy) + // -------------------------- + + saveGame = KStdGameAction:: + save ( + game, SLOT(saveGame()), actionCollection()); + saveGame-> setText (i18n("&Save Game...")); + saveGame-> setShortcut (Key_S); // Alternate key. + + // Pause + // Show High Scores + // Get a Hint + // Kill the Hero + // -------------------------- + + myPause = KStdGameAction:: + pause ( + this, SLOT(stopStart()), actionCollection()); + myPause-> setShortcut (Key_Escape); // Alternate key. + highScore = KStdGameAction:: + highscores ( + game, SLOT(showHighScores()), actionCollection()); + hintAction = new KAction ( + i18n("&Get Hint"), "ktip", + 0, + game, SLOT(showHint()), actionCollection(), + "get_hint"); + killHero = new KAction ( + i18n("&Kill Hero"), + Key_Q, + game, SLOT(herosDead()), actionCollection(), + "kill_hero"); + + // Quit + // -------------------------- + + (void) KStdGameAction:: + quit ( + this, SLOT(close()), actionCollection()); + + /**************************************************************************/ + /*************************** GAME EDITOR MENU **************************/ + /**************************************************************************/ + + // Create a Level + // Edit Any Level... + // Edit Next Level... + // -------------------------- + + (void) new KAction ( + i18n("&Create Level"), + 0, + game, SLOT(createLevel()), actionCollection(), + "create"); + (void) new KAction ( + i18n("&Edit Any Level..."), + 0, + game, SLOT(updateLevel()), actionCollection(), + "edit_any"); + (void) new KAction ( + i18n("Edit &Next Level..."), + 0, + game, SLOT(updateNext()), actionCollection(), + "edit_next"); + + // Save Edits... + // Move Level... + // Delete Level... + // -------------------------- + + saveEdits = new KAction ( + i18n("&Save Edits..."), + 0, + game, SLOT(saveLevelFile()), actionCollection(), + "save_edits"); + saveEdits->setEnabled (FALSE); // Nothing to save, yet. + + (void) new KAction ( + i18n("&Move Level..."), + 0, + game, SLOT(moveLevelFile()), actionCollection(), + "move_level"); + (void) new KAction ( + i18n("&Delete Level..."), + 0, + game, + SLOT(deleteLevelFile()), actionCollection(), + "delete_level"); + + // Create a Game + // Edit Game Info... + // -------------------------- + + (void) new KAction ( + i18n("Create Game..."), + 0, + this, SLOT(createGame()), actionCollection(), + "create_game"); + (void) new KAction ( + i18n("Edit Game Info..."), + 0, + this, + SLOT(editGameInfo()), actionCollection(), + "edit_game"); + + /**************************************************************************/ + /*************************** LANDSCAPES MENU ***************************/ + /**************************************************************************/ + + // Default shortcut keys are set by "kgoldrunnerui.rc". + + setKGoldrunner = new KRadioAction ( + "K&Goldrunner", + 0, // Default Shift+G + this, SLOT(lsKGoldrunner()), actionCollection(), + "kgoldrunner"); + setAppleII = new KRadioAction ( + "&Apple II", + 0, // Default Shift+A + this, SLOT(lsApple2()), actionCollection(), + "apple_2"); + setIceCave = new KRadioAction ( + i18n("&Ice Cave"), + 0, // Default Shift+I + this, SLOT(lsIceCave()), actionCollection(), + "ice_cave"); + setMidnight = new KRadioAction ( + i18n("&Midnight"), + 0, // Default Shift+M + this, SLOT(lsMidnight()), actionCollection(), + "midnight"); + setKDEKool = new KRadioAction ( + i18n("&KDE Kool"), + 0, // Default Shift+K + this, SLOT(lsKDEKool()), actionCollection(), + "kde_kool"); + + setKGoldrunner-> setExclusiveGroup ("landscapes"); + setAppleII-> setExclusiveGroup ("landscapes"); + setIceCave-> setExclusiveGroup ("landscapes"); + setMidnight-> setExclusiveGroup ("landscapes"); + setKDEKool-> setExclusiveGroup ("landscapes"); + setKGoldrunner-> setChecked (TRUE); + + /**************************************************************************/ + /**************************** SETTINGS MENU ****************************/ + /**************************************************************************/ + + // Mouse Controls Hero + // Keyboard Controls Hero + // -------------------------- + + setMouse = new KRadioAction ( + i18n("&Mouse Controls Hero"), + 0, + this, + SLOT(setMouseMode()), actionCollection(), + "mouse_mode"); + setKeyboard = new KRadioAction ( + i18n("&Keyboard Controls Hero"), + 0, + this, + SLOT(setKeyBoardMode()), actionCollection(), + "keyboard_mode"); + + setMouse-> setExclusiveGroup ("control"); + setKeyboard-> setExclusiveGroup ("control"); + setMouse-> setChecked (TRUE); + + // Normal Speed + // Beginner Speed + // Champion Speed + // Increase Speed + // Decrease Speed + // -------------------------- + + KRadioAction * nSpeed = new KRadioAction ( + i18n("Normal Speed"), + 0, + this, SLOT(normalSpeed()), actionCollection(), + "normal_speed"); + KRadioAction * bSpeed = new KRadioAction ( + i18n("Beginner Speed"), + 0, + this, SLOT(beginSpeed()), actionCollection(), + "beginner_speed"); + KRadioAction * cSpeed = new KRadioAction ( + i18n("Champion Speed"), + 0, + this, SLOT(champSpeed()), actionCollection(), + "champion_speed"); + (void) new KAction ( // Repeatable action. + i18n("Increase Speed"), + Key_Plus, + this, SLOT(incSpeed()), actionCollection(), + "increase_speed"); + (void) new KAction ( // Repeatable action. + i18n("Decrease Speed"), + Key_Minus, + this, SLOT(decSpeed()), actionCollection(), + "decrease_speed"); + + nSpeed-> setExclusiveGroup ("speed"); + bSpeed-> setExclusiveGroup ("speed"); + cSpeed-> setExclusiveGroup ("speed"); + nSpeed-> setChecked (TRUE); + + // Traditional Rules + // KGoldrunner Rules + // -------------------------- + + tradRules = new KRadioAction ( + i18n("&Traditional Rules"), + 0, + this, SLOT(setTradRules()), actionCollection(), + "trad_rules"); + kgrRules = new KRadioAction ( + i18n("K&Goldrunner Rules"), + 0, + this, SLOT(setKGrRules()), actionCollection(), + "kgr_rules"); + + tradRules-> setExclusiveGroup ("rules"); + kgrRules-> setExclusiveGroup ("rules"); + tradRules-> setChecked (TRUE); + + // Larger Playing Area + // Smaller Playing Area + // -------------------------- + + (void) new KAction ( + i18n("Larger Playing Area"), + 0, + this, SLOT(makeLarger()), actionCollection(), + "larger_area"); + (void) new KAction ( + i18n("Smaller Playing Area"), + 0, + this, SLOT(makeSmaller()), actionCollection(), + "smaller_area"); + + // Configure Shortcuts... + // Configure Toolbars... + // -------------------------- + + KStdAction::keyBindings ( + this, SLOT(optionsConfigureKeys()), + actionCollection()); + // KStdAction::configureToolbars ( + // this, SLOT(optionsConfigureToolbars()), + // actionCollection()); + + /**************************************************************************/ + /************************** KEYSTROKE ACTIONS **************************/ + /**************************************************************************/ + + // Two-handed KB controls and alternate one-handed controls for the hero. + + (void) new KAction (i18n("Move Up"), Key_Up, + this, SLOT(goUp()), actionCollection(), "move_up"); + (void) new KAction (i18n("Move Right"), Key_Right, + this, SLOT(goR()), actionCollection(), "move_right"); + (void) new KAction (i18n("Move Down"), Key_Down, + this, SLOT(goDown()), actionCollection(), "move_down"); + (void) new KAction (i18n("Move Left"), Key_Left, + this, SLOT(goL()), actionCollection(), "move_left"); + (void) new KAction (i18n("Stop"), Key_Space, + this, SLOT(stop()), actionCollection(), "stop"); + (void) new KAction (i18n("Dig Right"), Key_C, + this, SLOT(digR()), actionCollection(), "dig_right"); + (void) new KAction (i18n("Dig Left"), Key_Z, + this, SLOT(digL()), actionCollection(), "dig_left"); + + // Alternate one-handed controls. Set up in "kgoldrunnerui.rc". + + // Key_I, "move_up" + // Key_L, "move_right" + // Key_K, "move_down" + // Key_J, "move_left" + // Key_Space, "stop" (as above) + // Key_O, "dig_right" + // Key_U, "dig_left" + +#ifdef KGR_DEBUG + // Authors' debugging aids. + + (void) new KAction (i18n("Step"), Key_Period, + game, SLOT(doStep()), actionCollection(), "do_step"); + (void) new KAction (i18n("Test Bug Fix"), Key_B, + game, SLOT(bugFix()), actionCollection(), "bug_fix"); + (void) new KAction (i18n("Show Positions"), Key_D, + game, SLOT(showFigurePositions()), actionCollection(), "step"); + (void) new KAction (i18n("Start Logging"), Key_G, + game, SLOT(startLogging()), actionCollection(), "logging"); + (void) new KAction (i18n("Show Hero"), Key_H, + game, SLOT(showHeroState()), actionCollection(), "show_hero"); + (void) new KAction (i18n("Show Object"), Key_Question, + game, SLOT(showObjectState()), actionCollection(), "show_obj"); + (void) new KAction (i18n("Show Enemy") + "0", Key_0, + this, SLOT(showEnemy0()), actionCollection(), "show_enemy_0"); + (void) new KAction (i18n("Show Enemy") + "1", Key_1, + this, SLOT(showEnemy1()), actionCollection(), "show_enemy_1"); + (void) new KAction (i18n("Show Enemy") + "2", Key_2, + this, SLOT(showEnemy2()), actionCollection(), "show_enemy_2"); + (void) new KAction (i18n("Show Enemy") + "3", Key_3, + this, SLOT(showEnemy3()), actionCollection(), "show_enemy_3"); + (void) new KAction (i18n("Show Enemy") + "4", Key_4, + this, SLOT(showEnemy4()), actionCollection(), "show_enemy_4"); + (void) new KAction (i18n("Show Enemy") + "5", Key_5, + this, SLOT(showEnemy5()), actionCollection(), "show_enemy_5"); + (void) new KAction (i18n("Show Enemy") + "6", Key_6, + this, SLOT(showEnemy6()), actionCollection(), "show_enemy_6"); +#endif + + /**************************************************************************/ + /************************** NOW SET IT ALL UP **************************/ + /**************************************************************************/ + + createGUI(); +} + +/******************************************************************************/ +/********************** SLOTS FOR STATUS BAR UPDATES ************************/ +/******************************************************************************/ + +void KGoldrunner::initStatusBar() +{ + QString s = statusBar()->fontInfo().family(); // Set bold font. + int i = statusBar()->fontInfo().pointSize(); + statusBar()->setFont (QFont (s, i, QFont::Bold)); + + statusBar()->setSizeGripEnabled (FALSE); // Use Settings menu ... + + statusBar()->insertItem ("", ID_LIVES); + statusBar()->insertItem ("", ID_SCORE); + statusBar()->insertItem ("", ID_LEVEL); + statusBar()->insertItem ("", ID_HINTAVL); + statusBar()->insertItem ("", ID_MSG, QSizePolicy::Horizontally); + + showLives (5); // Start with 5 lives. + showScore (0); + showLevel (0); + adjustHintAction (FALSE); + + // Set the PAUSE/RESUME key-names into the status bar message. + pauseKeys = myPause->shortcut().toString(); + pauseKeys = pauseKeys.replace (';', "\" " + i18n("or") + " \""); + gameFreeze (FALSE); + + statusBar()->setItemFixed (ID_LIVES, -1); // Fix current sizes. + statusBar()->setItemFixed (ID_SCORE, -1); + statusBar()->setItemFixed (ID_LEVEL, -1); + + connect(game, SIGNAL (showLives (long)), SLOT (showLives (long))); + connect(game, SIGNAL (showScore (long)), SLOT (showScore (long))); + connect(game, SIGNAL (showLevel (int)), SLOT (showLevel (int))); + connect(game, SIGNAL (gameFreeze (bool)), SLOT (gameFreeze (bool))); +} + +void KGoldrunner::showLives (long newLives) +{ + QString tmp; + tmp.setNum (newLives); + if (newLives < 100) + tmp = tmp.rightJustify (3, '0'); + tmp.insert (0, i18n(" Lives: ")); + tmp.append (" "); + statusBar()->changeItem (tmp, ID_LIVES); +} + +void KGoldrunner::showScore (long newScore) +{ + QString tmp; + tmp.setNum (newScore); + if (newScore < 10000) + tmp = tmp.rightJustify (5, '0'); + tmp.insert (0, i18n(" Score: ")); + tmp.append (" "); + statusBar()->changeItem (tmp, ID_SCORE); +} + +void KGoldrunner::showLevel (int newLevelNo) +{ + QString tmp; + tmp.setNum (newLevelNo); + if (newLevelNo < 100) + tmp = tmp.rightJustify (3, '0'); + tmp.insert (0, i18n(" Level: ")); + tmp.append (" "); + statusBar()->changeItem (tmp, ID_LEVEL); +} + +void KGoldrunner::gameFreeze (bool on_off) +{ + if (on_off) + statusBar()->changeItem + (i18n("Press \"%1\" to RESUME").arg(pauseKeys), ID_MSG); + else + statusBar()->changeItem + (i18n("Press \"%1\" to PAUSE").arg(pauseKeys), ID_MSG); +} + +void KGoldrunner::adjustHintAction (bool hintAvailable) +{ + hintAction->setEnabled (hintAvailable); + + if (hintAvailable) { + statusBar()->changeItem (i18n(" Has hint "), ID_HINTAVL); + } + else { + statusBar()->changeItem (i18n(" No hint "), ID_HINTAVL); + } +} + +void KGoldrunner::markRuleType (char ruleType) +{ + if (ruleType == 'T') + tradRules->activate(); + else + kgrRules->activate(); +} + +void KGoldrunner::setEditMenu (bool on_off) +{ + saveEdits->setEnabled (on_off); + + saveGame->setEnabled (! on_off); + hintAction->setEnabled (! on_off); + killHero->setEnabled (! on_off); + highScore->setEnabled (! on_off); + + if (on_off){ + editToolbar->show(); + } + else { + editToolbar->hide(); + } +} + +/******************************************************************************/ +/******************* SLOTS FOR MENU AND KEYBOARD ACTIONS *******************/ +/******************************************************************************/ + +// Slot to halt (pause) or restart the game play. + +void KGoldrunner::stopStart() +{ + if (! (KGrObject::frozen)) { + game->freeze(); + } + else { + game->unfreeze(); + } +} + +// Local slots to create or edit game information. + +void KGoldrunner::createGame() {game->editCollection (SL_CR_GAME);} +void KGoldrunner::editGameInfo() {game->editCollection (SL_UPD_GAME);} + +// Local slots to set the landscape (colour scheme). + +void KGoldrunner::lsKGoldrunner() {view->changeLandscape ("KGoldrunner");} +void KGoldrunner::lsApple2() {view->changeLandscape ("Apple II");} +void KGoldrunner::lsIceCave() {view->changeLandscape ("Ice Cave");} +void KGoldrunner::lsMidnight() {view->changeLandscape ("Midnight");} +void KGoldrunner::lsKDEKool() {view->changeLandscape ("KDE Kool");} + +// Local slots to set mouse or keyboard control of the hero. + +void KGoldrunner::setMouseMode() {game->setMouseMode (TRUE);} +void KGoldrunner::setKeyBoardMode() {game->setMouseMode (FALSE);} + +// Local slots to set game speed. + +void KGoldrunner::normalSpeed() {hero->setSpeed (NSPEED);} +void KGoldrunner::beginSpeed() {hero->setSpeed (BEGINSPEED);} +void KGoldrunner::champSpeed() {hero->setSpeed (CHAMPSPEED);} +void KGoldrunner::incSpeed() {hero->setSpeed (+1);} +void KGoldrunner::decSpeed() {hero->setSpeed (-1);} + +// Slots to set Traditional or KGoldrunner rules. + +void KGoldrunner::setTradRules() +{ + KGrFigure::variableTiming = TRUE; + KGrFigure::alwaysCollectNugget = TRUE; + KGrFigure::runThruHole = TRUE; + KGrFigure::reappearAtTop = TRUE; + KGrFigure::searchStrategy = LOW; +} + +void KGoldrunner::setKGrRules() +{ + KGrFigure::variableTiming = FALSE; + KGrFigure::alwaysCollectNugget = FALSE; + KGrFigure::runThruHole = FALSE; + KGrFigure::reappearAtTop = FALSE; + KGrFigure::searchStrategy = MEDIUM; +} + +// Local slots to make playing area larger or smaller. + +void KGoldrunner::makeLarger() +{ + if (view->changeSize (+1)) + setFixedSize (view->size()); +} + +void KGoldrunner::makeSmaller() +{ + if (view->changeSize (-1)) + setFixedSize (view->size()); +} + +// Local slots for hero control keys. + +void KGoldrunner::goUp() {setKey (KB_UP);} +void KGoldrunner::goR() {setKey (KB_RIGHT);} +void KGoldrunner::goDown() {setKey (KB_DOWN);} +void KGoldrunner::goL() {setKey (KB_LEFT);} +void KGoldrunner::stop() {setKey (KB_STOP);} +void KGoldrunner::digR() {setKey (KB_DIGRIGHT);} +void KGoldrunner::digL() {setKey (KB_DIGLEFT);} + +// Local slots for authors' debugging aids. + +void KGoldrunner::showEnemy0() {game->showEnemyState (0);} +void KGoldrunner::showEnemy1() {game->showEnemyState (1);} +void KGoldrunner::showEnemy2() {game->showEnemyState (2);} +void KGoldrunner::showEnemy3() {game->showEnemyState (3);} +void KGoldrunner::showEnemy4() {game->showEnemyState (4);} +void KGoldrunner::showEnemy5() {game->showEnemyState (5);} +void KGoldrunner::showEnemy6() {game->showEnemyState (6);} + +void KGoldrunner::saveProperties(KConfig *config) +{ + // The 'config' object points to the session managed + // config file. Anything you write here will be available + // later when this app is restored. + + config->setGroup ("Game"); // Prevents a compiler warning. + printf ("I am in KGoldrunner::saveProperties.\n"); + // config->writeEntry("qqq", qqq); +} + +void KGoldrunner::readProperties(KConfig *config) +{ + // The 'config' object points to the session managed + // config file. This function is automatically called whenever + // the app is being restored. Read in here whatever you wrote + // in 'saveProperties' + + config->setGroup ("Game"); // Prevents a compiler warning. + printf ("I am in KGoldrunner::readProperties.\n"); + // QString qqq = config->readEntry("qqq"); +} + +void KGoldrunner::optionsShowToolbar() +{ + // this is all very cut and paste code for showing/hiding the + // toolbar + // if (m_toolbarAction->isChecked()) + // toolBar()->show(); + // else + // toolBar()->hide(); +} + +void KGoldrunner::optionsShowStatusbar() +{ + // this is all very cut and paste code for showing/hiding the + // statusbar + // if (m_statusbarAction->isChecked()) + // statusBar()->show(); + // else + // statusBar()->hide(); +} + +void KGoldrunner::optionsConfigureKeys() +{ + KKeyDialog::configure(actionCollection()); + + // Update the PAUSE/RESUME message in the status bar. + pauseKeys = myPause->shortcut().toString(); + pauseKeys = pauseKeys.replace (';', "\" " + i18n("or") + " \""); + gameFreeze (KGrObject::frozen); // Refresh the status bar text. +} + +void KGoldrunner::optionsConfigureToolbars() +{ + // use the standard toolbar editor +#if defined(KDE_MAKE_VERSION) +# if KDE_VERSION >= KDE_MAKE_VERSION(3,1,0) + saveMainWindowSettings(KGlobal::config(), autoSaveGroup()); +# else + saveMainWindowSettings(KGlobal::config()); +# endif +#else + saveMainWindowSettings(KGlobal::config()); +#endif +} + +void KGoldrunner::newToolbarConfig() +{ + // this slot is called when user clicks "Ok" or "Apply" in the toolbar editor. + // recreate our GUI, and re-apply the settings (e.g. "text under icons", etc.) + createGUI(); + +#if defined(KDE_MAKE_VERSION) +# if KDE_VERSION >= KDE_MAKE_VERSION(3,1,0) + applyMainWindowSettings(KGlobal::config(), autoSaveGroup()); +# else + applyMainWindowSettings(KGlobal::config()); +# endif +#else + applyMainWindowSettings(KGlobal::config()); +#endif +} + +void KGoldrunner::optionsPreferences() +{ + // popup some sort of preference dialog, here + // KGoldrunnerPreferences dlg; + // if (dlg.exec()) + // { + // redo your settings + // } +} + +void KGoldrunner::changeStatusbar(const QString& text) +{ + // display the text on the statusbar + statusBar()->message(text); +} + +void KGoldrunner::changeCaption(const QString& text) +{ + // display the text on the caption + setCaption(text); +} + +bool KGoldrunner::getDirectories() +{ + bool result = TRUE; + + // WHERE THINGS ARE: In the KDE 3 environment (Release 3.1.1), application + // documentation and data files are in a directory structure given by + // $KDEDIRS (e.g. "/usr/local/kde" or "/opt/kde3/"). Application user data + // files are in a directory structure given by $KDEHOME ("$HOME/.kde"). + // Within those two structures, the three sub-directories will typically be + // "share/doc/HTML/en/kgoldrunner/", "share/apps/kgoldrunner/system/" and + // "share/apps/kgoldrunner/user/". Note that it is necessary to have + // an extra path level ("system" or "user") after "kgoldrunner", otherwise + // all the KGoldrunner files have similar path names (after "apps") and + // KDE always locates directories in $KDEHOME and never the released games. + + // The directory strings are set by KDE at run time and might change in + // later releases, so use them with caution and only if something gets lost. + + KStandardDirs * dirs = new KStandardDirs(); + +#ifdef QT3 + QString myDir = "kgoldrunner"; +#else + QString myDir = "kgoldrun"; +#endif + + // Find the KGoldrunner Users' Guide, English version (en). + systemHTMLDir = dirs->findResourceDir ("html", "en/" + myDir + "/"); + if (systemHTMLDir.length() <= 0) { + KGrMessage::information (this, i18n("Get Folders"), + i18n("Cannot find documentation sub-folder 'en/%1/' " + "in area '%2' of the KDE folder ($KDEDIRS).") + .arg(myDir).arg(dirs->kde_default ("html"))); + // result = FALSE; // Don't abort if the doc is missing. + } + else + systemHTMLDir.append ("en/" + myDir + "/"); + + // Find the system collections in a directory of the required KDE type. + systemDataDir = dirs->findResourceDir ("data", myDir + "/system/"); + if (systemDataDir.length() <= 0) { + KGrMessage::information (this, i18n("Get Folders"), + i18n("Cannot find system games sub-folder '%1/system/' " + "in area '%2' of the KDE folder ($KDEDIRS).") + .arg(myDir).arg(dirs->kde_default ("data"))); + result = FALSE; // ABORT if the games data is missing. + } + else + systemDataDir.append (myDir + "/system/"); + + // Locate and optionally create directories for user collections and levels. + bool create = TRUE; + userDataDir = dirs->saveLocation ("data", myDir + "/user/", create); + if (userDataDir.length() <= 0) { + KGrMessage::information (this, i18n("Get Folders"), + i18n("Cannot find or create user games sub-folder '%1/user/' " + "in area '%2' of the KDE user area ($KDEHOME).") + .arg(myDir).arg(dirs->kde_default ("data"))); + // result = FALSE; // Don't abort if user area is missing. + } + else { + create = dirs->makeDir (userDataDir + "levels/"); + if (! create) { + KGrMessage::information (this, i18n("Get Folders"), + i18n("Cannot find or create 'levels/' folder in " + "sub-folder '%1/user/' in the KDE user area ($KDEHOME).").arg(myDir)); + // result = FALSE; // Don't abort if user area is missing. + } + } + + return (result); +} + +// This method is invoked when top-level window is closed, whether by selecting +// "Quit" from the menu or by clicking the "X" at the top right of the window. + +bool KGoldrunner::queryClose () +{ + // Last chance to save: user has clicked "X" widget or menu-Quit. + bool cannotContinue = TRUE; + game->saveOK (cannotContinue); + return (TRUE); +} + +void KGoldrunner::setKey (KBAction movement) +{ + if (game->inEditMode()) return; + + // Using keyboard control can automatically disable mouse control. + if (game->inMouseMode()) { + // Halt the game while a message is displayed. + game->setMessageFreeze (TRUE); + + switch (KGrMessage::warning (this, i18n("Switch to Keyboard Mode"), + i18n("You have pressed a key that can be used to move the " + "Hero. Do you want to switch automatically to keyboard " + "control? Mouse control is easier to use in the long term " + "- like riding a bike rather than walking!"), + i18n("Switch to &Keyboard Mode"), i18n("Stay in &Mouse Mode"))) + { + case 0: game->setMouseMode (FALSE); // Set internal mouse mode OFF. + setMouse->setChecked (FALSE); // Adjust the Settings menu. + setKeyboard->setChecked (TRUE); + break; + case 1: break; + } + + // Unfreeze the game, but only if it was previously unfrozen. + game->setMessageFreeze (FALSE); + + if (game->inMouseMode()) + return; // Stay in Mouse Mode. + } + + if ( game->getLevel() != 0 ) + { + if (! hero->started ) // Start when first movement + game->startPlaying(); // key is pressed ... + game->heroAction (movement); + } +} + +/******************************************************************************/ +/********************** MAKE A TOOLBAR FOR THE EDITOR **********************/ +/******************************************************************************/ + +#include +void KGoldrunner::makeEditToolbar() +{ + // Set up the pixmaps for the editable objects. + QPixmap pixmap; + QImage image; + + QPixmap brickbg = view->getPixmap (BRICK); + QPixmap fbrickbg = view->getPixmap (FBRICK); + + QPixmap freebg = view->getPixmap (FREE); + QPixmap nuggetbg = view->getPixmap (NUGGET); + QPixmap polebg = view->getPixmap (POLE); + QPixmap betonbg = view->getPixmap (BETON); + QPixmap ladderbg = view->getPixmap (LADDER); + QPixmap hladderbg = view->getPixmap (HLADDER); + QPixmap edherobg = view->getPixmap (HERO); + QPixmap edenemybg = view->getPixmap (ENEMY); + + if (usesBigPixmaps()) { // Scale up the pixmaps (to give cleaner looking + // icons than leaving it for QToolButton to do). + QWMatrix w; + w = w.scale (2.0, 2.0); + + // The pixmaps shown on the buttons used to remain small and incorrectly + // painted, in KDE, in spite of the 2x (32x32) scaling. "insertButton" + // calls QIconSet, to generate a set of icons from each pixmapx, then + // seems to select the small size to paint on the button. The following + // line forces all icons, large and small, to be size 32x32 in advance. + QIconSet::setIconSize (QIconSet::Small, QSize (32, 32)); + + brickbg = brickbg.xForm (w); + fbrickbg = fbrickbg.xForm (w); + + freebg = freebg.xForm (w); + nuggetbg = nuggetbg.xForm (w); + polebg = polebg.xForm (w); + betonbg = betonbg.xForm (w); + ladderbg = ladderbg.xForm (w); + hladderbg = hladderbg.xForm (w); + edherobg = edherobg.xForm (w); + edenemybg = edenemybg.xForm (w); + } + + editToolbar = new KToolBar (this, Qt::DockTop, TRUE, "Editor", TRUE); + + // Choose a colour that enhances visibility of the KGoldrunner pixmaps. + // editToolbar->setPalette (QPalette (QColor (150, 150, 230))); + + // editToolbar->setHorizontallyStretchable (TRUE); // Not effective in KDE. + + // All those separators are just to get reasonable visual spacing of the + // pixmaps in KDE. "setHorizontallyStretchable(TRUE)" does it in Qt. + + editToolbar->insertSeparator(); + + editToolbar->insertButton ("filenew", 0, SIGNAL(clicked()), game, + SLOT(createLevel()), TRUE, i18n("&Create a Level")); + editToolbar->insertButton ("fileopen", 1, SIGNAL(clicked()), game, + SLOT(updateLevel()), TRUE, i18n("&Edit Any Level...")); + editToolbar->insertButton ("filesave", 2, SIGNAL(clicked()), game, + SLOT(saveLevelFile()),TRUE, i18n("&Save Edits...")); + + editToolbar->insertSeparator(); + editToolbar->insertSeparator(); + + editToolbar->insertButton ("ktip", 3, SIGNAL(clicked()), game, + SLOT(editNameAndHint()),TRUE,i18n("Edit Name/Hint")); + + editToolbar->insertSeparator(); + editToolbar->insertSeparator(); + + editToolbar->insertButton (freebg, (int)FREE, SIGNAL(clicked()), this, + SLOT(freeSlot()), TRUE, i18n("Empty space")); + editToolbar->insertSeparator(); + editToolbar->insertButton (edherobg, (int)HERO, SIGNAL(clicked()), this, + SLOT (edheroSlot()), TRUE, i18n("Hero")); + editToolbar->insertSeparator(); + editToolbar->insertButton (edenemybg, (int)ENEMY, SIGNAL(clicked()), this, + SLOT (edenemySlot()), TRUE, i18n("Enemy")); + editToolbar->insertSeparator(); + editToolbar->insertButton (brickbg, (int)BRICK, SIGNAL(clicked()), this, + SLOT (brickSlot()), TRUE, i18n("Brick (can dig)")); + editToolbar->insertSeparator(); + editToolbar->insertButton (betonbg, (int)BETON, SIGNAL(clicked()), this, + SLOT (betonSlot()), TRUE, i18n("Concrete (cannot dig)")); + editToolbar->insertSeparator(); + editToolbar->insertButton (fbrickbg, (int)FBRICK, SIGNAL(clicked()), this, + SLOT (fbrickSlot()), TRUE, i18n("Trap (can fall through)")); + editToolbar->insertSeparator(); + editToolbar->insertButton (ladderbg, (int)LADDER, SIGNAL(clicked()), this, + SLOT (ladderSlot()), TRUE, i18n("Ladder")); + editToolbar->insertSeparator(); + editToolbar->insertButton (hladderbg, (int)HLADDER, SIGNAL(clicked()), this, + SLOT (hladderSlot()), TRUE, i18n("Hidden ladder")); + editToolbar->insertSeparator(); + editToolbar->insertButton (polebg, (int)POLE, SIGNAL(clicked()), this, + SLOT (poleSlot()), TRUE, i18n("Pole (or bar)")); + editToolbar->insertSeparator(); + editToolbar->insertButton (nuggetbg, (int)NUGGET, SIGNAL(clicked()), this, + SLOT (nuggetSlot()), TRUE, i18n("Gold nugget")); + + editToolbar->setToggle ((int) FREE, TRUE); + editToolbar->setToggle ((int) HERO, TRUE); + editToolbar->setToggle ((int) ENEMY, TRUE); + editToolbar->setToggle ((int) BRICK, TRUE); + editToolbar->setToggle ((int) BETON, TRUE); + editToolbar->setToggle ((int) FBRICK, TRUE); + editToolbar->setToggle ((int) LADDER, TRUE); + editToolbar->setToggle ((int) HLADDER, TRUE); + editToolbar->setToggle ((int) POLE, TRUE); + editToolbar->setToggle ((int) NUGGET, TRUE); + + pressedButton = (int) BRICK; + editToolbar->setButton (pressedButton, TRUE); +} + +/******************************************************************************/ +/********************* EDIT-BUTTON SLOTS **********************************/ +/******************************************************************************/ + +void KGoldrunner::freeSlot() + { game->setEditObj (FREE); setButton ((int) FREE); } +void KGoldrunner::edheroSlot() + { game->setEditObj (HERO); setButton ((int) HERO); } +void KGoldrunner::edenemySlot() + { game->setEditObj (ENEMY); setButton ((int) ENEMY); } +void KGoldrunner::brickSlot() + { game->setEditObj (BRICK); setButton ((int) BRICK); } +void KGoldrunner::betonSlot() + { game->setEditObj (BETON); setButton ((int) BETON); } +void KGoldrunner::fbrickSlot() + { game->setEditObj (FBRICK); setButton ((int) FBRICK); } +void KGoldrunner::ladderSlot() + { game->setEditObj (LADDER); setButton ((int) LADDER); } +void KGoldrunner::hladderSlot() + { game->setEditObj (HLADDER); setButton ((int) HLADDER); } +void KGoldrunner::poleSlot() + { game->setEditObj (POLE); setButton ((int) POLE); } +void KGoldrunner::nuggetSlot() + { game->setEditObj (NUGGET); setButton ((int) NUGGET); } +void KGoldrunner::defaultEditObj() + { setButton ((int) BRICK); } + +void KGoldrunner::setButton (int btn) +{ + editToolbar->setButton (pressedButton, FALSE); + pressedButton = btn; + editToolbar->setButton (pressedButton, TRUE); +} + +#include "kgoldrunner.moc" diff --git a/kgoldrunner/src/kgoldrunner.h b/kgoldrunner/src/kgoldrunner.h new file mode 100644 index 00000000..57123596 --- /dev/null +++ b/kgoldrunner/src/kgoldrunner.h @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2003 Ian Wadham and Marco Krüger + */ + +#ifndef _KGOLDRUNNER_H_ +#define _KGOLDRUNNER_H_ + +#ifdef HAVE_CONFIG_H +#include +#endif + +// Status bar +const int ID_LIVES = 0; // Field IDs in KDE status bar. +const int ID_SCORE = 1; +const int ID_LEVEL = 2; +const int ID_HINTAVL = 3; +const int ID_MSG = 4; + +const int L_LIVES = 15; // Lengths of fields. +const int L_SCORE = 17; +const int L_LEVEL = 15; + +#include +#include +#include +#include + +class KGrGame; +class KGrCanvas; +class KGrHero; + +/** + * This class serves as the main window for KGoldrunner. It handles the + * menus, toolbars, and status bars. + * + * @short Main window class + * @author $AUTHOR <$EMAIL> + * @version $APP_VERSION + */ +class KGoldrunner : public KMainWindow +{ + Q_OBJECT +public: + /** + * Default Constructor + */ + KGoldrunner(); + + /** + * Default Destructor + */ + virtual ~KGoldrunner(); + + bool startedOK() {return (startupOK);} + +protected: + /** + * This function is called when it is time for the app to save its + * properties for session management purposes. + */ + void saveProperties(KConfig *); + + /** + * This function is called when this app is restored. The KConfig + * object points to the session management config file that was saved + * with @ref saveProperties. + */ + void readProperties(KConfig *); + + bool queryClose (); // To save edits before closing. + +private slots: + // Slot to pause or restart the game. + void stopStart(); + + // Local slots to create or edit game information. + void createGame(); + void editGameInfo(); + + // Local slots to set the landscape (colour scheme). + void lsKGoldrunner(); + void lsApple2(); + void lsIceCave(); + void lsMidnight(); + void lsKDEKool(); + + // Local slots to set mouse/keyboard control of the hero. + void setMouseMode(); + void setKeyBoardMode(); + + // Local slots to set game speed. + void normalSpeed(); + void beginSpeed(); + void champSpeed(); + void incSpeed(); + void decSpeed(); + + // Slots to set Traditional or KGoldrunner rules. + void setTradRules(); + void setKGrRules(); + + // Local slots to make playing area larger or smaller. + void makeLarger(); + void makeSmaller(); + + // Local slots for hero control keys. + void goUp(); + void goR(); + void goDown(); + void goL(); + void stop(); + void digR(); + void digL(); + + void setKey (KBAction movement); + + // Local slots for authors' debugging aids. + void showEnemy0(); + void showEnemy1(); + void showEnemy2(); + void showEnemy3(); + void showEnemy4(); + void showEnemy5(); + void showEnemy6(); + + void optionsShowToolbar(); + void optionsShowStatusbar(); + void optionsConfigureKeys(); + void optionsConfigureToolbars(); + void optionsPreferences(); + void newToolbarConfig(); + + void changeStatusbar(const QString& text); + void changeCaption(const QString& text); + + void showLevel (int); // Show the current level number. + void showLives (long); // Show how many lives are remaining. + void showScore (long); // Show the player's score. + void gameFreeze (bool); // Status feedback on freeze/unfreeze. + + void adjustHintAction (bool); // Enable/disable "Hint" action. + void markRuleType (char ruleType); // Check game's rule type in the menu. + void setEditMenu (bool on_off); // Enable/disable "Save Edits" action. + +private: + void setupAccel(); + void setupActions(); + void initStatusBar(); + void makeEditToolbar(); + void setButton (int btn); + +private: + bool startupOK; + + KGrCanvas * view; + KGrGame * game; + + bool getDirectories(); // Get directory paths, as below. + QString systemHTMLDir; // Where the manual is stored. + QString systemDataDir; // Where the system levels are stored. + QString userDataDir; // Where the user levels are stored. + + KAction * saveGame; // Save game, level, lives and score. + + KAction * myPause; // Pause or resume the game. + QString pauseKeys; // Keystroke names to put in status bar. + + KAction * hintAction; // Display a hint, if available. + KAction * killHero; // Kill hero (disabled during edits). + KAction * highScore; // High scores (disabled during edits). + + KAction * saveEdits; // Save a level that has been edited. + + KRadioAction * setKGoldrunner; // Show default "KGoldrunner" landscape. + KRadioAction * setAppleII; // Show "Apple II" landscape. + KRadioAction * setIceCave; // Show "Ice Cave" landscape. + KRadioAction * setMidnight; // Show "Midnight" landscape. + KRadioAction * setKDEKool; // Show "KDE Kool" landscape. + + KRadioAction * setMouse; // Show mouse/keyboard mode on menu. + KRadioAction * setKeyboard; // Show mouse/keyboard mode on menu. + + KRadioAction * tradRules; // Set Traditional rules. + KRadioAction * kgrRules; // Set KGoldrunner rules. + + KGrHero * hero; // Pointer to the hero. + + // KToggleAction * m_toolbarAction; + // KToggleAction * m_statusbarAction; + + KToolBar * editToolbar; // Toolbar for creating/editing levels. + int pressedButton; // ID of currently set toolbar button. + +private slots: + void freeSlot(); // Set editObj to Free Space. + void edheroSlot(); // Set editObj to Hero. + void edenemySlot(); // Set editObj to Enemy. + void brickSlot(); // Set editObj to Brick. + void betonSlot(); // Set editObj to Concrete. + void fbrickSlot(); // Set editObj to Fall-through Brick. + void ladderSlot(); // Set editObj to Ladder. + void hladderSlot(); // Set editObj to Hidden Ladder. + void poleSlot(); // Set editObj to Pole (or Bar). + void nuggetSlot(); // Set editObj to Gold Nugget. + void defaultEditObj(); // Set editObj to default (brick). +}; + +#endif // _KGOLDRUNNER_H_ diff --git a/kgoldrunner/src/kgoldrunnerui.rc b/kgoldrunner/src/kgoldrunnerui.rc new file mode 100644 index 00000000..9b8ce1fe --- /dev/null +++ b/kgoldrunner/src/kgoldrunnerui.rc @@ -0,0 +1,75 @@ + + + + + + + + + + + + &Editor + + + + + + + + + + + + &Landscapes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kgoldrunner/src/kgraphics.h b/kgoldrunner/src/kgraphics.h new file mode 100644 index 00000000..81f004ad --- /dev/null +++ b/kgoldrunner/src/kgraphics.h @@ -0,0 +1,357 @@ +/*************************************************************************** + kgrgraphics.h - description + ------------------- + begin : Wed Jan 23 2002 + copyright : (C) 2002 by Marco Krüger and Ian Wadham + email : See menu "Help, About KGoldrunner" + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KGRAPHICS_H +#define KGRAPHICS_H + +// List of colour schemes. +static const char * colourScheme [] = { + "KGoldrunner", + "#5a5a9b", /* Border - periwinkle blue */ + "#ffffff", /* Title-text - pure-white. */ + ". c #c8b0a0", /* Background */ + "; c #b8a090", /* Background mortar */ + "o c #ff0000", /* Solid light */ + "x c #c05040", /* Solid */ + "s c #b00020", /* Solid dark */ + "+ c #500000", /* Mortar */ + ": c #b47444", /* Ladder light */ + "# c #845424", /* Ladder dark */ + "a c #ffffff", /* Pole or bar */ + "Apple II", + "#8a8acb", /* Border - Apple II blue */ + "#ffffff", /* Title-text - pure-white. */ + ". c #001020", /* Background */ + "; c #001020", /* Background mortar */ + "o c #8a8acb", /* Solid light */ + "x c #8a8acb", /* Solid */ + "s c #8a8acb", /* Solid dark */ + "+ c #001020", /* Mortar */ + ": c #dddddd", /* Ladder light */ + "# c #dddddd", /* Ladder dark */ + "a c #dddddd", /* Pole or bar */ + "Ice Cave", + "#aabaf0", /* Border - pale blue */ + "#ffffff", /* Title-text - pure-white. */ + ". c #efefff", /* Background */ + "; c #d0dfef", /* Background mortar */ + "o c #ffffff", /* Solid light */ + "x c #d0f0ff", /* Solid */ + "s c #b0d8f0", /* Solid dark */ + "+ c #a8c8ff", /* Mortar */ + ": c #ffffff", /* Ladder light */ + "# c #f9d26a", /* Ladder dark */ + "a c #40a0ff", /* Pole or bar */ + "Midnight", + "#5a5a9b", /* Border - periwinkle blue */ + "#ffffff", /* Title-text - pure-white. */ + ". c #000040", /* Background */ + "; c #000020", /* Background mortar */ + "o c #880000", /* Solid light */ + "x c #702820", /* Solid */ + "s c #680010", /* Solid dark */ + "+ c #200000", /* Mortar */ + ": c #563622", /* Ladder light */ + "# c #422a12", /* Ladder dark */ + "a c #666666", /* Pole or bar */ + "KDE Kool", + "#aabaf0", /* Border - pale blue */ + "#ffffff", /* Title-text - pure-white. */ + ". c #eef7ff", /* Background */ + "; c #eef7ff", /* Background mortar */ + "o c #ecfdfe", /* Solid light */ + "x c #c1dafe", /* Solid */ + "s c #c1dafe", /* Solid dark */ + "+ c #9a9afe", /* Mortar */ + ": c #f9d26a", /* Ladder light */ + "# c #c19a68", /* Ladder dark */ + "a c #af7516", /* Pole or bar */ + "" /* TERMINATOR */ +}; + +/* XPM - Background brick or square (free space) */ +static const char * hgbrick_xpm []={ +"16 16 9 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"...........;....", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +";;;;;;;;;;;;;;;;", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +";;;;;;;;;;;;;;;;"}; + +/* XPM - Editor's icon for the hero */ +static const char * edithero_xpm []={ +"16 16 13 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"c c #008000", +"a c #00c000", +"b c #00ff00", +"d c #808080", +"...........;....", +"...........;....", +"..........a;....", +"........baacc...", +"........bcccc...", +".......bccca....", +"....caacccccc...", +";;;;bccacccaccc.", +"...;bccaacccaccc", +"...;caacccc.....", +"...;..dbccca....", +"...;.ccacccca...", +"...bcccccaccc...", +"...ac....bccc...", +"...;.....bccc...", +";;;;;;;;;caccc;;"}; + +/* XPM - Editor's icon for an enemy */ +static const char * editenemy_xpm []={ +"16 16 13 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"d c #000080", +"c c #0000ff", +"b c #008080", +"a c #00ffff", +"...........;....", +"...........;....", +"...........;....", +"........abbcc...", +"........acccc...", +".......acccd....", +"....bbbcccccc...", +";;;;accbcccbccbd", +"....accbbccbbccc", +"....bbbcccc.bdd.", +"....;..acccb....", +"....;bbbccccc...", +"...acccccbccc...", +"...bcddddaccc...", +"....;....accc...", +";;;;;;;;;bbccc;;"}; + +/* XPM - Ladder */ +static const char * ladder_xpm []={ +"16 16 9 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +":##........;.:##", +":##........;.:##", +":##........;.:##", +":#::::::::::::##", +":############:##", +":##........;.:##", +":##........;.:##", +":##;;;;;;;;;;:##", +":##.;........:##", +":##.;........:##", +":##.;........:##", +":#::::::::::::##", +":############:##", +":##.;........:##", +":##.;........:##", +":##;;;;;;;;;;:##"}; + +/* XPM - Hidden ladder (for Editor only) */ +static const char * hladder_xpm []={ +"16 16 9 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +":##........;.:##", +":##........;.:##", +":##........;.:##", +":#:::::::..;.:##", +":########..;.:##", +":##........;.:##", +":##........;.:##", +":##;;;;;;;;;;:##", +":##.;........:##", +":##.;........:##", +":##.;........:##", +":##.;..:::::::##", +":##.;..######:##", +":##.;........:##", +":##.;........:##", +":##;;;;;;;;;;:##"}; + +/* XPM - Nugget */ +static const char * nugget_xpm []={ +"16 16 12 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"a c #c0b000", +"c c #e08000", +"b c #ffff00", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +"......bba..;....", +";;;;bbbbbcca;;;;", +"...bbbababacc...", +"..abbbababaac...", +"..bbbbbababaa...", +"..bbbbbbabaac...", +"..abbababacca...", +"...ababacacc....", +"....acacacc;....", +";;;;;;aaa;;;;;;;"}; + +/* XPM - Pole or bar */ +static const char * pole_xpm []={ +"16 16 9 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"...........;....", +"...........;....", +"aaaaaaaaaaaaaaaa", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +";;;;;;;;;;;;;;;;", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +";;;;;;;;;;;;;;;;"}; + +/* XPM - Concrete */ +static const char * beton_xpm []={ +"16 16 9 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"sxssxssxssxsssss", +"sxxsxxsxxsxsxxsx", +"xsxsxsxssxxssxxs", +"xssxsxxsxssxsxss", +"sxxsxssxsxsxxsxs", +"xxsxxxssxsxsxxsx", +"sxsxssxxsxxssxsx", +"xsxsxssxsxsxsxss", +"xssxxsxssxssxsxs", +"xsxsxssxssxxxsxx", +"sxxssxxsxxsxsxss", +"sxsxsssxsxsxsxxs", +"sxssxxssxsxssxss", +"ssxssxsxsxsxsxxs", +"xsxxsxssxsxxsxss", +"++++++++++++++++"}; + +/* XPM - Bricks (10 pics - from whole brick to background [hole] and back) */ +static const char * bricks_xpm []={ +"160 16 9 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"sosossosos++osssssx;x;.x;;++s++sx;+;x;.x;;;;;+++x;x+x+.x;;;;;;.;;;;;;;.;;;;;;+.;...........;....osos+;.x+++;ssossososossos++osssososososso++ossssossossoso++osss", +"sxsxsxsxsx++oxsxsx+..;...x;++;+x+....;...++;;+++.....;+.+x;;;..;.....;...x;;+..;...........;....xs;++;...x;;+++xsx+;;+;+;+++o+xxxsxsx;sxsx++oxsx+xsxsxxxsx++osxs", +"xsxsxsxxsx++osxsxxsxs;+x;.;+osxs+;+;++;x;.;;x;+;+x;.x.;x;.;;++++;x;.x.;x;.;;x;.;...........;....+;+;x+;x;.+;+;sxs+;++++++;++o++xsxxx+++sxx++osxssxsxssxsxx++oxsx", +"xsxssxsxsx++oxxsxxssxsx+x+++osxsxsxs;..+;x;;++;s+++.;....x+;;.;+..;.;.+..x;;;.;;...........;....s++.;....x+;;+;xsxs++.+++++;s++xsssx++xsx++;osxxxsxxxxsxss++oxss", +"xsxxxsxxsx++osxssxsxsxsxsx++osxss+++x+++++;;x++;+.x.x;.+.;+;x+x;;.x.x;.;.;;;x.x;...........;....;.x.++.;.;;;++x.s;+xx++;.;+;o;+sxxxsx+.;.+;;s+sxsxssxsxx+x++osxs", +"xsxsx+sxss++oxsxxxxxsxsxss++osxxsx+++;+;+;+;++sxs;.+..x.;x;;.+++.;.;..x.++;;.;.x...........;....+;...+x.;x;;.+.xxs+;.+x.+;+;o+;ssxs++.x.+;+;osxxsxx++;s+++++oxsx", +"ssxxsxxxsx++oxssxssxxsxxxs++oxsxxs+;+++++++;oxss;+;.;;;;++;;++;s;x+.;;;+.;;;;.;+...........;....;x....+..;;;+.;;s;++;;+;.+;;s++xsxxs;;;;.+;;oxsxsx+sxsx;.;++oxsx", +"+++++++++++++++++++++++++++++++++++++++;+++;++++;;;;;+;;;;;;++++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+;;;;;;;;;;++;;++;;;;;;;;;;;++++++++;;;;;;;++++++++++;;;;;+++++", +"sss++ossssossssssss++ossssosssososo++osssssossso+;;+;s;+;x++++so;...;x;++x;x+.x+....;...........x....x+x;x;x;.x;s+++;x;x;x;x;++ssss++++x;x++++++sso++osx;xs+ssso", +"xsx++osxsx+xsxxsxsx++oxsxxsxsxsxxsx++oxsxsxsxxsxsxx+;+++...+;+;s++x+;;......;+......;.............x+;;......+...x+;+;+......++;sxsx++s;..+;+;+;sxsx++ox.+s++xsxs", +"sxs++oxsxsxsxssx+sx++osxs+xsxxx+sxs++osx+xs+ss;sxs;+;++;++.;+++x.;;;;x++;++x+;+;....;...........;+;;;x..x..x;.++s+++;+;;;;;s;+xsxxs+++++;+sx++xsxxs++osxx+sxsxxx", +"xsx++oxssxsxsxssxss++oxsxsxxsssxsxx++oxsssxxsxssxsx++os;+xs+xsxsx.++;+++++++++++....;...........++x+;.....;.+.;+x+;++os..s;+++++xsx++ox++;s+xxsxxsx++oxs++s++xss", +"xsx++oxsxxxsxxxsxxx++oxsxsx+xsxxxss++osxxxssxsxxsxx++oxssxsxsxxs;+;;;;+;+;++;+x+....;...........;+;;;+.x;.+;+..+s;+++ox+;;++;+++sxx++os;++;xssxssxx++osxsxsxssxx", +"xsx++ox+xssxssxsxsx++oxxssxsxxssxxx++osxxxxsxxsxssx++osxsxxsxssxsx;;;+++++x+++++....;...........xs++;.+.++;s+.+;sxs++o++sx+x++;;ssx++oxsxs+sxsxxsxs++oxsxsxxxxsx", +"sxs++osssxxsxxsxxsx++osxsxsxssxssxs++oxssxsxsxsxxxs++osxxsxsxsxssx+;;+;;;+++++++....;...........s;s+;.x..x.;+;sxx+x++o+;++++++sxsxs++ossxs;sxsxsxxs++oxssxssxssx", +"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++;;;;;;++;+++;;;;;;;;;;;;;;;;+;++;;;;;;;;;+++++++++++++++++++++++++++++++++++++++++++++++++++"}; +#endif // KGRAPHICS_H diff --git a/kgoldrunner/src/kgrcanvas.cpp b/kgoldrunner/src/kgrcanvas.cpp new file mode 100644 index 00000000..6104939e --- /dev/null +++ b/kgoldrunner/src/kgrcanvas.cpp @@ -0,0 +1,561 @@ +/*************************************************************************** + kgrcanvas.cpp - description + ------------------- + begin : Wed Jan 23 2002 + copyright : (C) 2002 by Marco Krüger and Ian Wadham + email : See menu "Help, About KGoldrunner" + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifdef KGR_PORTABLE +// If compiling for portability, redefine KDE's i18n. +#define i18n tr +#endif + +#include "kgrconsts.h" + +#include "kgrdialog.h" +#include "kgrcanvas.h" +#include "kgrgame.h" + +// Graphics files for moving figures and background. +#include "hero.xpm" +#include "enemy1.xpm" +#include "enemy2.xpm" +#include "kgraphics.h" + +class KGoldrunner; + +KGrCanvas::KGrCanvas (QWidget * parent, const char *name) + : QCanvasView (0, parent, name) +{ + setBackgroundMode (NoBackground); + m = new QCursor (); // For handling the mouse. + + scaleStep = STEP; // Initial scale is 1:1. + baseScale = scaleStep; + baseFontSize = fontInfo().pointSize(); + + border = 4; // Allow 2 tile-widths on each side for border. + cw = 4*STEP; // Playfield cell width (= four steps). + bw = border*cw/2; // Total border width (= two cells). + lw = cw/8; // Line width (for edge of border). + mw = bw - lw; // Border main-part width. + + initView(); // Set up the graphics, etc. +} + +KGrCanvas::~KGrCanvas() +{ +} + +void KGrCanvas::changeLandscape (const QString & name) +{ + for (int i = 0; strcmp (colourScheme [i], "") != 0; i++) { + if (colourScheme [i] == name) { + + // Change XPM colours and re-draw the tile-pictures used by QCanvas. + changeColours (& colourScheme [i]); + makeTiles(); + + // Set all cells to same tile-numbers as before, but new colours. + int tileNo [FIELDWIDTH] [FIELDHEIGHT]; + int offset = border / 2; + + for (int x = 0; x < FIELDWIDTH; x++) { + for (int y = 0; y < FIELDHEIGHT; y++) { + tileNo[x][y] = field->tile (x + offset, y + offset); + } + } + + field->setTiles (bgPix, (FIELDWIDTH+border), (FIELDHEIGHT+border), + bgw, bgh); // Sets all tile-numbers to 0. + + for (int x = 0; x < FIELDWIDTH; x++) { + for (int y = 0; y < FIELDHEIGHT; y++) { + field->setTile (x + offset, y + offset, tileNo[x][y]); + } + } + + borderB->setBrush (QBrush (borderColor)); + borderL->setBrush (QBrush (borderColor)); + borderR->setBrush (QBrush (borderColor)); + + QString t = title->text(); + makeTitle (); + setTitle (t); + + // Repaint the playing area. + updateCanvas(); + return; + } + } +} + +bool KGrCanvas::changeSize (int d) +{ +#ifdef QT3 + if ((d < 0) && (scaleStep <= STEP)) { + // Note: Smaller scales lose detail (e.g. the joints in brickwork). + KGrMessage::information (this, i18n("Change Size"), + i18n("Sorry, you cannot make the play area any smaller.")); + return FALSE; + } + + if ((d >= 0) && (scaleStep >= 2 * STEP)) { + // Note: Larger scales go off the edge of the monitor. + KGrMessage::information (this, i18n("Change Size"), + i18n("Sorry, you cannot make the play area any larger.")); + return FALSE; + } + + QWMatrix wm = worldMatrix(); + double wmScale = 1.0; + + // Set the scale back to 1:1 and calculate the new scale factor. + wm.reset(); + scaleStep = (d < 0) ? (scaleStep - 1) : (scaleStep + 1); + + // If scale > 1:1, scale up to the new factor (e.g. 1.25:1, 1.5:1, etc.) + if (scaleStep > STEP) { + wmScale = (wmScale * scaleStep) / STEP; + wm.scale (wmScale, wmScale); + } + setWorldMatrix (wm); + + // Force the title size and position to be re-calculated. + QString t = title->text(); + makeTitle (); + setTitle (t); + + // Fit the QCanvasView and its frame to the canvas. + int frame = frameWidth()*2; + setFixedSize ((FIELDWIDTH + 4) * 4 * scaleStep + frame, + (FIELDHEIGHT + 4) * 4 * scaleStep + frame); + return TRUE; + +#else + KGrMessage::information (this, i18n( "Change Size" ), + i18n( "Sorry, you cannot change the size of the playing area. " + "That function requires Qt Library version 3 or later." )); + return FALSE; +#endif +} + +void KGrCanvas::updateCanvas() +{ + field->update(); +} + +void KGrCanvas::paintCell (int x, int y, char type, int offset) +{ + int tileNumber = 0; + + switch (type) { + case FREE: tileNumber = freebg; break; // Free space. + case NUGGET: tileNumber = nuggetbg; break; // Nugget. + case POLE: tileNumber = polebg; break; // Pole or bar. + case LADDER: tileNumber = ladderbg; break; // Ladder. + case HLADDER: tileNumber = hladderbg; break;// Hidden ladder (for editing). + case HERO: tileNumber = edherobg; break; // Static hero (for editing). + case ENEMY: tileNumber = edenemybg; break;// Static enemy (for editing). + case BETON: tileNumber = betonbg; break; // Concrete. + case BRICK: tileNumber = brickbg; break; // Solid brick. + case FBRICK: tileNumber = fbrickbg; break; // False brick (for editing). + default: tileNumber = freebg; break; + } + + tileNumber = tileNumber + offset; // Offsets 1-9 are for digging sequence. + + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + x++; y++; + field->setTile (x, y, tileNumber); // Paint cell with required pixmap. +} + +void KGrCanvas::setBaseScale () +{ + // Synchronise the desktop font size with the initial canvas scale. + baseScale = scaleStep; + QString t = title->text(); + makeTitle (); + setTitle (t); +} + +void KGrCanvas::setTitle (QString newTitle) +{ + title->setText (newTitle); +} + +void KGrCanvas::makeTitle () +{ + // This uses a calculated QLabel and QFont size because a QCanvasText + // object does not always display scaled-up fonts cleanly (in Qt 3.1.1). + + if (title != 0) + title->close (TRUE); // Close and delete previous title. + + title = new QLabel ("", this); + title->setFixedWidth (((FIELDWIDTH * cw + 2 * bw) * scaleStep) / STEP); + title->setFixedHeight ((mw * scaleStep) / STEP); + title->move (0, 0); + title->setPaletteBackgroundColor (borderColor); + title->setPaletteForegroundColor (textColor); + title->setFont (QFont (fontInfo().family(), + (baseFontSize * scaleStep) / baseScale, QFont::Bold)); + title->setAlignment (Qt::AlignCenter); + title->raise(); + title->show(); +} + +void KGrCanvas::contentsMousePressEvent (QMouseEvent * m) { + emit mouseClick (m->button ()); +} + +void KGrCanvas::contentsMouseReleaseEvent (QMouseEvent * m) { + emit mouseLetGo (m->button ()); +} + +QPoint KGrCanvas::getMousePos () +{ + int i, j; + int fw = frameWidth(); + int cell = 4 * scaleStep; + QPoint p = mapFromGlobal (m->pos()); + + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + i = ((p.x() - fw) / cell) - 1; j = ((p.y() - fw) / cell) - 1; + + return (QPoint (i, j)); +} + +void KGrCanvas::setMousePos (int i, int j) +{ + int fw = frameWidth(); + int cell = 4 * scaleStep; + + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + i++; j++; + //m->setPos (mapToGlobal (QPoint (i * 4 * STEP + 8, j * 4 * STEP + 8))); + //m->setPos (mapToGlobal (QPoint (i * 5 * STEP + 10, j * 5 * STEP + 10))); + m->setPos (mapToGlobal ( + QPoint (i * cell + fw + cell / 2, j * cell + fw + cell / 2))); +} + +void KGrCanvas::makeHeroSprite (int i, int j, int startFrame) +{ + heroSprite = new QCanvasSprite (heroArray, field); + + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + i++; j++; + heroSprite->move (i * 4 * STEP, j * 4 * STEP, startFrame); + heroSprite->setZ (1); + heroSprite->setVisible (TRUE); +} + +void KGrCanvas::setHeroVisible (bool newState) +{ + heroSprite->setVisible (newState); // Show or hide the hero. +} + +void KGrCanvas::makeEnemySprite (int i, int j, int startFrame) +{ + QCanvasSprite * enemySprite = new QCanvasSprite (enemyArray, field); + + enemySprites->append (enemySprite); + + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + i++; j++; + enemySprite->move (i * 4 * STEP, j * 4 * STEP, startFrame); + enemySprite->setZ (2); + enemySprite->show(); +} + +void KGrCanvas::moveHero (int x, int y, int frame) +{ + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + heroSprite->move (x + 4 * STEP, y + 4 * STEP, frame); + updateCanvas(); +} + +void KGrCanvas::moveEnemy (int id, int x, int y, int frame, int nuggets) +{ + if (nuggets != 0) { // If enemy is carrying gold, + frame = frame + goldEnemy; // show him with gold outline. + } + + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + enemySprites->at(id)->move (x + 4 * STEP, y + 4 * STEP, frame); + updateCanvas(); +} + +void KGrCanvas::deleteEnemySprites() +{ + enemySprites->clear(); +} + +QPixmap KGrCanvas::getPixmap (char type) +{ + QPixmap pic (bgw, bgh, bgd); + QPainter p (& pic); + int tileNumber; + + // Get a pixmap from the tile-array for use on an edit-button. + switch (type) { + case FREE: tileNumber = freebg; break; // Free space. + case NUGGET: tileNumber = nuggetbg; break; // Nugget. + case POLE: tileNumber = polebg; break; // Pole or bar. + case LADDER: tileNumber = ladderbg; break; // Ladder. + case HLADDER: tileNumber = hladderbg; break;// Hidden ladder (for editing). + case HERO: tileNumber = edherobg; break; // Static hero (for editing). + case ENEMY: tileNumber = edenemybg; break;// Static enemy (for editing). + case BETON: tileNumber = betonbg; break; // Concrete. + case BRICK: tileNumber = brickbg; break; // Solid brick. + case FBRICK: tileNumber = fbrickbg; break; // False brick (for editing). + default: tileNumber = freebg; break; + } + + // Copy a tile of width bgw and height bgh from the tile-array. + p.drawPixmap (0, 0, bgPix, tileNumber * bgw, 0, bgw, bgh); + p.end(); + + return (pic); +} + +void KGrCanvas::initView() +{ + changeColours (& colourScheme [0]); // Set "KGoldrunner" landscape. + + // Set up the pixmaps for the editable objects. + freebg = 0; // Free space. + nuggetbg = 1; // Nugget. + polebg = 2; // Pole or bar. + ladderbg = 3; // Ladder. + hladderbg = 4; // Hidden ladder (for editing). + edherobg = 5; // Static hero (for editing). + edenemybg = 6; // Static enemy (for editing). + betonbg = 7; // Concrete. + + // The bricks have 10 pixmaps (showing various stages of digging). + brickbg = 8; // Solid brick - 1st pixmap. + fbrickbg = 15; // False brick - 8th pixmap (for editing). + + QPixmap pixmap; + QImage image; + + pixmap = QPixmap (hgbrick_xpm); + + bgw = pixmap.width(); // Save dimensions for "getPixmap". + bgh = pixmap.height(); + bgd = pixmap.depth(); + + // Assemble the background and editing pixmaps into a strip (18 pixmaps). + bgPix = QPixmap ((brickbg + 10) * bgw, bgh, bgd); + + makeTiles(); // Fill the strip with 18 tiles. + + // Define the canvas as an array of tiles. Default tile is 0 (free space). + int frame = frameWidth()*2; + field = new QCanvas ((FIELDWIDTH+border) * bgw, (FIELDHEIGHT+border) * bgh); + field->setTiles (bgPix, (FIELDWIDTH+border), (FIELDHEIGHT+border), + bgw, bgh); + + // Embed the canvas in the view and make it occupy the whole of the view. + setCanvas (field); + setVScrollBarMode (QScrollView::AlwaysOff); + setHScrollBarMode (QScrollView::AlwaysOff); + setFixedSize (field->width() + frame, field->height() + frame); + + ////////////////////////////////////////////////////////////////////////// + // The pixmaps for hero and enemies are arranged in strips of 20: walk // + // right (4), walk left (4), climb right along bar (4), climb left (4), // + // climb up ladder (2) and fall (2) --- total 20. // + ////////////////////////////////////////////////////////////////////////// + + // Convert the pixmap strip for hero animation into a QCanvasPixmapArray. + pixmap = QPixmap (hero_xpm); + image = pixmap.convertToImage (); + +#ifdef QT3 + QPixmap pm; + QValueList pmList; + + for (int i = 0; i < 20; i++) { + pm.convertFromImage (image.copy (i * 16, 0, 16, 16)); + pmList.append (pm); + } + + heroArray = new QCanvasPixmapArray (pmList); // Hot spots all (0,0). +#else + QPixmap * pm; + QPoint * pt; + QList pmList; + QList ptList; + + pt = new QPoint (0, 0); // "Hot spot" not used in KGoldrunner. + + for (int i = 0; i < 20; i++) { + pm = new QPixmap (); + pm->convertFromImage (image.copy (i * 16, 0, 16, 16)); + pmList.append (pm); + ptList.append (pt); + } + + heroArray = new QCanvasPixmapArray (pmList, ptList); +#endif + + // Convert pixmap strips for enemy animations into a QCanvasPixmapArray. + // First convert the pixmaps for enemies with no gold ... + pixmap = QPixmap (enemy1_xpm); + image = pixmap.convertToImage (); + + pmList.clear(); + +#ifdef QT3 + for (int i = 0; i < 20; i++) { + pm.convertFromImage (image.copy (i * 16, 0, 16, 16)); + pmList.append (pm); + } +#else + ptList.clear(); + + for (int i = 0; i < 20; i++) { + pm = new QPixmap (); + pm->convertFromImage (image.copy (i * 16, 0, 16, 16)); + pmList.append (pm); + ptList.append (pt); + } +#endif + + // ... then convert the gold-carrying enemies. + pixmap = QPixmap (enemy2_xpm); + image = pixmap.convertToImage (); + +#ifdef QT3 + for (int i = 0; i < 20; i++) { + pm.convertFromImage (image.copy (i * 16, 0, 16, 16)); + pmList.append (pm); + } + + enemyArray = new QCanvasPixmapArray (pmList); // Hot spots all (0,0). +#else + for (int i = 0; i < 20; i++) { + pm = new QPixmap (); + pm->convertFromImage (image.copy (i * 16, 0, 16, 16)); + pmList.append (pm); + ptList.append (pt); + } + + enemyArray = new QCanvasPixmapArray (pmList, ptList); +#endif + + goldEnemy = 20; // Offset of gold-carrying frames. + + // Draw the border around the playing area (z = 0). + makeBorder(); + + // Create a title item, in off-white colour, on top of the border. + title = 0; + makeTitle(); + + // Create an empty list of enemy sprites. +#ifdef QT3 + enemySprites = new QPtrList (); +#else + enemySprites = new QList (); +#endif + enemySprites->setAutoDelete(TRUE); +} + +void KGrCanvas::makeTiles () +{ + QPainter p (& bgPix); + + // First draw the single pixmaps (8 tiles) ... + p.drawPixmap (freebg * bgw, 0, QPixmap (hgbrick_xpm)); // Free space. + p.drawPixmap (nuggetbg * bgw, 0, QPixmap (nugget_xpm)); // Nugget. + p.drawPixmap (polebg * bgw, 0, QPixmap (pole_xpm)); // Pole or bar. + p.drawPixmap (ladderbg * bgw, 0, QPixmap (ladder_xpm)); // Ladder. + p.drawPixmap (hladderbg * bgw, 0, QPixmap (hladder_xpm)); // Hidden laddr. + p.drawPixmap (edherobg * bgw, 0, QPixmap (edithero_xpm)); // Static hero. + p.drawPixmap (edenemybg * bgw, 0, QPixmap (editenemy_xpm)); // Static enemy. + p.drawPixmap (betonbg * bgw, 0, QPixmap (beton_xpm)); // Concrete. + + // ... then add the 10 brick pixmaps. + p.drawPixmap (brickbg * bgw, 0, QPixmap (bricks_xpm)); // Bricks. + + p.end(); +} + +void KGrCanvas::makeBorder () +{ + // Draw main part of border, in the order: top, bottom, left, right. + // Allow some overlap to prevent slits appearing when using "changeSize". + colour = borderColor; + + // The first rectangle is actually a QLabel drawn by "makeTitle()". + // borderT = drawRectangle (11, 0, 0, FIELDWIDTH*cw + 2*bw, mw); + borderB = drawRectangle (11, 0, FIELDHEIGHT*cw + bw + lw, + FIELDWIDTH*cw + 2*bw, mw); + borderL = drawRectangle (12, 0, bw - lw - 1, mw, FIELDHEIGHT*cw + 2*lw + 2); + borderR = drawRectangle (12, FIELDWIDTH*cw + bw + lw, bw - lw - 1, + mw, FIELDHEIGHT*cw + 2*lw + 2); + + // Draw inside edges of border, in the same way. + colour = QColor (black); + drawRectangle (10, bw-lw, bw-lw-1, FIELDWIDTH*cw + 2*lw, lw+1); + drawRectangle (10, bw-lw, FIELDHEIGHT*cw + bw, FIELDWIDTH*cw + 2*lw, lw+1); + drawRectangle (10, bw - lw, bw, lw, FIELDHEIGHT*cw); + drawRectangle (10, FIELDWIDTH*cw + bw, bw, lw, FIELDHEIGHT*cw); +} + +QCanvasRectangle * KGrCanvas::drawRectangle (int z, int x, int y, int w, int h) +{ + QCanvasRectangle * r = new QCanvasRectangle (x, y, w, h, field); + + r->setBrush (QBrush (colour)); + r->setPen (QPen (NoPen)); + r->setZ (z); + r->show(); + + return (r); +} + +void KGrCanvas::changeColours (const char * colours []) +{ + recolourObject (hgbrick_xpm, colours); + recolourObject (nugget_xpm, colours); + recolourObject (pole_xpm, colours); + recolourObject (ladder_xpm, colours); + recolourObject (hladder_xpm, colours); + recolourObject (edithero_xpm, colours); + recolourObject (edithero_xpm, colours); + recolourObject (editenemy_xpm, colours); + recolourObject (beton_xpm, colours); + recolourObject (bricks_xpm, colours); + + borderColor = QColor (colours [1]); + textColor = QColor (colours [2]); + + KGrThumbNail::backgroundColor = QColor (QString(colours [3]).right(7)); + KGrThumbNail::brickColor = QColor (QString(colours [6]).right(7)); + KGrThumbNail::ladderColor = QColor (QString(colours [9]).right(7)); + KGrThumbNail::poleColor = QColor (QString(colours [11]).right(7)); +} + +void KGrCanvas::recolourObject (const char * object [], const char * colours []) +{ + int i; + for (i = 0; i < 9; i++) { + object [i+1] = colours [i+3]; + } +} + +#include "kgrcanvas.moc" diff --git a/kgoldrunner/src/kgrcanvas.h b/kgoldrunner/src/kgrcanvas.h new file mode 100644 index 00000000..02b89bed --- /dev/null +++ b/kgoldrunner/src/kgrcanvas.h @@ -0,0 +1,111 @@ +/*************************************************************************** + kgrcanvas.h - description + ------------------- + begin : Wed Jan 23 2002 + copyright : (C) 2002 by Marco Krüger and Ian Wadham + email : See menu "Help, About KGoldrunner" + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KGRCANVAS_H +#define KGRCANVAS_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +class KGrCanvas : public QCanvasView +{ + Q_OBJECT +public: + KGrCanvas (QWidget * parent = 0, const char *name = 0); + virtual ~KGrCanvas(); + + void changeLandscape (const QString & name); + + QPoint getMousePos (); + void setMousePos (int, int); + + bool changeSize (int); + void setBaseScale (); + + void updateCanvas (); + void paintCell (int, int, char, int offset = 0); + void setTitle (QString); + + void makeHeroSprite (int, int, int); + void setHeroVisible (bool); + void moveHero (int, int, int); + + void makeEnemySprite (int, int, int); + void moveEnemy (int, int, int, int, int); + void deleteEnemySprites(); + + QPixmap getPixmap (char type); + +signals: + void mouseClick (int); + void mouseLetGo (int); + +protected: + void contentsMousePressEvent (QMouseEvent *); + void contentsMouseReleaseEvent (QMouseEvent *); + +private: + QCursor * m; + + QCanvas * field; + QCanvasView * fieldView; + int scaleStep; // Current scale-factor of canvas. + int baseScale; // Starting scale-factor of canvas. + int baseFontSize; + + int border; // Number of tiles allowed for border. + int cw, bw, lw, mw; // Dimensions (in pixels) of the border. + QColor borderColor, textColor; // Border colours. + QLabel * title; // Title and top part of border. + QCanvasRectangle * borderB; // Bottom part of border. + QCanvasRectangle * borderL; // Left-hand part of border. + QCanvasRectangle * borderR; // Right-hand part of border. + + int freebg, nuggetbg, polebg, ladderbg, hladderbg; + int edherobg, edenemybg, betonbg, brickbg, fbrickbg; + int bgw, bgh, bgd; + QPixmap bgPix; + + QCanvasPixmapArray * heroArray; + QCanvasPixmapArray * enemyArray; + int goldEnemy; + + QCanvasSprite * heroSprite; +#ifdef QT3 + QPtrList * enemySprites; +#else + QList * enemySprites; +#endif + + void initView(); + void makeTiles(); + void makeBorder(); + void makeTitle(); + QColor colour; + QCanvasRectangle * drawRectangle (int, int, int, int, int); + void changeColours (const char * colours []); + void recolourObject (const char * object [], const char * colours []); +}; + +#endif // KGRCANVAS_H diff --git a/kgoldrunner/src/kgrconsts.h b/kgoldrunner/src/kgrconsts.h new file mode 100644 index 00000000..2006648a --- /dev/null +++ b/kgoldrunner/src/kgrconsts.h @@ -0,0 +1,85 @@ +/*************************************************************************** + * kgrconsts.h - description * + * ------------------- * + * Copyright (C) 2003 by Ian Wadham and Marco Krüger * + * email : See menu "Help, About KGoldrunner" * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifndef KGRCONSTS_H +#define KGRCONSTS_H + +enum Owner {SYSTEM, USER}; + +const char FREE = ' '; +const char ENEMY = 'E'; +const char HERO = 'R'; +const char BETON = 'X'; +const char BRICK = 'M'; +const char FBRICK = 'F'; +const char HLADDER = 'Z'; +const char LADDER = 'H'; +const char NUGGET = 'N'; +const char POLE = 'T'; +const char HOLE = 'O'; +const char USEDHOLE= 'U'; + +const char CANWALKLEFT = 0x1; +const char CANWALKRIGHT = 0x2; +const char CANWALKUP = 0x4; +const char CANWALKDOWN = 0x8; +const char VISITED = 0x10; + +const char FIELDWIDTH = 28; +const char FIELDHEIGHT = 20; + +const char VERTIKAL = 0; +const char HORIZONTAL = 1; + +/* Action times ... */ +#define NSPEED 12 +#define MAXSPEED NSPEED * 2 +#define MINSPEED NSPEED / 4 + +#define BEGINSPEED NSPEED / 2 +#define NOVICESPEED (3 * NSPEED) / 4 +#define CHAMPSPEED (3 * NSPEED) / 2 + +typedef struct { + int hwalk; + int hfall; + int ewalk; + int efall; + int ecaptive; + int hole; +} Timing; + +const int DIGDELAY = 200; + +const int STEP = 4; + +const double DROPNUGGETDELAY = 70.0; // Enemy holds gold for avg. 12.5 cells. + +enum Position {RIGHTWALK1,RIGHTWALK2,RIGHTWALK3,RIGHTWALK4, + LEFTWALK1,LEFTWALK2,LEFTWALK3,LEFTWALK4, + RIGHTCLIMB1,RIGHTCLIMB2,RIGHTCLIMB3,RIGHTCLIMB4, + LEFTCLIMB1,LEFTCLIMB2,LEFTCLIMB3,LEFTCLIMB4, + CLIMB1,CLIMB2, + FALL1,FALL2}; +enum Status {STANDING,FALLING,WALKING,CLIMBING,CAPTIVE}; +enum Direction {RIGHT,LEFT,UP,DOWN,STAND}; +enum SearchStrategy {LOW,MEDIUM,HIGH}; + +// Keyboard action codes +enum KBAction {KB_UP, KB_DOWN, KB_LEFT, KB_RIGHT, + KB_DIGLEFT, KB_DIGRIGHT, KB_STOP}; + +// Action codes when selecting a level or game for play or editing. +enum SelectAction {SL_START, SL_ANY, SL_CREATE, SL_UPDATE, SL_SAVE, + SL_MOVE, SL_DELETE, SL_CR_GAME, SL_UPD_GAME}; + +#endif // KGRCONSTS_H diff --git a/kgoldrunner/src/kgrdialog.cpp b/kgoldrunner/src/kgrdialog.cpp new file mode 100644 index 00000000..61ca2f29 --- /dev/null +++ b/kgoldrunner/src/kgrdialog.cpp @@ -0,0 +1,974 @@ +/*************************************************************************** + * Copyright (C) 2003 by Ian Wadham and Marco Krger * + * ianw2@optusnet.com.au * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifdef KGR_PORTABLE +// If compiling for portability, redefine KDE's i18n. +#define i18n tr +#endif + +#include "kgrconsts.h" +#include "kgrcanvas.h" +#include "kgrgame.h" +#include "kgrdialog.h" + +#ifndef KGR_PORTABLE +#include +#endif + +/******************************************************************************/ +/***************** DIALOG BOX TO SELECT A GAME AND LEVEL *****************/ +/******************************************************************************/ + +#ifdef KGR_PORTABLE +KGrSLDialog::KGrSLDialog (int action, int requestedLevel, int collnIndex, + QPtrList & gamesList, KGrGame * theGame, + QWidget * parent, const char * name) + : QDialog (parent, name, TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title) +#else +KGrSLDialog::KGrSLDialog (int action, int requestedLevel, int collnIndex, + QPtrList & gamesList, KGrGame * theGame, + QWidget * parent, const char * name) + : KDialogBase (KDialogBase::Plain, i18n("Select Game"), + KDialogBase::Ok | KDialogBase::Cancel | KDialogBase::Help, + KDialogBase::Ok, parent, name) +#endif +{ + slAction = action; + defaultLevel = requestedLevel; + defaultGame = collnIndex; + collections = gamesList; + game = theGame; + collection = collections.at(defaultGame); + slParent = parent; + +#ifdef KGR_PORTABLE + int margin = 10; + int spacing = 10; + QWidget * dad = this; +#else + int margin = marginHint(); + int spacing = spacingHint(); + QWidget * dad = plainPage(); +#endif + + QVBoxLayout * mainLayout = new QVBoxLayout (dad, margin, spacing); + + collnL = new QLabel (i18n("List of games:"), dad); + mainLayout->addWidget (collnL); + colln = new QListBox (dad); + mainLayout->addWidget (colln); + + QHBox * gameInfo = new QHBox (dad); + mainLayout->addWidget (gameInfo); + gameInfo->setSpacing (spacing); + collnN = new QLabel ("", gameInfo); // Name of selected collection. + QFont f = collnN->font(); + f.setBold (TRUE); + collnN->setFont (f); + collnA = new QPushButton (i18n("More Info"), gameInfo); + + collnD = new QLabel ("", dad); // Description of collection. + mainLayout->addWidget (collnD); + + QFrame * separator = new QFrame (dad); + separator->setFrameStyle (QFrame::HLine + QFrame::Sunken); + mainLayout->addWidget (separator); + + if ((action == SL_START) || (action == SL_UPD_GAME)) { + dad-> setCaption (i18n("Select Game")); + QLabel * startMsg = new QLabel + ("" + i18n("Level 1 of the selected game is:") + "", dad); + mainLayout->addWidget (startMsg); + } + else { + dad-> setCaption (i18n("Select Game/Level")); + QLabel * selectLev = new QLabel (i18n("Select level:"), dad); + mainLayout->addWidget (selectLev); + } + + QGridLayout * grid = new QGridLayout (3, 2, -1); + mainLayout->addLayout (grid); + + // Initial range 1->150, small step 1, big step 10 and default value 1. + number = new QScrollBar (1, 150, 1, 10, 1, + QScrollBar::Horizontal, dad); + grid->addWidget (number, 1, 1); + + QHBox * numberPair = new QHBox (dad); + grid->addWidget (numberPair, 2, 1); + numberPair->setSpacing (spacing); + numberL = new QLabel (i18n("Level number:"), numberPair); + display = new QLineEdit (numberPair); + + levelNH = new QPushButton (i18n("Edit Level Name && Hint"), dad); + mainLayout->addWidget (levelNH); + + slName = new QLabel ("", dad); + grid->addWidget (slName, 3, 1); + thumbNail = new KGrThumbNail (dad); + grid->addMultiCellWidget (thumbNail, 1, 3, 2, 2); + + // Set thumbnail cell size to about 1/5 of game cell size. + int cellSize = parent->width() / (5 * (FIELDWIDTH + 4)); + thumbNail-> setFixedWidth ((FIELDWIDTH * cellSize) + 2); + thumbNail-> setFixedHeight ((FIELDHEIGHT * cellSize) + 2); + +#ifdef KGR_PORTABLE + QHBox * buttons = new QHBox (this); + mainLayout->addWidget (buttons); + buttons->setSpacing (spacing); + // Buttons are for Qt-only portability. NOT COMPILED in KDE environment. + HELP = new QPushButton (i18n("Help"), buttons); + OK = new QPushButton (i18n("&OK"), buttons); + CANCEL = new QPushButton (i18n("&Cancel"), buttons); + + QPoint p = parent->mapToGlobal (QPoint (0,0)); + + // Base the geometry of the dialog box on the playing area. + int cell = parent->width() / (FIELDWIDTH + 4); + dad-> move (p.x()+2*cell, p.y()+2*cell); + dad-> setMinimumSize ((FIELDWIDTH*cell/2), (FIELDHEIGHT-1)*cell); + + OK-> setAccel (Key_Return); + HELP-> setAccel (Key_F1); + CANCEL-> setAccel (Key_Escape); +#endif + + // Set the default for the level-number in the scrollbar. + number-> setTracking (TRUE); + number->setValue (requestedLevel); + + slSetCollections (defaultGame); + + // Vary the dialog according to the action. + QString OKText = ""; + switch (slAction) { + case SL_START: // Must start at level 1, but can choose a collection. + OKText = i18n("Start Game"); + number->setValue (1); + number->setEnabled(FALSE); + display->setEnabled(FALSE); + number->hide(); + numberL->hide(); + display->hide(); + break; + case SL_ANY: // Can start playing at any level in any collection. + OKText = i18n("Play Level"); + break; + case SL_UPDATE: // Can use any level in any collection as edit input. + OKText = i18n("Edit Level"); + break; + case SL_CREATE: // Can save a new level only in a USER collection. + OKText = i18n("Save New"); + break; + case SL_SAVE: // Can save an edited level only in a USER collection. + OKText = i18n("Save Change"); + break; + case SL_DELETE: // Can delete a level only in a USER collection. + OKText = i18n("Delete Level"); + break; + case SL_MOVE: // Can move a level only into a USER collection. + OKText = i18n("Move To..."); + break; + case SL_UPD_GAME: // Can only edit USER collection details. + OKText = i18n("Edit Game Info"); + number->setValue (1); + number->setEnabled(FALSE); + display->setEnabled(FALSE); + number->hide(); + numberL->hide(); + display->hide(); + break; + + default: break; // Keep the default settings. + } + if (!OKText.isEmpty()) { +#ifdef KGR_PORTABLE + OK->setText (OKText); +#else + setButtonOK (OKText); +#endif + } + + // Set value in the line-edit box. + slShowLevel (number->value()); + + if (display->isEnabled()) { + display->setFocus(); // Set the keyboard input on. + display->selectAll(); + display->setCursorPosition (0); + } + + // Paint a thumbnail sketch of the level. + thumbNail->setFrameStyle (QFrame::Box | QFrame::Plain); + thumbNail->setLineWidth (1); + slPaintLevel(); + thumbNail->show(); + + connect (colln, SIGNAL (highlighted (int)), this, SLOT (slColln (int))); + connect (collnA, SIGNAL (clicked ()), this, SLOT (slAboutColln ())); + + connect (display, SIGNAL (textChanged (const QString &)), + this, SLOT (slUpdate (const QString &))); + + connect (number, SIGNAL (valueChanged(int)), this, SLOT(slShowLevel(int))); + + // Only enable name and hint dialog here if saving a new or edited level. + // At other times the name and hint have not been loaded or initialised yet. + if ((slAction == SL_CREATE) || (slAction == SL_SAVE)) { + connect (levelNH, SIGNAL (clicked()), game, SLOT (editNameAndHint())); + } + else { + levelNH->setEnabled (FALSE); + levelNH->hide(); + } + + connect (colln, SIGNAL (highlighted (int)), this, SLOT (slPaintLevel ())); + connect (number, SIGNAL (sliderReleased()), this, SLOT (slPaintLevel())); + connect (number, SIGNAL (nextLine()), this, SLOT (slPaintLevel())); + connect (number, SIGNAL (prevLine()), this, SLOT (slPaintLevel())); + connect (number, SIGNAL (nextPage()), this, SLOT (slPaintLevel())); + connect (number, SIGNAL (prevPage()), this, SLOT (slPaintLevel())); + +#ifdef KGR_PORTABLE + // Set the exits from this dialog box. + connect (OK, SIGNAL (clicked ()), this, SLOT (accept ())); + connect (CANCEL, SIGNAL (clicked ()), this, SLOT (reject ())); + connect (HELP, SIGNAL (clicked ()), this, SLOT (slotHelp ())); +#endif +} + +KGrSLDialog::~KGrSLDialog() +{ +} + +/******************************************************************************/ +/***************** LOAD THE LIST OF GAMES (COLLECTIONS) *****************/ +/******************************************************************************/ + +void KGrSLDialog::slSetCollections (int cIndex) +{ + int i; + int imax = collections.count(); + + // Set values in the combo box that holds collection names. + colln->clear(); + slCollnIndex = -1; + + for (i = 0; i < imax; i++) { + colln->insertItem (collections.at(i)->name, -1); + if (slCollnIndex < 0) { + slCollnIndex = i; // There is at least one collection. + } + } + + if (slCollnIndex < 0) { + return; // There are no collections (unlikely). + } + // Mark the currently selected collection (or default 0). + colln->setCurrentItem (cIndex); + colln->setSelected (cIndex, TRUE); + + // Fetch and display information on the selected collection. + slColln (cIndex); +} + +/******************************************************************************/ +/***************** SLOTS USED BY LEVEL SELECTION DIALOG *****************/ +/******************************************************************************/ + +void KGrSLDialog::slColln (int i) +{ + if (slCollnIndex < 0) { + // Ignore the "highlighted" signal caused by inserting in an empty box. + return; + } + + // User "highlighted" a new collection (with one click) ... + colln->setSelected (i, TRUE); // One click = selected. + slCollnIndex = i; + int n = slCollnIndex; // Collection selected. + int N = defaultGame; // Current collection. + if (collections.at(n)->nLevels > 0) + number->setMaxValue (collections.at(n)->nLevels); + else + number->setMaxValue (1); // Avoid range errors. + + // Set a default level number for the selected collection. + switch (slAction) { + case SL_ANY: + case SL_UPDATE: + case SL_DELETE: + case SL_UPD_GAME: + // If selecting the current collection, use the current level number. + if (n == N) + number->setValue (defaultLevel); + else + number->setValue (1); // Else use level 1. + break; + case SL_CREATE: + case SL_SAVE: + case SL_MOVE: + if ((n == N) && (slAction != SL_CREATE)) { + // Saving/moving level in current collection: use current number. + number->setValue (defaultLevel); + } + else { + // Saving new/edited level or relocating a level: use "nLevels + 1". + number->setMaxValue (collections.at(n)->nLevels + 1); + number->setValue (number->maxValue()); + } + break; + default: + number->setValue (1); // Default is level 1. + break; + } + + slShowLevel (number->value()); + +#ifndef KGR_PORTABLE + int levCnt = collections.at(n)->nLevels; + if (collections.at(n)->settings == 'K') + collnD->setText (i18n("1 level, uses KGoldrunner rules.", + "%n levels, uses KGoldrunner rules.", levCnt)); + else + collnD->setText (i18n("1 level, uses Traditional rules.", + "%n levels, uses Traditional rules.", levCnt)); +#else + QString levCnt; + levCnt = levCnt.setNum (collections.at(n)->nLevels); + if (collections.at(n)->settings == 'K') + collnD->setText (levCnt + i18n(" levels, uses KGoldrunner rules.")); + else + collnD->setText (levCnt + i18n(" levels, uses Traditional rules.")); +#endif + collnN->setText (collections.at(n)->name); +} + +void KGrSLDialog::slAboutColln () +{ + // User clicked the "About" button ... + int n = slCollnIndex; + QString title = i18n("About \"%1\"").arg(collections.at(n)->name); + + if (collections.at(n)->about.length() > 0) { + // Convert game description to ASCII and UTF-8 codes, then translate it. + KGrMessage::wrapped (slParent, title, + i18n((const char *) collections.at(n)->about.utf8())); + } + else { + KGrMessage::wrapped (slParent, title, + i18n("Sorry, there is no further information about this game.")); + } +} + +void KGrSLDialog::slShowLevel (int i) +{ + // Display the level number as the slider is moved. + QString tmp; + tmp.setNum(i); + tmp = tmp.rightJustify(3,'0'); + display->setText(tmp); +} + +void KGrSLDialog::slUpdate (const QString & text) +{ + // Move the slider when a valid level number is entered. + QString s = text; + bool ok = FALSE; + int n = s.toInt (&ok); + if (ok) { + number->setValue (n); + slPaintLevel(); + } + else + KGrMessage::information (this, i18n("Select Level"), + i18n("This level number is not valid. It can not be used.")); +} + +void KGrSLDialog::slPaintLevel () +{ + // Repaint the thumbnail sketch of the level whenever the level changes. + int n = slCollnIndex; + if (n < 0) { + return; // Owner has no collections. + } + QString filePath = game->getFilePath + (collections.at(n)->owner, collections.at(n), number->value()); + thumbNail->setFilePath (filePath, slName); + thumbNail->repaint(); // Will call "drawContents (p)". +} + +void KGrSLDialog::slotHelp () +{ + // Help for "Select Game and Level" dialog box. + QString s = + i18n("The main button at the bottom echoes the " + "menu action you selected. Click it after choosing " + "a game and level - or use \"Cancel\"."); + + if (slAction == SL_START) { + s += i18n("\n\nIf this is your first time in KGoldrunner, select the " + "tutorial game or click \"Cancel\" and click that item in " + "the Game or Help menu. The tutorial game gives you hints " + "as you go.\n\n" + "Otherwise, just click on the name of a game (in the list box), " + "then, to start at level 001, click on the main button at the " + "bottom. Play begins when you move the mouse or press a key."); + } + else { + switch (slAction) { + case SL_UPDATE: + s += i18n("\n\nYou can select System levels for editing (or " + "copying), but you must save the result in a game you have " + "created. Use the mouse as a paintbrush and the editor " + "toolbar buttons as a palette. Use the 'Empty Space' button " + "to erase."); + break; + case SL_CREATE: + s += i18n("\n\nYou can add a name and hint to your new level here, " + "but you must save the level you have created into one of " + "your own games. By default your new level will go at the " + "end of your game, but you can also select a level number and " + "save into the middle of your game."); + break; + case SL_SAVE: + s += i18n("\n\nYou can create or edit a name and hint here, before " + "saving. If you change the game or level, you can do a copy " + "or \"Save As\", but you must always save into one of your " + "own games. If you save a level into the middle of a series, " + "the other levels are automatically re-numbered."); + break; + case SL_DELETE: + s += i18n("\n\nYou can only delete levels from one of your own " + "games. If you delete a level from the middle of a series, " + "the other levels are automatically re-numbered."); + break; + case SL_MOVE: + s += i18n("\n\nTo move (re-number) a level, you must first select " + "it by using \"Edit Any Level...\", then you can use " + "\"Move Level...\" to assign it a new number or even a different " + "game. Other levels are automatically re-numbered as " + "required. You can only move levels within your own games."); + break; + case SL_UPD_GAME: + s += i18n("\n\nWhen editing game info you only need to choose a " + "game, then you can go to a dialog where you edit the " + "details of the game."); + break; + default: + break; + } + s += i18n("\n\nClick on the list box to choose a game. " + "Below the list box you can see \"More Info\" about the " + "selected game, how many levels there are and what " + "rules the enemies follow (see the Settings menu).\n\n" + "You select " + "a level number by typing it or using the scroll bar. As " + "you vary the game or level, the thumbnail area shows a " + "preview of your choice."); + } + + KGrMessage::wrapped (slParent, i18n("Help: Select Game & Level"), s); +} + +/******************************************************************************* +*************** DIALOG BOX TO CREATE/EDIT A LEVEL NAME AND HINT **************** +*******************************************************************************/ + +#ifdef KGR_PORTABLE +KGrNHDialog::KGrNHDialog(const QString & levelName, const QString & levelHint, + QWidget * parent, const char * name) + : QDialog (parent, name, TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title) +#else +KGrNHDialog::KGrNHDialog(const QString & levelName, const QString & levelHint, + QWidget * parent, const char * name) + : KDialogBase (KDialogBase::Plain, i18n("Edit Name & Hint"), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok, parent, name) +#endif +{ +#ifdef KGR_PORTABLE + int margin = 10; + int spacing = 10; + QWidget * dad = this; +#else + int margin = marginHint(); + int spacing = spacingHint(); + QWidget * dad = plainPage(); +#endif + + QVBoxLayout * mainLayout = new QVBoxLayout (dad, margin, spacing); + + QLabel * nameL = new QLabel (i18n("Name of level:"), dad); + mainLayout->addWidget (nameL); + nhName = new QLineEdit (dad); + mainLayout->addWidget (nhName); + + QLabel * mleL = new QLabel (i18n("Hint for level:"), dad); + mainLayout->addWidget (mleL); + + // Set up a widget to hold the wrapped text, using \n for paragraph breaks. +#ifdef QT3 + mle = new QTextEdit (dad); + mle-> setTextFormat (PlainText); +#else + mle = new QMultiLineEdit (dad); +#endif + mainLayout->addWidget (mle); + +#ifdef KGR_PORTABLE + QHBox * buttons = new QHBox (dad); + mainLayout->addWidget (buttons); + buttons->setSpacing (spacing); + // Buttons are for Qt-only portability. NOT COMPILED in KDE environment. + QPushButton * OK = new QPushButton (i18n("&OK"), buttons); + QPushButton * CANCEL = new QPushButton (i18n("&Cancel"), buttons); + + dad-> setCaption (i18n("Edit Name & Hint")); +#endif + + // Base the geometry of the text box on the playing area. + QPoint p = parent->mapToGlobal (QPoint (0,0)); + int c = parent->width() / (FIELDWIDTH + 4); + dad-> move (p.x()+4*c, p.y()+4*c); + mle-> setMinimumSize ((FIELDWIDTH*c/2), (FIELDHEIGHT/2)*c); + + // Configure the text box. + mle-> setAlignment (AlignLeft); +#ifndef QT3 + mle-> setWordWrap (QMultiLineEdit::WidgetWidth); + mle-> setFixedVisibleLines (9); +#endif + + nhName-> setText (levelName); + mle-> setText (levelHint); + +#ifdef KGR_PORTABLE + // OK-> setAccel (Key_Return); // No! We need it in "mle" box. + CANCEL-> setAccel (Key_Escape); + + connect (OK, SIGNAL (clicked ()), dad, SLOT (accept ())); + connect (CANCEL, SIGNAL (clicked ()), dad, SLOT (reject ())); +#endif +} + +KGrNHDialog::~KGrNHDialog() +{ +} + +/******************************************************************************* +*************** DIALOG BOX TO CREATE OR EDIT A GAME (COLLECTION) *************** +*******************************************************************************/ + +#ifdef KGR_PORTABLE +KGrECDialog::KGrECDialog (int action, int collnIndex, + QPtrList & gamesList, + QWidget * parent, const char * name) + : QDialog (parent, name, TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title) +#else +KGrECDialog::KGrECDialog (int action, int collnIndex, + QPtrList & gamesList, + QWidget * parent, const char * name) + : KDialogBase (KDialogBase::Plain, i18n("Edit Game Info"), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok, parent, name) +#endif +{ + collections = gamesList; + defaultGame = collnIndex; + +#ifdef KGR_PORTABLE + int margin = 10; + int spacing = 10; + QWidget * dad = this; +#else + int margin = marginHint(); + int spacing = spacingHint(); + QWidget * dad = plainPage(); +#endif + + QVBoxLayout * mainLayout = new QVBoxLayout (dad, margin, spacing); + + QHBox * nameBox = new QHBox (dad); + mainLayout->addWidget (nameBox); + nameBox->setSpacing (spacing); + nameL = new QLabel (i18n("Name of game:"), nameBox); + ecName = new QLineEdit (nameBox); + + QHBox * prefixBox = new QHBox (dad); + mainLayout->addWidget (prefixBox); + prefixBox->setSpacing (spacing); + prefixL = new QLabel (i18n("File name prefix:"), prefixBox); + ecPrefix = new QLineEdit (prefixBox); + + ecGrp = new QButtonGroup (1, QButtonGroup::Horizontal, 0, dad); + mainLayout->addWidget (ecGrp); + ecTradB = new QRadioButton (i18n("Traditional rules"), ecGrp); + ecKGrB = new QRadioButton (i18n("KGoldrunner rules"), ecGrp); + + nLevL = new QLabel (i18n( "0 levels" ), dad); + mainLayout->addWidget (nLevL); + + mleL = new QLabel (i18n("About this game:"), dad); + mainLayout->addWidget (mleL); + + // Set up a widget to hold the wrapped text, using \n for paragraph breaks. +#ifdef QT3 + mle = new QTextEdit (dad); + mle-> setTextFormat (PlainText); +#else + mle = new QMultiLineEdit (dad); +#endif + mainLayout->addWidget (mle); + +#ifdef KGR_PORTABLE + QHBox * buttons = new QHBox (dad); + mainLayout->addWidget (buttons); + buttons->setSpacing (spacing); + // Buttons are for Qt-only portability. NOT COMPILED in KDE environment. + OK = new QPushButton (i18n("&OK"), buttons); + CANCEL = new QPushButton (i18n("&Cancel"), buttons); + + QPoint p = parent->mapToGlobal (QPoint (0,0)); + + // Base the geometry of the dialog box on the playing area. + int cell = parent->width() / (FIELDWIDTH + 4); + dad-> move (p.x()+2*cell, p.y()+2*cell); + dad-> setMinimumSize ((FIELDWIDTH*cell/2), (FIELDHEIGHT-1)*cell); +#endif + + if (action == SL_CR_GAME) { + setCaption (i18n("Create Game")); + } + else { + setCaption (i18n("Edit Game Info")); + } + + QString OKText = ""; + if (action == SL_UPD_GAME) { // Edit existing collection. + ecName-> setText (collections.at(defaultGame)->name); + ecPrefix-> setText (collections.at(defaultGame)->prefix); + if (collections.at(defaultGame)->nLevels > 0) { + // Collection already has some levels, so cannot change the prefix. + ecPrefix-> setEnabled (FALSE); + } + QString s; +#ifndef KGR_PORTABLE + nLevL-> setText (i18n("1 level", "%n levels", + collections.at(defaultGame)->nLevels)); +#else + nLevL-> setText (i18n("%1 levels") + .arg(collections.at(defaultGame)->nLevels)); +#endif + OKText = i18n("Save Changes"); + } + else { // Create a collection. + ecName-> setText (""); + ecPrefix-> setText (""); + nLevL-> setText (i18n("0 levels")); + OKText = i18n("Save New"); + } +#ifdef KGR_PORTABLE + OK->setText (OKText); +#else + setButtonOK (OKText); +#endif + + if ((action == SL_CR_GAME) || + (collections.at(defaultGame)->settings == 'T')) { + ecSetRules ('T'); // Traditional settings. + } + else { + ecSetRules ('K'); // KGoldrunner settings. + } + + // Configure the edit box. + mle-> setAlignment (AlignLeft); +#ifndef QT3 + mle-> setWordWrap (QMultiLineEdit::WidgetWidth); + mle-> setFixedVisibleLines (8); +#endif + + if ((action == SL_UPD_GAME) && + (collections.at(defaultGame)->about.length() > 0)) { + // Display and edit the game description in its original language. + mle-> setText (collections.at(defaultGame)->about); + } + else { + mle-> setText (""); + } + + connect (ecKGrB, SIGNAL (clicked ()), this, SLOT (ecSetKGr ())); + connect (ecTradB, SIGNAL (clicked ()), this, SLOT (ecSetTrad ())); + +#ifdef KGR_PORTABLE + OK-> setGeometry (10, 145 + mle->height(), 100, 25); + // OK-> setAccel (Key_Return); // No! We need it in "mle" box. + + CANCEL-> setGeometry (190, 145 + mle->height(), 100, 25); + CANCEL-> setAccel (Key_Escape); + + dad-> resize (300, 175 + mle->height()); + + connect (OK, SIGNAL (clicked ()), this, SLOT (accept())); + connect (CANCEL, SIGNAL (clicked ()), this, SLOT (reject())); +#endif +} + +KGrECDialog::~KGrECDialog() +{ +} + +void KGrECDialog::ecSetRules (const char settings) +{ + ecKGrB-> setChecked (FALSE); + ecTradB-> setChecked (FALSE); + if (settings == 'K') + ecKGrB-> setChecked (TRUE); + else + ecTradB-> setChecked (TRUE); +} + +void KGrECDialog::ecSetKGr () {ecSetRules ('K');} // Radio button slots. +void KGrECDialog::ecSetTrad () {ecSetRules ('T');} + +/******************************************************************************* +*************** DIALOG TO SELECT A SAVED GAME TO BE RE-LOADED **************** +*******************************************************************************/ + +#ifdef KGR_PORTABLE +KGrLGDialog::KGrLGDialog (QFile * savedGames, + QPtrList & collections, + QWidget * parent, const char * name) + : QDialog (parent, name, TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title) +#else +KGrLGDialog::KGrLGDialog (QFile * savedGames, + QPtrList & collections, + QWidget * parent, const char * name) + : KDialogBase (KDialogBase::Plain, i18n("Select Saved Game"), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok, parent, name) +#endif +{ +#ifdef KGR_PORTABLE + int margin = 10; + int spacing = 10; + QWidget * dad = this; +#else + int margin = marginHint(); + int spacing = spacingHint(); + QWidget * dad = plainPage(); +#endif + + QVBoxLayout * mainLayout = new QVBoxLayout (dad, margin, spacing); + + QLabel * lgHeader = new QLabel ( + i18n("Game Level/Lives/Score " + "Day Date Time "), dad); + + lgList = new QListBox (dad); +#ifdef KGR_PORTABLE + QFont f ("courier", 12); +#else + QFont f = KGlobalSettings::fixedFont(); // KDE version. +#endif + f.setFixedPitch (TRUE); + lgList-> setFont (f); + f.setBold (TRUE); + lgHeader-> setFont (f); + + mainLayout-> addWidget (lgHeader); + mainLayout-> addWidget (lgList); + +#ifdef KGR_PORTABLE + QHBox * buttons = new QHBox (dad); + buttons-> setSpacing (spacing); + // Buttons are for Qt-only portability. NOT COMPILED in KDE environment. + QPushButton * OK = new QPushButton (i18n("&OK"), buttons); + QPushButton * CANCEL = new QPushButton (i18n("&Cancel"), buttons); + mainLayout-> addWidget (buttons); + + dad-> setCaption (i18n("Select Saved Game")); + + // Base the geometry of the list box on the playing area. + QPoint p = parent->mapToGlobal (QPoint (0,0)); + int c = parent->width() / (FIELDWIDTH + 4); + dad-> move (p.x()+2*c, p.y()+2*c); + lgList-> setMinimumHeight ((FIELDHEIGHT/2)*c); + OK-> setMaximumWidth (4*c); + CANCEL-> setMaximumWidth (4*c); + + OK-> setAccel (Key_Return); + CANCEL-> setAccel (Key_Escape); +#endif + + lgHighlight = -1; + + QTextStream gameText (savedGames); + QString s = ""; + QString pr = ""; + int i; + int imax = collections.count(); + + // Read the saved games into the list box. + while (! gameText.endData()) { + s = gameText.readLine(); // Read in one saved game. + pr = s.left (s.find (" ", 0, FALSE)); // Get the collection prefix. + for (i = 0; i < imax; i++) { // Get the collection name. + if (collections.at(i)->prefix == pr) { + s = s.insert (0, + collections.at(i)->name.leftJustify (20, ' ', TRUE) + " "); + break; + } + } + lgList-> insertItem (s); + } + savedGames->close(); + + // Mark row 0 (the most recently saved game) as the default selection. + lgList-> setCurrentItem (0); + lgList-> setSelected (0, TRUE); + lgHighlight = 0; + + connect (lgList, SIGNAL (highlighted (int)), this, SLOT (lgSelect (int))); +#ifdef KGR_PORTABLE + connect (OK, SIGNAL (clicked ()), this, SLOT (accept ())); + connect (CANCEL, SIGNAL (clicked ()), this, SLOT (reject ())); +#endif +} + +void KGrLGDialog::lgSelect (int n) +{ + lgHighlight = n; +} + +/******************************************************************************* +*********************** CENTRALISED MESSAGE FUNCTIONS ************************ +*******************************************************************************/ + +void KGrMessage::information (QWidget * parent, const QString &caption, const QString &text) +{ +#ifdef KGR_PORTABLE + // Force Qt to do word-wrapping (but it ignores "\n" line-breaks). + QMessageBox::information (parent, caption, + "" + text + ""); +#else + // KDE does word-wrapping and will observe "\n" line-breaks. + KMessageBox::information (parent, text, caption); +#endif +} + +int KGrMessage::warning (QWidget * parent, QString caption, QString text, + QString label0, QString label1, QString label2) +{ + int ans = 0; +#ifdef KGR_PORTABLE + // Display a box with 2 or 3 buttons, depending on if label2 is empty or not. + // Force Qt to do word-wrapping (but it ignores "\n" line-breaks). + ans = QMessageBox::warning (parent, caption, + "" + text + "", + label0, label1, label2, + 0, (label2.isEmpty()) ? 1 : 2); +#else + // KDE does word-wrapping and will observe "\n" line-breaks. + if (label2.isEmpty()) { + // Display a box with 2 buttons. + ans = KMessageBox::questionYesNo (parent, text, caption, + label0, label1); + ans = (ans == KMessageBox::Yes) ? 0 : 1; + } + else { + // Display a box with 3 buttons. + ans = KMessageBox::questionYesNoCancel (parent, text, caption, + label0, label1); + if (ans == KMessageBox::Cancel) + ans = 2; + else + ans = (ans == KMessageBox::Yes) ? 0 : 1; + } +#endif + return (ans); +} + +/******************************************************************************/ +/********************** WORD-WRAPPED MESSAGE BOX ************************/ +/******************************************************************************/ + +void KGrMessage::wrapped (QWidget * parent, QString title, QString contents) +{ +#ifndef KGR_PORTABLE + KMessageBox::information (parent, contents, title); +#else + QDialog * mm = new QDialog (parent, "wrappedMessage", TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title); + + int margin = 10; + int spacing = 10; + QVBoxLayout * mainLayout = new QVBoxLayout (mm, margin, spacing); + + // Make text background grey not white (i.e. same as widget background). + QPalette pl = mm->palette(); +#ifdef QT3 + pl.setColor (QColorGroup::Base, mm->paletteBackgroundColor()); +#else + pl.setColor (QColorGroup::Base, mm->backgroundColor()); +#endif + mm-> setPalette (pl); + + // Set up a widget to hold the wrapped text, using \n for paragraph breaks. +#ifdef QT3 + QTextEdit * mle = new QTextEdit (mm); + mle-> setTextFormat (PlainText); +#else + QMultiLineEdit * mle = new QMultiLineEdit (mm); +#endif + mainLayout->addWidget (mle); + + // Button is for Qt-only portability. NOT COMPILED in KDE environment. + QPushButton * OK = new QPushButton (i18n("&OK"), mm); + mainLayout->addWidget (OK, Qt::AlignHCenter); + + mm-> setCaption (title); + + // Base the geometry of the text box on the playing area. + QPoint p = parent->mapToGlobal (QPoint (0,0)); + int c = parent->width() / (FIELDWIDTH + 4); + mm-> move (p.x()+4*c, p.y()+4*c); + mle-> setMinimumSize ((FIELDWIDTH*c/2), (FIELDHEIGHT/2)*c); + OK-> setMaximumWidth (3*c); + + mle-> setFrameStyle (QFrame::NoFrame); + mle-> setAlignment (AlignLeft); + mle-> setReadOnly (TRUE); + mle-> setText (contents); + +#ifndef QT3 + mle-> setWordWrap (QMultiLineEdit::WidgetWidth); + mle-> setFixedVisibleLines (10); + if (mle-> numLines() < 10) { + mle-> setFixedVisibleLines (mle->numLines()); + } +#endif + + OK-> setAccel (Key_Return); + connect (OK, SIGNAL (clicked ()), mm, SLOT (accept ())); + + mm-> exec (); + + delete mm; +#endif // KGR_PORTABLE +} + +#include "kgrdialog.moc" diff --git a/kgoldrunner/src/kgrdialog.h b/kgoldrunner/src/kgrdialog.h new file mode 100644 index 00000000..2e2e847c --- /dev/null +++ b/kgoldrunner/src/kgrdialog.h @@ -0,0 +1,218 @@ +/*************************************************************************** + * Copyright (C) 2003 by Ian Wadham and Marco Krüger * + * ianw2@optusnet.com.au * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ +#ifndef KGRDIALOG_QT_H +#define KGRDIALOG_QT_H + +// If portable version, use QDialog and QMessageBox. +// If KDE version, use KDialogBase and KMessageBox. + +#ifdef KGR_PORTABLE +#include +#define KGR_DIALOG QDialog +#include + +#else +#include +#include +#define KGR_DIALOG KDialogBase +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef QT3 +#include +#else +#include +#endif + +#include + +/** +@author Ian Wadham and Marco Krüger +*/ + +class KGrCanvas; +class KGrGame; +class KGrCollection; +class KGrThumbNail; + +/******************************************************************************/ +/******************* DIALOG TO SELECT A GAME AND LEVEL *******************/ +/******************************************************************************/ + +class KGrSLDialog : public KGR_DIALOG // KGR_PORTABLE sets QDialog/KDialogBase +{ +Q_OBJECT +public: + KGrSLDialog (int action, int requestedLevel, int collnIndex, + QPtrList & gamesList, KGrGame * theGame, + QWidget * parent = 0, const char *name = 0); + ~KGrSLDialog(); + + int selectedLevel() {return (number->value());} + int selectedGame() {return (slCollnIndex);} + +private slots: + void slSetCollections (int cIndex); + void slColln (int i); + void slAboutColln (); + void slShowLevel (int i); + void slUpdate (const QString & text); + void slPaintLevel (); + void slotHelp (); // Will replace KDE slotHelp(). + +private: + int slAction; + QPtrList collections; // List of games. + int defaultLevel; + int defaultGame; + int slCollnIndex; + KGrGame * game; + KGrCollection * collection; + QWidget * slParent; + + QLabel * collnL; + QListBox * colln; + QLabel * collnN; + QLabel * collnD; + QPushButton * collnA; + + QLabel * numberL; + QLineEdit * display; + QScrollBar * number; + QPushButton * levelNH; + QLabel * slName; + KGrThumbNail * thumbNail; + +#ifdef KGR_PORTABLE + QPushButton * OK; + QPushButton * HELP; + QPushButton * CANCEL; +#endif +}; + +/******************************************************************************* +*************** DIALOG BOX TO CREATE/EDIT A LEVEL NAME AND HINT **************** +*******************************************************************************/ + +class KGrNHDialog : public KGR_DIALOG // KGR_PORTABLE sets QDialog/KDialogBase +{ +Q_OBJECT +public: + KGrNHDialog (const QString & levelName, const QString & levelHint, + QWidget * parent = 0, const char * name = 0); + ~KGrNHDialog(); + + QString getName() {return (nhName->text());} + QString getHint() {return (mle->text());} + +private: + QLineEdit * nhName; +#ifdef QT3 + QTextEdit * mle; +#else + QMultiLineEdit * mle; +#endif +}; + +/******************************************************************************* +***************** DIALOG TO CREATE OR EDIT A GAME (COLLECTION) ***************** +*******************************************************************************/ + +class KGrECDialog : public KGR_DIALOG // KGR_PORTABLE sets QDialog/KDialogBase +{ +Q_OBJECT +public: + KGrECDialog (int action, int collnIndex, + QPtrList & gamesList, + QWidget *parent = 0, const char *name = 0); + ~KGrECDialog(); + + QString getName() {return (ecName->text());} + QString getPrefix() {return (ecPrefix->text());} + bool isTrad() {return (ecTradB->isChecked());} + QString getAboutText() {return (mle->text());} + +private slots: + void ecSetRules (const char settings); + void ecSetKGr(); // Radio button slots. + void ecSetTrad(); + +private: + QPtrList collections; // List of existing games. + int defaultGame; + + QLabel * nameL; + QLineEdit * ecName; + QLabel * prefixL; + QLineEdit * ecPrefix; + QButtonGroup * ecGrp; + QRadioButton * ecKGrB; + QRadioButton * ecTradB; + QLabel * nLevL; + + QLabel * mleL; +#ifdef QT3 + QTextEdit * mle; +#else + QMultiLineEdit * mle; +#endif + +#ifdef KGR_PORTABLE + QPushButton * OK; + QPushButton * CANCEL; +#endif +}; + +/******************************************************************************* +*************** DIALOG TO SELECT A SAVED GAME TO BE RE-LOADED **************** +*******************************************************************************/ + +#include +#include + +class KGrLGDialog : public KGR_DIALOG // KGR_PORTABLE sets QDialog/KDialogBase +{ +Q_OBJECT +public: + KGrLGDialog (QFile * savedGames, QPtrList & collections, + QWidget * parent, const char * name); + QString getCurrentText() {return (lgList->currentText());} + +private slots: + void lgSelect (int n); + +private: + QListBox * lgList; + int lgHighlight; +}; + +/******************************************************************************* +****************** PORTABLE MESSAGE FUNCTIONS (Qt Version) ******************* +*******************************************************************************/ + +class KGrMessage : public QDialog +{ +public: + static void information (QWidget * parent, const QString &caption, const QString &text); + static int warning (QWidget * parent, QString caption, QString text, + QString label0, QString label1, QString label2 = ""); + static void wrapped (QWidget * parent, QString caption, QString text); +}; + +#endif diff --git a/kgoldrunner/src/kgrfigure.cpp b/kgoldrunner/src/kgrfigure.cpp new file mode 100644 index 00000000..08fc91d4 --- /dev/null +++ b/kgoldrunner/src/kgrfigure.cpp @@ -0,0 +1,1803 @@ +/*************************************************************************** + * kgrfigure.cpp - description * + * ------------------- * + * Copyright (C) 2003 by Ian Wadham and Marco Krüger * + * email : See menu "Help, About KGoldrunner" * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#include "kgrconsts.h" +#include "kgrobject.h" +#include "kgrcanvas.h" + +#include "kgrfigure.h" + +#include + +KGrFigure :: KGrFigure (int px, int py) +{ + x = mem_x = px; + y = mem_y = py; + relx = mem_relx = 0; + rely = mem_rely = 0; + + absx = px*16; + absy = py*16; + + nuggets = 0; + status = STANDING; + + walkTimer = new QTimer (this); + fallTimer = new QTimer (this); +} + +// Initialise the global settings flags. +bool KGrFigure::variableTiming = TRUE; +bool KGrFigure::alwaysCollectNugget = TRUE; +bool KGrFigure::runThruHole = TRUE; +bool KGrFigure::reappearAtTop = TRUE; +SearchStrategy KGrFigure::searchStrategy = LOW; + +int KGrFigure::herox = 0; +int KGrFigure::heroy = 0; + +// Initialise the global game-speed factors. +int KGrFigure::speed = NSPEED; +int KGrBrick::speed = NSPEED; + +// Initialise constants for fixed (KGoldrunner) and variable (Traditional) +// timing. Each row contains timings for hero walk and fall, enemy walk and +// fall, enemy captured in hole and dug brick. + +Timing KGrFigure::fixedTiming = {45, 50, 55, 100, 500, 40}; // KGr original. + +Timing KGrFigure::varTiming[6] = { // Traditional. + {40, 58, 78, 88, 170, 23}, // No enemies. + {50, 68, 78, 88, 170, 32}, // 1 enemy. + {57, 67, 114, 128, 270, 37}, // 2 enemies. + {60, 70, 134, 136, 330, 40}, // 3 enemies. + {63, 76, 165, 150, 400, 46}, // 4 enemies. + {70, 80, 189, 165, 460, 51} // >4 enemies. +}; + +int KGrBrick::HOLETIME = 0; + +int KGrFigure::getx() +{ + return absx; +} + +int KGrFigure::gety() +{ + return absy; +} + +Status KGrFigure::getStatus() +{ + return status; +} + +void KGrFigure::init(int a,int b) +{ + walkTimer->stop(); + fallTimer->stop(); + x = mem_x = a; + y = mem_y = b; + relx = mem_relx = 0; + rely = mem_rely = 0; + nuggets = 0; + status = STANDING; +} + +void KGrFigure:: setNuggets(int n) +{ + nuggets = n; +} + + +bool KGrFigure::canWalkRight() +{ + return (((*playfield)[x+1][y]->whatIam() != BRICK) && + ((*playfield)[x+1][y]->whatIam() != BETON) && + ((*playfield)[x+1][y]->whatIam() != FBRICK)); +} + +bool KGrFigure::canWalkLeft() +{ + return (((*playfield)[x-1][y]->whatIam() != BRICK) && + ((*playfield)[x-1][y]->whatIam() != BETON) && + ((*playfield)[x-1][y]->whatIam() != FBRICK)); + } + +bool KGrFigure::canWalkUp() +{ + return (((*playfield)[x][y-1]->whatIam() != BRICK) && + ((*playfield)[x][y-1]->whatIam() != BETON) && + ((*playfield)[x][y-1]->whatIam() != FBRICK) && + ((*playfield)[x][y]->whatIam() == LADDER)); +} + +bool KGrFigure::canWalkDown() +{ + return (((*playfield)[x][y+1]->whatIam() != BRICK) && + ((*playfield)[x][y+1]->whatIam() != BETON) && + // v0.3 FIX - Let figure step down into FBRICK from a ladder. + // ((*playfield)[x][y+1]->whatIam() != FBRICK)&& + (((*playfield)[x][y+1]->whatIam() == LADDER)|| + ((*playfield)[x][y]->whatIam() == LADDER))); +} + +bool KGrFigure::canStand() +{ + return (((*playfield)[x][y+1]->whatIam() == BRICK) || + ((*playfield)[x][y+1]->whatIam() == BETON) || + ((*playfield)[x][y+1]->whatIam() == USEDHOLE)|| + ((*playfield)[x][y+1]->whatIam() == LADDER)|| + ((*playfield)[x][y]->whatIam() == LADDER)|| + standOnEnemy()); + } + +bool KGrFigure::hangAtPole() +{ + return ((*playfield)[x][y]->whatIam() == POLE); +} + +void KGrFigure::walkUp(int WALKDELAY) +{ + actualPixmap = (actualPixmap == CLIMB1) ? CLIMB2 : CLIMB1; + if (walkCounter++ < 4) { + // Not end of 4-step cycle: move one step up, if possible. + if (canWalkUp()) { + rely -= STEP; + absy -= STEP; + } + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + } + else { + // End of 4-step cycle: move up to next cell, if possible. + if (canWalkUp()) { + y--; + } + // Always reset position, in case we are stuck partly into a brick. + rely = 0; + absy = y*16; + + // Wait for caller to set next direction. + status = STANDING; + } +} + +void KGrFigure::walkDown(int WALKDELAY, int FALLDELAY) +{ + if (hangAtPole() || (! canStand())) { + // On bar or no firm ground underneath: so must fall. + initFall (FALL2, FALLDELAY); + } + else { + actualPixmap = (actualPixmap == CLIMB1) ? CLIMB2 : CLIMB1; + if (walkCounter++ < 4) { + // Not end of 4-step cycle: move one step down, if possible. + if (canWalkDown()) { + rely += STEP; + absy += STEP; + } + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + } + else { + // End of 4-step cycle: move down to next cell, if possible. + if (canWalkDown()) { + y++; + } + // Always reset position, in case we are stuck partly into a brick. + rely = 0; + absy = y*16; + + // Must be able to halt at a pole when going down. + if (! (canStand() || hangAtPole())) + initFall(FALL2, FALLDELAY); // kein Halt....urgs + else + // Wait for caller to set next direction. + status = STANDING; + } + } +} + +void KGrFigure::walkLeft (int WALKDELAY, int FALLDELAY) +{ + // If counter != 0, the figure is walking, otherwise he is turning around. + if (walkCounter++ != 0) { + // Change to the next pixmap in the animation. + if ((++actualPixmap%4) != 0) { + // Not end of 4-pixmap cycle: move one step left, if possible. + if (canWalkLeft()) { + relx -= STEP; + absx -=STEP; + } + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + } + else { + // End of 4-pixmap cycle: start again, in next cell if possible. + actualPixmap -= 4; + if (canWalkLeft()) { + x--; + } + // Always reset position, in case we are stuck partly into a brick. + relx = 0; + absx = x*16; + + // If cannot stand or hang, start fall, else await next assignment. + if (! (canStand() || hangAtPole())) + initFall (FALL1, FALLDELAY); + else + status = STANDING; // Caller should set next direction. + } + } + else { + status = STANDING; // The figure is turning around. + } +} + +void KGrFigure::walkRight(int WALKDELAY, int FALLDELAY) +{ + if (walkCounter++) { // wenn 0, soll sich Figur nur umdrehen + if (++actualPixmap % 4) { // wenn true, dann ist kein vollständiger Schritt gemacht + if (canWalkRight()) { + relx += STEP; + absx += STEP; // nur vorwärts gehen, wenn es auch möglich ist + } + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + } + else { + actualPixmap -= 4; // Schritt war vollendet + if (canWalkRight()) { + x++; + } //gehe entgültig nach rechts + // Always reset position, in case we are stuck partly into a brick. + relx = 0; + absx = x*16; + + if (!(canStand()||hangAtPole())) // kein Halt mehr...arrrgghhh + initFall (FALL2, FALLDELAY); + else + status = STANDING; // Figur hat Halt + } + } + else { + status = STANDING; // Figur sollte sich nur Umdrehen. + } +} + +void KGrFigure::initFall(int apm, int FALLDELAY) +{ + status = FALLING; + actualPixmap = apm; + walkCounter=1; + walkTimer->stop(); + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); +} + +void KGrFigure::showFigure () +{ +} + +void KGrFigure::setPlayfield (KGrObject * (*p)[30][22]) +{ + playfield = p; +} + +KGrFigure :: ~KGrFigure () +{ +} + +KGrHero :: KGrHero (KGrCanvas * view, int x, int y) + :KGrFigure (x, y) +{ + heroView = view; + status = STANDING; + actualPixmap = FALL1; + + herox = x; + heroy = y; + + started = FALSE; + mouseMode = TRUE; + walkCounter = 1; + + walkFrozen = FALSE; + fallFrozen = FALSE; + + connect (walkTimer, SIGNAL (timeout ()), SLOT (walkTimeDone ())); + connect (fallTimer, SIGNAL (timeout ()), SLOT (fallTimeDone ())); +} + +int KGrHero::WALKDELAY = 0; +int KGrHero::FALLDELAY = 0; + +/* Es ist Notwendig der eigentlichen Timerfunktion diese + Startwalk vorzuschalten, um bei einem evtl. notwendigem + Richtungswechsel der Figur diese Bewegung mit einzufügen */ +void KGrHero::startWalk () +{ + switch (nextDir) { + case UP: + if ((*playfield)[x][y]->whatIam () == LADDER) + {walkCounter = 1; + direction = UP;} + break; + case RIGHT: + if (hangAtPole()) + actualPixmap = RIGHTCLIMB1; + else + actualPixmap = RIGHTWALK1; + if (direction != RIGHT) + walkCounter = 0; + else + walkCounter = 1; + direction = RIGHT; + break; + case DOWN: + if (((*playfield)[x][y]->whatIam () == LADDER)|| + ((*playfield)[x][y+1]->whatIam () == LADDER)) + {walkCounter = 1; + direction = DOWN;} + else // wenn figur an Stange haengt und nichts unter ihr, + if (hangAtPole() && (!canStand())) // dann soll sie fallen + { status = STANDING; + actualPixmap = (direction==RIGHT)?19:18; + walkCounter=1; + direction=STAND; + walkTimer->stop(); + } + break; + case LEFT: + if (hangAtPole()) + actualPixmap = LEFTCLIMB1; + else + actualPixmap = LEFTWALK1; + if (direction != LEFT) + walkCounter = 0; + else + walkCounter = 1; + direction = LEFT; + break; + default : + direction = STAND; + status = FALLING; + break; + } + nextDir = STAND; + if (status != FALLING)//immer ausführen, ausser beim fallen + { status = WALKING; // da sonst der FALLINGstatus wieder + showFigure (); // geaendert wird und der falsche Timer anspringt. + } +} // END KGrHero::startWalk + +void KGrHero::setKey(Direction key) +{ + // Keyboard control of hero: direction is fixed until next key is pressed. + // Sets a simulated mouse-pointer above, below, left, right or on the hero. + mouseMode = FALSE; + stopped = FALSE; + switch (key) { + case UP: mousex = x; mousey = 0; break; + case DOWN: mousex = x; mousey = FIELDHEIGHT + 1; break; + case LEFT: mousex = 0; mousey = y; break; + case RIGHT: mousex = FIELDWIDTH + 1; mousey = y; break; + case STAND: stopped = TRUE; mousex = x; mousey = y; break; + } +} + +void KGrHero::setDirection(int i, int j) +{ + // Mouse control of hero: direction is updated continually on a timer. + mouseMode = TRUE; + stopped = FALSE; + mousex = i; + mousey = j; +} + +void KGrHero::setNextDir() +{ + int dx, dy; + + if (! mouseMode) { + // Keyboard control of hero: adjust simulated mouse-pointer. + if (stopped) { + mousex = x; + mousey = y; + } + if ((mousey < 1) || (mousey > FIELDHEIGHT)) { + mousex = x; // Stay directly above/below the hero. + } + else if ((mousex < 1) || (mousex > FIELDWIDTH)) { + mousey = y; // Stay directly left/right of the hero. + } + } + + dx = mousex - x; dy = mousey - y; + + if ((dy == 0) && (y == 1) && (nuggets <= 0)) { + nextDir = UP; + } + else if ((dy > 0) && + (canWalkDown() || + standOnEnemy() || + (hangAtPole() && ((*playfield)[x][y+1]->whatIam() != BRICK) && + ((*playfield)[x][y+1]->whatIam() != BETON)))) { + nextDir = DOWN; + } + else if ((dy < 0) && canWalkUp ()) { + nextDir = UP; + } + else if (dx > 0) { + nextDir = RIGHT; + } + else if (dx < 0) { + nextDir = LEFT; + } + else if (dx == 0) { + nextDir = STAND; + } +} + +void KGrHero::doStep() { + if (walkFrozen) { + walkFrozen = FALSE; + walkTimeDone(); + } + if (fallFrozen) { + fallFrozen = FALSE; + fallTimeDone(); + } +} + +void KGrHero::showState(char option) +{ + printf("(%02d,%02d) - Hero ", x, y); + switch (option) { + case 'p': printf ("\n"); break; + case 's': printf (" nuggets %02d status %d walk-ctr %d ", + nuggets, status, walkCounter); + printf ("dirn %d next dirn %d\n", direction, nextDir); + printf (" rel (%02d,%02d) abs (%03d,%03d)", + relx, rely, absx, absy); + printf (" pix %02d", actualPixmap); + printf (" mem %d %d %d %d", mem_x, mem_y, mem_relx, mem_rely); + if (walkFrozen) printf (" wBlock"); + if (fallFrozen) printf (" fBlock"); + printf ("\n"); + break; + } +} + +void KGrHero::init(int a,int b) +{ + walkTimer->stop(); + fallTimer->stop(); + walkCounter = 1; + started = FALSE; + + x = mem_x = a; + y = mem_y = b; + relx = mem_relx = 0; + rely = mem_rely = 0; + + absx = 16*x; + absy = 16*y; + + nuggets = 0; + + if (herox < 1) { // If first call to init, ... + heroView->makeHeroSprite (x, y, actualPixmap); + } + herox = x; + heroy = y; + + actualPixmap = FALL2; + heroView->moveHero (absx, absy, actualPixmap); +} + +void KGrHero::start() +{ + started = TRUE; + walkFrozen = FALSE; + fallFrozen = FALSE; + + if (!(canStand()||hangAtPole())) { // Held muss wohl fallen... + status = FALLING; + fallTimeDone(); + } + else { + status = STANDING; + walkTimeDone(); + } +} + +void KGrHero::setSpeed (int gamespeed) +{ + if (gamespeed >= 0) { + if (gamespeed < MINSPEED) + speed++; // Increase speed. + else + speed = gamespeed; // Set selected speed. + if (speed > MAXSPEED) + speed = MAXSPEED; // Set upper limit. + } + else { + speed--; // Reduce speed. + if (speed < MINSPEED) + speed = MINSPEED; // Set lower limit. + } + + KGrBrick::speed = speed; // Make a copy for bricks. +} + +void KGrHero::walkTimeDone () +{ + if (! started) return; // Ignore signals from earlier play. + if (KGrObject::frozen) {walkFrozen = TRUE; return; } + + if ((*playfield)[x][y]->whatIam() == BRICK) { + emit caughtHero(); // Brick closed over hero. + return; + } + + if ((y==1)&&(nuggets<=0)) { // If on top row and all nuggets collected, + emit leaveLevel(); // the hero has won and can go to next level. + return; + } + + if (status == STANDING) + setNextDir(); + if ((status == STANDING) && (nextDir != STAND)) { + if ((standOnEnemy()) && (nextDir == DOWN)) { + emit caughtHero(); // Hero is going to step down into an enemy. + return; + } + startWalk(); + } + if (status != STANDING) { + switch (direction) { + case UP: walkUp (WALKDELAY); break; + case DOWN: walkDown (WALKDELAY, FALLDELAY); break; + case RIGHT: walkRight (WALKDELAY, FALLDELAY); break; + case LEFT: walkLeft (WALKDELAY, FALLDELAY); break; + default : + // The following code is strange. It makes the hero fall off a pole. + // It works because of other strange code in "startWalk(), case DOWN:". + if (!canStand()||hangAtPole()) // falling + initFall(FALL1, FALLDELAY); + else status = STANDING; + break; + } + herox=x;heroy=y; // Koordinatenvariablen neu + // wenn Held genau ein Feld weitergelaufen ist, + if ((relx==0)&&(rely==0)) // dann setzte statische + { + collectNugget(); // und nehme evtl. Nugget + } + showFigure(); // Is this REDUNDANT now? See showFigure() below. + ////////////////////////////////////////////////// + } + if (status == STANDING) + if (!canStand()&&!hangAtPole()) + initFall(FALL1, FALLDELAY); + else + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + + // This additional showFigure() is to update the hero position after it is + // altered by the hero-enemy deadlock fix in standOnEnemy(). Messy, but ... + //////////////////////////////////////////////////////////////////////////// + showFigure(); + if(isInEnemy()) { + walkTimer->stop(); + emit caughtHero(); + } +} + +void KGrHero::fallTimeDone() +{ + if (! started) return; // Ignore signals from earlier play. + if (KGrObject::frozen) {fallFrozen = TRUE; return; } + + if (!standOnEnemy()) { + if (walkCounter++ < 4) { // Held fällt vier Positionen + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); + rely+=STEP; + absy+=STEP; + } + else { //Held ist ein Feld weitergefallen + // Verschiebung der Figur zum 0-Punkt des Objekts (Brick etc...) + heroy = ++y; + rely = 0; + absy = y*16; // wird Null und Figur eins runter + collectNugget(); // gesetzt. Zeit evtl. Nugget zu nehmen + if (! (canStand()||hangAtPole())) { // Held muss wohl weiterfallen. + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); + walkCounter = 1; + } + else { // Held hat Boden (oder Feind) unter den + status = STANDING; // Füssen oder hängt an Stange -> steh! + walkTimer->start((WALKDELAY * NSPEED) / speed, TRUE); + direction = (actualPixmap == 19) ? RIGHT : LEFT; + if ((*playfield)[x][y]->whatIam() == POLE) + actualPixmap = (direction == RIGHT)? RIGHTCLIMB1:LEFTCLIMB1; + // else + // Reduce jerkiness when descending over a falling enemy. + // actualPixmap = (direction == RIGHT)? RIGHTWALK1:LEFTWALK1; + } + } + showFigure(); + } + else { + if (rely == 0) { + // If at the bottom of a cell, try to walk or just stand still. + status = STANDING; + direction = (actualPixmap == 19) ? RIGHT : LEFT; + if ((*playfield)[x][y]->whatIam() == POLE) + actualPixmap = (direction == RIGHT)? RIGHTCLIMB1:LEFTCLIMB1; + // else + // Reduce jerkiness when descending over a falling enemy. + // actualPixmap = (direction == RIGHT)? RIGHTWALK1:LEFTWALK1; + walkTimer->start((WALKDELAY * NSPEED) / speed, TRUE); + } + else { + // Else, freeze hero until enemy moves out of the way. + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); + } + } + if (isInEnemy() && (! standOnEnemy())) + emit caughtHero(); +} + + +void KGrHero::showFigure () { + + heroView->moveHero (absx, absy, actualPixmap); + + // Merke alte Werte zum löschen der Figur + mem_x = x; + mem_y = y; + mem_relx = relx; + mem_rely = rely; +} + +void KGrHero::dig(){ + if (direction == LEFT) + digLeft(); + else + if (direction == RIGHT) + digRight(); +} + +void KGrHero::digLeft(){ + int i = 1; // If stationary or moving up/down, dig at x-1. + if (status == STANDING) + setNextDir(); + if ((status == WALKING) || + ((status == STANDING) && ((nextDir == LEFT) || (nextDir == RIGHT)))) { + if ((direction == LEFT) && canWalkLeft()) + i = 2; // If walking left, dig at x-2 and stop at x-1. + else if ((direction == RIGHT) && canWalkRight()) + i = 0; // If walking right, dig at x and stop at x+1. + } + if (((*playfield)[x-i][y+1]->whatIam() == BRICK)&& + (((*playfield)[x-i][y]->whatIam() == HLADDER)|| + ((*playfield)[x-i][y]->whatIam() == FREE)|| + ((*playfield)[x-i][y]->whatIam() == HOLE))) + ((KGrBrick*)(*playfield)[x-i][y+1])->dig(); +} + +void KGrHero::digRight(){ + int i = 1; // If stationary or moving up/down, dig at x+1. + if (status == STANDING) + setNextDir(); + if ((status == WALKING) || + ((status == STANDING) && ((nextDir == LEFT) || (nextDir == RIGHT)))) { + if ((direction == LEFT) && canWalkLeft()) + i = 0; // If walking left, dig at x and stop at x-1. + else if ((direction == RIGHT) && canWalkRight()) + i = 2; // If walking right, dig at x+2 and stop at x+1. + } + if (((*playfield)[x+i][y+1]->whatIam() == BRICK)&& + (((*playfield)[x+i][y]->whatIam() == HLADDER)|| + ((*playfield)[x+i][y]->whatIam() == FREE)|| + ((*playfield)[x+i][y]->whatIam() == HOLE))) + ((KGrBrick*)(*playfield)[x+i][y+1])->dig(); +} + +#ifdef QT3 +void KGrHero::setEnemyList(QPtrList *e) +#else +void KGrHero::setEnemyList(QList *e) +#endif +{ + enemies = e; +} + +bool KGrHero::standOnEnemy() +{ + int c = 0; + int range = enemies->count(); + if (range > 0) { + for (KGrEnemy * enemy = enemies->at (c); c < range; ) { + enemy = enemies->at(c++); + // Test if hero's foot is at or just below enemy's head (tolerance + // of 4 pixels) and the two figures overlap in the X direction. + if ((((absy + 16) == enemy->gety()) || + ((absy + 12) == enemy->gety())) && + (((absx - 16) < enemy->getx()) && + ((absx + 16) > enemy->getx()))) { + if (((absy + 12) == enemy->gety()) && + (enemy->getStatus() != FALLING)) { + absy = absy - rely; // Bounce back from overlap, to avoid + rely = 0; // hero-enemy mid-cycle deadlock. + walkCounter = 1; + } + return true; + } + } + } + return false; +} + +void KGrHero::collectNugget(){ + + if ((*playfield)[x][y]->whatIam() == NUGGET) + { + ((KGrFree *)(*playfield)[x][y])->setNugget(false); + if (!(--nuggets)) + emit haveAllNuggets(); // sendet der Application dass alle Nuggets + // gesammelt sind, um versteckte Leitern zu zeigen + emit gotNugget(250); // sendet der Application ein Nugget um Score zu erhöhen + + } +} + +void KGrHero::loseNugget() { + + // Enemy trapped or dead and could not drop nugget (NO SCORE for this). + if (! (--nuggets)) + emit haveAllNuggets(); // Sendet der Application dass alle Nuggets + // gesammelt sind, um versteckte Leitern zu zeigen. +} + +bool KGrHero::isInEnemy(){ + + int c=0; + int range=enemies->count(); + if (range) + for (KGrEnemy *enemy=enemies->at(c);cat(c++); + if (isInside(enemy->getx(),enemy->gety())|| + isInside(enemy->getx()-15,enemy->gety())|| + isInside(enemy->getx(),enemy->gety()-15)) + return true;} + return false; +} + +bool KGrHero::isInside(int enemyx, int enemyy){ + + return ((absx >= enemyx)&& + (absx <= enemyx+15)&& + (absy >= enemyy)&& + (absy <= enemyy+15)); +} + + +KGrHero :: ~KGrHero (){ + + delete walkTimer; + delete fallTimer; +} + + +KGrEnemy :: KGrEnemy (KGrCanvas * view, int x, int y) + : KGrFigure (x, y) +{ + enemyView = view; + actualPixmap = FALL1; + nuggets = 0; + enemyView->makeEnemySprite (x, y, actualPixmap); + + walkCounter = 1; + captiveCounter = 0; + + searchStatus = HORIZONTAL; + + birthX=x; + birthY=y; + + walkFrozen = FALSE; + fallFrozen = FALSE; + captiveFrozen = FALSE; + + captiveTimer = new QTimer (this); + connect (captiveTimer,SIGNAL(timeout()),SLOT(captiveTimeDone())); + connect (walkTimer, SIGNAL (timeout ()), SLOT (walkTimeDone ())); + connect (fallTimer, SIGNAL (timeout ()), SLOT (fallTimeDone ())); +} + +int KGrEnemy::WALKDELAY = 0; +int KGrEnemy::FALLDELAY = 0; +int KGrEnemy::CAPTIVEDELAY = 0; + +void KGrEnemy::doStep() { + if (walkFrozen) { + walkFrozen = FALSE; + walkTimeDone(); + } + if (fallFrozen) { + fallFrozen = FALSE; + fallTimeDone(); + } + if (captiveFrozen) { + captiveFrozen = FALSE; + captiveTimeDone(); + } +} + +void KGrEnemy::showState(char option) +{ + printf("(%02d,%02d) - Enemy [%d]", x, y, enemyId); + switch (option) { + case 'p': printf ("\n"); break; + case 's': printf (" nuggets %02d status %d walk-ctr %d ", + nuggets, status, walkCounter); + printf ("dirn %d search %d capt-ctr %d\n", + direction, searchStatus, captiveCounter); + printf (" rel (%02d,%02d) abs (%03d,%03d)", + relx, rely, absx, absy); + printf (" pix %02d", actualPixmap); + printf (" mem %d %d %d %d", mem_x, mem_y, mem_relx, mem_rely); + if (walkFrozen) printf (" wBlock"); + if (fallFrozen) printf (" fBlock"); + if (captiveFrozen) printf (" cBlock"); + printf ("\n"); + break; + } +} + +void KGrEnemy::init(int a,int b) +{ + walkTimer->stop(); // alles stoppen bevor die Werte neu gesetzt + fallTimer->stop(); // werden, da es sonst zu ungewollten Effekten + captiveTimer->stop(); // kommen kann + walkCounter = 1; + captiveCounter = 0; + + x = mem_x = a; + y = mem_y = b; + relx = mem_relx = 0; + rely = mem_rely = 0; + + absx=16*x; + absy=16*y; + + actualPixmap = 19; + + status = STANDING; +} + +void KGrEnemy::walkTimeDone () +{ + if (KGrObject::frozen) {walkFrozen = TRUE; return; } + + // Check we are alive BEFORE checking for friends being in the way. + // Maybe a friend overhead is blocking our escape from a brick. + if ((*playfield)[x][y]->whatIam()==BRICK) { // sollte er aber in einem Brick + dieAndReappear(); // sein, dann stirbt er wohl + return; // Must leave "walkTimeDone" when an enemy dies. + } + + if (! bumpingFriend()) { + switch (direction) { + case UP: walkUp (WALKDELAY); + if ((rely == 0) && + ((*playfield)[x][y+1]->whatIam() == USEDHOLE)) + // Enemy kletterte grad aus einem Loch hinaus + // -> gib es frei! + ((KGrBrick *)(*playfield)[x][y+1])->unUseHole(); + break; + case DOWN: walkDown (WALKDELAY, FALLDELAY); break; + case RIGHT: walkRight (WALKDELAY, FALLDELAY); break; + case LEFT: walkLeft (WALKDELAY, FALLDELAY); break; + default: // Switch search direction in KGoldrunner search (only). + searchStatus = (searchStatus==VERTIKAL) ? + HORIZONTAL : VERTIKAL; + + // In KGoldrunner rules, if a hole opens under an enemy + // who is standing and waiting to move, he should fall. + if (!(canStand()||hangAtPole())) { + initFall (actualPixmap, FALLDELAY); + } + else { + status = STANDING; + } + + break; + } + // wenn die Figur genau ein Feld gelaufen ist + if (status == STANDING) { // dann suche den Helden + direction = searchbestway(x,y,herox,heroy); // und + if (walkCounter >= 4) { + if (! nuggets) + collectNugget(); + else + dropNugget(); + } + status = WALKING; // initialisiere die Zählervariablen und + walkCounter = 1; // den Timer um den Held weiter + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); // zu jagen + startWalk (); + } + } + else { + // A friend is in the way. Try a new direction, but not if leaving a hole. + Direction dirn; + + // In KGoldrunner rules, change the search strategy, + // to avoid enemy-enemy deadlock. + searchStatus = (searchStatus==VERTIKAL) ? HORIZONTAL : VERTIKAL; + + dirn = searchbestway (x, y, herox, heroy); + if ((dirn != direction) && ((*playfield)[x][y]->whatIam() != USEDHOLE)) { + direction = dirn; + status = WALKING; + walkCounter = 1; + relx = 0; absx = 16 * x; + rely = 0; absy = 16 * y; + startWalk (); + } + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + } + showFigure(); +} + +void KGrEnemy::fallTimeDone () +{ + if (KGrObject::frozen) {fallFrozen = TRUE; return; } + + if ((*playfield)[x][y+1]->whatIam() == HOLE) { // wenn Enemy ins Loch fällt + ((KGrBrick*)(*playfield)[x][y+1])->useHole(); // reserviere das Loch, damit + // kein anderer es benutzt und + if (nuggets) { // er muss Gold vorher fallen lassen + nuggets=0; + switch ((*playfield)[x][y]->whatIam()) { + case FREE: + case HLADDER: + ((KGrFree *)(*playfield)[x][y])->setNugget(true); break; + default: + emit lostNugget(); break; // Cannot drop the nugget here. + } + } + emit trapped (75); // Enemy trapped: score 75. + } + else if (walkCounter <= 1) { + // Enemies collect nuggets when falling. + if (!nuggets) { + collectNugget(); + } + } + + if ((*playfield)[x][y]->whatIam()==BRICK) { // sollte er aber in einem Brick + dieAndReappear(); // sein, dann stirbt er wohl + return; // Must leave "fallTimeDone" when an enemy dies. + } + + if (standOnEnemy()) { // Don't fall into a friend. + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); + return; + } + + if (walkCounter++ < 4){ + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); + { rely+=STEP; absy+=STEP;} + } + else { + rely = 0; y ++; absy=16*y; + if ((*playfield)[x][y]->whatIam() == USEDHOLE) { + captiveCounter = 0; + status = CAPTIVE; + captiveTimer->start((CAPTIVEDELAY * NSPEED) / speed, TRUE); + } + else if (!(canStand()||hangAtPole())) { + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); + walkCounter=1; + } + else { + status = STANDING; + if (hangAtPole()) + actualPixmap=(direction ==RIGHT)?8:12; + } + } + if (status == STANDING) { + status = WALKING; + walkCounter = 1; + direction = searchbestway(x,y,herox,heroy); + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + startWalk (); + if (!nuggets) + collectNugget(); + else + dropNugget(); + } + showFigure(); +} + +void KGrEnemy::captiveTimeDone() +{ + if (KGrObject::frozen) {captiveFrozen = TRUE; return; } + if ((*playfield)[x][y]->whatIam()==BRICK) + dieAndReappear(); + else + if (captiveCounter > 6){ + status = WALKING; + walkCounter = 1; + direction = UP; + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + captiveCounter = 0; + } else { + captiveCounter ++; + captiveTimer->start((CAPTIVEDELAY * NSPEED) / speed, TRUE); + showFigure(); + } +} + +void KGrEnemy::startSearching() +{ + // Called from "KGoldrunner::startPlaying" and "KGrEnemy::dieAndReappear". + init(x,y); + + if (canStand()||((*playfield)[x][y+1]->whatIam()==USEDHOLE)) { + status = WALKING; + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + } + else { + status = FALLING; + fallTimer->start ((FALLDELAY * NSPEED) / speed, TRUE); + } + + walkCounter = 1; + direction = searchbestway(x,y,herox,heroy); + startWalk(); +} + +void KGrEnemy::collectNugget() +{ + if (((*playfield)[x][y]->whatIam() == NUGGET) && (nuggets == 0) && + (alwaysCollectNugget || ((int)(5.0*rand()/RAND_MAX+1.0) > 4))){ + ((KGrFree *)(*playfield)[x][y])->setNugget(false); + nuggets=1; + } +} + +void KGrEnemy::dropNugget() +{ + if (((int)(DROPNUGGETDELAY*rand()/RAND_MAX+1.0) > DROPNUGGETDELAY-5) && + ((*playfield)[x][y]->whatIam() == FREE)) { + ((KGrFree *)(*playfield)[x][y])->setNugget(true); + nuggets=0; + } +} + +void KGrEnemy::showFigure () +{ + enemyView->moveEnemy (enemyId, absx, absy, actualPixmap, nuggets); + + // Merke alte Werte zum löschen der Figur + mem_x = x; + mem_y = y; + mem_relx = relx; + mem_rely = rely; +} + +bool KGrEnemy::canWalkUp() +{ + return (((*playfield)[x][y-1]->whatIam() != BRICK) && + ((*playfield)[x][y-1]->whatIam() != BETON) && + ((*playfield)[x][y-1]->whatIam() != FBRICK) && + (((*playfield)[x][y]->whatIam() == USEDHOLE) || + ((*playfield)[x][y]->whatIam() == LADDER))); +} + +void KGrEnemy::startWalk () +{ + switch (direction) { + case UP: break; + case RIGHT: if (hangAtPole()) + actualPixmap = RIGHTCLIMB1; + else + actualPixmap = RIGHTWALK1; + break; + case DOWN: break; + case LEFT: if (hangAtPole()) + actualPixmap = LEFTCLIMB1; + else + actualPixmap = LEFTWALK1; + break; + default: break; + } +} + +void KGrEnemy::dieAndReappear() +{ + bool looking; + int i; + + if (nuggets > 0) { + nuggets = 0; // Enemy died and could not drop nugget. + emit lostNugget(); // NO SCORE for lost nugget. + } + emit killed (75); // Killed an enemy: score 75. + + if (reappearAtTop) { + // Follow Traditional rules: enemies reappear at top. + looking = TRUE; + y = 2; + // Randomly look for a free spot in row 2. Limit the number of tries. + for (i = 1; ((i <= 3) && looking); i++) { + x = (int)((FIELDWIDTH * (float) rand()) / RAND_MAX) + 1; + switch ((*playfield)[x][2]->whatIam()) { + case FREE: + case HLADDER: + looking = FALSE; + break; + default: + break; + } + } + // If unsuccessful, choose the first free spot in row 3 or below. + while ((ywhatIam()) { + case FREE: + case HLADDER: + looking = FALSE; + break; + default: + break; + } + } + } + } + else { + // Follow KGoldrunner rules: enemies reappear where they started. + x = birthX; + y = birthY; + } + + // Enemy reappears and starts searching for the hero. + startSearching(); +} + +Direction KGrEnemy::searchbestway(int ew,int eh,int hw,int hh) +{ + Direction dirn; + + if ((*playfield)[x][y]->whatIam() == USEDHOLE) // Could not get out of + return UP; // hole (eg. brick above + // closed): keep trying. + + if (!canStand() && // Cannot stand, + !hangAtPole() && // not on pole, not + !standOnEnemy() && // walking on friend and + !((*playfield)[x][y+1]->whatIam() == HOLE)) // not just out of hole, + return DOWN; // so must fall. + + switch (searchStrategy) { + + // Traditional search strategy. + case LOW: + dirn = STAND; + if (eh == hh) { // Hero on same row. + dirn = lowGetHero (ew, eh, hw); + } + if (dirn != STAND) return (dirn); // Can go towards him. + + if (eh >= hh) { // Hero above enemy. + dirn = lowSearchUp (ew, eh, hh); // Find a way up. + } + else { // Hero below enemy. + dirn = lowSearchDown (ew, eh, hh); // Find way down to him. + if (dirn == STAND) { + dirn = lowSearchUp (ew, eh, hh); // No go: try up. + } + } + + if (dirn == STAND) { // When all else fails, + dirn = lowSearchDown (ew, eh, eh - 1); // find way below hero. + } + return dirn; + break; + + // KGoldrunner search strategy. + case MEDIUM: + case HIGH: + if(searchStatus==VERTIKAL){ + if (eh > hh) + return searchupway(ew,eh); + if (eh < hh) + return searchdownway(ew,eh); + return STAND; + } else { + if (ew > hw) + return searchleftway(ew,eh); + if (ew < hw) + return searchrightway(ew,eh); + return STAND; + } + break; + } + return STAND; +} + +/////////////////////////////////////////////// +// Methods for medium-level search strategy. // +/////////////////////////////////////////////// + +Direction KGrEnemy :: searchdownway(int ew,int eh){ +int i,j; + i=j=ew; + if ( (*playfield)[ew][eh]->searchValue & CANWALKDOWN) + return DOWN; + else while((i>=0)||(j<=FIELDWIDTH)){ + if (i>=0) + if ( (*playfield)[i][eh]->searchValue & CANWALKDOWN) + return LEFT; + else + if (!(( (*playfield)[i--][eh]->searchValue & CANWALKLEFT) || + (runThruHole && ( (*playfield)[i][eh]->whatIam() == HOLE)))) + i=-1; + if (j<=FIELDWIDTH) + if ( (*playfield)[j][eh]->searchValue & CANWALKDOWN) + return RIGHT; + else + if (!(( (*playfield)[j++][eh]->searchValue & CANWALKRIGHT) || + (runThruHole && ( (*playfield)[j][eh]->whatIam() == HOLE)))) + j=FIELDWIDTH+1; + } + return STAND; +} + +Direction KGrEnemy :: searchupway(int ew,int eh){ +int i,j; + i=j=ew; + if ( (*playfield)[ew][eh]->searchValue & CANWALKUP) + return UP; + else while((i>=0)||(j<=FIELDWIDTH)){// search for the first way up + if (i>=0) + if ( (*playfield)[i][eh]->searchValue & CANWALKUP) + return LEFT; + else + if (!(( (*playfield)[i--][eh]->searchValue & CANWALKLEFT) || + (runThruHole && ( (*playfield)[i][eh]->whatIam() == HOLE)))) + i=-1; + if (j<=FIELDWIDTH) + if ( (*playfield)[j][eh]->searchValue & CANWALKUP) + return RIGHT; + else + if (!(( (*playfield)[j++][eh]->searchValue & CANWALKRIGHT) || + (runThruHole && ( (*playfield)[j][eh]->whatIam() == HOLE)))) + j=FIELDWIDTH+1; + } + // BUG FIX - Ian W., 30/4/01 - Don't leave an enemy standing in mid air. + if (!canStand()) return DOWN; else return STAND; +} + +Direction KGrEnemy :: searchleftway(int ew,int eh){ +int i,j; + i=j=eh; + if ( ((*playfield)[ew][eh]->searchValue & CANWALKLEFT) || /* kann figur nach links laufen ?*/ + (runThruHole && ( (*playfield)[ew-1][eh]->whatIam() == HOLE))) + return LEFT; + else while((i>=0)||(j<=FIELDHEIGHT)){ /* in den grenzen ?*/ + if (i>=0) + if ( ((*playfield)[ew][i]->searchValue & CANWALKLEFT) || /* ein weg nach links- oben gefunden ?*/ + (runThruHole && ( (*playfield)[ew-1][i]->whatIam() == HOLE))) + return UP; /* geh nach oben */ + else + if (!( (*playfield)[ew][i--]->searchValue & CANWALKUP)) /* sonst ...*/ + i=-1; + if (j<=FIELDHEIGHT) + if ( ((*playfield)[ew][j]->searchValue & CANWALKLEFT) || /* ein weg nach links- unten gefunden ?*/ + (runThruHole && ( (*playfield)[ew-1][j]->whatIam() == HOLE))) + return DOWN; /* geh nach unten */ + else + if (!( (*playfield)[ew][j++]->searchValue&CANWALKDOWN)) /* sonst ... */ + j=FIELDHEIGHT+1; + } + return STAND; /* default */ +} + +Direction KGrEnemy :: searchrightway(int ew,int eh) +{ + int i,j; + i=j=eh; + if ( ((*playfield)[ew][eh]->searchValue & CANWALKRIGHT) || + (runThruHole && ( (*playfield)[ew+1][eh]->whatIam() == HOLE))) + return RIGHT; + else while((i>=0)||(j<=FIELDHEIGHT)){ + if (i>=0) + if ( ((*playfield)[ew][i]->searchValue & CANWALKRIGHT) || + (runThruHole && ( (*playfield)[ew+1][i]->whatIam() == HOLE))) + return UP; + else + if (!( (*playfield)[ew][i--]->searchValue & CANWALKUP)) + i=-1; + if (j<=FIELDHEIGHT) + if ( ((*playfield)[ew][j]->searchValue & CANWALKRIGHT) || + (runThruHole && ( (*playfield)[ew+1][j]->whatIam() == HOLE))) + return DOWN; + else + if (!( (*playfield)[ew][j++]->searchValue & CANWALKDOWN)) + j=FIELDHEIGHT+1; + } + return STAND; +} + +//////////////////////////////////////////// +// Methods for low-level search strategy. // +//////////////////////////////////////////// + +Direction KGrEnemy::lowSearchUp (int ew, int eh, int hh) +{ + int i, ilen, ipos, j, jlen, jpos, deltah, rungs; + + deltah = eh - hh; // Get distance up to hero's level. + + // Search for the best ladder right here or on the left. + i = ew; ilen = 0; ipos = -1; + while (i >= 1) { + rungs = distanceUp (i, eh, deltah); + if (rungs > ilen) { + ilen = rungs; // This the best yet. + ipos = i; + } + if (searchOK (-1, i, eh)) + i--; // Look further to the left. + else + i = -1; // Cannot go any further to the left. + } + + // Search for the best ladder on the right. + j = ew; jlen = 0; jpos = -1; + while (j < FIELDWIDTH) { + if (searchOK (+1, j, eh)) { + j++; // Look further to the right. + rungs = distanceUp (j, eh, deltah); + if (rungs > jlen) { + jlen = rungs; // This the best yet. + jpos = j; + } + } + else + j = FIELDWIDTH+1; // Cannot go any further to the right. + } + + if ((ilen == 0) && (jlen == 0)) // No ladder found. + return STAND; + + // Choose a ladder to go to. + if (ilen != jlen) { // If the ladders are not the same + // length, choose the longer one. + if (ilen > jlen) { + if (ipos == ew) // If already on the best ladder, go up. + return UP; + else + return LEFT; + } + else + return RIGHT; + } + else { // Both ladders are the same length. + + if (ipos == ew) // If already on the best ladder, go up. + return UP; + else if (ilen == deltah) { // If both reach the hero's level, + if ((ew - ipos) <= (jpos - ew)) // choose the closest. + return LEFT; + else + return RIGHT; + } + else return LEFT; // Else choose the left ladder. + } +} + +Direction KGrEnemy::lowSearchDown (int ew, int eh, int hh) +{ + int i, ilen, ipos, j, jlen, jpos, deltah, rungs, path; + + deltah = hh - eh; // Get distance down to hero's level. + + // Search for the best way down, right here or on the left. + ilen = 0; ipos = -1; + i = (willNotFall (ew, eh)) ? ew : -1; + rungs = distanceDown (ew, eh, deltah); + if (rungs > 0) { + ilen = rungs; ipos = ew; + } + + while (i >= 1) { + rungs = distanceDown (i - 1, eh, deltah); + if (((rungs > 0) && (ilen == 0)) || + ((deltah > 0) && (rungs > ilen)) || + ((deltah <= 0) && (rungs < ilen) && (rungs != 0))) { + ilen = rungs; // This the best way yet. + ipos = i - 1; + } + if (searchOK (-1, i, eh)) + i--; // Look further to the left. + else + i = -1; // Cannot go any further to the left. + } + + // Search for the best way down, on the right. + j = ew; jlen = 0; jpos = -1; + while (j < FIELDWIDTH) { + rungs = distanceDown (j + 1, eh, deltah); + if (((rungs > 0) && (jlen == 0)) || + ((deltah > 0) && (rungs > jlen)) || + ((deltah <= 0) && (rungs < jlen) && (rungs != 0))) { + jlen = rungs; // This the best way yet. + jpos = j + 1; + } + if (searchOK (+1, j, eh)) { + j++; // Look further to the right. + } + else + j = FIELDWIDTH+1; // Cannot go any further to the right. + } + + if ((ilen == 0) && (jlen == 0)) // Found no way down. + return STAND; + + // Choose a way down to follow. + if (ilen == 0) + path = jpos; + else if (jlen == 0) + path = ipos; + else if (ilen != jlen) { // If the ways down are not same length, + // choose closest to hero's level. + if (deltah > 0) { + if (jlen > ilen) + path = jpos; + else + path = ipos; + } + else { + if (jlen > ilen) + path = ipos; + else + path = jpos; + } + } + else { // Both ways down are the same length. + if ((deltah > 0) && // If both reach the hero's level, + (ilen == deltah)) { // choose the closest. + if ((ew - ipos) <= (jpos - ew)) + path = ipos; + else + path = jpos; + } + else + path = ipos; // Else, go left or down. + } + + if (path == ew) + return DOWN; + else if (path < ew) + return LEFT; + else + return RIGHT; +} + +Direction KGrEnemy::lowGetHero (int ew, int eh, int hw) +{ + int i, inc, returnValue; + + inc = (ew > hw) ? -1 : +1; + i = ew; + while (i != hw) { + returnValue = canWalkLR (inc, i, eh); + if (returnValue > 0) + i = i + inc; // Can run further towards the hero. + else if (returnValue < 0) + break; // Will run into a wall regardless. + else + return STAND; // Won't run over a hole. + } + + if (i < ew) return LEFT; + else if (i > ew) return RIGHT; + else return STAND; +} + +int KGrEnemy::distanceUp (int x, int y, int deltah) +{ + int rungs = 0; + + // If there is a ladder at (x.y), return its length, else return zero. + while ( (*playfield)[x][y - rungs]->whatIam() == LADDER) { + rungs++; + if (rungs >= deltah) // To hero's level is enough. + break; + } + return rungs; +} + +int KGrEnemy::distanceDown (int x, int y, int deltah) +{ + // When deltah > 0, we want an exit sideways at the hero's level or above. + // When deltah <= 0, we can go down any distance (as a last resort). + + int rungs = -1; + int exitRung = 0; + bool canGoThru = TRUE; + char objType; + + // If there is a way down at (x,y), return its length, else return zero. + // Because rungs == -1, we first check that level y is not blocked here. + while (canGoThru) { + objType = (*playfield)[x][y + rungs + 1]->whatIam(); + switch (objType) { + case BRICK: + case BETON: + case HOLE: // Enemy cannot go DOWN through a hole. + case USEDHOLE: + if ((deltah > 0) && (rungs <= deltah)) + exitRung = rungs; + if ((objType == HOLE) && (rungs < 0)) + rungs = 0; // Enemy can go SIDEWAYS through a hole. + else + canGoThru = FALSE; // Cannot go through here. + break; + case LADDER: + case POLE: // Can go through or stop. + rungs++; // Add to path length. + if ((deltah > 0) && (rungs >= 0)) { + // If at or above hero's level, check for an exit from ladder. + if ((rungs - 1) <= deltah) { + // Maybe can stand on top of ladder and exit L or R. + if ((objType == LADDER) && (searchOK (-1, x, y+rungs-1) || + searchOK (+1, x, y+rungs-1))) + exitRung = rungs - 1; + // Maybe can exit L or R from ladder body or pole. + if ((rungs <= deltah) && (searchOK (-1, x, y+rungs) || + searchOK (+1, x, y+rungs))) + exitRung = rungs; + } + else + canGoThru = FALSE; // Should stop at hero's level. + } + break; + default: + rungs++; // Can go through. Add to path length. + break; + } + } + if (rungs == 1) { + for (KGrEnemy *enemy=enemies->first();enemy!=0;enemy=enemies->next()) { + if((x*16==enemy->getx()) && (y*16+16==enemy->gety())) + rungs = 0; // Pit is blocked. Find another way. + } + } + if (rungs <= 0) + return 0; // There is no way down. + else if (deltah > 0) + return exitRung; // We want to take an exit, if any. + else + return rungs; // We can go down all the way. +} + +bool KGrEnemy::searchOK (int direction, int x, int y) +{ + // Check whether it is OK to search left or right. + if (canWalkLR (direction, x, y) > 0) { + // Can go into that cell, but check for a fall. + if (willNotFall (x+direction, y)) + return TRUE; + } + return FALSE; // Cannot go into and through that cell. +} + +int KGrEnemy::canWalkLR (int direction, int x, int y) +{ + if (willNotFall (x, y)) { + switch ((*playfield)[x+direction][y]->whatIam()) { + case BETON: + case BRICK: + case USEDHOLE: + return -1; break; // Will be halted in current cell. + default: + // NB. FREE, LADDER, HLADDER, NUGGET and POLE are OK of course, + // but enemies can also walk left/right through a HOLE and + // THINK they can walk left/right through a FBRICK. + + return +1; break; // Can walk into next cell. + } + } + else + return 0; // Will fall before getting there. +} + +bool KGrEnemy::willNotFall (int x, int y) +{ + int c, cmax; + KGrEnemy *enemy; + + // Check the ceiling. + switch ( (*playfield)[x][y]->whatIam()) { + case LADDER: + case POLE: + return TRUE; break; // OK, can hang on ladder or pole. + default: + break; + } + + // Check the floor. + switch ( (*playfield)[x][y+1]->whatIam()) { + + // Cases where the enemy knows he will fall. + case FREE: + case HLADDER: + case FBRICK: + + // N.B. The enemy THINKS he can run over a NUGGET, a buried POLE or a HOLE. + // The last of these cases allows the hero to trap the enemy, of course. + + // Note that there are several Traditional levels that require an enemy to + // be trapped permanently in a pit containing a nugget, as he runs towards + // you. It is also possible to use a buried POLE in the same way. + + cmax = enemies->count(); + for (c = 0; c < cmax; c++) { + enemy = enemies->at(c); + if ((enemy->getx()==16*x) && (enemy->gety()==16*(y+1))) + return TRUE; // Standing on a friend. + } + return FALSE; break; // Will fall: there is no floor. + + default: + return TRUE; break; // OK, will not fall. + } +} + +#ifdef QT3 +void KGrEnemy::setEnemyList(QPtrList *e) +#else +void KGrEnemy::setEnemyList(QList *e) +#endif +{ + enemies = e; +} + +bool KGrEnemy::standOnEnemy() +{ + int c = 0; + int range = enemies->count(); + if (range > 1) { + for (KGrEnemy * enemy = enemies->at (c); c < range; ) { + enemy = enemies->at(c++); + // Test if enemy's foot is at or just below enemy's head (tolerance + // of 4 pixels) and the two figures overlap in the X direction. + if ((((absy + 16) == enemy->gety()) || + ((absy + 12) == enemy->gety())) && + (((absx - 16) < enemy->getx()) && + ((absx + 16) > enemy->getx()))) { + return true; + } + } + } + return false; +} + +bool KGrEnemy::bumpingFriend() +{ +// Enemies that are falling are treated as being invisible (i.e. a walking +// enemy will walk through them or they will stop on that enemy's head). +// +// If two enemies are moving in opposite directions, they are allowed to come +// within two cell widths of each other (8 steps). Then one must stop before +// entering the next cell and let the other one go into it. If both are about +// to enter a new cell, the one on the right or above gives way to the one on +// the left or below (implemented by letting the latter approach to 7 steps +// apart before detecting an impending collision, by which time the first +// enemy should have stopped and given way). +// +// In all other cases, enemies are allowed to approach to 4 steps apart (i.e. +// bumping a friend), before being forced to stop and wait. If they somehow +// get closer than 4 steps (i.e. overlapping), the lower ID enemy is allowed +// to move out while the higher ID enemy waits. This can happen if one enemy +// falls into another or is reborn where another enemy is located. + + int c, cmax; + KGrEnemy *enemy; + int dx, dy; + + cmax = enemies->count(); + for (c = 0; c < cmax; c++) { + enemy = enemies->at(c); + if ((enemy->enemyId != enemyId) && (enemy->status != FALLING)) { + dx = enemy->getx() - absx; + dy = enemy->gety() - absy; + switch (direction) { + case LEFT: + if ((dx >= -32) && (dx < 16) && (dy > -16) && (dy < 16)) { + if ((enemy->direction == RIGHT) && + (enemy->status == WALKING) && (absx%16==0)) { + return TRUE; + } + else if (dx >= -16) { + if ((dx > -16) && (enemyId < enemy->enemyId)) + return FALSE; + else + return TRUE; // Wait for the one in front. + } + } + break; + case RIGHT: + if ((dx > -16) && (dx < 32) && (dy > -16) && (dy < 16)) { + if ((enemy->direction == LEFT) && + (enemy->status == WALKING) && (absx%16==0)) { + return TRUE; + } + else if (dx <= 16) { + if ((dx < 16) && (enemyId < enemy->enemyId)) + return FALSE; + else + return TRUE; // Wait for the one in front. + } + } + break; + case UP: + if ((dy >= -32) && (dy < 16) && (dx > -16) && (dx < 16)) { + if ((enemy->direction == DOWN) && + (enemy->status == WALKING) && (absy%16==0)) { + return TRUE; + } + else if (dy >= -16) { + if ((dy > -16) && (enemyId < enemy->enemyId)) + return FALSE; + else + return TRUE; // Wait for the one above. + } + } + break; + case DOWN: + if ((dy > -16) && (dy < 32) && (dx > -16) && (dx < 16)) { + if ((enemy->direction == UP) && + (enemy->status == WALKING) && (absy%16==0)) { + return TRUE; + } + else if (dy <= 16) { + if ((dy < 16) && (enemyId < enemy->enemyId)) + return FALSE; + else + return TRUE; // Wait for the one below. + } + } + break; + default: + break; + } + } + } + return FALSE; +} + +KGrEnemy :: ~KGrEnemy () +{ + delete captiveTimer; + delete walkTimer; + delete fallTimer; +} + +#include "kgrfigure.moc" diff --git a/kgoldrunner/src/kgrfigure.h b/kgoldrunner/src/kgrfigure.h new file mode 100644 index 00000000..e6ee0f2e --- /dev/null +++ b/kgoldrunner/src/kgrfigure.h @@ -0,0 +1,231 @@ +/*************************************************************************** + * kgrfigure.h - description * + * ------------------- * + * Copyright (C) 2003 by Ian Wadham and Marco Krüger * + * email : See menu "Help, About KGoldrunner" * + * ianw2@optusnet.com.au * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifndef KGRFIGURE_H +#define KGRFIGURE_H + +// Obsolete - #include +#include + +#include +#ifdef QT3 +#include +#else +#include +#endif +#include +#include +#include +#include +#include // für Zufallsfunktionen + +class KGrCanvas; +class KGrObject; +class KGrEnemy; + +class KGrFigure : public QObject +{ + Q_OBJECT +public: + KGrFigure (int, int); + virtual ~KGrFigure(); + + // STATIC GLOBAL FLAGS. + static bool variableTiming; // More enemies imply less speed. + static bool alwaysCollectNugget; // Enemies always collect nuggets. + static bool runThruHole; // Enemy can run L/R through dug hole. + static bool reappearAtTop; // Enemies are reborn at top of screen. + static SearchStrategy searchStrategy; // Low, medium or high difficulty. + + static Timing fixedTiming; // Original set of 6 KGr timing values. + + static Timing varTiming [6]; // Optional 6 sets of timing values, + // dependent on number of enemies. + int getx(); + int gety(); + Status getStatus(); + + int getnuggets(); + void setNuggets(int n); + void setPlayfield(KGrObject *(*p)[30][22]); + void showFigure(); //zeigt Figur + virtual void init(int,int); + void eraseOldFigure(); + +protected: + // STATIC GLOBAL VARIABLES. + static int herox; + static int heroy; + + static int speed; // Adjustable game-speed factor. + + int x, y; + int absx, absy; + int relx, rely; // Figur wird um relx,rely Pixel verschoben + int mem_x,mem_y,mem_relx,mem_rely; + int walkCounter; + int nuggets; + int actualPixmap; // ArrayPos der zu zeichnenden Pixmap + QTimer *walkTimer; + QTimer *fallTimer; + + KGrObject *(*playfield)[30][22]; + Status status; + Direction direction; + bool canWalkRight(); + bool canWalkLeft(); + virtual bool canWalkUp(); + bool canWalkDown(); + bool canStand(); + bool hangAtPole(); + virtual bool standOnEnemy()=0; + void walkUp(int); + void walkDown(int, int); + void walkRight(int, int); + void walkLeft(int, int); + void initFall(int, int); + + bool walkFrozen; + bool fallFrozen; +}; + +class KGrHero : public KGrFigure +{ + Q_OBJECT +public: + KGrHero(KGrCanvas *, int , int); + virtual ~KGrHero(); + bool started; + void showFigure(); + void dig(); + void digLeft(); + void digRight(); + void startWalk(); +#ifdef QT3 + void setEnemyList(QPtrList *); +#else + void setEnemyList(QList *); +#endif + void init(int,int); + void setKey(Direction); + void setDirection(int, int); + void start(); + void loseNugget(); + static int WALKDELAY; + static int FALLDELAY; + void setSpeed(int); + void doStep(); + void showState (char); + +private: +#ifdef QT3 + QPtrList *enemies; +#else + QList *enemies; +#endif + KGrCanvas * heroView; + bool standOnEnemy(); + bool isInEnemy(); + bool isInside(int, int); + + Direction nextDir; + void collectNugget(); + + bool mouseMode; + bool stopped; + int mousex; + int mousey; + void setNextDir(); + +public slots: + void walkTimeDone(); + void fallTimeDone(); + +signals: + void gotNugget(int); + void haveAllNuggets(); + void leaveLevel(); + void caughtHero(); +}; + +class KGrEnemy : public KGrFigure +{ + Q_OBJECT +public: + KGrEnemy (KGrCanvas *, int , int); + virtual ~KGrEnemy(); + void showFigure(); + void startSearching(); +#ifdef QT3 + void setEnemyList(QPtrList *); +#else + void setEnemyList(QList *); +#endif + virtual void init(int,int); + static int WALKDELAY; + static int FALLDELAY; + static int CAPTIVEDELAY; + int enemyId; + void doStep(); + void showState (char); + +private: + KGrCanvas * enemyView; + int birthX, birthY; + int searchStatus; + int captiveCounter; + QTimer *captiveTimer; + bool canWalkUp(); +#ifdef QT3 + QPtrList *enemies; +#else + QList *enemies; +#endif + bool standOnEnemy(); + bool bumpingFriend(); + + void startWalk(); + void dieAndReappear(); + Direction searchbestway(int,int,int,int); + Direction searchdownway(int,int); + Direction searchupway(int,int); + Direction searchleftway(int,int); + Direction searchrightway(int,int); + + Direction lowSearchUp(int,int,int); + Direction lowSearchDown(int,int,int); + Direction lowGetHero(int,int,int); + + int distanceUp (int, int, int); + int distanceDown (int, int, int); + bool searchOK (int, int, int); + int canWalkLR (int, int, int); + bool willNotFall (int, int); + + void collectNugget(); + void dropNugget(); + + bool captiveFrozen; + +public slots: + void walkTimeDone(); + void fallTimeDone(); + void captiveTimeDone(); + +signals: + void lostNugget(); + void trapped(int); + void killed(int); +}; + +#endif // KGRFIGURE_H diff --git a/kgoldrunner/src/kgrgame.cpp b/kgoldrunner/src/kgrgame.cpp new file mode 100644 index 00000000..93164d88 --- /dev/null +++ b/kgoldrunner/src/kgrgame.cpp @@ -0,0 +1,2613 @@ +/*************************************************************************** + * Copyright (C) 2003 by Ian Wadham and Marco Krüger * + * ianw2@optusnet.com.au * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifdef KGR_PORTABLE +// If compiling for portability, redefine KDE's i18n. +#define i18n tr +#endif + +#include "kgrconsts.h" +#include "kgrobject.h" +#include "kgrfigure.h" +#include "kgrcanvas.h" +#include "kgrdialog.h" + +#include "kgrgame.h" + +// Obsolete - #include +#include +#include +#include + +#include +#include + +#ifndef KGR_PORTABLE +#include +#endif + +/******************************************************************************/ +/*********************** KGOLDRUNNER GAME CLASS *************************/ +/******************************************************************************/ + +KGrGame::KGrGame (KGrCanvas * theView, QString theSystemDir, QString theUserDir) +{ + view = theView; + systemDataDir = theSystemDir; + userDataDir = theUserDir; + + // Set the game-editor OFF, but available. + editMode = FALSE; + paintEditObj = FALSE; + editObj = BRICK; + shouldSave = FALSE; + + enemies.setAutoDelete(TRUE); + + hero = new KGrHero (view, 0, 0); // The hero is born ... Yay !!! + hero->setPlayfield (&playfield); + + setBlankLevel (TRUE); // Fill the playfield with blank walls. + + enemy = NULL; + newLevel = TRUE; // Next level will be a new one. + loading = TRUE; // Stop input until it is loaded. + + modalFreeze = FALSE; + messageFreeze = FALSE; + + connect (hero, SIGNAL (gotNugget(int)), SLOT (incScore(int))); + connect (hero, SIGNAL (caughtHero()), SLOT (herosDead())); + connect (hero, SIGNAL (haveAllNuggets()), SLOT (showHiddenLadders())); + connect (hero, SIGNAL (leaveLevel()), SLOT (goUpOneLevel())); + + dyingTimer = new QTimer (this); + connect (dyingTimer, SIGNAL (timeout()), SLOT (finalBreath())); + + // Get the mouse position every 40 msec. It is used to steer the hero. + mouseSampler = new QTimer (this); + connect (mouseSampler, SIGNAL(timeout()), SLOT (readMousePos ())); + mouseSampler->start (40, FALSE); + + srand(1); // initialisiere Random-Generator +} + +KGrGame::~KGrGame() +{ +} + +/******************************************************************************/ +/************************* GAME SELECTION PROCEDURES ************************/ +/******************************************************************************/ + +void KGrGame::startLevelOne() +{ + startLevel (SL_START, 1); +} + +void KGrGame::startAnyLevel() +{ + startLevel (SL_ANY, level); +} + +void KGrGame::startNextLevel() +{ + startLevel (SL_ANY, level + 1); +} + +void KGrGame::startLevel (int startingAt, int requestedLevel) +{ + if (! saveOK (FALSE)) { // Check unsaved work. + return; + } + // Use dialog box to select game and level: startingAt = ID_FIRST or ID_ANY. + int selectedLevel = selectLevel (startingAt, requestedLevel); + if (selectedLevel > 0) { // If OK, start the selected game and level. + newGame (selectedLevel, selectedGame); + } else { + level = 0; + } +} + +/******************************************************************************/ +/************************ MAIN GAME EVENT PROCEDURES ************************/ +/******************************************************************************/ + +void KGrGame::incScore (int n) +{ + score = score + n; // SCORING: trap enemy 75, kill enemy 75, + emit showScore (score); // collect gold 250, complete the level 1500. +} + +void KGrGame::herosDead() +{ + if ((level < 1) || (lives <= 0)) + return; // Game over: we are in the "ENDE" screen. + + // Lose a life. + if (--lives > 0) { + // Still some life left, so PAUSE and then re-start the level. + emit showLives (lives); + KGrObject::frozen = TRUE; // Freeze the animation and let + dyingTimer->start (1500, TRUE); // the player see what happened. + } + else { + // Game over: display the "ENDE" screen. + emit showLives (lives); + freeze(); + QString gameOver = "" + i18n("GAME OVER !!!") + ""; + KGrMessage::information (view, collection->name, gameOver); + checkHighScore(); // Check if there is a high score for this game. + + enemyCount = 0; + enemies.clear(); // Stop the enemies catching the hero again ... + view->deleteEnemySprites(); + unfreeze(); // ... NOW we can unfreeze. + newLevel = TRUE; + level = 0; + loadLevel (level); // Display the "ENDE" screen. + newLevel = FALSE; + } +} + +void KGrGame::finalBreath() +{ + // Fix bug 95202: Avoid re-starting if the player selected + // edit mode before the 1.5 seconds were up. + if (! editMode) { + enemyCount = 0; // Hero is dead: re-start the level. + loadLevel (level); + } + KGrObject::frozen = FALSE; // Unfreeze the game, but don't move yet. +} + +void KGrGame::showHiddenLadders() +{ + int i,j; + for (i=1;i<21;i++) + for (j=1;j<29;j++) + if (playfield[j][i]->whatIam()==HLADDER) + ((KGrHladder *)playfield[j][i])->showLadder(); + view->updateCanvas(); + initSearchMatrix(); +} + +void KGrGame::goUpOneLevel() +{ + lives++; // Level completed: gain another life. + emit showLives (lives); + incScore (1500); + + if (level >= collection->nLevels) { + freeze(); + KGrMessage::information (view, collection->name, + i18n("CONGRATULATIONS !!!!" + "

You have conquered the last level in the %1 game !!

") + .arg("\"" + collection->name + "\"")); + checkHighScore(); // Check if there is a high score for this game. + + unfreeze(); + level = 0; // Game completed: display the "ENDE" screen. + } + else { + level++; // Go up one level. + emit showLevel (level); + } + + enemyCount = 0; + enemies.clear(); + view->deleteEnemySprites(); + newLevel = TRUE; + loadLevel (level); + newLevel = FALSE; +} + +void KGrGame::loseNugget() +{ + hero->loseNugget(); // Enemy trapped/dead and holding a nugget. +} + +KGrHero * KGrGame::getHero() +{ + return (hero); // Return a pointer to the hero. +} + +int KGrGame::getLevel() // Return the current game-level. +{ + return (level); +} + +bool KGrGame::inMouseMode() +{ + return (mouseMode); // Return TRUE if game is under mouse control. +} + +bool KGrGame::inEditMode() +{ + return (editMode); // Return TRUE if the game-editor is active. +} + +bool KGrGame::isLoading() +{ + return (loading); // Return TRUE if a level is being loaded. +} + +void KGrGame::setMouseMode (bool on_off) +{ + mouseMode = on_off; // Set Mouse OR keyboard control. +} + +void KGrGame::freeze() +{ + if ((! modalFreeze) && (! messageFreeze)) { + emit gameFreeze (TRUE); // Do visual feedback in the GUI. + } + KGrObject::frozen = TRUE; // Halt the game, by blocking all timer events. +} + +void KGrGame::unfreeze() +{ + if ((! modalFreeze) && (! messageFreeze)) { + emit gameFreeze (FALSE);// Do visual feedback in the GUI. + } + KGrObject::frozen = FALSE; // Restart the game. Because frozen == FALSE, + restart(); // the game goes on running after the next step. +} + +void KGrGame::setMessageFreeze (bool on_off) +{ + if (on_off) { // Freeze the game action during a message. + messageFreeze = FALSE; + if (! KGrObject::frozen) { + messageFreeze = TRUE; + freeze(); + } + } + else { // Unfreeze the game action after a message. + if (messageFreeze) { + unfreeze(); + messageFreeze = FALSE; + } + } +} + +void KGrGame::setBlankLevel(bool playable) +{ + for (int j=0;j<20;j++) + for (int i=0;i<28;i++) { + if (playable) { + //playfield[i+1][j+1] = new KGrFree (freebg, nuggetbg, false, view); + playfield[i+1][j+1] = new KGrFree (FREE,i+1,j+1,view); + } + else { + //playfield[i+1][j+1] = new KGrEditable (freebg, view); + playfield[i+1][j+1] = new KGrEditable (FREE); + view->paintCell (i+1, j+1, FREE); + } + editObjArray[i+1][j+1] = FREE; + } + for (int j=0;j<30;j++) { + //playfield[j][0]=new KGrBeton(QPixmap ()); + playfield[j][0]=new KGrObject (BETON); + editObjArray[j][0] = BETON; + //playfield[j][21]=new KGrBeton(QPixmap ()); + playfield[j][21]=new KGrObject (BETON); + editObjArray[j][21] = BETON; + } + for (int i=0;i<22;i++) { + //playfield[0][i]=new KGrBeton(QPixmap ()); + playfield[0][i]=new KGrObject (BETON); + editObjArray[0][i] = BETON; + //playfield[29][i]=new KGrBeton(QPixmap ()); + playfield[29][i]=new KGrObject (BETON); + editObjArray[29][i] = BETON; + } + //for (int j=0;j<22;j++) + //for (int i=0;i<30;i++) { + //playfield[i][j]->move(16+i*16,16+j*16); + //} +} + +void KGrGame::newGame (const int lev, const int gameIndex) +{ + // Ignore player input from keyboard or mouse while the screen is set up. + loading = TRUE; // "loadLevel (level)" will reset it. + + if (editMode) { + emit setEditMenu (FALSE); // Disable edit menu items and toolbar. + + editMode = FALSE; + paintEditObj = FALSE; + editObj = BRICK; + + view->setHeroVisible (TRUE); + } + + newLevel = TRUE; + level = lev; + collnIndex = gameIndex; + collection = collections.at (collnIndex); + owner = collection->owner; + + lives = 5; // Start with 5 lives. + score = 0; + startScore = 0; + + emit showLives (lives); + emit showScore (score); + emit showLevel (level); + + enemyCount = 0; + enemies.clear(); + view->deleteEnemySprites(); + + newLevel = TRUE;; + loadLevel (level); + newLevel = FALSE; +} + +void KGrGame::startTutorial() +{ + if (! saveOK (FALSE)) { // Check unsaved work. + return; + } + + int i, index; + int imax = collections.count(); + bool found = FALSE; + + index = 0; + for (i = 0; i < imax; i++) { + index = i; // Index within owner. + if (collections.at(i)->prefix == "tute") { + found = TRUE; + break; + } + } + if (found) { + // Start the tutorial. + collection = collections.at (index); + owner = collection->owner; + emit markRuleType (collection->settings); + collnIndex = index; + level = 1; + newGame (level, collnIndex); + } + else { + KGrMessage::information (view, i18n("Start Tutorial"), + i18n("Cannot find the tutorial game (file-prefix %1) in " + "the %2 files.") + .arg("'tute'").arg("'games.dat'")); + } +} + +void KGrGame::showHint() +{ + // Put out a hint for this level. + QString caption = i18n("Hint"); + + if (levelHint.length() > 0) + myMessage (view, caption, levelHint); + else + myMessage (view, caption, + i18n("Sorry, there is no hint for this level.")); +} + +int KGrGame::loadLevel (int levelNo) +{ + int i,j; + QFile openlevel; + + if (! openLevelFile (levelNo, openlevel)) { + return 0; + } + + // Ignore player input from keyboard or mouse while the screen is set up. + loading = TRUE; + + nuggets = 0; + enemyCount=0; + startScore = score; // What we will save, if asked. + + // lade den Level + for (j=1;j<21;j++) + for (i=1;i<29;i++) { + changeObject(openlevel.getch(),i,j); + } + + // Absorb a newline character, then read in the level name and hint (if any). + int c = openlevel.getch(); + levelName = ""; + levelHint = ""; + QCString levelNameC = ""; + QCString levelHintC = ""; + i = 1; + while ((c = openlevel.getch()) != EOF) { + switch (i) { + case 1: if (c == '\n') // Level name is on one line. + i = 2; + else + levelNameC += (char) c; + break; + + case 2: levelHintC += (char) c; // Hint is on rest of file. + break; + } + } + openlevel.close(); + + // If there is a name, recode any UTF-8 substrings and translate it right now. + if (levelNameC.length() > 0) + levelName = i18n((const char *) levelNameC); + + // Indicate on the menus whether there is a hint for this level. + int len = levelHintC.length(); + emit hintAvailable (len > 0); + + // If there is a hint, remove the final newline and translate it right now. + if (len > 0) + levelHint = i18n((const char *) levelHintC.left(len-1)); + + // Disconnect edit-mode slots from signals from "view". + disconnect (view, SIGNAL (mouseClick(int)), 0, 0); + disconnect (view, SIGNAL (mouseLetGo(int)), 0, 0); + + if (newLevel) { + hero->setEnemyList (&enemies); + for (enemy=enemies.first();enemy != 0; enemy = enemies.next()) + enemy->setEnemyList(&enemies); + } + + hero->setNuggets(nuggets); + setTimings(); + + // Set direction-flags to use during enemy searches. + initSearchMatrix(); + + // Re-draw the playfield frame, level title and figures. + view->setTitle (getTitle()); + view->updateCanvas(); + + // Check if this is a tutorial collection and we are not on the "ENDE" screen. + if ((collection->prefix.left(4) == "tute") && (levelNo != 0)) { + // At the start of a tutorial, put out an introduction. + if (levelNo == 1) + myMessage (view, collection->name, + i18n((const char *) collection->about.utf8())); + + // Put out an explanation of this level. + myMessage (view, getTitle(), levelHint); + } + + // Put the mouse pointer on the hero. + if (mouseMode) + view->setMousePos (startI, startJ); + + // Connect play-mode slot to signal from "view". + connect (view, SIGNAL(mouseClick(int)), SLOT(doDig(int))); + + // Re-enable player input. + loading = FALSE; + + return 1; +} + +bool KGrGame::openLevelFile (int levelNo, QFile & openlevel) +{ + QString filePath; + QString msg; + + filePath = getFilePath (owner, collection, levelNo); + + openlevel.setName (filePath); + + // gucken ob und welcher Level existiert + + if (! openlevel.exists()) { + KGrMessage::information (view, i18n("Load Level"), + i18n("Cannot find file '%1'. Please make sure '%2' has been " + "run in the '%3' folder.") + .arg(filePath).arg("tar xf levels.tar").arg(systemDataDir.myStr())); + return (FALSE); + } + + // öffne Level zum lesen + if (! openlevel.open (IO_ReadOnly)) { + KGrMessage::information (view, i18n("Load Level"), + i18n("Cannot open file '%1' for read-only.").arg(filePath)); + return (FALSE); + } + + return (TRUE); +} + +void KGrGame::changeObject (unsigned char kind, int i, int j) +{ + delete playfield[i][j]; + switch(kind) { + case FREE: createObject(new KGrFree (FREE,i,j,view),FREE,i,j);break; + case LADDER: createObject(new KGrObject (LADDER),LADDER,i,j);break; + case HLADDER: createObject(new KGrHladder (HLADDER,i,j,view),FREE,i,j);break; + case BRICK: createObject(new KGrBrick (BRICK,i,j,view),BRICK,i,j);break; + case BETON: createObject(new KGrObject (BETON),BETON,i,j);break; + case FBRICK: createObject(new KGrObject (FBRICK),BRICK,i,j);break; + case POLE: createObject(new KGrObject (POLE),POLE,i,j);break; + case NUGGET: createObject(new KGrFree (NUGGET,i,j,view),NUGGET,i,j); + nuggets++;break; + case HERO: createObject(new KGrFree (FREE,i,j,view),FREE,i,j); + hero->init(i,j); + startI = i; startJ = j; + hero->started = FALSE; + hero->showFigure(); + break; + case ENEMY: createObject(new KGrFree (FREE,i,j,view),FREE,i,j); + if (newLevel){ + // Starting a level for the first time. + enemy = new KGrEnemy (view, i, j); + enemy->setPlayfield(&playfield); + enemy->enemyId = enemyCount++; + enemies.append(enemy); + connect(enemy, SIGNAL(lostNugget()), SLOT(loseNugget())); + connect(enemy, SIGNAL(trapped(int)), SLOT(incScore(int))); + connect(enemy, SIGNAL(killed(int)), SLOT(incScore(int))); + } else { + // Starting a level again after losing. + enemy=enemies.at(enemyCount); + enemy->enemyId=enemyCount++; + enemy->setNuggets(0); + enemy->init(i,j); // Re-initialise the enemy's state information. + } + enemy->showFigure(); + break; + default : createObject(new KGrBrick(BRICK,i,j,view),BRICK,i,j);break; + } +} + +void KGrGame::createObject (KGrObject *o, char picType, int x, int y) +{ + playfield[x][y] = o; + view->paintCell (x, y, picType); // Pic maybe not same as object. +} + +void KGrGame::setTimings () +{ + Timing * timing; + int c = -1; + + if (KGrFigure::variableTiming) { + c = enemies.count(); // Timing based on enemy count. + c = (c > 5) ? 5 : c; + timing = &(KGrFigure::varTiming[c]); + } + else { + timing = &(KGrFigure::fixedTiming); // Fixed timing. + } + + KGrHero::WALKDELAY = timing->hwalk; + KGrHero::FALLDELAY = timing->hfall; + KGrEnemy::WALKDELAY = timing->ewalk; + KGrEnemy::FALLDELAY = timing->efall; + KGrEnemy::CAPTIVEDELAY = timing->ecaptive; + KGrBrick::HOLETIME = timing->hole; +} + +void KGrGame::initSearchMatrix() +{ + // Called at start of level and also when hidden ladders appear. + int i,j; + + for (i=1;i<21;i++){ + for (j=1;j<29;j++) + { + // If on ladder, can walk L, R, U or D. + if (playfield[j][i]->whatIam()==LADDER) + playfield[j][i]->searchValue = CANWALKLEFT + CANWALKRIGHT + + CANWALKUP + CANWALKDOWN; + else + // If on solid ground, can walk L or R. + if ((playfield[j][i+1]->whatIam()==BRICK)|| + (playfield[j][i+1]->whatIam()==HOLE)|| + (playfield[j][i+1]->whatIam()==USEDHOLE)|| + (playfield[j][i+1]->whatIam()==BETON)) + playfield[j][i]->searchValue=CANWALKLEFT+CANWALKRIGHT; + else + // If on pole or top of ladder, can walk L, R or D. + if ((playfield[j][i]->whatIam()==POLE)|| + (playfield[j][i+1]->whatIam()==LADDER)) + playfield[j][i]->searchValue=CANWALKLEFT+CANWALKRIGHT+CANWALKDOWN; + else + // Otherwise, gravity takes over ... + playfield[j][i]->searchValue=CANWALKDOWN; + + // Clear corresponding bits if there are solids to L, R, U or D. + if(playfield[j][i-1]->blocker) + playfield[j][i]->searchValue &= ~CANWALKUP; + if(playfield[j-1][i]->blocker) + playfield[j][i]->searchValue &= ~CANWALKLEFT; + if(playfield[j+1][i]->blocker) + playfield[j][i]->searchValue &= ~CANWALKRIGHT; + if(playfield[j][i+1]->blocker) + playfield[j][i]->searchValue &= ~CANWALKDOWN; + } + } +} + +void KGrGame::startPlaying () { + if (! hero->started) { + // Start the enemies and the hero. + for (--enemyCount; enemyCount>=0; --enemyCount) { + enemy=enemies.at(enemyCount); + enemy->startSearching(); + } + hero->start(); + } +} + +QString KGrGame::getFilePath (Owner o, KGrCollection * colln, int lev) +{ + QString filePath; + + if (lev == 0) { + // End of game: show the "ENDE" screen. + o = SYSTEM; + filePath = "level000.grl"; + } + else { + filePath.setNum (lev); // Convert INT -> QString. + filePath = filePath.rightJustify (3,'0'); // Add 0-2 zeros at left. + filePath.append (".grl"); // Add KGoldrunner level-suffix. + filePath.prepend (colln->prefix); // Add collection file-prefix. + } + + filePath.prepend (((o == SYSTEM)? systemDataDir : userDataDir) + "levels/"); + + return (filePath); +} + +QString KGrGame::getTitle() +{ + QString levelTitle; + if (level == 0) { + // Generate a special title: end of game or creating a new level. + if (! editMode) + levelTitle = "E N D --- F I N --- E N D E"; + else + levelTitle = i18n("New Level"); + } + else { + // Generate title string "Collection-name - NNN - Level-name". + levelTitle.setNum (level); + levelTitle = levelTitle.rightJustify (3,'0'); + levelTitle = collection->name + " - " + levelTitle; + if (levelName.length() > 0) { + levelTitle = levelTitle + " - " + levelName; + } + } + return (levelTitle); +} + +void KGrGame::readMousePos() +{ + QPoint p; + int i, j; + + // If loading a level for play or editing, ignore mouse-position input. + if (loading) return; + + // If game control is currently by keyboard, ignore the mouse. + if ((! mouseMode) && (! editMode)) return; + + p = view->getMousePos (); + i = p.x(); j = p.y(); + + if (editMode) { + // Editing - check if we are in paint mode and have moved the mouse. + if (paintEditObj && ((i != oldI) || (j != oldJ))) { + insertEditObj (i, j); + view->updateCanvas(); + oldI = i; + oldJ = j; + } + } + else { + // Playing - if the level has started, control the hero. + if (KGrObject::frozen) return; // If game is stopped, do nothing. + + hero->setDirection (i, j); + + // Start playing when the mouse moves off the hero. + if ((! hero->started) && ((i != startI) || (j != startJ))) { + startPlaying(); + } + } +} + +void KGrGame::doDig (int button) { + + // If game control is currently by keyboard, ignore the mouse. + if (editMode) return; + if (! mouseMode) return; + + // If loading a level for play or editing, ignore mouse-button input. + if ((! loading) && (! KGrObject::frozen)) { + if (! hero->started) { + startPlaying(); // If first player-input, start playing. + } + switch (button) { + case LeftButton: hero->digLeft (); break; + case RightButton: hero->digRight (); break; + default: break; + } + } +} + +void KGrGame::heroAction (KBAction movement) +{ + switch (movement) { + case KB_UP: hero->setKey (UP); break; + case KB_DOWN: hero->setKey (DOWN); break; + case KB_LEFT: hero->setKey (LEFT); break; + case KB_RIGHT: hero->setKey (RIGHT); break; + case KB_STOP: hero->setKey (STAND); break; + case KB_DIGLEFT: hero->setKey (STAND); hero->digLeft (); break; + case KB_DIGRIGHT: hero->setKey (STAND); hero->digRight (); break; + } +} + +/******************************************************************************/ +/************************** SAVE AND RE-LOAD GAMES **************************/ +/******************************************************************************/ + +void KGrGame::saveGame() // Save game ID, score and level. +{ + if (editMode) {myMessage (view, i18n("Save Game"), + i18n("Sorry, you cannot save your game play while you are editing. " + "Please try menu item %1.").arg("\"" + i18n("&Save Edits...") + "\"")); + return; + } + if (hero->started) {myMessage (view, i18n("Save Game"), + i18n("Please note: for reasons of simplicity, your saved game " + "position and score will be as they were at the start of this " + "level, not as they are now.")); + } + + QDate today = QDate::currentDate(); + QTime now = QTime::currentTime(); + QString saved; + QString day; +#ifdef QT3 + day = today.shortDayName(today.dayOfWeek()); +#else + day = today.dayName(today.dayOfWeek()); +#endif + saved = saved.sprintf + ("%-6s %03d %03ld %7ld %s %04d-%02d-%02d %02d:%02d\n", + collection->prefix.myStr(), level, lives, startScore, + day.myStr(), + today.year(), today.month(), today.day(), + now.hour(), now.minute()); + + QFile file1 (userDataDir + "savegame.dat"); + QFile file2 (userDataDir + "savegame.tmp"); + + if (! file2.open (IO_WriteOnly)) { + KGrMessage::information (view, i18n("Save Game"), + i18n("Cannot open file '%1' for output.") + .arg(userDataDir + "savegame.tmp")); + return; + } + QTextStream text2 (&file2); + text2 << saved; + + if (file1.exists()) { + if (! file1.open (IO_ReadOnly)) { + KGrMessage::information (view, i18n("Save Game"), + i18n("Cannot open file '%1' for read-only.") + .arg(userDataDir + "savegame.dat")); + return; + } + + QTextStream text1 (&file1); + int n = 30; // Limit the file to the last 30 saves. + while ((! text1.endData()) && (--n > 0)) { + saved = text1.readLine() + "\n"; + text2 << saved; + } + file1.close(); + } + + file2.close(); + + QDir dir; + dir.rename (file2.name(), file1.name(), TRUE); + KGrMessage::information (view, i18n("Save Game"), + i18n("Your game has been saved.")); +} + +void KGrGame::loadGame() // Re-load game, score and level. +{ + if (! saveOK (FALSE)) { // Check unsaved work. + return; + } + + QFile savedGames (userDataDir + "savegame.dat"); + if (! savedGames.exists()) { + // Use myMessage() because it stops the game while the message appears. + myMessage (view, i18n("Load Game"), + i18n("Sorry, there are no saved games.")); + return; + } + + if (! savedGames.open (IO_ReadOnly)) { + KGrMessage::information (view, i18n("Load Game"), + i18n("Cannot open file '%1' for read-only.") + .arg(userDataDir + "savegame.dat")); + return; + } + + // Halt the game during the loadGame() dialog. + modalFreeze = FALSE; + if (!KGrObject::frozen) { + modalFreeze = TRUE; + freeze(); + } + + QString s; + + KGrLGDialog * lg = new KGrLGDialog (&savedGames, collections, + view, "loadDialog"); + + if (lg->exec() == QDialog::Accepted) { + s = lg->getCurrentText(); + } + + bool found = FALSE; + QString pr; + int lev; + int i; + int imax = collections.count(); + + if (! s.isNull()) { + pr = s.mid (21, 7); // Get the collection prefix. + pr = pr.left (pr.find (" ", 0, FALSE)); + + for (i = 0; i < imax; i++) { // Find the collection. + if (collections.at(i)->prefix == pr) { + collection = collections.at(i); + collnIndex = i; + owner = collections.at(i)->owner; + found = TRUE; + break; + } + } + if (found) { + // Set the rules for the selected game. + emit markRuleType (collection->settings); + lev = s.mid (28, 3).toInt(); + newGame (lev, collnIndex); // Re-start the selected game. + lives = s.mid (32, 3).toLong(); // Update the lives. + emit showLives (lives); + score = s.mid (36, 7).toLong(); // Update the score. + emit showScore (score); + } + else { + KGrMessage::information (view, i18n("Load Game"), + i18n("Cannot find the game with prefix '%1'.").arg(pr)); + } + } + + // Unfreeze the game, but only if it was previously unfrozen. + if (modalFreeze) { + unfreeze(); + modalFreeze = FALSE; + } + + delete lg; +} + +/******************************************************************************/ +/************************** HIGH-SCORE PROCEDURES ***************************/ +/******************************************************************************/ + +void KGrGame::checkHighScore() +{ + bool prevHigh = TRUE; + Q_INT16 prevLevel = 0; + Q_INT32 prevScore = 0; + QString thisUser = i18n("Unknown"); + int highCount = 0; + + // Don't keep high scores for tutorial games. + if (collection->prefix.left(4) == "tute") + return; + + if (score <= 0) + return; + + // Look for user's high-score file or for a released high-score file. + QFile high1 (userDataDir + "hi_" + collection->prefix + ".dat"); + QDataStream s1; + + if (! high1.exists()) { + high1.setName (systemDataDir + "hi_" + collection->prefix + ".dat"); + if (! high1.exists()) { + prevHigh = FALSE; + } + } + + // If a previous high score file exists, check the current score against it. + if (prevHigh) { + if (! high1.open (IO_ReadOnly)) { + QString high1_name = high1.name(); + KGrMessage::information (view, i18n("Check for High Score"), + i18n("Cannot open file '%1' for read-only.").arg(high1_name)); + return; + } + + // Read previous users, levels and scores from the high score file. + s1.setDevice (&high1); + bool found = FALSE; + highCount = 0; + while (! s1.endData()) { + char * prevUser; + char * prevDate; + s1 >> prevUser; + s1 >> prevLevel; + s1 >> prevScore; + s1 >> prevDate; + delete prevUser; + delete prevDate; + highCount++; + if (score > prevScore) { + found = TRUE; // We have a high score. + break; + } + } + + // Check if higher than one on file or fewer than 10 previous scores. + if ((! found) && (highCount >= 10)) { + return; // We did not have a high score. + } + } + + /* ************************************************************* */ + /* If we have come this far, we have a new high score to record. */ + /* ************************************************************* */ + + QFile high2 (userDataDir + "hi_" + collection->prefix + ".tmp"); + QDataStream s2; + + if (! high2.open (IO_WriteOnly)) { + KGrMessage::information (view, i18n("Check for High Score"), + i18n("Cannot open file '%1' for output.") + .arg(userDataDir + "hi_" + collection->prefix + ".tmp")); + return; + } + + // Dialog to ask the user to enter their name. + QDialog * hsn = new QDialog (view, "hsNameDialog", TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title); + + int margin = 10; + int spacing = 10; + QVBoxLayout * mainLayout = new QVBoxLayout (hsn, margin, spacing); + + QLabel * hsnMessage = new QLabel ( + i18n("Congratulations !!! " + "You have achieved a high " + "score in this game. Please enter your name so that " + "it may be enshrined in the KGoldrunner Hall of Fame."), + hsn); + QLineEdit * hsnUser = new QLineEdit (hsn); + QPushButton * OK = new KPushButton (KStdGuiItem::ok(), hsn); + + mainLayout-> addWidget (hsnMessage); + mainLayout-> addWidget (hsnUser); + mainLayout-> addWidget (OK); + + hsn-> setCaption (i18n("Save High Score")); + + QPoint p = view->mapToGlobal (QPoint (0,0)); + hsn-> move (p.x() + 50, p.y() + 50); + + OK-> setAccel (Key_Return); + hsnUser-> setFocus(); // Set the keyboard input on. + + connect (hsnUser, SIGNAL (returnPressed ()), hsn, SLOT (accept ())); + connect (OK, SIGNAL (clicked ()), hsn, SLOT (accept ())); + + while (TRUE) { + hsn->exec(); + thisUser = hsnUser->text(); + if (thisUser.length() > 0) + break; + KGrMessage::information (view, i18n("Save High Score"), + i18n("You must enter something. Please try again.")); + } + + delete hsn; + + QDate today = QDate::currentDate(); + QString hsDate; +#ifdef QT3 + QString day = today.shortDayName(today.dayOfWeek()); +#else + QString day = today.dayName(today.dayOfWeek()); +#endif + hsDate = hsDate.sprintf + ("%s %04d-%02d-%02d", + day.myStr(), + today.year(), today.month(), today.day()); + + s2.setDevice (&high2); + + if (prevHigh) { + high1.reset(); + bool scoreRecorded = FALSE; + highCount = 0; + while ((! s1.endData()) && (highCount < 10)) { + char * prevUser; + char * prevDate; + s1 >> prevUser; + s1 >> prevLevel; + s1 >> prevScore; + s1 >> prevDate; + if ((! scoreRecorded) && (score > prevScore)) { + highCount++; + // Recode the user's name as UTF-8, in case it contains + // non-ASCII chars (e.g. "Krüger" is encoded as "Krüger"). + s2 << (const char *) thisUser.utf8(); + s2 << (Q_INT16) level; + s2 << (Q_INT32) score; + s2 << hsDate.myStr(); + scoreRecorded = TRUE; + } + if (highCount < 10) { + highCount++; + s2 << prevUser; + s2 << prevLevel; + s2 << prevScore; + s2 << prevDate; + } + delete prevUser; + delete prevDate; + } + if ((! scoreRecorded) && (highCount < 10)) { + // Recode the user's name as UTF-8, in case it contains + // non-ASCII chars (e.g. "Krüger" is encoded as "Krüger"). + s2 << (const char *) thisUser.utf8(); + s2 << (Q_INT16) level; + s2 << (Q_INT32) score; + s2 << hsDate.myStr(); + } + high1.close(); + } + else { + // Recode the user's name as UTF-8, in case it contains + // non-ASCII chars (e.g. "Krüger" is encoded as "Krüger"). + s2 << (const char *) thisUser.utf8(); + s2 << (Q_INT16) level; + s2 << (Q_INT32) score; + s2 << hsDate.myStr(); + } + + high2.close(); + + QDir dir; + dir.rename (high2.name(), + userDataDir + "hi_" + collection->prefix + ".dat", TRUE); + KGrMessage::information (view, i18n("Save High Score"), + i18n("Your high score has been saved.")); + + showHighScores(); + return; +} + +void KGrGame::showHighScores() +{ + // Don't keep high scores for tutorial games. + if (collection->prefix.left(4) == "tute") { + KGrMessage::information (view, i18n("Show High Scores"), + i18n("Sorry, we do not keep high scores for tutorial games.")); + return; + } + + Q_INT16 prevLevel = 0; + Q_INT32 prevScore = 0; + int n = 0; + + // Look for user's high-score file or for a released high-score file. + QFile high1 (userDataDir + "hi_" + collection->prefix + ".dat"); + QDataStream s1; + + if (! high1.exists()) { + high1.setName (systemDataDir + "hi_" + collection->prefix + ".dat"); + if (! high1.exists()) { + KGrMessage::information (view, i18n("Show High Scores"), + i18n("Sorry, there are no high scores for the %1 game yet.") + .arg("\"" + collection->name + "\"")); + return; + } + } + + if (! high1.open (IO_ReadOnly)) { + QString high1_name = high1.name(); + KGrMessage::information (view, i18n("Show High Scores"), + i18n("Cannot open file '%1' for read-only.").arg(high1_name)); + return; + } + + QDialog * hs = new QDialog (view, "hsDialog", TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title); + + int margin = 10; + int spacing = 10; + QVBoxLayout * mainLayout = new QVBoxLayout (hs, margin, spacing); + + QLabel * hsHeader = new QLabel (i18n ( + "

KGoldrunner Hall of Fame


" + "

\"%1\" Game

") + .arg(collection->name), + hs); + QLabel * hsColHeader = new QLabel ( + i18n(" Name " + "Level Score Date"), hs); +#ifdef KGR_PORTABLE + QFont f ("courier", 12); +#else + QFont f = KGlobalSettings::fixedFont(); // KDE version. +#endif + f. setFixedPitch (TRUE); + f. setBold (TRUE); + hsColHeader-> setFont (f); + + QLabel * hsLine [10]; + + QHBox * buttons = new QHBox (hs); + buttons-> setSpacing (spacing); + QPushButton * OK = new KPushButton (KStdGuiItem::close(), buttons); + + mainLayout-> addWidget (hsHeader); + mainLayout-> addWidget (hsColHeader); + + hs-> setCaption (i18n("High Scores")); + + OK-> setAccel (Key_Return); + + // Set up the format for the high-score lines. + f. setBold (FALSE); + QString line; + const char * hsFormat = "%2d. %-30.30s %3d %7ld %s"; + + // Read and display the users, levels and scores from the high score file. + s1.setDevice (&high1); + n = 0; + while ((! s1.endData()) && (n < 10)) { + char * prevUser; + char * prevDate; + s1 >> prevUser; + s1 >> prevLevel; + s1 >> prevScore; + s1 >> prevDate; + + // QString::sprintf expects UTF-8 encoding in its string arguments, so + // prevUser has been saved on file as UTF-8 to allow non=ASCII chars + // in the user's name (e.g. "Krüger" is encoded as "Krüger" in UTF-8). + + line = line.sprintf (hsFormat, + n+1, prevUser, prevLevel, prevScore, prevDate); + hsLine [n] = new QLabel (line, hs); + hsLine [n]->setFont (f); + mainLayout->addWidget (hsLine [n]); + + delete prevUser; + delete prevDate; + n++; + } + + QFrame * separator = new QFrame (hs); + separator->setFrameStyle (QFrame::HLine + QFrame::Sunken); + mainLayout->addWidget (separator); + + OK-> setMaximumWidth (100); + mainLayout-> addWidget (buttons); + + QPoint p = view->mapToGlobal (QPoint (0,0)); + hs-> move (p.x() + 50, p.y() + 50); + + // Start up the dialog box. + connect (OK, SIGNAL (clicked ()), hs, SLOT (accept ())); + hs-> exec(); + + delete hs; +} + +/******************************************************************************/ +/************************** AUTHORS' DEBUGGING AIDS **************************/ +/******************************************************************************/ + +void KGrGame::doStep() +{ + if (KGrObject::frozen) { // The game must have been halted. + restart(); // Do one step and halt again. + } +} + +void KGrGame::restart() +{ + bool temp; + int i,j; + + if (editMode) // Can't move figures when in Edit Mode. + return; + + temp = KGrObject::frozen; + + KGrObject::frozen = FALSE; // Temporarily restart the game, by re-running + // any timer events that have been blocked. + + readMousePos(); // Set hero's direction. + hero->doStep(); // Move the hero one step. + + j = enemies.count(); // Move each enemy one step. + for (i = 0; i < j; i++) { + enemy = enemies.at(i); // Need to use an index because called methods + enemy->doStep(); // change the "current()" of the "enemies" list. + } + + for (i=1; i<=28; i++) + for (j=1; j<=20; j++) { + if ((playfield[i][j]->whatIam() == HOLE) || + (playfield[i][j]->whatIam() == USEDHOLE) || + (playfield[i][j]->whatIam() == BRICK)) + ((KGrBrick *)playfield[i][j])->doStep(); + } + + KGrObject::frozen = temp; // If frozen was TRUE, halt again, which gives a + // single-step effect, otherwise go on running. +} + +void KGrGame::showFigurePositions() +{ + if (KGrObject::frozen) { + hero->showState('p'); + for (enemy=enemies.first();enemy != 0; enemy = enemies.next()) { + enemy->showState('p'); + } + } +} + +void KGrGame::showHeroState() +{ + if (KGrObject::frozen) { + hero->showState('s'); + } +} + +void KGrGame::showEnemyState(int enemyId) +{ + if (KGrObject::frozen) { + for (enemy=enemies.first();enemy != 0; enemy = enemies.next()) { + if (enemy->enemyId == enemyId) enemy->showState('s'); + } + } +} + +void KGrGame::showObjectState() +{ + QPoint p; + int i, j; + KGrObject * myObject; + + if (KGrObject::frozen) { + p = view->getMousePos (); + i = p.x(); j = p.y(); + myObject = playfield[i][j]; + switch (myObject->whatIam()) { + case BRICK: + case HOLE: + case USEDHOLE: + ((KGrBrick *)myObject)->showState(i, j); break; + default: myObject->showState(i, j); break; + } + } +} + +void KGrGame::bugFix() +{ + if (KGrObject::frozen) { // Toggle a bug fix on/off dynamically. + KGrObject::bugFixed = (KGrObject::bugFixed) ? FALSE : TRUE; + printf ("%s", (KGrObject::bugFixed) ? "\n" : ""); + printf (">>> Bug fix is %s\n", (KGrObject::bugFixed) ? "ON" : "OFF\n"); + } +} + +void KGrGame::startLogging() +{ + if (KGrObject::frozen) { // Toggle logging on/off dynamically. + KGrObject::logging = (KGrObject::logging) ? FALSE : TRUE; + printf ("%s", (KGrObject::logging) ? "\n" : ""); + printf (">>> Logging is %s\n", (KGrObject::logging) ? "ON" : "OFF\n"); + } +} + +/******************************************************************************/ +/************ GAME EDITOR FUNCTIONS ACTIVATED BY MENU OR TOOLBAR ************/ +/******************************************************************************/ + +void KGrGame::setEditObj (char newEditObj) +{ + editObj = newEditObj; +} + +void KGrGame::createLevel() +{ + int i, j; + + if (! saveOK (FALSE)) { // Check unsaved work. + return; + } + + if (! ownerOK (USER)) { + KGrMessage::information (view, i18n("Create Level"), + i18n("You cannot create and save a level " + "until you have created a game to hold " + "it. Try menu item \"Create Game\".")); + return; + } + + // Ignore player input from keyboard or mouse while the screen is set up. + loading = TRUE; + + level = 0; + initEdit(); + levelName = ""; + levelHint = ""; + + // Clear the playfield. + editObj = FREE; + for (i = 1; i <= FIELDWIDTH; i++) + for (j = 1; j <= FIELDHEIGHT; j++) { + insertEditObj (i, j); + editObjArray[i][j] = editObj; + } + + editObj = HERO; + insertEditObj (1, 1); + editObjArray[1][1] = editObj; + editObj = BRICK; + + showEditLevel(); + + for (j = 1; j <= FIELDHEIGHT; j++) + for (i = 1; i <= FIELDWIDTH; i++) { + lastSaveArray[i][j] = editObjArray[i][j]; // Copy for "saveOK()". + } + + // Re-enable player input. + loading = FALSE; + + view->updateCanvas(); // Show the edit area. + view->update(); // Show the level name. +} + +void KGrGame::updateLevel() +{ + if (! saveOK (FALSE)) { // Check unsaved work. + return; + } + + if (! ownerOK (USER)) { + KGrMessage::information (view, i18n("Edit Level"), + i18n("You cannot edit and save a level until you " + "have created a game and a level. Try menu item \"Create Game\".")); + return; + } + + if (level < 0) level = 0; + int lev = selectLevel (SL_UPDATE, level); + if (lev == 0) + return; + + if (owner == SYSTEM) { + KGrMessage::information (view, i18n("Edit Level"), + i18n("It is OK to edit a system level, but you MUST save " + "the level in one of your own games. You're not just " + "taking a peek at the hidden ladders " + "and fall-through bricks, are you? :-)")); + } + + loadEditLevel (lev); +} + +void KGrGame::updateNext() +{ + if (! saveOK (FALSE)) { // Check unsaved work. + return; + } + level++; + updateLevel(); +} + +void KGrGame::loadEditLevel (int lev) +{ + int i, j; + QFile levelFile; + + if (! openLevelFile (lev, levelFile)) + return; + + // Ignore player input from keyboard or mouse while the screen is set up. + loading = TRUE; + + level = lev; + initEdit(); + + // Load the level. + for (j = 1; j <= FIELDHEIGHT; j++) + for (i = 1; i <= FIELDWIDTH; i++) { + editObj = levelFile.getch (); + insertEditObj (i, j); + editObjArray[i][j] = editObj; + lastSaveArray[i][j] = editObjArray[i][j]; // Copy for "saveOK()". + } + + // Read a newline character, then read in the level name and hint (if any). + int c = levelFile.getch(); + QCString levelHintC = ""; + QCString levelNameC = ""; + levelHint = ""; + levelName = ""; + i = 1; + while ((c = levelFile.getch()) != EOF) { + switch (i) { + case 1: if (c == '\n') // Level name is on one line. + i = 2; + else + levelNameC += (char) c; + break; + + case 2: levelHintC += (char) c; // Hint is on rest of file. + break; + } + } + + // Retain the original language of the name and hint when editing, + // but remove the final \n and convert non-ASCII, UTF-8 substrings + // to Unicode (eg. ü to ü). + int len = levelHintC.length(); + if (len > 0) + levelHint = QString::fromUtf8((const char *) levelHintC.left(len-1)); + + len = levelNameC.length(); + if (len > 0) + levelName = QString::fromUtf8((const char *) levelNameC); + + editObj = BRICK; // Reset default object. + levelFile.close (); + + view->setTitle (getTitle()); // Show the level name. + view->updateCanvas(); // Show the edit area. + showEditLevel(); // Reconnect signals. + + // Re-enable player input. + loading = FALSE; +} + +void KGrGame::editNameAndHint() +{ + if (! editMode) + return; + + // Run a dialog box to create/edit the level name and hint. + KGrNHDialog * nh = new KGrNHDialog (levelName, levelHint, view, "NHDialog"); + + if (nh->exec() == QDialog::Accepted) { + levelName = nh->getName(); + levelHint = nh->getHint(); + shouldSave = TRUE; + } + + delete nh; +} + +bool KGrGame::saveLevelFile() +{ + bool isNew; + int action; + int selectedLevel = level; + + int i, j; + QString filePath; + + if (! editMode) { + KGrMessage::information (view, i18n("Save Level"), + i18n("Inappropriate action: you are not editing a level.")); + return (FALSE); + } + + // Save the current collection index. + int N = collnIndex; + + if (selectedLevel == 0) { + // New level: choose a number. + action = SL_CREATE; + } + else { + // Existing level: confirm the number or choose a new number. + action = SL_SAVE; + } + + // Pop up dialog box, which could change the collection or level or both. + selectedLevel = selectLevel (action, selectedLevel); + if (selectedLevel == 0) + return (FALSE); + + // Get the new collection (if changed). + int n = collnIndex; + + // Set the name of the output file. + filePath = getFilePath (owner, collection, selectedLevel); + QFile levelFile (filePath); + + if ((action == SL_SAVE) && (n == N) && (selectedLevel == level)) { + // This is a normal edit: the old file is to be re-written. + isNew = FALSE; + } + else { + isNew = TRUE; + // Check if the file is to be inserted in or appended to the collection. + if (levelFile.exists()) { + switch (KGrMessage::warning (view, i18n("Save Level"), + i18n("Do you want to insert a level and " + "move existing levels up by one?"), + i18n("&Insert Level"), i18n("&Cancel"))) { + + case 0: if (! reNumberLevels (n, selectedLevel, + collections.at(n)->nLevels, +1)) { + return (FALSE); + } + break; + case 1: return (FALSE); + break; + } + } + } + + // Open the output file. + if (! levelFile.open (IO_WriteOnly)) { + KGrMessage::information (view, i18n("Save Level"), + i18n("Cannot open file '%1' for output.").arg(filePath)); + return (FALSE); + } + + // Save the level. + for (j = 1; j < 21; j++) + for (i = 1; i < 29; i++) { + levelFile.putch (editObjArray[i][j]); + lastSaveArray[i][j] = editObjArray[i][j]; // Copy for "saveOK()". + } + levelFile.putch ('\n'); + + // Save the level name, changing non-ASCII chars to UTF-8 (eg. ü to ü). + QCString levelNameC = levelName.utf8(); + int len1 = levelNameC.length(); + if (len1 > 0) { + for (i = 0; i < len1; i++) + levelFile.putch (levelNameC[i]); + levelFile.putch ('\n'); // Add a newline. + } + + // Save the level hint, changing non-ASCII chars to UTF-8 (eg. ü to ü). + QCString levelHintC = levelHint.utf8(); + int len2 = levelHintC.length(); + char ch = '\0'; + + if (len2 > 0) { + if (len1 <= 0) + levelFile.putch ('\n'); // Leave blank line for name. + for (i = 0; i < len2; i++) { + ch = levelHintC[i]; + levelFile.putch (ch); // Copy the character. + } + if (ch != '\n') + levelFile.putch ('\n'); // Add a newline character. + } + + levelFile.close (); + shouldSave = FALSE; + + if (isNew) { + collections.at(n)->nLevels++; + saveCollections (owner); + } + + level = selectedLevel; + emit showLevel (level); + view->setTitle (getTitle()); // Display new title. + view->updateCanvas(); // Show the edit area. + return (TRUE); +} + +void KGrGame::moveLevelFile () +{ + if (level <= 0) { + KGrMessage::information (view, i18n("Move Level"), + i18n("You must first load a level to be moved. Use " + "the %1 or %2 menu.") + .arg("\"" + i18n("Game") + "\"") + .arg("\"" + i18n("Editor") + "\"")); + return; + } + + int action = SL_MOVE; + + int fromC = collnIndex; + int fromL = level; + int toC = fromC; + int toL = fromL; + + if (! ownerOK (USER)) { + KGrMessage::information (view, i18n("Move Level"), + i18n("You cannot move a level until you " + "have created a game and at least two levels. Try " + "menu item \"Create Game\".")); + return; + } + + if (collections.at(fromC)->owner != USER) { + KGrMessage::information (view, i18n("Move Level"), + i18n("Sorry, you cannot move a system level.")); + return; + } + + // Pop up dialog box to get the collection and level number to move to. + while ((toC == fromC) && (toL == fromL)) { + toL = selectLevel (action, toL); + if (toL == 0) + return; + + toC = collnIndex; + + if ((toC == fromC) && (toL == fromL)) { + KGrMessage::information (view, i18n("Move Level"), + i18n("You must change the level or the game or both.")); + } + } + + QDir dir; + QString filePath1; + QString filePath2; + + // Save the "fromN" file under a temporary name. + filePath1 = getFilePath (USER, collections.at(fromC), fromL); + filePath2 = filePath1; + filePath2 = filePath2.append (".tmp"); + dir.rename (filePath1, filePath2, TRUE); + + if (toC == fromC) { // Same collection. + if (toL < fromL) { // Decrease level. + // Move "toL" to "fromL - 1" up by 1. + if (! reNumberLevels (toC, toL, fromL-1, +1)) { + return; + } + } + else { // Increase level. + // Move "fromL + 1" to "toL" down by 1. + if (! reNumberLevels (toC, fromL+1, toL, -1)) { + return; + } + } + } + else { // Different collection. + // In "fromC", move "fromL + 1" to "nLevels" down and update "nLevels". + if (! reNumberLevels (fromC, fromL + 1, + collections.at(fromC)->nLevels, -1)) { + return; + } + collections.at(fromC)->nLevels--; + + // In "toC", move "toL + 1" to "nLevels" up and update "nLevels". + if (! reNumberLevels (toC, toL, collections.at(toC)->nLevels, +1)) { + return; + } + collections.at(toC)->nLevels++; + + saveCollections (USER); + } + + // Rename the saved "fromL" file to become "toL". + filePath1 = getFilePath (USER, collections.at(toC), toL); + dir.rename (filePath2, filePath1, TRUE); + + level = toL; + collection = collections.at(toC); + view->setTitle (getTitle()); // Re-write title. + view->updateCanvas(); // Re-display details of level. + emit showLevel (level); +} + +void KGrGame::deleteLevelFile () +{ + int action = SL_DELETE; + int lev = level; + + if (! ownerOK (USER)) { + KGrMessage::information (view, i18n("Delete Level"), + i18n("You cannot delete a level until you " + "have created a game and a level. Try " + "menu item \"Create Game\".")); + return; + } + + // Pop up dialog box to get the collection and level number. + lev = selectLevel (action, level); + if (lev == 0) + return; + + QString filePath; + + // Set the name of the file to be deleted. + int n = collnIndex; + filePath = getFilePath (USER, collections.at(n), lev); + QFile levelFile (filePath); + + // Delete the file for the selected collection and level. + if (levelFile.exists()) { + if (lev < collections.at(n)->nLevels) { + switch (KGrMessage::warning (view, i18n("Delete Level"), + i18n("Do you want to delete a level and " + "move higher levels down by one?"), + i18n("&Delete Level"), i18n("&Cancel"))) { + case 0: break; + case 1: return; break; + } + levelFile.remove (); + if (! reNumberLevels (n, lev + 1, collections.at(n)->nLevels, -1)) { + return; + } + } + else { + levelFile.remove (); + } + } + else { + KGrMessage::information (view, i18n("Delete Level"), + i18n("Cannot find file '%1' to be deleted.").arg(filePath)); + return; + } + + collections.at(n)->nLevels--; + saveCollections (USER); + if (lev <= collections.at(n)->nLevels) { + level = lev; + } + else { + level = collections.at(n)->nLevels; + } + + // Repaint the screen with the level that now has the selected number. + if (editMode && (level > 0)) { + loadEditLevel (level); // Load level in edit mode. + } + else if (level > 0) { + enemyCount = 0; // Load level in play mode. + enemies.clear(); + view->deleteEnemySprites(); + newLevel = TRUE;; + loadLevel (level); + newLevel = FALSE; + } + else { + createLevel(); // No levels left in collection. + } + emit showLevel (level); +} + +void KGrGame::editCollection (int action) +{ + int lev = level; + int n = -1; + + // If editing, choose a collection. + if (action == SL_UPD_GAME) { + lev = selectLevel (SL_UPD_GAME, level); + if (lev == 0) + return; + level = lev; + n = collnIndex; + } + + KGrECDialog * ec = new KGrECDialog (action, n, collections, + view, "editGameDialog"); + + while (ec->exec() == QDialog::Accepted) { // Loop until valid. + + // Validate the collection details. + QString ecName = ec->getName(); + int len = ecName.length(); + if (len == 0) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("You must enter a name for the game.")); + continue; + } + + QString ecPrefix = ec->getPrefix(); + if ((action == SL_CR_GAME) || (collections.at(n)->nLevels <= 0)) { + // The filename prefix could have been entered, so validate it. + len = ecPrefix.length(); + if (len == 0) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("You must enter a filename prefix for the game.")); + continue; + } + if (len > 5) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("The filename prefix should not " + "be more than 5 characters.")); + continue; + } + + bool allAlpha = TRUE; + for (int i = 0; i < len; i++) { + if (! isalpha(ecPrefix.myChar(i))) { + allAlpha = FALSE; + break; + } + } + if (! allAlpha) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("The filename prefix should be " + "all alphabetic characters.")); + continue; + } + + bool duplicatePrefix = FALSE; + KGrCollection * c; + int imax = collections.count(); + for (int i = 0; i < imax; i++) { + c = collections.at(i); + if ((c->prefix == ecPrefix) && (i != n)) { + duplicatePrefix = TRUE; + break; + } + } + + if (duplicatePrefix) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("The filename prefix '%1' is already in use.") + .arg(ecPrefix)); + continue; + } + } + + // Save the collection details. + char settings = 'K'; + if (ec->isTrad()) { + settings = 'T'; + } + if (action == SL_CR_GAME) { + collections.append (new KGrCollection (USER, + ecName, ecPrefix, settings, 0, ec->getAboutText())); + } + else { + collection->name = ecName; + collection->prefix = ecPrefix; + collection->settings = settings; + collection->about = ec->getAboutText(); + } + + saveCollections (USER); + break; // All done now. + } + + delete ec; +} + +/******************************************************************************/ +/********************* SUPPORTING GAME EDITOR FUNCTIONS *********************/ +/******************************************************************************/ + +bool KGrGame::saveOK (bool exiting) +{ + int i, j; + bool result; + QString option2 = i18n("&Go on editing"); + + result = TRUE; + + if (editMode) { + if (exiting) { // If window is closing, + option2 = ""; // can't go on editing. + } + for (j = 1; j <= FIELDHEIGHT; j++) + for (i = 1; i <= FIELDWIDTH; i++) { // Check cell changes. + if ((shouldSave) || (editObjArray[i][j] != lastSaveArray[i][j])) { + // If shouldSave == TRUE, level name or hint was edited. + switch (KGrMessage::warning (view, i18n("Editor"), + i18n("You have not saved your work. Do " + "you want to save it now?"), + i18n("&Save"), i18n("&Don't Save"), option2)) { + case 0: result = saveLevelFile(); break;// Save and continue. + case 1: shouldSave = FALSE; break; // Continue: don't save. + case 2: result = FALSE; break; // Go back to editing. + } + return (result); + } + } + } + return (result); +} + +void KGrGame::initEdit() +{ + if (! editMode) { + + editMode = TRUE; + emit setEditMenu (TRUE); // Enable edit menu items and toolbar. + + // We were previously in play mode: stop the hero running or falling. + hero->init (1, 1); + view->setHeroVisible (FALSE); + } + + paintEditObj = FALSE; + + // Set the default object and button. + editObj = BRICK; + emit defaultEditObj(); // Set default edit-toolbar button. + + oldI = 0; + oldJ = 0; + heroCount = 0; + enemyCount = 0; + enemies.clear(); + view->deleteEnemySprites(); + nuggets = 0; + + emit showLevel (level); + emit showLives (0); + emit showScore (0); + + deleteLevel(); + setBlankLevel(FALSE); // Fill playfield with Editable objects. + + view->setTitle (getTitle());// Show title of level. + view->updateCanvas(); // Show the edit area. + + shouldSave = FALSE; // Used to flag editing of name or hint. +} + +void KGrGame::deleteLevel() +{ + int i,j; + for (i = 1; i <= FIELDHEIGHT; i++) + for (j = 1; j <= FIELDWIDTH; j++) + delete playfield[j][i]; +} + +void KGrGame::insertEditObj (int i, int j) +{ + if ((i < 1) || (j < 1) || (i > FIELDWIDTH) || (j > FIELDHEIGHT)) + return; // Do nothing: mouse pointer is out of playfield. + + if (editObjArray[i][j] == HERO) { + // The hero is in this cell: remove him. + editObjArray[i][j] = FREE; + heroCount = 0; + } + + if (editObj == HERO) { + if (heroCount != 0) { + // Can only have one hero: remove him from his previous position. + for (int m = 1; m <= FIELDWIDTH; m++) + for (int n = 1; n <= FIELDHEIGHT; n++) { + if (editObjArray[m][n] == HERO) { + setEditableCell (m, n, FREE); + } + } + } + heroCount = 1; + } + + setEditableCell (i, j, editObj); +} + +void KGrGame::setEditableCell (int i, int j, char type) +{ + ((KGrEditable *) playfield[i][j])->setType (type); + view->paintCell (i, j, type); + editObjArray[i][j] = type; +} + +void KGrGame::showEditLevel() +{ + // Disconnect play-mode slots from signals from "view". + disconnect (view, SIGNAL(mouseClick(int)), 0, 0); + disconnect (view, SIGNAL(mouseLetGo(int)), 0, 0); + + // Connect edit-mode slots to signals from "view". + connect (view, SIGNAL(mouseClick(int)), SLOT(doEdit(int))); + connect (view, SIGNAL(mouseLetGo(int)), SLOT(endEdit(int))); +} + +bool KGrGame::reNumberLevels (int cIndex, int first, int last, int inc) +{ + int i, n, step; + QDir dir; + QString file1, file2; + + if (inc > 0) { + i = last; + n = first - 1; + step = -1; + } + else { + i = first; + n = last + 1; + step = +1; + } + + while (i != n) { + file1 = getFilePath (USER, collections.at(cIndex), i); + file2 = getFilePath (USER, collections.at(cIndex), i - step); + if (! dir.rename (file1, file2, TRUE)) { // Allow absolute paths. + KGrMessage::information (view, i18n("Save Level"), + i18n("Cannot rename file '%1' to '%2'.") + .arg(file1).arg(file2)); + return (FALSE); + } + i = i + step; + } + + return (TRUE); +} + +void KGrGame::setLevel (int lev) +{ + level = lev; + return; +} + +/******************************************************************************/ +/********************* EDIT ACTION SLOTS **********************************/ +/******************************************************************************/ + +void KGrGame::doEdit (int button) +{ + // Mouse button down: start making changes. + QPoint p; + int i, j; + + p = view->getMousePos (); + i = p.x(); j = p.y(); + + switch (button) { + case LeftButton: + case RightButton: + paintEditObj = TRUE; + insertEditObj (i, j); + view->updateCanvas(); + oldI = i; + oldJ = j; + break; + default: + break; + } +} + +void KGrGame::endEdit (int button) +{ + // Mouse button released: finish making changes. + QPoint p; + int i, j; + + p = view->getMousePos (); + i = p.x(); j = p.y(); + + switch (button) { + case LeftButton: + case RightButton: + paintEditObj = FALSE; + if ((i != oldI) || (j != oldJ)) { + insertEditObj (i, j); + view->updateCanvas(); + } + break; + default: + break; + } +} + +/******************************************************************************/ +/********************** LEVEL SELECTION DIALOG BOX **********************/ +/******************************************************************************/ + +int KGrGame::selectLevel (int action, int requestedLevel) +{ + int selectedLevel = 0; // 0 = no selection (Cancel) or invalid. + + // Halt the game during the dialog. + modalFreeze = FALSE; + if (! KGrObject::frozen) { + modalFreeze = TRUE; + freeze(); + } + + // Create and run a modal dialog box to select a game and level. + KGrSLDialog * sl = new KGrSLDialog (action, requestedLevel, collnIndex, + collections, this, view, "levelDialog"); + while (sl->exec() == QDialog::Accepted) { + selectedGame = sl->selectedGame(); + selectedLevel = 0; // In case the selection is invalid. + if (collections.at(selectedGame)->owner == SYSTEM) { + switch (action) { + case SL_CREATE: // Can save only in a USER collection. + case SL_SAVE: + case SL_MOVE: + KGrMessage::information (view, i18n("Select Level"), + i18n("Sorry, you can only save or move " + "into one of your own games.")); + continue; // Re-run the dialog box. + break; + case SL_DELETE: // Can delete only in a USER collection. + KGrMessage::information (view, i18n("Select Level"), + i18n("Sorry, you can only delete a level " + "from one of your own games.")); + continue; // Re-run the dialog box. + break; + case SL_UPD_GAME: // Can edit info only in a USER collection. + KGrMessage::information (view, i18n("Edit Game Info"), + i18n("Sorry, you can only edit the game " + "information on your own games.")); + continue; // Re-run the dialog box. + break; + default: + break; + } + } + + selectedLevel = sl->selectedLevel(); + if ((selectedLevel > collections.at (selectedGame)->nLevels) && + (action != SL_CREATE) && (action != SL_SAVE) && + (action != SL_MOVE) && (action != SL_UPD_GAME)) { + KGrMessage::information (view, i18n("Select Level"), + i18n("There is no level %1 in %2, " + "so you cannot play or edit it.") + .arg(selectedLevel) + .arg("\"" + collections.at(selectedGame)->name + "\"")); + selectedLevel = 0; // Set an invalid selection. + continue; // Re-run the dialog box. + } + + // If "OK", set the results. + collection = collections.at (selectedGame); + owner = collection->owner; + collnIndex = selectedGame; + // Set default rules for selected game. + emit markRuleType (collection->settings); + break; + } + + // Unfreeze the game, but only if it was previously unfrozen. + if (modalFreeze) { + unfreeze(); + modalFreeze = FALSE; + } + + delete sl; + return (selectedLevel); // 0 = cancelled or invalid. +} + +bool KGrGame::ownerOK (Owner o) +{ + // Check that this owner has at least one collection. + KGrCollection * c; + bool OK = FALSE; + + for (c = collections.first(); c != 0; c = collections.next()) { + if (c->owner == o) { + OK = TRUE; + break; + } + } + + return (OK); +} + +/******************************************************************************/ +/********************** CLASS TO DISPLAY THUMBNAIL ***********************/ +/******************************************************************************/ + +KGrThumbNail::KGrThumbNail (QWidget * parent, const char * name) + : QFrame (parent, name) +{ + // Let the parent do all the work. We need a class here so that + // QFrame::drawContents (QPainter *) can be re-implemented and + // the thumbnail can be automatically re-painted when required. +} + +QColor KGrThumbNail::backgroundColor = QColor ("#dddddd"); +QColor KGrThumbNail::brickColor = QColor ("#ff0000"); +QColor KGrThumbNail::ladderColor = QColor ("#ddcc00"); +QColor KGrThumbNail::poleColor = QColor ("#aa7700"); + +void KGrThumbNail::setFilePath (QString & fp, QLabel * sln) +{ + filePath = fp; // Keep safe copies of file + lName = sln; // path and level name field. +} + +void KGrThumbNail::drawContents (QPainter * p) // Activated via "paintEvent". +{ + QFile openFile; + QPen pen = p->pen(); + char obj = FREE; + int fw = 1; // Set frame width. + int n = width() / FIELDWIDTH; // Set thumbnail cell-size. + + pen.setColor (backgroundColor); + p->setPen (pen); + + openFile.setName (filePath); + if ((! openFile.exists()) || (! openFile.open (IO_ReadOnly))) { + // There is no file, so fill the thumbnail with "FREE" cells. + p->drawRect (QRect(fw, fw, FIELDWIDTH*n, FIELDHEIGHT*n)); + return; + } + + for (int j = 0; j < FIELDHEIGHT; j++) + for (int i = 0; i < FIELDWIDTH; i++) { + + obj = openFile.getch(); + + // Set the colour of each object. + switch (obj) { + case BRICK: + case BETON: + case FBRICK: + pen.setColor (brickColor); p->setPen (pen); break; + case LADDER: + pen.setColor (ladderColor); p->setPen (pen); break; + case POLE: + pen.setColor (poleColor); p->setPen (pen); break; + case HERO: + pen.setColor (green); p->setPen (pen); break; + case ENEMY: + pen.setColor (blue); p->setPen (pen); break; + default: + // Set the background for FREE, HLADDER and NUGGET. + pen.setColor (backgroundColor); p->setPen (pen); break; + } + + // Draw nxn pixels as n lines of length n. + p->drawLine (i*n+fw, j*n+fw, i*n+(n-1)+fw, j*n+fw); + if (obj == POLE) { + // For a pole, only the top line is drawn in white. + pen.setColor (backgroundColor); + p->setPen (pen); + } + for (int k = 1; k < n; k++) { + p->drawLine (i*n+fw, j*n+k+fw, i*n+(n-1)+fw, j*n+k+fw); + } + + // For a nugget, add just a vertical touch of yellow (2-3 pixels). + if (obj == NUGGET) { + int k = (n/2)+fw; + // pen.setColor (QColor("#ffff00")); + pen.setColor (ladderColor); + p->setPen (pen); + p->drawLine (i*n+k, j*n+k, i*n+k, j*n+(n-1)+fw); + p->drawLine (i*n+k+1, j*n+k, i*n+k+1, j*n+(n-1)+fw); + } + } + + // Absorb a newline character, then read in the level name (if any). + int c = openFile.getch(); + QCString s = ""; + while ((c = openFile.getch()) != EOF) { + if (c == '\n') // Level name is on one line. + break; + s += (char) c; + } + if (s.length() > 0) // If there is a name, translate it. + lName->setText (i18n((const char *) s)); + else + lName->setText (""); + + openFile.close(); +} + +/******************************************************************************/ +/************************* COLLECTIONS HANDLING ***************************/ +/******************************************************************************/ + +// NOTE: Macros "myStr" and "myChar", defined in "kgrgame.h", are used +// to smooth out differences between Qt 1 and Qt2 QString classes. + +bool KGrGame::initCollections () +{ + // Initialise the list of collections of levels (i.e. the list of games). + collections.setAutoDelete(TRUE); + owner = SYSTEM; // Use system levels initially. + if (! loadCollections (SYSTEM)) // Load system collections list. + return (FALSE); // If no collections, abort. + loadCollections (USER); // Load user collections list. + // If none, don't worry. + + mapCollections(); // Check ".grl" file integrity. + + // Set the default collection (first one in the SYSTEM "games.dat" file). + collnIndex = 0; + collection = collections.at (collnIndex); + level = 1; // Default start is at level 1. + + return (TRUE); +} + +void KGrGame::mapCollections() +{ + QDir d; + KGrCollection * colln; + QString d_path; + QString fileName1; + QString fileName2; + + // Find KGoldrunner level files, sorted by name (same as numerical order). + for (colln = collections.first(); colln != 0; colln = collections.next()) { + d.setPath ((colln->owner == SYSTEM) ? systemDataDir + "levels/" + : userDataDir + "levels/"); + d_path = d.path(); + if (! d.exists()) { + // There is no "levels" sub-directory: OK if game has no levels yet. + if (colln->nLevels > 0) { + KGrMessage::information (view, i18n("Check Games & Levels"), + i18n("There is no folder '%1' to hold levels for" + " the '%2' game. Please make sure '%3' " + "has been run in the '%4' folder.") + .arg(d_path) + .arg(colln->name) + .arg("tar xf levels.tar") + .arg(systemDataDir)); + } + continue; + } + + const QFileInfoList * files = d.entryInfoList + (colln->prefix + "???.grl", QDir::Files, QDir::Name); + QFileInfoListIterator i (* files); + QFileInfo * file; + + if ((files->count() <= 0) && (colln->nLevels > 0)) { + KGrMessage::information (view, i18n("Check Games & Levels"), + i18n("There are no files '%1/%2???.grl' for the %3 game.") + .arg(d_path) + .arg(colln->prefix) + .arg("\"" + colln->name + "\"")); + continue; + } + + // If the prefix is "level", the first file is the "ENDE" screen. + int lev = (colln->prefix == "level") ? 0 : 1; + + while ((file = i.current())) { + // Get the name of the file found on disk. + fileName1 = file->fileName(); + + while (TRUE) { + // Work out what the file name should be, based on the level no. + fileName2.setNum (lev); // Convert to QString. + fileName2 = fileName2.rightJustify (3,'0'); // Add zeros. + fileName2.append (".grl"); // Add level-suffix. + fileName2.prepend (colln->prefix); // Add colln. prefix. + + if (lev > colln->nLevels) { + KGrMessage::information (view, + i18n("Check Games & Levels"), + i18n("File '%1' is beyond the highest level for " + "the %2 game and cannot be played.") + .arg(fileName1) + .arg("\"" + colln->name + "\"")); + break; + } + else if (fileName1 == fileName2) { + lev++; + break; + } + else if (fileName1.myStr() < fileName2.myStr()) { + KGrMessage::information (view, + i18n("Check Games & Levels"), + i18n("File '%1' is before the lowest level for " + "the %2 game and cannot be played.") + .arg(fileName1) + .arg("\"" + colln->name + "\"")); + break; + } + else { + KGrMessage::information (view, + i18n("Check Games & Levels"), + i18n("Cannot find file '%1' for the %2 game.") + .arg(fileName2) + .arg("\"" + colln->name + "\"")); + lev++; + } + } + ++i; // Go to next file info entry. + } + } +} + +bool KGrGame::loadCollections (Owner o) +{ + QString filePath; + + filePath = ((o == SYSTEM)? systemDataDir : userDataDir) + "games.dat"; + + QFile c (filePath); + + if (! c.exists()) { + // If the user has not yet created a collection, don't worry. + if (o == SYSTEM) { + KGrMessage::information (view, i18n("Load Game Info"), + i18n("Cannot find game info file '%1'.") + .arg(filePath)); + } + return (FALSE); + } + + if (! c.open (IO_ReadOnly)) { + KGrMessage::information (view, i18n("Load Game Info"), + i18n("Cannot open file '%1' for read-only.").arg(filePath)); + return (FALSE); + } + + QCString line = ""; + QCString name = ""; + QString prefix = ""; + char settings = ' '; + int nLevels = -1; + + int ch = 0; + while (ch >= 0) { + ch = c.getch(); + if (((char) ch != '\n') && (ch >= 0)) { + // If not end-of-line and not end-of-file, add to the line. + if (ch == '\r') {line += '\n';} + else if (ch == '\\') {ch = c.getch(); line += '\n';} + else {line += (char) ch;} + } + else { + // If first character is a digit, we have a new collection. + if (isdigit(line[0])) { + if (nLevels >= 0) { + // If previous collection with no "about" exists, load it. + collections.append (new KGrCollection + (o, name, prefix, settings, nLevels, "")); + name = ""; prefix = ""; settings = ' '; nLevels = -1; + } + // Decode the first (maybe the only) line in the new collection. + line = line.simplifyWhiteSpace(); + int i, j, len; + len = line.length(); + i = 0; j = line.find(' ',i); nLevels = line.left(j).toInt(); + i = j+1; j = line.find(' ',i); settings = line[i]; + i = j+1; j = line.find(' ',i); prefix = line.mid(i,j-i); + i = j+1; name = line.right(len-i); + } + // If first character is not a digit, the line should be an "about". + else if (nLevels >= 0) { + collections.append (new KGrCollection + (o, i18n((const char *) name), // Translate now. + prefix, settings, nLevels, + QString::fromUtf8((const char *) line))); + name = ""; prefix = ""; settings = ' '; nLevels = -1; + } + else if (ch >= 0) { + // Not EOF: it's an empty line or out-of-context "about" line. + KGrMessage::information (view, i18n("Load Game Info"), + i18n("Format error in game info file '%1'.") + .arg(filePath)); + c.close(); + return (FALSE); + } + line = ""; + } + } + + c.close(); + return (TRUE); +} + +bool KGrGame::saveCollections (Owner o) +{ + QString filePath; + + if (o != USER) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("You can only modify user games.")); + return (FALSE); + } + + filePath = ((o == SYSTEM)? systemDataDir : userDataDir) + "games.dat"; + + QFile c (filePath); + + // Open the output file. + if (! c.open (IO_WriteOnly)) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("Cannot open file '%1' for output.").arg(filePath)); + return (FALSE); + } + + // Save the collections. + KGrCollection * colln; + QCString line; + int i, len; + char ch; + + for (colln = collections.first(); colln != 0; colln = collections.next()) { + if (colln->owner == o) { + line.sprintf ("%03d %c %s %s\n", colln->nLevels, colln->settings, + colln->prefix.myStr(), + (const char *) colln->name.utf8()); + len = line.length(); + for (i = 0; i < len; i++) + c.putch (line[i]); + + len = colln->about.length(); + if (len > 0) { + QCString aboutC = colln->about.utf8(); + len = aboutC.length(); // Might be longer now. + for (i = 0; i < len; i++) { + ch = aboutC[i]; + if (ch != '\n') { + c.putch (ch); // Copy the character. + } + else { + c.putch ('\\'); // Change newline to \ and n. + c.putch ('n'); + } + } + c.putch ('\n'); // Add a real newline. + } + } + } + + c.close(); + return (TRUE); +} + +/******************************************************************************/ +/********************** WORD-WRAPPED MESSAGE BOX ************************/ +/******************************************************************************/ + +void KGrGame::myMessage (QWidget * parent, QString title, QString contents) +{ + // Halt the game while the message is displayed. + setMessageFreeze (TRUE); + + KGrMessage::wrapped (parent, title, contents); + + // Unfreeze the game, but only if it was previously unfrozen. + setMessageFreeze (FALSE); +} + +/******************************************************************************/ +/*********************** COLLECTION DATA CLASS **************************/ +/******************************************************************************/ + +KGrCollection::KGrCollection (Owner o, const QString & n, const QString & p, + const char s, int nl, const QString & a) +{ + // Holds information about a collection of KGoldrunner levels (i.e. a game). + owner = o; name = n; prefix = p; settings = s; nLevels = nl; about = a; +} + +#include "kgrgame.moc" diff --git a/kgoldrunner/src/kgrgame.h b/kgoldrunner/src/kgrgame.h new file mode 100644 index 00000000..86144ef6 --- /dev/null +++ b/kgoldrunner/src/kgrgame.h @@ -0,0 +1,309 @@ +/*************************************************************************** + * Copyright (C) 2003 by Ian Wadham and Marco Krüger * + * ianw2@optusnet.com.au * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ +#ifndef KGRGAME_H +#define KGRGAME_H + +// Macros to smooth out the differences between Qt 1 and Qt 2 classes. +// +// "myStr" converts a QString object to a C language "char*" character string. +// "myChar" extracts a C language character (type "char") from a QString object. +// "endData" checks for an end-of-file condition. +// +#define myStr latin1 +#define myChar(i) at((i)).latin1() +#define endData atEnd + +#include + +#ifdef QT3 +#include +#else +#include +#endif +#include + +#include +#include +#include +#include + +#include + +/** +Sets up games and levels in KGoldrunner and controls the play. + +@author Ian Wadham +*/ + +class KGrObject; +class KGrHero; +class KGrEnemy; +class KGrCollection; + +class KGrGame : public QObject +{ +Q_OBJECT +public: + KGrGame (KGrCanvas * theView, QString theSystemDir, QString theUserDir); + ~KGrGame(); + + bool initCollections(); + KGrHero * getHero(); + + int getLevel(); + + void startPlaying(); + + bool inMouseMode(); // True if the game is in mouse mode. + bool inEditMode(); // True if the game is in editor mode. + bool isLoading(); // True if a level is being loaded. + + bool saveOK (bool exiting); // Check if edits were saved. + + QString getTitle(); // Collection - Level NNN, Name. + + void setEditObj (char newEditObj); // Set object for editor to paint. + + QString getFilePath (Owner o, KGrCollection * colln, int lev); + +public slots: + void startLevelOne(); // Start any game from level 1. + void startAnyLevel(); // Start any game from any level. + void startNextLevel(); // Start next level of current game. + + void setMouseMode (bool on_off); // Set mouse OR keyboard control. + void startLevel (int startingAt, int requestedLevel); + void newGame (const int lev, const int gameIndex); + void startTutorial(); // Start tutorial game. + void showHint(); // Show hint for current level. + + void showHighScores(); // Show high scores for current game. + + void incScore(int); // Update the score. + void herosDead(); // Hero was caught or he quit (key Q). + void showHiddenLadders(); // Show hidden ladders (nuggets gone). + void goUpOneLevel(); // Hero completed the level. + void loseNugget(); // Nugget destroyed (not collected). + void heroAction (KBAction movement);// Move hero under keyboard control. + + void saveGame(); // Save game ID, score and level. + void loadGame(); // Re-load game, score and level. + +signals: + void showScore (long); // For main window to show the score. + void showLives (long); // For main window to show lives left. + void showLevel (int); // For main window to show the level. + + void hintAvailable (bool); // For main window to adjust menu text. + + void setEditMenu (bool); // Enable/Disable edit menu items. + void defaultEditObj(); // Set default edit-toolbar button. + + void markRuleType (char); // Mark KGoldrunner/Traditional rules. + void gameFreeze (bool); // Do visual feedback in the GUI. + +private slots: + void finalBreath (); // Hero is dead: re-start the level. + void readMousePos (); // Timed reading of mouse position. + void doDig (int button); // Dig when under mouse-button control. + +private: + void setBlankLevel (bool playable); + int loadLevel (int levelNo); + bool openLevelFile (int levelNo, QFile & openlevel); + void changeObject (unsigned char kind, int i, int j); + void createObject (KGrObject *o, char picType, int x, int y); + void setTimings (); + void initSearchMatrix(); + + void checkHighScore(); // Check if high score for current game. + + int selectLevel (int action, int requestedLevel); + int selectedGame; + + void restart(); // Kickstart the game action. + +/******************************************************************************/ +/************************** PLAYFIELD AND GAME DATA *************************/ +/******************************************************************************/ + +private: + KGrCanvas * view; // Where the game is displayed. + QString systemDataDir; // System games are stored here. + QString userDataDir; // User games are stored here. + + KGrObject * playfield[30][22]; // Array of playfield objects. + char editObjArray[30][22]; // Character-code equivalent. + char lastSaveArray[30][22]; // Copy for use in "saveOK()". + + int level; // Current play/edit level. + QString levelName; // Level name (optional). + QString levelHint; // Level hint (optional). + + long lives; // Lives remaining. + long score; // Current score. + long startScore; // Score at start of level. + + KGrHero * hero; // The HERO figure !! Yay !!! + int startI, startJ; // The hero's starting position. + +#ifdef QT3 + QPtrList enemies; // The list of enemies. +#else + QList enemies; // The list of enemies. +#endif + int enemyCount; // How many enemies. + KGrEnemy * enemy; // One of the enemies. + + int nuggets; // How many gold nuggets. + + bool newLevel; // Next level will be a new one. + bool loading; // Stop input until it's loaded. + + bool modalFreeze; // Stop game during dialog. + bool messageFreeze; // Stop game during message. + + QTimer * mouseSampler; // Timer for mouse tracking. + QTimer * dyingTimer; // For pause when the hero dies. + + int lgHighlight; // Row selected in "loadGame()". + +/******************************************************************************/ +/************************** AUTHORS' DEBUGGING AIDS **************************/ +/******************************************************************************/ + +public slots: + void doStep(); // Do one animation step. + void showFigurePositions(); // Show everybody's co-ordinates. + void showHeroState(); // Show hero's co-ordinates and state. + void showEnemyState (int); // Show enemy's co-ordinates and state. + void showObjectState(); // Show an object's state. + void bugFix(); // Turn a bug fix on/off dynamically. + void startLogging(); // Turn logging on/off. + +/******************************************************************************/ +/******************** GAME EDITOR PROPERTIES AND METHODS ********************/ +/******************************************************************************/ + +public slots: // Slots connected to the Menu and Edit Toolbar. + void createLevel(); // Set up a blank level-display for edit. + void updateLevel(); // Update an existing level. + void updateNext(); // Update the current level + 1. + void editNameAndHint(); // Run a dialog to edit the level name and hint. + bool saveLevelFile(); // Save the edited level in a text file (.grl). + void moveLevelFile(); // Move level to another collection or number. + void deleteLevelFile(); // Delete a level file. + + void editCollection (int action); + + void setLevel (int lev); // Set level to be edited. + + void freeze(); // Stop the gameplay action. + void unfreeze(); // Restart the gameplay action. + void setMessageFreeze (bool); + +private: + bool mouseMode; // Flag to set up keyboard OR mouse control. + bool editMode; // Flag to change keyboard and mouse functions. + char editObj; // Type of object to be painted by the mouse. + bool paintEditObj; // Sets painting on/off (toggled by clicking). + int oldI, oldJ; // Last mouse position painted. + int editLevel; // Level to be edited (= 0 for new level). + int heroCount; // Can enter at most one hero. + bool shouldSave; // True if name or hint was edited. + +private: + void loadEditLevel (int); // Load and display an existing level for edit. + void initEdit(); + void deleteLevel(); + void insertEditObj (int, int); + void setEditableCell (int, int, char); + void showEditLevel(); + bool reNumberLevels (int, int, int, int); + bool ownerOK (Owner o); + + // Pixmaps for repainting objects as they are edited. + QPixmap digpix[10]; + QPixmap brickbg, fbrickbg; + QPixmap freebg, nuggetbg, polebg, betonbg, ladderbg, hladderbg; + QPixmap edherobg, edenemybg; + +private slots: + void doEdit(int); // For mouse-click when in edit-mode. + void endEdit(int); // For mouse-release when in edit-mode. + +/******************************************************************************/ +/******************** COLLECTION PROPERTIES AND METHODS *******************/ +/******************************************************************************/ + +private: + +// Note that a collection of KGoldrunner levels is the same thing as a "game". +#ifdef QT3 + QPtrList collections; // List of ALL collections. +#else + QList collections; // List of ALL collections. +#endif + + KGrCollection * collection; // Collection currently in use. + Owner owner; // Collection owner. + int collnIndex; // Index in collections list. + + void mapCollections(); + bool loadCollections (Owner); + bool saveCollections (Owner); + +/******************************************************************************/ +/********************** WORD-WRAPPED MESSAGE BOX ************************/ +/******************************************************************************/ + + void myMessage (QWidget * parent, QString title, QString contents); +}; + +/******************************************************************************/ +/********************** CLASS TO DISPLAY THUMBNAIL ***********************/ +/******************************************************************************/ + +class KGrThumbNail : public QFrame +{ +public: + KGrThumbNail (QWidget *parent = 0, const char *name = 0); + void setFilePath (QString &, QLabel *); // Set filepath and name field. + + static QColor backgroundColor; + static QColor brickColor; + static QColor ladderColor; + static QColor poleColor; + +protected: + void drawContents (QPainter *); // Draw a preview of a level. + QString filePath; + QLabel * lName; +}; + +/******************************************************************************/ +/*********************** COLLECTION DATA CLASS **************************/ +/******************************************************************************/ + +// Note that a collection of KGoldrunner levels is the same thing as a "game". +class KGrCollection +{ +public: + KGrCollection (Owner o, const QString & n, const QString & p, + const char s, int nl, const QString & a); + Owner owner; // Collection owner: "System" or "User". + QString name; // Collection name. + QString prefix; // Collection's filename prefix. + char settings; // Collection rules: KGoldrunner or Traditional. + int nLevels; // Number of levels in the collection. + QString about; // Optional text about the collection. +}; + +#endif diff --git a/kgoldrunner/src/kgrobject.cpp b/kgoldrunner/src/kgrobject.cpp new file mode 100644 index 00000000..948fd540 --- /dev/null +++ b/kgoldrunner/src/kgrobject.cpp @@ -0,0 +1,196 @@ +/*************************************************************************** + kgrobject.cpp - description + ------------------- + begin : Wed Jan 23 2002 + copyright : (C) 2002 by Marco Krüger and Ian Wadham + email : See menu "Help, About KGoldrunner" + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kgrconsts.h" +#include "kgrcanvas.h" + +#include "kgrobject.h" + +#include + +KGrObject::KGrObject (char objType) +{ + iamA = objType; + searchValue = 0; + blocker = FALSE; + if ((objType == BRICK) || (objType == BETON) || (objType == FBRICK)) { + blocker = TRUE; + } + xpos = 0; + ypos = 0; + objectView = NULL; +} + +bool KGrObject::frozen = FALSE; // Initialise game as running, not halted. +bool KGrObject::bugFixed = FALSE;// Initialise game with dynamic bug-fix OFF. +bool KGrObject::logging = FALSE;// Initialise game with log printing OFF. + +char KGrObject::whatIam () +{ + return iamA; +} + +void KGrObject::showState (int i, int j) +{ + printf("(%02d,%02d) - Object [%c] search %d", i, j, iamA, searchValue); + if (blocker) printf(" blocker"); + printf("\n"); +} + +KGrObject :: ~KGrObject () +{ +} + +KGrEditable::KGrEditable (char editType) : KGrObject (editType) +{ +} + +void KGrEditable::setType (char editType) +{ + iamA = editType; +} + +KGrEditable::~KGrEditable () +{ +} + +KGrFree::KGrFree (char objType, int i, int j, KGrCanvas * view) + : KGrObject (objType) +{ + xpos = i; + ypos = j; + objectView = view; + theRealMe = FREE; // Remember what we are, even "iamA == NUGGET". +} + +void KGrFree::setNugget(bool nug) +{ + // This code must work over a hidden ladder as well as a free cell. + if (! nug) { + iamA = theRealMe; + objectView->paintCell (xpos, ypos, FREE); + } + else { + iamA = NUGGET; + objectView->paintCell (xpos, ypos, NUGGET); + } +} + +KGrFree :: ~KGrFree () +{ +} + +/* +++++++++++++++ BRICK ++++++++++++++++ */ + +KGrBrick::KGrBrick (char objType, int i, int j, KGrCanvas * view) + : KGrObject (objType) +{ + xpos = i; + ypos = j; + objectView = view; + dig_counter = 0; + holeFrozen = FALSE; + iamA = BRICK; + timer = new QTimer (this); + connect (timer, SIGNAL (timeout ()), SLOT (timeDone ())); +} + +void KGrBrick::dig (void) +{ + dig_counter = 1; + hole_counter = HOLETIME; + iamA = HOLE; + objectView->paintCell (xpos, ypos, BRICK, dig_counter); + objectView->updateCanvas(); + timer->start ((DIGDELAY * NSPEED) / speed, TRUE); +} + +void KGrBrick::doStep() { + if (holeFrozen) { + holeFrozen = FALSE; + timeDone(); + } +} + +void KGrBrick::showState (int i, int j) +{ + printf ("(%02d,%02d) - Brick [%c] search %d dig-counter %d", + i, j, iamA, searchValue, dig_counter); + if (blocker) + printf (" blocker"); + printf ("\n"); +} + +void KGrBrick::timeDone () +{ + if (KGrObject::frozen) {holeFrozen = TRUE; return;} + + // When the hole is complete, we need a longer delay. + if (dig_counter == 5) { + hole_counter--; + if (hole_counter > 0) { + timer->start ((DIGDELAY * NSPEED) / speed, TRUE); + return; + } + } + if (dig_counter < 9) { + dig_counter++; + timer->start ((DIGDELAY * NSPEED) / speed, TRUE); + if (dig_counter >= 8) + iamA = BRICK; + } + else + dig_counter = 0; + + // Brick pix:- 0 normal, 1-4 crumbling, 5 hole complete, 6-9 re-growing. + objectView->paintCell (xpos, ypos, BRICK, dig_counter); + objectView->updateCanvas(); +} + +void KGrBrick::useHole() { + if (iamA == HOLE) + iamA = USEDHOLE; +} + +void KGrBrick::unUseHole() { + if (iamA == USEDHOLE) + iamA = HOLE; +} + +KGrBrick :: ~KGrBrick () +{ + delete timer; +} + +KGrHladder::KGrHladder (char objType, int i, int j, KGrCanvas * view) + : KGrFree (objType, i, j, view) + // Must inherit "setNugget()" from "KGrFree". +{ + theRealMe = HLADDER; // But remember we are a hidden ladder ... +} + +void KGrHladder::showLadder() +{ + iamA = LADDER; + objectView->paintCell (xpos, ypos, LADDER); +} + +KGrHladder :: ~KGrHladder () +{ +} + +#include "kgrobject.moc" diff --git a/kgoldrunner/src/kgrobject.h b/kgoldrunner/src/kgrobject.h new file mode 100644 index 00000000..a48eba88 --- /dev/null +++ b/kgoldrunner/src/kgrobject.h @@ -0,0 +1,107 @@ +/*************************************************************************** + kgrobject.h - description + ------------------- + begin : Wed Jan 23 2002 + copyright : (C) 2002 by Marco Krüger and Ian Wadham + email : See menu "Help, About KGoldrunner" + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KGROBJECT_H +#define KGROBJECT_H + +// Obsolete - #include +#include + +#include +#include // for random + +class KGrCanvas; + +class KGrObject : public QObject +{ + Q_OBJECT +public: + KGrObject (char objType); + virtual ~KGrObject(); + + // STATIC GLOBAL FLAGS. + static bool frozen; // Game play halted (use ESCAPE key). + static bool bugFixed; // Dynamic bug fix turned on (key B, if halted). + static bool logging; // Log printing turned on. + + char whatIam(); + int searchValue; + bool blocker; // Beton or Brick -> TRUE + void showState (int, int); + +protected: + KGrCanvas * objectView; + int xpos; + int ypos; + char iamA; +}; + +class KGrEditable : public KGrObject +{ + Q_OBJECT +public: + KGrEditable (char editType); + virtual ~KGrEditable (); + void setType (char); +}; + +class KGrFree : public KGrObject +{ Q_OBJECT +public: + KGrFree (char objType, int i, int j, KGrCanvas * view); + virtual ~KGrFree(); + void setNugget(bool); + +protected: + char theRealMe; // Set to FREE or HLADDER, even when "iamA == NUGGET". +}; + +class KGrBrick : public KGrObject +{ + Q_OBJECT +public: + KGrBrick (char objType, int i, int j, KGrCanvas * view); + virtual ~KGrBrick(); + void dig(void); + void useHole(); + void unUseHole(); + static int speed; // Digging & repair speed (copy of KGrFigure::speed). + static int HOLETIME; // Number of timing cycles for a hole to remain open. + void doStep(); + void showState (int, int); + +protected slots: + void timeDone(void); + +private: + int dig_counter; + int hole_counter; + bool holeFrozen; + QTimer *timer; +}; + +class KGrHladder : public KGrFree +{ + Q_OBJECT +public: + // BUG FIX - Ian W., 21/6/01 - must inherit "setNugget()" from "KGrFree". + KGrHladder (char objType, int i, int j, KGrCanvas * view); + virtual ~KGrHladder(); + void showLadder(); +}; + +#endif // KGROBJECT_H diff --git a/kgoldrunner/src/main.cpp b/kgoldrunner/src/main.cpp new file mode 100644 index 00000000..63be6a71 --- /dev/null +++ b/kgoldrunner/src/main.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2003 Ian Wadham and Marco Krüger + */ + +#include +#include +#include +#include +#include + +#include "kgrconsts.h" +#include "kgoldrunner.h" + +static const char description[] = + I18N_NOOP("KGoldrunner is a game of action and puzzle solving"); + +static const char version[] = "2.0"; + +int main (int argc, char **argv) +{ + KAboutData about("kgoldrunner", I18N_NOOP("KGoldrunner" ), + version, description, + KAboutData::License_GPL, + "(C) 2003 Ian Wadham and Marco Krüger"); + about.addAuthor( "Ian Wadham", I18N_NOOP("Current author"), + "ianw2@optusnet.com.au" ); + about.addAuthor( "Marco Krüger", I18N_NOOP("Original author"), 0); + + KCmdLineArgs::init (argc, argv, &about); + + KApplication app; + + // Register as a DCOP client. + app.dcopClient()->registerAs (app.name(), false); + + // See if we are starting with session management. + if (app.isRestored()) + { + RESTORE(KGoldrunner); + return app.exec(); + } + else + { + KGoldrunner * widget = new KGoldrunner; + if (widget->startedOK()) { + widget->show(); + return app.exec(); + } + } +} diff --git a/kjumpingcube/AUTHORS b/kjumpingcube/AUTHORS new file mode 100644 index 00000000..2dc6d21a --- /dev/null +++ b/kjumpingcube/AUTHORS @@ -0,0 +1,2 @@ +Matthias Kiefer +Benjamin Meyer diff --git a/kjumpingcube/ChangeLog b/kjumpingcube/ChangeLog new file mode 100644 index 00000000..f6b6f5cc --- /dev/null +++ b/kjumpingcube/ChangeLog @@ -0,0 +1,27 @@ +chagnes 1.1: +- Added Settings dialog +- finetuned the code +- removed a lot of unessessary code with newer qt3 code. + +changes 0.7.3: +- finetuning and some bugfixes + +changes 0.7.2: +- rewritten everything (just to learn better c++ ;-) +- implemented better and faster algorithm for the computerplayer +- added possibility to save/load games in/from any file. +- KJumpingCube is now fully KDE-compliant + +changes 0.7.0.1: +- rewrote parts of CubeBox and Cube, move is now managed from CubeBox +- added computerplayer +- added undo-function + +changes 0.6.1: +- after reseting cubes are drawn in the global configured backgroundcolor + +changes 0.6: +- added sessionmanagment +- added standardaccelerators +- added possibility to save and restore a game + diff --git a/kjumpingcube/Makefile.am b/kjumpingcube/Makefile.am new file mode 100644 index 00000000..fdb5a185 --- /dev/null +++ b/kjumpingcube/Makefile.am @@ -0,0 +1,23 @@ + +INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes) +METASOURCES = AUTO +KDE_ICON = kjumpingcube + +bin_PROGRAMS = kjumpingcube +kjumpingcube_SOURCES = kjumpingcube.cpp kcubeboxwidget.cpp main.cpp \ + kcubewidget.cpp cube.cpp brain.cpp cubebox.cpp \ + settings.ui prefs.kcfgc + +kjumpingcube_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kjumpingcube_LDADD = $(LIB_KDEGAMES) $(LIB_KFILE) +kjumpingcube_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +xdg_apps_DATA = kjumpingcube.desktop +kde_kcfg_DATA = kjumpingcube.kcfg + +rcdir = $(kde_datadir)/kjumpingcube +rc_DATA = kjumpingcubeui.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kjumpingcube.pot + diff --git a/kjumpingcube/README b/kjumpingcube/README new file mode 100644 index 00000000..52c9040f --- /dev/null +++ b/kjumpingcube/README @@ -0,0 +1,39 @@ +KJumpingCube +Matthias Kiefer +---------------------------------------------------------------------- + +KJumpingCube is a tactical one- or two-player game. +For further information read the online help-documentation. + +For installing instructions read INSTALL. + +I hope you will enjoy it. Have a lot of fun :-) + + +Don't hesitate to contact me if you find bugs or if you have +questions/suggestions on the implementation. + + +Thanks to Krzysztof P. Jasiutowicz for suggestions and moral support. + + + + Copyright (C) 1998,1999 by Matthias Kiefer + + KJumpingCube is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +-- +Matthias Kiefer +5.Apr 1999 diff --git a/kjumpingcube/brain.cpp b/kjumpingcube/brain.cpp new file mode 100644 index 00000000..e2d63815 --- /dev/null +++ b/kjumpingcube/brain.cpp @@ -0,0 +1,621 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ + +#include "brain.h" +#include "cube.h" + +#include + +#include + +#undef DEBUG // uncomment this to get useful messages +#include + +#ifdef DEBUG +#include +#endif + +#include "prefs.h" + +Brain::Brain(int initValue) +{ + setSkill(Prefs::EnumSkill::Beginner); + stopped=false; + active=false; + currentLevel=0; + + // initialize the random number generator + random.setSeed(initValue); +} + +void Brain::setSkill(int newSkill) +{ + _skill=newSkill; + + switch(_skill) + { + case Prefs::EnumSkill::Beginner: + maxLevel=1; + break; + case Prefs::EnumSkill::Average: + maxLevel=3; + break; + case Prefs::EnumSkill::Expert: + maxLevel=5; + break; + default: + break; + } +} + +int Brain::skill() const +{ + return _skill; +} + +void Brain::stop() +{ + stopped=true; +} + + +bool Brain::isActive() const +{ + return active; +} + + + +bool Brain::getHint(int& row, int& column,CubeBox::Player player ,CubeBox box) +{ + if(isActive()) + return false; + + active=true; + stopped=false; + currentPlayer=player; + + int i=0,j=0; + int moves=0; // how many moves are the favourable ones + CubeBox::Player opponent=(player==CubeBox::One)?CubeBox::Two : CubeBox::One; + + // if more than one cube has the same rating this array is used to select + // one + coordinate* c2m=new coordinate[box.dim()*box.dim()]; + + // Array, which holds the assessment of the separate moves + double **worth=new double*[box.dim()]; + for(i=0;iowner()!=(Cube::Owner)opponent) + { + if(worth[c2m[i].row][c2m[i].column]>max ) + { + max=worth[c2m[i].row][c2m[i].column]; + } + } + } + +#ifdef DEBUG + cerr << "found Maximum : " << max << endl; +#endif + + // found maximum more than one time ? + int counter=0; + for(i=0;iowner() != (Cube::Owner)opponent) + { + c2m[counter].row=c2m[i].row; + c2m[counter].column=c2m[i].column; + counter++; + } + } + + assert(counter>0); + + + // if some moves are equal, choose a random one + if(counter>1) + { + +#ifdef DEBUG + cerr << "choosing a random cube: " << endl ; +#endif + counter=random.getLong(counter); + } + + row=c2m[counter].row; + column=c2m[counter].column; +#ifdef DEBUG + cerr << "cube: " << row << "," << column << endl; +#endif + } + + // clean up + for(i=0;iprocessEvents(); + + // if thinking process stopped + if(stopped) + { + currentLevel--; + return 0; + } + + // simulate every possible move + worth+=doMove(c2m[i].row,c2m[i].column,player,box); + } + } + delete [] c2m; + currentLevel--; + return worth; + + } + else + { + // if maximum depth of recursive calls are reached, return the assessment + currentLevel--; + box.simulateMove(player,row,column); + + return box.assessField(currentPlayer); + } + +} + +int Brain::findCubes2Move(coordinate *c2m,CubeBox::Player player,CubeBox& box) +{ + int i,j; + int opponent=(player==CubeBox::One)? CubeBox::Two : CubeBox::One; + int moves=0; + int min=9999; + + if(_skill==Prefs::EnumSkill::Beginner) + { + int max=0; + for(i=0;iowner() != opponent) + { + c2m[moves].row=i; + c2m[moves].column=j; + c2m[moves].val=box[i][j]->value(); + + if(c2m[moves].val>max) + max=c2m[moves].val; + + moves++; + + } + } + + // find all moves with maximum value + int counter=0; + for(i=0;iowner() != opponent) + { + int val; + + // check neighbours of every cube + val=assessCube(i,j,player,box); + + +#ifdef DEBUG + if(currentLevel==0) + cerr << i << "," << j << " : " << val << endl; +#endif + // only if val >= 0 its a favourable move + if( val > 0 ) + { + if(valowner() != opponent) + { + c2m[moves].row=i; + c2m[moves].column=j; + c2m[moves].val=( box[i][j]->max() - box[i][j]->value() ); + if(c2m[moves].val maxMoves) + { + // find maxMoves random cubes to move with + coordinate* tempC2M=new coordinate[maxMoves]; + + coordinate tmp={-1,-1,0}; + for(i=0;imax()-box[row][column]->value() ); + + int val; + val=diff-temp+1; + val=val*(temp+1); + + return val; +} + + +int Brain::getDiff(int row,int column, CubeBox::Player player, CubeBox& box) const +{ + int diff; + + if(box[row][column]->owner() != (Cube::Owner)player) + { + diff=( box[row][column]->max() - box[row][column]->value() ); + } + else + { + diff=( box[row][column]->max() - box[row][column]->value()+1 ); + } + + return diff; +} + diff --git a/kjumpingcube/brain.h b/kjumpingcube/brain.h new file mode 100644 index 00000000..8f270705 --- /dev/null +++ b/kjumpingcube/brain.h @@ -0,0 +1,135 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#ifndef BRAIN_H +#define BRAIN_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "cubebox.h" + +/** @internal */ +struct coordinate +{ + int row; + int column; + + int val; +}; + + +/** +* Class Brain computes a (good) possibility to move +* for a given playingfield. +* +* It puts a value on every cube by looking at its neighbours +* and searches the best cubes to move. It then simulates what would +* happen, if you would click on these cubes. This is done recursively +* to a certain depth and the playingfield will be valued. +* +* @short The games brain +*/ +class Brain +{ +public: + /** + * @param initValue value to initialize the random number generator with + * if no value is given a truly random value is used + */ + Brain(int initValue=0); + + /** + * Computes a good possible move at the given field. + * The index of this Cube is stored in given 'row' and 'column' + * + * @return false if computing was stopped + * @see Brain#stop; + */ + bool getHint(int& row, int& column, CubeBox::Player player, CubeBox field); + + /** stops thinking */ + void stop(); + /** @return true if the Brain is thinking at the moment */ + bool isActive() const; + + /** skill according to Prefs::EnumSkill **/ + void setSkill(int); + int skill() const; + +private: + /** + * checks if a move is possible at cube row,column from player 'player' and + * simulates this move. Then it checks the new playingfield for possible moves + * and calls itself for every possible move until the maximum depth 'maxLevel' + * is reached. + * + * If the maximum depth is reached, it puts a value on the playingfield and returns this. + * @see CubeBox#simulateMove + * @see CubeBox#assessField + * @see Brain#findCubes2Move + * + * @param row,column coordinates of cube to increase + * @param player for which player the cube has to be increased + * @param box playingfield to do the moves on + * @return the value put on the field + */ + double doMove(int row,int column,CubeBox::Player player, CubeBox box); + /** + * Checks the given playingfield, which cubes are favourable to do a move + * by checking every cubes neighbours. And looking for the difference to overflow. + * + * @param c2m Array in which the coordinates of the best cubes to move will be stored + * @param player for which player to check + * @param box playingfield to check + * @param debug if debugmessages should be printed + * @return number of found cubes to move + */ + int findCubes2Move(coordinate* c2m,CubeBox::Player player,CubeBox& box); + /** + * + */ + int assessCube(int row,int column,CubeBox::Player,CubeBox& box) const; + int getDiff(int row,int column, CubeBox::Player player, CubeBox& box) const; + + /** current depth of recursive simulating of the moves */ + int currentLevel; + /** maximum depth of recursive thinking */ + int maxLevel; + /** the player for which to check the moves */ + CubeBox::Player currentPlayer; + + + /** flag, if the engine has to be stopped */ + bool stopped; + /** flag, if the engine is active */ + bool active; + /** skill of the Brain, see Prefs::EnumSkill */ + int _skill; + + /** Sequence generator */ + KRandomSequence random; +}; + +#endif //BRAIN_H diff --git a/kjumpingcube/cube.cpp b/kjumpingcube/cube.cpp new file mode 100644 index 00000000..59e2dc58 --- /dev/null +++ b/kjumpingcube/cube.cpp @@ -0,0 +1,99 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#include "cube.h" +#include + +/* ****************************************************** ** +** Class Cube ** +** ****************************************************** */ + +Cube::Cube(Owner owner,int value,int maximum) +{ + _owner = owner; + _value = value; + _max = maximum; +} + + +Cube::Owner Cube::setOwner(Owner owner) +{ + Owner old=_owner; + _owner=owner; + + return old; +} + +void Cube::setValue(int value) +{ +#ifdef DEBUG + assert(value>0); +#endif + + _value = (value<1)? 1 : value; +} + + +void Cube::setMax(int max) +{ +#ifdef DEBUG + assert(max>1); +#endif + + _max = (max<2)? 2 : max; +} + + +void Cube::decrease() +{ + setValue(_value-_max); +} + +Cube::Owner Cube::owner() const +{ + return _owner; +} + + +int Cube::value() const +{ + return _value; +} + +bool Cube::increase(Owner newOwner) +{ + setValue(value()+1); + setOwner(newOwner); + + return (_value > _max); +} + +int Cube::max() const +{ + return _max; +} + + +bool Cube::overMax() const +{ + return (_value > _max); +} + diff --git a/kjumpingcube/cube.h b/kjumpingcube/cube.h new file mode 100644 index 00000000..9a58c02f --- /dev/null +++ b/kjumpingcube/cube.h @@ -0,0 +1,99 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#ifndef CUBE_H +#define CUBE_H + + +#ifdef HAVE_CONFIG_H +#include +#endif + +/** +* This Class is the internal representation of a cube. +*/ +class Cube +{ +public: + enum Owner{Nobody=0,One=1,Two=2}; + + /** + * constructs a Cube + */ + Cube(Owner owner=Nobody,int value=1,int max=4); + + + /** + * changes owner of the Cube + * @return old Owner + */ + virtual Owner setOwner(Owner owner); + + /** + * changes value of the Cube + */ + virtual void setValue(int value); + + /** + * sets maximum value of the Cube + */ + virtual void setMax(int max); + + /** + * increase the value of the Cube and set the owner of the Cube + * to 'newOwner'. + * @return true if the Cube's new value is over maximum + */ + virtual bool increase(Owner newOwner); + + /** + * substracts the maximum from the Cube's value + */ + virtual void decrease(); + + /** + * returns current owner + */ + Owner owner() const; + /** + * returns current value + */ + int value() const; + /** + * returns the maximum value of the cube + */ + int max() const; + + /** + * checks if the Cube's value is over maximum + */ + bool overMax() const; + +private: + + Owner _owner; + int _value; + int _max; + +}; + + +#endif diff --git a/kjumpingcube/cubebox.cpp b/kjumpingcube/cubebox.cpp new file mode 100644 index 00000000..91a90cc6 --- /dev/null +++ b/kjumpingcube/cubebox.cpp @@ -0,0 +1,292 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#include +#include +#include "cubebox.h" +#include "kcubeboxwidget.h" + + +CubeBox::CubeBox(const int d) + :CubeBoxBase(d) +{ + initCubes(); +} + +CubeBox::CubeBox(const CubeBox& box) + :CubeBoxBase(box.dim()) +{ + initCubes(); + + int i,j; + for(i=0;i(box.dim()) +{ + initCubes(); + + int i,j; + for(i=0;iowner()!=(Cube::Owner)fromWhom && cubes[row][column]->owner()!=Cube::Nobody) + return false; + + cubes[row][column]->increase((Cube::Owner)fromWhom); + + do + { + int i,j; + finished=true; + playerWon=true; + + // check all Cubes + for(i=0;ioverMax()) + { + increaseNeighbours(fromWhom,i,j); + cubes[i][j]->decrease(); + finished=false; + } + + if(cubes[i][j]->owner()!=(Cube::Owner)fromWhom) + playerWon=false; + } + } + + if(playerWon) + return true; + } + while(!finished); + + + return true; +} + +double CubeBox::assessField(Player player) const +{ + int cubesOne=0; + int cubesTwo=0; + int pointsOne=0; + int pointsTwo=0; + Player otherPlayer = ((player==One)? Two : One); + bool playerWon=true; + bool otherPlayerWon=true; + + int i,j; + + for(i=0;iowner()==(Cube::Owner)One) + { + cubesOne++; + pointsOne+=(int)pow((float)cubes[i][j]->value(),2); + } + else if(cubes[i][j]->owner()==(Cube::Owner)Two) + { + cubesTwo++; + pointsTwo+=(int)pow((float)cubes[i][j]->value(),2); + } + + if(cubes[i][j]->owner()!=(Cube::Owner)player) + playerWon=false; + + if(cubes[i][j]->owner()!=(Cube::Owner)otherPlayer) + otherPlayerWon=false; + } + + } + + + + if(player==One) + { + return (int)pow((float)cubesOne,2)+pointsOne-(int)pow(cubesTwo,2)-pointsTwo; + } + else + return (int)pow((float)cubesTwo,2)+pointsTwo-(int)pow(cubesOne,2)-pointsOne; + +} + +bool CubeBox::playerWon(Player who) const +{ + int i,j; + + for(i=0;iowner()!=(Cube::Owner)who) + return false; + } + + return true; +} + + +void CubeBox::increaseNeighbours(CubeBox::Player forWhom,int row,int column) +{ + Cube::Owner _player = (Cube::Owner)(forWhom); + + if(row==0) + { + if(column==0) // top left corner + { + cubes[0][1]->increase(_player); + cubes[1][0]->increase(_player); + return; + } + else if(column==dim()-1) // top right corner + { + cubes[0][dim()-2]->increase(_player); + cubes[1][dim()-1]->increase(_player); + return; + } + else // top edge + { + cubes[0][column-1]->increase(_player); + cubes[0][column+1]->increase(_player); + cubes[1][column]->increase(_player); + return; + } + } + else if(row==dim()-1) + { + if(column==0) // left bottom corner + { + cubes[dim()-2][0]->increase(_player); + cubes[dim()-1][1]->increase(_player); + return; + } + + else if(column==dim()-1) // right bottom corner + { + cubes[dim()-2][dim()-1]->increase(_player); + cubes[dim()-1][dim()-2]->increase(_player); + return; + } + else // bottom edge + { + cubes[dim()-1][column-1]->increase(_player); + cubes[dim()-1][column+1]->increase(_player); + cubes[dim()-2][column]->increase(_player); + return; + } + } + else if(column==0) // left edge + { + cubes[row-1][0]->increase(_player); + cubes[row+1][0]->increase(_player); + cubes[row][1]->increase(_player); + return; + } + else if(column==dim()-1) // right edge + { + cubes[row-1][dim()-1]->increase(_player); + cubes[row+1][dim()-1]->increase(_player); + cubes[row][dim()-2]->increase(_player); + return; + } + else + { + cubes[row][column-1]->increase(_player); + cubes[row][column+1]->increase(_player); + cubes[row-1][column]->increase(_player); + cubes[row+1][column]->increase(_player); + return; + } + + +} diff --git a/kjumpingcube/cubebox.h b/kjumpingcube/cubebox.h new file mode 100644 index 00000000..d1236410 --- /dev/null +++ b/kjumpingcube/cubebox.h @@ -0,0 +1,62 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#ifndef CUBEBOX_H +#define CUBEBOX_H + +#include "cubeboxbase.h" + +class Cube; +class KCubeBoxWidget; + +#ifdef HAVE_CONFIG_H +#include +#endif + +/** +* Class for storing information about the playingfield, e.g. +* to undo a move or computing the next move +*/ +class CubeBox : public CubeBoxBase +{ +public: + /** + * constructs a CubeBox with 'dim' x 'dim' Cubes + */ + CubeBox(const int dim=1); + CubeBox(const CubeBox&); + CubeBox(KCubeBoxWidget&); + virtual ~CubeBox(); + + CubeBox& operator= (const CubeBox& box); + CubeBox& operator= (KCubeBoxWidget& box); + + bool simulateMove(Player fromWhom,int row, int column); + double assessField(Player forWhom) const; + bool playerWon(Player who) const; + +private: + void increaseNeighbours(CubeBox::Player forWhom,int row,int column); + +}; + +#endif // CUBEBOX_H + diff --git a/kjumpingcube/cubeboxbase.h b/kjumpingcube/cubeboxbase.h new file mode 100644 index 00000000..63bc0529 --- /dev/null +++ b/kjumpingcube/cubeboxbase.h @@ -0,0 +1,246 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#ifndef CUBEBOXBASE_H +#define CUBEBOXBASE_H + +#ifdef DEBUG +#include +#endif + +template +class CubeBoxBase +{ +public: + enum Player{One=1,Two=2}; + + CubeBoxBase(const int dim=1); + virtual ~CubeBoxBase(); + + T** operator[](const int index); + /** + * sets number of Cubes in a row/column to 'size'. + */ + virtual void setDim(int dim); + + /** + * returns number of Cubes in a row/column + */ + inline int dim() const { return _dim; } + + inline Player player() const { return currentPlayer; } + +protected: + virtual void deleteCubes(); + virtual void initCubes(); + + /** increases the neighbours of cube at ['row','column'] */ + //void increaseNeighbours(int forWhom,int row,int column); + + T*** cubes; + Player currentPlayer; + +private: + int _dim; + +}; + +template +CubeBoxBase::CubeBoxBase(const int dim) +{ +#ifdef DEBUG + assert(dim>0); +#endif + + _dim=dim; + currentPlayer=One; +} + +template +CubeBoxBase::~CubeBoxBase() +{ + if(cubes) + deleteCubes(); +} + +template +T** CubeBoxBase::operator[](const int index) +{ +#ifdef DEBUG + assert(index >= 0); +#endif + + return cubes[index]; +} + +template +void CubeBoxBase::setDim(int d) +{ + if(d != _dim) + { + deleteCubes(); + + _dim=d; + + initCubes(); + } +} + + +template +void CubeBoxBase::initCubes() +{ + const int s=dim(); + + int i,j; + // create new cubes + cubes = new T**[s]; + for(i=0;isetMax(2); + cubes[0][max]->setMax(2); + cubes[max][0]->setMax(2); + cubes[max][max]->setMax(2); + + for(i=0;i<=max;i++) + { + cubes[i][0]->setMax(3); + cubes[i][max]->setMax(3); + cubes[0][i]->setMax(3); + cubes[max][i]->setMax(3); + } + + for(i=1;isetMax(4); + } +} + +template +void CubeBoxBase::deleteCubes() +{ + int i,j; + for(i=0;i +void CubeBoxBase::increaseNeighbours(int forWhom,int row,int column) +{ + int _player = (T::Owner)(forWhom); + + if(row==0) + { + if(column==0) // linke obere Ecke + { + cubes[0][1]->increase(_player); + cubes[1][0]->increase(_player); + return; + } + else if(column==dim()-1) // rechte obere Ecke + { + cubes[0][dim()-2]->increase(_player); + cubes[1][dim()-1]->increase(_player); + return; + } + else // oberer Rand + { + cubes[0][column-1]->increase(_player); + cubes[0][column+1]->increase(_player); + cubes[1][column]->increase(_player); + return; + } + } + else if(row==dim()-1) + { + if(column==0) // linke untere Ecke + { + cubes[dim()-2][0]->increase(_player); + cubes[dim()-1][1]->increase(_player); + return; + } + + else if(column==dim()-1) // rechte untere Ecke + { + cubes[dim()-2][dim()-1]->increase(_player); + cubes[dim()-1][dim()-2]->increase(_player); + return; + } + else // unterer Rand + { + cubes[dim()-1][column-1]->increase(_player); + cubes[dim()-1][column+1]->increase(_player); + cubes[dim()-2][column]->increase(_player); + return; + } + } + else if(column==0) // linker Rand + { + cubes[row-1][0]->increase(_player); + cubes[row+1][0]->increase(_player); + cubes[row][1]->increase(_player); + return; + } + else if(column==dim()-1) // rechter Rand + { + cubes[row-1][dim()-1]->increase(_player); + cubes[row+1][dim()-1]->increase(_player); + cubes[row][dim()-2]->increase(_player); + return; + } + else + { + cubes[row][column-1]->increase(_player); + cubes[row][column+1]->increase(_player); + cubes[row-1][column]->increase(_player); + cubes[row+1][column]->increase(_player); + return; + } + + +} +*/ + +#endif // CUBEBOXBASE_H + diff --git a/kjumpingcube/hi128-app-kjumpingcube.png b/kjumpingcube/hi128-app-kjumpingcube.png new file mode 100644 index 00000000..30bf2fa2 Binary files /dev/null and b/kjumpingcube/hi128-app-kjumpingcube.png differ diff --git a/kjumpingcube/hi16-app-kjumpingcube.png b/kjumpingcube/hi16-app-kjumpingcube.png new file mode 100644 index 00000000..1ff05f55 Binary files /dev/null and b/kjumpingcube/hi16-app-kjumpingcube.png differ diff --git a/kjumpingcube/hi22-app-kjumpingcube.png b/kjumpingcube/hi22-app-kjumpingcube.png new file mode 100644 index 00000000..d2d86082 Binary files /dev/null and b/kjumpingcube/hi22-app-kjumpingcube.png differ diff --git a/kjumpingcube/hi32-app-kjumpingcube.png b/kjumpingcube/hi32-app-kjumpingcube.png new file mode 100644 index 00000000..aeff307e Binary files /dev/null and b/kjumpingcube/hi32-app-kjumpingcube.png differ diff --git a/kjumpingcube/hi48-app-kjumpingcube.png b/kjumpingcube/hi48-app-kjumpingcube.png new file mode 100644 index 00000000..7bcd613d Binary files /dev/null and b/kjumpingcube/hi48-app-kjumpingcube.png differ diff --git a/kjumpingcube/hi64-app-kjumpingcube.png b/kjumpingcube/hi64-app-kjumpingcube.png new file mode 100644 index 00000000..22ef300a Binary files /dev/null and b/kjumpingcube/hi64-app-kjumpingcube.png differ diff --git a/kjumpingcube/kcubeboxwidget.cpp b/kjumpingcube/kcubeboxwidget.cpp new file mode 100644 index 00000000..98f305d9 --- /dev/null +++ b/kjumpingcube/kcubeboxwidget.cpp @@ -0,0 +1,711 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#include "kcubeboxwidget.h" + +#include +#include +#include +#include +#include +#include + +#include "prefs.h" + +KCubeBoxWidget::KCubeBoxWidget(const int d,QWidget *parent,const char *name) + : QWidget(parent,name), + CubeBoxBase(d) +{ + init(); +} + + + +KCubeBoxWidget::KCubeBoxWidget(CubeBox& box,QWidget *parent,const char *name) + :QWidget(parent,name), + CubeBoxBase(box.dim()) +{ + init(); + + int i,j; + for(i=0;i(box.dim()) +{ + init(); + + int i,j; + for(i=0;ireset(); + } + + KCubeWidget::enableClicks(true); + + currentPlayer=One; + + emit playerChanged(One); + checkComputerplayer(One); +} + +void KCubeBoxWidget::undo() +{ + if(isActive()) + return; + + Player oldPlayer=currentPlayer; + + *this=*undoBox; + + if(oldPlayer!=currentPlayer) + emit playerChanged(currentPlayer); + + checkComputerplayer(currentPlayer); + +} + +void KCubeBoxWidget::getHint() +{ + if(isActive()) + return; + + int d=dim(); + for(int i=0;istopHint(); + } + + int row=0,column=0; + CubeBox field=*this; + + emit startedThinking(); + bool canceled=!brain.getHint(row,column,(CubeBox::Player)currentPlayer,field); + emit stoppedThinking(); + + if(canceled) + { + return; // return if thinking was stopped + } + cubes[row][column]->showHint(); +} + +void KCubeBoxWidget::setColor(Player player,QPalette color) +{ + KCubeWidget::setColor((Cube::Owner)player,color); + + for(int row=0;rowupdateColors(); + } +} + +void KCubeBoxWidget::setDim(int d) +{ + if(d != dim()) + { + undoBox->setDim(d); + CubeBoxBase::setDim(d); + } +} + +void KCubeBoxWidget::setComputerplayer(Player player,bool flag) +{ + if(player==One) + computerPlOne=flag; + else if(player==Two) + computerPlTwo=flag; +} + + +void KCubeBoxWidget::stopActivities() +{ + if(moveTimer->isActive()) + { + stopLoop(); + emit stoppedMoving(); + } + if(brain.isActive()) + { + brain.stop(); + emit stoppedThinking(); + } + +} + +void KCubeBoxWidget::saveProperties(KConfigBase* config) +{ + if(isMoving()) + { + stopActivities(); + undo(); + } + else if(brain.isActive()) + stopActivities(); + + // save current player + config->writeEntry("onTurn",(int)currentPlayer); + + QStrList list; + list.setAutoDelete(true); + QString owner, value, key; + int cubeDim=dim(); + + for(int row=0; row < cubeDim ; row++) + for(int column=0; column < cubeDim ; column++) + { + key.sprintf("%u,%u",row,column); + owner.sprintf("%u",cubes[row][column]->owner()); + value.sprintf("%u",cubes[row][column]->value()); + list.append(owner.ascii()); + list.append(value.ascii()); + config->writeEntry(key , list); + + list.clear(); + } + config->writeEntry("CubeDim",dim()); +} + +void KCubeBoxWidget::readProperties(KConfigBase* config) +{ + QStrList list; + list.setAutoDelete(true); + QString owner, value, key; + setDim(config->readNumEntry("CubeDim",5)); + int cubeDim=dim(); + + for(int row=0; row < cubeDim ; row++) + for(int column=0; column < cubeDim ; column++) + { + key.sprintf("%u,%u",row,column); + config->readListEntry(key, list); + owner=list.first(); + value=list.next(); + cubes[row][column]->setOwner((KCubeWidget::Owner)owner.toInt()); + cubes[row][column]->setValue(value.toInt()); + + list.clear(); + } + + + // set current player + int onTurn=config->readNumEntry("onTurn",1); + currentPlayer=(Player)onTurn; + emit playerChanged(onTurn); + checkComputerplayer((Player)onTurn); +} + +/* ***************************************************************** ** +** slots ** +** ***************************************************************** */ +void KCubeBoxWidget::setWaitCursor() +{ + setCursor(KCursor::waitCursor()); +} + + + +void KCubeBoxWidget::setNormalCursor() +{ + setCursor(KCursor::handCursor()); +} + +void KCubeBoxWidget::stopHint() +{ + + int d=dim(); + for(int i=0;istopHint(); + } + +} + +bool KCubeBoxWidget::checkClick(int row,int column, bool isClick) +{ + if(isActive()) + return false; + + // make the game start when computer player is player one and user clicks + if(isClick && currentPlayer == One && computerPlOne) + { + checkComputerplayer(currentPlayer); + return false; + } + else if((Cube::Owner)currentPlayer==cubes[row][column]->owner() || + cubes[row][column]->owner()==Cube::Nobody) + { + doMove(row,column); + return true; + } + else + return false; +} + +void KCubeBoxWidget::checkComputerplayer(Player player) +{ + // checking if a process is running or the Widget isn't shown yet + if(isActive() || !isVisibleToTLW()) + return; + if((player==One && computerPlOne && currentPlayer==One) + || (player==Two && computerPlTwo && currentPlayer==Two)) + { + KCubeWidget::enableClicks(false); + + CubeBox field(*this); + int row=0,column=0; + emit startedThinking(); + bool canceled=!brain.getHint(row,column,(CubeBoxBase::Player)player,field); + emit stoppedThinking(); + + if(!canceled) + { + cubes[row][column]->showHint(500,2); + + bool result=checkClick(row,column,false); + assert(result); + } + } + +} + +/* ***************************************************************** ** +** status functions ** +** ***************************************************************** */ + +bool KCubeBoxWidget::isActive() const +{ + bool flag=false; + if(moveTimer->isActive()) + flag=true; + else if(brain.isActive()) + flag=true; + + return flag; +} + +bool KCubeBoxWidget::isMoving() const +{ + return moveTimer->isActive(); +} + +bool KCubeBoxWidget::isComputer(Player player) const +{ + if(player==One) + return computerPlOne; + else + return computerPlTwo; +} + + +int KCubeBoxWidget::skill() const +{ + return brain.skill(); +} + +QPalette KCubeBoxWidget::color(Player forWhom) +{ + return KCubeWidget::color((KCubeWidget::Owner)forWhom); +} + +/* ***************************************************************** ** +** initializing functions ** +** ***************************************************************** */ +void KCubeBoxWidget::init() +{ + initCubes(); + + undoBox=new CubeBox(dim()); + + currentPlayer=One; + moveDelay=100; + moveTimer=new QTimer(this); + computerPlOne=false; + computerPlTwo=false; + KCubeWidget::enableClicks(true); + loadSettings(); + + connect(moveTimer,SIGNAL(timeout()),SLOT(nextLoopStep())); + connect(this,SIGNAL(startedThinking()),SLOT(setWaitCursor())); + connect(this,SIGNAL(stoppedThinking()),SLOT(setNormalCursor())); + connect(this,SIGNAL(startedMoving()),SLOT(setWaitCursor())); + connect(this,SIGNAL(stoppedMoving()),SLOT(setNormalCursor())); + connect(this,SIGNAL(playerWon(int)),SLOT(stopActivities())); + + setNormalCursor(); + + emit playerChanged(One); +} + +void KCubeBoxWidget::initCubes() +{ + const int s=dim(); + int i,j; + + // create Layout + layout=new QGridLayout(this,s,s); + + + for(i=0;isetRowStretch(i,1); + layout->setColStretch(i,1); + } + + + // create new cubes + cubes = new KCubeWidget**[s]; + for(i=0;isetCoordinates(i,j); + layout->addWidget(cubes[i][j],i,j); + cubes[i][j]->show(); + connect(cubes[i][j],SIGNAL(clicked(int,int,bool)),SLOT(stopHint())); + connect(cubes[i][j],SIGNAL(clicked(int,int,bool)),SLOT(checkClick(int,int,bool))); + } + + // initialize cubes + int max=dim()-1; + + cubes[0][0]->setMax(2); + cubes[0][max]->setMax(2); + cubes[max][0]->setMax(2); + cubes[max][max]->setMax(2); + + for(i=1;isetMax(3); + cubes[i][max]->setMax(3); + cubes[0][i]->setMax(3); + cubes[max][i]->setMax(3); + } + + for(i=1;isetMax(4); + } + +} + +QSize KCubeBoxWidget::sizeHint() const +{ + return QSize(400,400); +} + +void KCubeBoxWidget::deleteCubes() +{ + if(layout) + delete layout; + + CubeBoxBase::deleteCubes(); +} + + +/* ***************************************************************** ** +** other private functions ** +** ***************************************************************** */ + +void KCubeBoxWidget::doMove(int row,int column) +{ + // if a move hasn't finished yet don't do another move + if(isActive()) + return; + + // for undo-function copy field + *undoBox=*this; + + cubes[row][column]->increase((Cube::Owner)currentPlayer); + + if(cubes[row][column]->overMax()) + { + KCubeWidget::enableClicks(false); + startLoop(); + } + else + changePlayer(); +} + +void KCubeBoxWidget::startLoop() +{ + emit startedMoving(); + + KCubeWidget::enableClicks(false); + + loop.row=0; + loop.column=0; + loop.finished=true; + + moveTimer->start(moveDelay); +} + +void KCubeBoxWidget::stopLoop() +{ + moveTimer->stop(); + emit stoppedMoving(); + KCubeWidget::enableClicks(true); +} + +void KCubeBoxWidget::nextLoopStep() +{ + // search cube with to many points + while(!cubes[loop.row][loop.column]->overMax()) + { + loop.column++; + if(loop.column==dim()) + { + if(loop.row==dim()-1) + { + if(!loop.finished) + { + loop.row=0; + loop.column=0; + loop.finished=true; + return; + } + else // loop finished + { + stopLoop(); + changePlayer(); + + return; + } + } + else + { + loop.row++; + loop.column=0; + } + } + } + + + increaseNeighbours(currentPlayer,loop.row,loop.column); + cubes[loop.row][loop.column]->decrease(); + loop.finished=false; + + if(hasPlayerWon(currentPlayer)) + { + emit playerWon((int)currentPlayer); + stopLoop(); + return; + } +} + +bool KCubeBoxWidget::hasPlayerWon(Player player) +{ + for(int i=0;iowner()!=(Cube::Owner)player) + { + return false; + } + } + return true; +} + +KCubeBoxWidget::Player KCubeBoxWidget::changePlayer() +{ + currentPlayer=(currentPlayer==One)? Two : One; + + emit playerChanged(currentPlayer); + checkComputerplayer(currentPlayer); + KCubeWidget::enableClicks(true); + return currentPlayer; +} + + +void KCubeBoxWidget::increaseNeighbours(KCubeBoxWidget::Player forWhom,int row,int column) +{ + KCubeWidget::Owner _player = (KCubeWidget::Owner)(forWhom); + + if(row==0) + { + if(column==0) // top left corner + { + cubes[0][1]->increase(_player); + cubes[1][0]->increase(_player); + return; + } + else if(column==dim()-1) // top right corner + { + cubes[0][dim()-2]->increase(_player); + cubes[1][dim()-1]->increase(_player); + return; + } + else // top edge + { + cubes[0][column-1]->increase(_player); + cubes[0][column+1]->increase(_player); + cubes[1][column]->increase(_player); + return; + } + } + else if(row==dim()-1) + { + if(column==0) // left bottom corner + { + cubes[dim()-2][0]->increase(_player); + cubes[dim()-1][1]->increase(_player); + return; + } + + else if(column==dim()-1) // right bottom corner + { + cubes[dim()-2][dim()-1]->increase(_player); + cubes[dim()-1][dim()-2]->increase(_player); + return; + } + else // bottom edge + { + cubes[dim()-1][column-1]->increase(_player); + cubes[dim()-1][column+1]->increase(_player); + cubes[dim()-2][column]->increase(_player); + return; + } + } + else if(column==0) // left edge + { + cubes[row-1][0]->increase(_player); + cubes[row+1][0]->increase(_player); + cubes[row][1]->increase(_player); + return; + } + else if(column==dim()-1) // right edge + { + cubes[row-1][dim()-1]->increase(_player); + cubes[row+1][dim()-1]->increase(_player); + cubes[row][dim()-2]->increase(_player); + return; + } + else + { + cubes[row][column-1]->increase(_player); + cubes[row][column+1]->increase(_player); + cubes[row-1][column]->increase(_player); + cubes[row+1][column]->increase(_player); + return; + } + + +} + +#include "kcubeboxwidget.moc" + diff --git a/kjumpingcube/kcubeboxwidget.h b/kjumpingcube/kcubeboxwidget.h new file mode 100644 index 00000000..ff2f4ac0 --- /dev/null +++ b/kjumpingcube/kcubeboxwidget.h @@ -0,0 +1,188 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#ifndef KCUBEBOXWIDGET_H +#define KCUBEBOXWIDGET_H + +#include "cubeboxbase.h" +#include "kcubewidget.h" +#include "brain.h" +#include + +class QGridLayout; +class CubeBox; +class QPalette; +class QTimer; +class KConfigBase; + +#ifdef HAVE_CONFIG_H +#include +#endif + +/** +*@internal +*/ +struct Loop +{ + int row; + int column; + bool finished; +}; + + +class KCubeBoxWidget : public QWidget , public CubeBoxBase +{ + Q_OBJECT +public: + KCubeBoxWidget(const int dim=1,QWidget *parent=0,const char *name=0); + + KCubeBoxWidget(CubeBox& box, QWidget *parent=0,const char *name=0); + KCubeBoxWidget(const KCubeBoxWidget& box,QWidget *parent=0,const char *name=0); + virtual ~KCubeBoxWidget(); + + KCubeBoxWidget& operator= (CubeBox& box); + KCubeBoxWidget& operator= ( const KCubeBoxWidget& box); + + /** + * reset cubebox for a new game + */ + void reset(); + + /** undo last move */ + void undo(); + + /** + * set colors that are used to show owners of the cubes + * + * @param forWhom for which player the color should be set + * @param color color for player one + */ + void setColor(Player forWhom,QPalette color); + /** + * sets number of Cubes in a row/column to 'size'. + */ + virtual void setDim(int dim); + + /** + * sets player 'player' as computer or human + * + * @param player + * @param flag: true for computer, false for human + */ + void setComputerplayer(Player player,bool flag); + + /** returns current skill, according to Prefs::EnumSkill */ + int skill() const; + + /** returns true if player 'player' is a computerPlayer */ + bool isComputer(Player player) const; + + /** returns true if CubeBox is doing a move or getting a hint */ + bool isActive() const; + bool isMoving() const; + + /** returns current Color for Player ´forWhom´ */ + QPalette color(Player forWhom); + + /** + * checks if 'player' is a computerplayer an computes next move if TRUE + */ + void checkComputerplayer(Player player); + + inline void saveGame(KConfigBase *c) { saveProperties(c); } + inline void restoreGame(KConfigBase *c) { readProperties(c); } + +public slots: + /** stops all activities like getting a hint or doing a move */ + void stopActivities(); + /** + * computes a possibility to move and shows it by highlightning + * this cube + */ + void getHint(); + + void loadSettings(); + +signals: + void playerChanged(int newPlayer); + void playerWon(int player); + void startedMoving(); + void startedThinking(); + void stoppedMoving(); + void stoppedThinking(); + +protected: + virtual QSize sizeHint() const; + virtual void deleteCubes(); + virtual void initCubes(); + + void saveProperties(KConfigBase *); + void readProperties(KConfigBase *); + +protected slots: + /** sets the cursor to an waitcursor */ + void setWaitCursor(); + /** restores the original cursor */ + void setNormalCursor(); + +private: + void init(); + + QGridLayout *layout; + CubeBox *undoBox; + Brain brain; + + QTimer *moveTimer; + int moveDelay; + Loop loop; + /** */ + void startLoop(); + /** */ + void stopLoop(); + + Player changePlayer(); + bool hasPlayerWon(Player player); + bool computerPlOne; + bool computerPlTwo; + + /** + * increases the cube at row 'row' and column 'column' , + * and starts the Loop for checking the playingfield + */ + void doMove(int row,int column); + + void increaseNeighbours(KCubeBoxWidget::Player forWhom,int row,int column); + +private slots: + void nextLoopStep(); + /** + * checks if cube at ['row','column'] is clickable by the current player. + * if true, it increases this cube and checks the playingfield + */ + bool checkClick(int row,int column,bool isClick); + + /** turns off blinking, if an other cube is clicked */ + void stopHint(); + +}; + +#endif // KCUBEBOXWIDGET_H + diff --git a/kjumpingcube/kcubewidget.cpp b/kjumpingcube/kcubewidget.cpp new file mode 100644 index 00000000..f23e3cce --- /dev/null +++ b/kjumpingcube/kcubewidget.cpp @@ -0,0 +1,348 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#include "kcubewidget.h" + +#include +#include + +#include +#include + +/* ****************************************************** ** +** static elements ** +** ****************************************************** */ +bool KCubeWidget::_clicksAllowed=true; +QPalette KCubeWidget::color1; +QPalette KCubeWidget::color2; + + +void KCubeWidget::enableClicks(bool flag) +{ + _clicksAllowed=flag; +} + + +void KCubeWidget::setColor(Owner forWhom, QPalette newPalette) +{ + if(forWhom==One) + { + color1=newPalette; + } + else if(forWhom==Two) + { + color2=newPalette; + } +} + +QPalette KCubeWidget::color(Owner forWhom) +{ + QPalette color; + if(forWhom==One) + { + color=color1; + } + else if(forWhom==Two) + { + color=color2; + } + + return color; +} + + +/* ****************************************************** ** +** public functions ** +** ****************************************************** */ + +KCubeWidget::KCubeWidget(QWidget* parent,const char* name + ,Owner owner,int value,int max) + : QFrame(parent,name), + Cube(owner,value,max) +{ + setFrameStyle(Panel|Raised); + //setLineWidth(2); + setMinimumSize(20,20); + + setCoordinates(0,0); + + //initialize hintTimer + // will be automatically destroyed by the parent + hintTimer = new QTimer(this); + hintCounter=0; + connect(hintTimer,SIGNAL(timeout()),SLOT(hint())); + + setPalette(kapp->palette()); + + // show values + repaint(false); +} + +KCubeWidget::~KCubeWidget() +{ +} + + +KCubeWidget& KCubeWidget::operator=(const Cube& cube) +{ + if(this!=&cube) + { + setOwner(cube.owner()); + setValue(cube.value()); + setMax(cube.max()); + } + + return *this; +} + +KCubeWidget& KCubeWidget::operator=(const KCubeWidget& cube) +{ + if(this!=&cube) + { + setOwner(cube.owner()); + setValue(cube.value()); + setMax(cube.max()); + } + + return *this; +} +KCubeWidget::Owner KCubeWidget::setOwner(Owner newOwner) +{ + Owner old=Cube::setOwner(newOwner); + + updateColors(); + + return old; +} + +void KCubeWidget::setValue(int newValue) +{ + Cube::setValue(newValue); + update(); +} + + +void KCubeWidget::showHint(int interval,int number) +{ + if(hintTimer->isActive()) + return; + + hintCounter=2*number; + hintTimer->start(interval); +} + + +void KCubeWidget::animate(bool ) +{ +} + + +void KCubeWidget::setCoordinates(int row,int column) +{ + _row=row; + _column=column; +} + +int KCubeWidget::row() const +{ + return _row; +} + +int KCubeWidget::column() const +{ + return _column; +} + + + + +/* ****************************************************** ** +** public slots ** +** ****************************************************** */ + +void KCubeWidget::reset() +{ + setValue(1); + setOwner(Nobody); +} + + +void KCubeWidget::updateColors() +{ + if(owner()==One) + setPalette(color1); + else if(owner()==Two) + setPalette(color2); + else if(owner()==Nobody) + setPalette(kapp->palette()); +} + +void KCubeWidget::stopHint() +{ + if(hintTimer->isActive()) + { + hintTimer->stop(); + setBackgroundMode(PaletteBackground); + } + +} + + + +/* ****************************************************** ** +** protected slots ** +** ****************************************************** */ + +void KCubeWidget::hint() +{ + hintCounter--; + if(hintCounter%2==1) + { + setBackgroundMode(PaletteLight); + } + else + { + setBackgroundMode(PaletteBackground); + } + if(hintCounter==0) + { + stopHint(); + } +} + + + +/* ****************************************************** ** +** Event handler ** +** ****************************************************** */ + +void KCubeWidget::mouseReleaseEvent(QMouseEvent *e) +{ + // only accept click if it was inside this cube + if(e->x()< 0 || e->x() > width() || e->y() < 0 || e->y() > height()) + return; + + if(e->button() == LeftButton && _clicksAllowed) + { + stopHint(); + emit clicked(row(),column(),true); + } +} + + + +void KCubeWidget::drawContents(QPainter *painter) +{ + QRect contents=contentsRect(); + QPixmap buffer(contents.size()); + buffer.fill(this,contents.topLeft()); + QPainter *p=new QPainter; + p->begin(&buffer); + int h=contents.height(); + int w=contents.width(); + int circleSize=(hsetBrush(brush); + p->setPen(pen); + switch(points) + { + case 1: + p->drawEllipse(w/2-circleSize/2,h/2-circleSize/2,circleSize,circleSize); + break; + + case 3: + p->drawEllipse(w/2-circleSize/2,h/2-circleSize/2,circleSize,circleSize); + case 2: + p->drawEllipse(w/4-circleSize/2,h/4-circleSize/2,circleSize,circleSize); + p->drawEllipse(3*w/4-circleSize/2,3*h/4-circleSize/2, + circleSize,circleSize); + break; + + case 5: + p->drawEllipse(w/2-circleSize/2,h/2-circleSize/2,circleSize,circleSize); + case 4: + p->drawEllipse(w/4-circleSize/2,h/4-circleSize/2,circleSize,circleSize); + p->drawEllipse(3*w/4-circleSize/2,h/4-circleSize/2, + circleSize,circleSize); + p->drawEllipse(w/4-circleSize/2,3*h/4-circleSize/2, + circleSize,circleSize); + p->drawEllipse(3*w/4-circleSize/2,3*h/4-circleSize/2, + circleSize,circleSize); + break; + + case 8: + p->drawEllipse(w/2-circleSize/2,2*h/3-circleSize/2, + circleSize,circleSize); + case 7: + p->drawEllipse(w/2-circleSize/2,h/3-circleSize/2, + circleSize,circleSize); + case 6: + p->drawEllipse(w/4-circleSize/2,h/6-circleSize/2,circleSize,circleSize); + p->drawEllipse(3*w/4-circleSize/2,h/6-circleSize/2, + circleSize,circleSize); + p->drawEllipse(w/4-circleSize/2,3*h/6-circleSize/2, + circleSize,circleSize); + p->drawEllipse(3*w/4-circleSize/2,3*h/6-circleSize/2, + circleSize,circleSize); + p->drawEllipse(w/4-circleSize/2,5*h/6-circleSize/2, + circleSize,circleSize); + p->drawEllipse(3*w/4-circleSize/2,5*h/6-circleSize/2, + circleSize,circleSize); + break; + + + case 9: + p->drawEllipse(w/4-circleSize/2,h/6-circleSize/2,circleSize,circleSize); + p->drawEllipse(3*w/4-circleSize/2,h/6-circleSize/2, + circleSize,circleSize); + p->drawEllipse(w/4-circleSize/2,3*h/6-circleSize/2, + circleSize,circleSize); + p->drawEllipse(3*w/4-circleSize/2,3*h/6-circleSize/2, + circleSize,circleSize); + p->drawEllipse(w/4-circleSize/2,5*h/6-circleSize/2, + circleSize,circleSize); + p->drawEllipse(3*w/4-circleSize/2,5*h/6-circleSize/2, + circleSize,circleSize); + p->drawEllipse(w/2-circleSize/2,2*h/7-circleSize/2, + circleSize,circleSize); + p->drawEllipse(w/2-circleSize/2,5*h/7-circleSize/2, + circleSize,circleSize); + p->drawEllipse(w/2-circleSize/2,h/2-circleSize/2, + circleSize,circleSize); + break; + + default: + kdDebug() << "cube had value " << points << endl; + QString s; + s.sprintf("%d",points); + p->drawText(w/2,h/2,s); + break; + } + p->end(); + delete p; + + painter->drawPixmap(contents.topLeft(),buffer); + +} + +#include "kcubewidget.moc" diff --git a/kjumpingcube/kcubewidget.h b/kjumpingcube/kcubewidget.h new file mode 100644 index 00000000..f0d8d8cd --- /dev/null +++ b/kjumpingcube/kcubewidget.h @@ -0,0 +1,119 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#ifndef KCUBEWIDGET_H +#define KCUBEWIDGET_H + +#include +#include "cube.h" + +class QPalette; +class QTimer; + + +/** +* +*/ +class KCubeWidget : public QFrame , public Cube +{ + Q_OBJECT + +public: + /** constructs a new KCubeWidget*/ + KCubeWidget(QWidget* parent=0,const char* name=0 + ,Owner owner=Cube::Nobody,int value=1,int max=0); + virtual ~KCubeWidget(); + + virtual Owner setOwner(Owner newOwner); + virtual void setValue(int newValue); + + + /** takes the information from a Cube */ + KCubeWidget& operator=(const Cube&); + KCubeWidget& operator=(const KCubeWidget&); + + /** shows a tip e.g. blinks with the interval 500 and number times */ + void showHint(int interval=500,int number=5); + /** stops showing a hint */ + void stopHint(); + + /** + * animates the cube if possible (if feature is enabled) + * In KCubeWidget this function does nothing, it's just for having + * a public interface for all classes that inherits KCubeWidget + */ + virtual void animate(bool flag); + + /** + * sets the coordinates of the Cube in a Cubebox; + * needed for identification when clicked. + */ + void setCoordinates(int row,int column); + /** returns the row */ + int row() const; + /** returns the column */ + int column() const; + + /** enables or disables possibility to click a cube*/ + static void enableClicks(bool flag); + static void setColor(Owner forWhom, QPalette newPalette); + static QPalette color(Owner forWhom); + +public slots: + /** resets the Cube to default values */ + virtual void reset(); + /** shows changed colors*/ + virtual void updateColors(); + +signals: + void clicked(int row,int column,bool isClick); + +protected: + /** checks, if mouseclick was inside this cube*/ + virtual void mouseReleaseEvent(QMouseEvent*); + + /** refreshs the contents of the Cube */ + virtual void drawContents(QPainter*); + + + + int hintCounter; + +protected slots: + /** + * To this function the hintTimer is connected. + * It manage a periodical way of showing a hint. + */ + virtual void hint(); + +private: + int _row; + int _column; + + QTimer *hintTimer; + + static bool _clicksAllowed; + static QPalette color1; + static QPalette color2; +}; + + +#endif // KCUBEWIDGET_H diff --git a/kjumpingcube/kjumpingcube.cpp b/kjumpingcube/kjumpingcube.cpp new file mode 100644 index 00000000..13619d99 --- /dev/null +++ b/kjumpingcube/kjumpingcube.cpp @@ -0,0 +1,278 @@ +/***************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#include "kjumpingcube.h" +#include "kcubeboxwidget.h" +#include "version.h" + +// Settings +#include "settings.h" +#include + +#include "prefs.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ID_STATUS_TURN_TEXT 1000 +#define ID_STATUS_TURN 2000 + +#define MESSAGE_TIME 2000 + + +KJumpingCube::KJumpingCube() + : view(new KCubeBoxWidget(5, this, "KCubeBoxWidget")) +{ + connect(view,SIGNAL(playerChanged(int)),SLOT(changePlayer(int))); + connect(view,SIGNAL(stoppedMoving()),SLOT(disableStop())); + connect(view,SIGNAL(stoppedThinking()),SLOT(disableStop())); + connect(view,SIGNAL(startedMoving()),SLOT(enableStop_Moving())); + connect(view,SIGNAL(startedThinking()),SLOT(enableStop_Thinking())); + connect(view,SIGNAL(playerWon(int)),SLOT(showWinner(int))); + + // tell the KMainWindow that this is indeed the main widget + setCentralWidget(view); + + // init statusbar + QString s = i18n("Current player:"); + statusBar()->insertItem(s,ID_STATUS_TURN_TEXT, false); + statusBar()->changeItem(s,ID_STATUS_TURN_TEXT); + statusBar()->setItemAlignment (ID_STATUS_TURN_TEXT, AlignLeft | AlignVCenter); + statusBar()->setFixedHeight( statusBar()->sizeHint().height() ); + + currentPlayer = new QWidget(this, "currentPlayer"); + currentPlayer->setFixedWidth(40); + statusBar()->addWidget(currentPlayer, ID_STATUS_TURN, false); + statusBar()->setItemAlignment(ID_STATUS_TURN, AlignLeft | AlignVCenter); + + initKAction(); + changePlayer(1); +} + +void KJumpingCube::initKAction() { + KStdGameAction::gameNew(this, SLOT(newGame()), actionCollection()); + KStdGameAction::load(this, SLOT(openGame()), actionCollection()); + KStdGameAction::save(this, SLOT(save()), actionCollection()); + KStdGameAction::saveAs(this, SLOT(saveAs()), actionCollection()); + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + + hintAction = KStdGameAction::hint(view, SLOT(getHint()), actionCollection()); + stopAction = new KAction(i18n("Stop &Thinking"), "stop", + Qt::Key_Escape, this, SLOT(stop()), actionCollection(), "game_stop"); + stopAction->setEnabled(false); + undoAction = KStdGameAction::undo(this, SLOT(undo()), actionCollection()); + undoAction->setEnabled(false); + KStdAction::preferences(this, SLOT(showOptions()), actionCollection()); + + setupGUI(); +} + +void KJumpingCube::newGame(){ + undoAction->setEnabled(false); + view->reset(); + statusBar()->message(i18n("New Game"),MESSAGE_TIME); +} + +void KJumpingCube::saveGame(bool saveAs) +{ + if(saveAs || gameURL.isEmpty()) + { + int result=0; + KURL url; + + do + { + url = KFileDialog::getSaveURL(gameURL.url(),"*.kjc",this,0); + + if(url.isEmpty()) + return; + + // check filename + QRegExp pattern("*.kjc",true,true); + if(!pattern.exactMatch(url.filename())) + { + url.setFileName( url.filename()+".kjc" ); + } + + if(KIO::NetAccess::exists(url,false,this)) + { + QString mes=i18n("The file %1 exists.\n" + "Do you want to overwrite it?").arg(url.url()); + result = KMessageBox::warningContinueCancel(this, mes, QString::null, i18n("Overwrite")); + if(result==KMessageBox::Cancel) + return; + } + } + while(result==KMessageBox::No); + + gameURL=url; + } + + KTempFile tempFile; + tempFile.setAutoDelete(true); + KSimpleConfig config(tempFile.name()); + + config.setGroup("KJumpingCube"); + config.writeEntry("Version",KJC_VERSION); + config.setGroup("Game"); + view->saveGame(&config); + config.sync(); + + if(KIO::NetAccess::upload( tempFile.name(),gameURL,this )) + { + QString s=i18n("game saved as %1"); + s=s.arg(gameURL.url()); + statusBar()->message(s,MESSAGE_TIME); + } + else + { + KMessageBox::sorry(this,i18n("There was an error in saving file\n%1").arg(gameURL.url())); + } +} + +void KJumpingCube::openGame() +{ + bool fileOk=true; + KURL url; + + do + { + url = KFileDialog::getOpenURL( gameURL.url(), "*.kjc", this, 0 ); + if( url.isEmpty() ) + return; + if(!KIO::NetAccess::exists(url,true,this)) + { + QString mes=i18n("The file %1 does not exist!").arg(url.url()); + KMessageBox::sorry(this,mes); + fileOk=false; + } + } + while(!fileOk); + + QString tempFile; + if( KIO::NetAccess::download( url, tempFile, this ) ) + { + KSimpleConfig config(tempFile,true); + config.setGroup("KJumpingCube"); + if(!config.hasKey("Version")) + { + QString mes=i18n("The file %1 isn't a KJumpingCube gamefile!") + .arg(url.url()); + KMessageBox::sorry(this,mes); + return; + } + + gameURL=url; + config.setGroup("Game"); + view->restoreGame(&config); + + undoAction->setEnabled(false); + + KIO::NetAccess::removeTempFile( tempFile ); + } + else + KMessageBox::sorry(this,i18n("There was an error loading file\n%1").arg( url.url() )); +} + +void KJumpingCube::stop() +{ + + if(view->isMoving()) + undoAction->setEnabled(true); + + view->stopActivities(); + + statusBar()->message(i18n("stopped activity"),MESSAGE_TIME); +} + +void KJumpingCube::undo() +{ + if(view->isActive()) + return; + view->undo(); + undoAction->setEnabled(false); +} + +void KJumpingCube::changePlayer(int newPlayer) +{ + undoAction->setEnabled(true); + currentPlayer->setBackgroundColor(newPlayer == 1 ? Prefs::color1() : Prefs::color2()); + currentPlayer->repaint(); +} + +void KJumpingCube::showWinner(int player) { + QString s=i18n("Winner is Player %1!").arg(player); + KMessageBox::information(this,s,i18n("Winner")); + view->reset(); +} + +void KJumpingCube::disableStop() +{ +// toolBar()->setItemEnabled(ID_GAME_STOP_HINT,false); +// game->setItemEnabled(ID_GAME_STOP_HINT,false); +// toolBar()->setItemEnabled(ID_GAME_HINT,true); +// game->setItemEnabled(ID_GAME_HINT,true); + stopAction->setEnabled(false); + hintAction->setEnabled(true); + statusBar()->clear(); +} + + +void KJumpingCube::enableStop_Moving() +{ +// toolBar()->setItemEnabled(ID_GAME_STOP_HINT,true); +// game->setItemEnabled(ID_GAME_STOP_HINT,true); +// toolBar()->setItemEnabled(ID_GAME_HINT,false); +// game->setItemEnabled(ID_GAME_HINT,false); + stopAction->setEnabled(true); + hintAction->setEnabled(false); + statusBar()->message(i18n("Performing move.")); +} + +void KJumpingCube::enableStop_Thinking(){ + stopAction->setEnabled(true); + hintAction->setEnabled(false); + statusBar()->message(i18n("Computing next move.")); +} + +/** + * Show Configure dialog. + */ +void KJumpingCube::showOptions(){ + if(KConfigDialog::showDialog("settings")) + return; + + KConfigDialog *dialog = new KConfigDialog(this, "settings", Prefs::self(), KDialogBase::Swallow); + dialog->addPage(new Settings(0, "General"), i18n("General"), "package_settings"); + connect(dialog, SIGNAL(settingsChanged()), view, SLOT(loadSettings())); + dialog->show(); +} + +#include "kjumpingcube.moc" + diff --git a/kjumpingcube/kjumpingcube.desktop b/kjumpingcube/kjumpingcube.desktop new file mode 100644 index 00000000..3df8f3d6 --- /dev/null +++ b/kjumpingcube/kjumpingcube.desktop @@ -0,0 +1,102 @@ +[Desktop Entry] +Name=KJumpingCube +Name[af]=Kjumpingcube +Name[ar]=لعبة المكعب القاÙز (KJumpingCube) +Name[az]=K Hoppanan Kub +Name[be]=Скокаючы кубік +Name[bn]=কে-জামà§à¦ªà¦¿à¦‚কিউব +Name[br]=KDiñsALamm +Name[eo]=Saltanta kubo +Name[hi]=के-जमà¥à¤ªà¤¿à¤‚गकà¥à¤¯à¥‚ब +Name[hu]=Ugráló kocka +Name[is]=Hoppandi kubbur +Name[ne]=केडीई जमà¥à¤ªà¤¿à¤™ कà¥à¤¯à¥à¤¬ +Name[pa]=ਕੇ-ਜੰਪ ਘਣ +Name[pl]=SkaczÄ…cy szeÅ›cian +Name[pt]=Cubo Saltitão +Name[pt_BR]=KSaltandoCubo +Name[ro]=Cubul săritor +Name[sk]=KSkákajúca kocka +Name[sv]=Kjumpingcube +Name[ta]=கேகà¯à®¤à®¿à®•à¯à®•à¯à®®à¯ கனசதà¯à®°à®®à¯ +Name[tg]=KКубикҳои Ҷиҳанда +Name[tr]=Zıplayan Küp +Name[xh]=Ityhubhu yeKJumping +Name[zh_TW]=KJumpingCube è·³èºç«‹æ–¹é«” +Exec=kjumpingcube -caption "%c" %i %m +Icon=kjumpingcube +Type=Application +DocPath=kjumpingcube/index.html +GenericName=Tactical Game +GenericName[af]=Taktiese Speletjie +GenericName[ar]=لعبة تكتيكية +GenericName[az]=Taktik oyunu +GenericName[be]=Ð¢Ð°ÐºÑ‚Ñ‹Ñ‡Ð½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ +GenericName[bg]=ТактичеÑка игра +GenericName[bn]=কৌশলের খেলা +GenericName[br]=C'hoari a vrezelekaouriezh +GenericName[bs]=TaktiÄka igra +GenericName[ca]=Joc de tàctica +GenericName[cs]=Taktická hra +GenericName[cy]=Gêm Dactegol +GenericName[da]=Taktisk spil +GenericName[de]=Taktikspiel +GenericName[el]=Παιχνίδι τακτικής +GenericName[eo]=Taktika ludo +GenericName[es]=Juego de táctica +GenericName[et]=Taktikamäng +GenericName[eu]=Joko taktikoa +GenericName[fa]=بازی برنامه‌ریزی‌شده +GenericName[fi]=Taktiikkapeli +GenericName[fo]=Taktiskt spæl +GenericName[fr]=Jeu de tactique +GenericName[ga]=Cluiche Taicticiúil +GenericName[gl]=Xogo de Táctica +GenericName[he]=משחק טקטי +GenericName[hi]=रणनीतिक खेल +GenericName[hr]=TaktiÄka igra +GenericName[hu]=Logikai +GenericName[id]=Permainan Taktik +GenericName[is]=Herkænskuleikur +GenericName[it]=Gioco di tattica +GenericName[ja]=戦略的ゲーム +GenericName[km]=ល្បែង​ក្បួន​យុទ្ធសាស្ážáŸ’ážš +GenericName[ko]=ì „ëžµ 게임 +GenericName[lt]=Taktinis žaidimas +GenericName[lv]=TaktiskÄ spÄ“le +GenericName[mk]=Тактичка игра +GenericName[mt]=Logħba ta' tattika +GenericName[nb]=Taktikk-spill +GenericName[nds]=Taktikspeel +GenericName[ne]=यà¥à¤•à¥à¤¤à¤¿à¤¸à¤‚गत खेल +GenericName[nl]=Tactisch spel +GenericName[nn]=Taktisk spel +GenericName[pa]=ਟਾਕਟੀਕਲ ਖੇਡ +GenericName[pl]=Gra taktyczna +GenericName[pt]=Jogo de Estratégia +GenericName[pt_BR]=Jogo Tático +GenericName[ro]=Un joc de tactică +GenericName[ru]=ТактичеÑÐºÐ°Ñ Ð¸Ð³Ñ€Ð° +GenericName[rw]=Umukino Mugambi +GenericName[se]=Taktihkkaspeallu +GenericName[sk]=Taktická hra +GenericName[sl]=TaktiÄna igra +GenericName[sr]=Тактичка игра +GenericName[sr@Latn]=TaktiÄka igra +GenericName[sv]=Taktikspel +GenericName[ta]=தநà¯à®¤à®¿à®°à®®à®¾à®© விளையாடà¯à®Ÿà¯ +GenericName[tg]=Бозии Тактикӣ +GenericName[th]=เà¸à¸¡à¸§à¸²à¸‡à¹à¸œà¸™ +GenericName[tr]=Taktik oyunu +GenericName[uk]=Тактична гра +GenericName[ven]=Mutambo wa Tactical +GenericName[vi]=Trò chÆ¡i chiến thuật +GenericName[wa]=Djeu di tactike +GenericName[xh]=Umdlalo onamaqhinga +GenericName[zh_CN]=æˆ˜æœ¯æ¸¸æˆ +GenericName[zh_TW]=戰術éŠæˆ² +GenericName[zu]=Umdlalo wamasu +Terminal=false +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;StrategyGame; diff --git a/kjumpingcube/kjumpingcube.h b/kjumpingcube/kjumpingcube.h new file mode 100644 index 00000000..7ed961c7 --- /dev/null +++ b/kjumpingcube/kjumpingcube.h @@ -0,0 +1,76 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#ifndef KJUMPINGCUBE_H +#define KJUMPINGCUBE_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +class KAction; +class KCubeBoxWidget; + +/** + * This class serves as the main window for KJumpingCube. It handles the + * menus, toolbars, and status bars. + * + * @short Main window class + * @author Matthias Kiefer + * @version 0.7.2 + */ +class KJumpingCube : public KMainWindow { + Q_OBJECT + +public: + /** Default Constructor */ + KJumpingCube(); + +private: + KCubeBoxWidget *view; + QWidget *currentPlayer; + KAction *undoAction, *stopAction, *hintAction; + + KURL gameURL; + void initKAction(); + +private slots: + void newGame(); + void saveGame(bool saveAs=false); + inline void saveAs() { saveGame(true); } + inline void save() { saveGame(false); } + void openGame(); + void stop(); + void undo(); + void changePlayer(int newPlayer); + void showWinner(int); + void disableStop(); + void enableStop_Moving(); + void enableStop_Thinking(); + + void showOptions(); +}; + +#endif // KJUMPINGCUBE_H + diff --git a/kjumpingcube/kjumpingcube.kcfg b/kjumpingcube/kjumpingcube.kcfg new file mode 100644 index 00000000..ecd27d09 --- /dev/null +++ b/kjumpingcube/kjumpingcube.kcfg @@ -0,0 +1,38 @@ + + + + + + + darkred + + + + darkblue + + + + 6 + + + + + + + + + Average + + + + false + + + + true + + + diff --git a/kjumpingcube/kjumpingcubeui.rc b/kjumpingcube/kjumpingcubeui.rc new file mode 100644 index 00000000..359a87db --- /dev/null +++ b/kjumpingcube/kjumpingcubeui.rc @@ -0,0 +1,20 @@ + + + + + &Game + + + + +Main Toolbar + + + + + + + + + + diff --git a/kjumpingcube/main.cpp b/kjumpingcube/main.cpp new file mode 100644 index 00000000..eda42342 --- /dev/null +++ b/kjumpingcube/main.cpp @@ -0,0 +1,58 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#include "version.h" +#include "kjumpingcube.h" +#include +#include +#include + + +static const char description[] = + I18N_NOOP("Tactical one or two player game"); + +// A hack to circumvent tricky i18n issue, not used later on in the code. +// Both context and contents must be exactly the same as for the entry in +// kdelibs/kdeui/ui_standards.rc +static const char dummy[] = I18N_NOOP2("Menu title", "&Move"); + +int main(int argc, char *argv[]) +{ + KAboutData aboutData( "kjumpingcube", I18N_NOOP("KJumpingCube"), + KJC_VERSION, description, KAboutData::License_GPL, + "(c) 1998-2000, Matthias Kiefer"); + aboutData.addAuthor("Matthias Kiefer",0, "matthias.kiefer@gmx.de"); + aboutData.addAuthor("Benjamin Meyer",I18N_NOOP("Various improvements"), "ben+kjumpingcube@meyerhome.net"); + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication app; + KGlobal::locale()->insertCatalogue("libkdegames"); + + // All session management is handled in the RESTORE macro + if (app.isRestored()) + RESTORE(KJumpingCube) + else { + KJumpingCube *kjumpingcube = new KJumpingCube; + app.setMainWidget(kjumpingcube); + kjumpingcube->show(); + } + return app.exec(); +} diff --git a/kjumpingcube/prefs.kcfgc b/kjumpingcube/prefs.kcfgc new file mode 100644 index 00000000..95d81c69 --- /dev/null +++ b/kjumpingcube/prefs.kcfgc @@ -0,0 +1,8 @@ +# Code generation options for kconfig_compiler +File=kjumpingcube.kcfg +#IncludeFiles=defines.h +ClassName=Prefs +Singleton=true +#Mutators=true +#CustomAdditions=true +#Mutators=true diff --git a/kjumpingcube/settings.ui b/kjumpingcube/settings.ui new file mode 100644 index 00000000..43908b76 --- /dev/null +++ b/kjumpingcube/settings.ui @@ -0,0 +1,268 @@ + +Settings + + + Settings + + + + 0 + 0 + 422 + 214 + + + + + unnamed + + + 0 + + + 0 + + + + frame3 + + + StyledPanel + + + Plain + + + 0 + + + + unnamed + + + + groupBox3 + + + GroupBoxPanel + + + Sunken + + + Board Size + + + + unnamed + + + + kcfg_CubeDim + + + 5 + + + 10 + + + 1 + + + 6 + + + Horizontal + + + Right + + + + + textLabel6 + + + 5x5 + + + + + textLabel8 + + + 10x10 + + + AlignVCenter|AlignRight + + + + + + + spacer1 + + + Vertical + + + Expanding + + + + 20 + 16 + + + + + + groupBox7 + + + Board Color + + + + unnamed + + + + kcfg_Color2 + + + + + + + + textLabel1 + + + Player 1: + + + + + textLabel2 + + + Player 2: + + + + + kcfg_Color1 + + + + + + + + + + groupBox2 + + + Computer Skill + + + + unnamed + + + + textLabel4 + + + Average + + + AlignCenter + + + + + textLabel3 + + + Beginner + + + + + textLabel5 + + + Expert + + + AlignVCenter|AlignRight + + + + + kcfg_Skill + + + 0 + + + 2 + + + 1 + + + Horizontal + + + Right + + + + + + + groupBox1 + + + Computer Plays As + + + + unnamed + + + + kcfg_ComputerPlayer1 + + + Player 1 + + + + + kcfg_ComputerPlayer2 + + + Player 2 + + + true + + + + + + + + + + + kcolorbutton.h + + diff --git a/kjumpingcube/version.h b/kjumpingcube/version.h new file mode 100644 index 00000000..bbd8f90d --- /dev/null +++ b/kjumpingcube/version.h @@ -0,0 +1 @@ +#define KJC_VERSION "1.1" diff --git a/klickety/CHANGELOG b/klickety/CHANGELOG new file mode 100644 index 00000000..2ecde970 --- /dev/null +++ b/klickety/CHANGELOG @@ -0,0 +1,22 @@ +1.0.3 (4 July 2004) [KDE 3.3 stable] + * use zoom in/out actions (bug #65456) + +1.0.2 (10 March 2004) [KDE 3.2.2 stable] + * fix bug #65490 (default with simpler removed display) + * fix bug #77170 (crash at startup) + +1.0.1 (18 December 2002) [KDE 3.2 stable] + * notifications + +1.0.0b (18 December 2002) [KDE 3.1 stable] + * fix uninitialized variable + +1.0.0 (16 June 2002) + * adapt to library changes + * world-wide highscores + +0.0.1 (23 May 2002) + * initial version in CVS + +----------------- +see also the CHANGELOG for ksirtet diff --git a/klickety/LICENSE b/klickety/LICENSE new file mode 100644 index 00000000..250ac00d --- /dev/null +++ b/klickety/LICENSE @@ -0,0 +1,18 @@ +KLICKETY +-------- +Copyright (c) 2001-2004 Nicolas HADACEK (hadacek@kde.org) + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. diff --git a/klickety/Makefile.am b/klickety/Makefile.am new file mode 100644 index 00000000..5d9e6336 --- /dev/null +++ b/klickety/Makefile.am @@ -0,0 +1,70 @@ +SUBDIRS = pics + +INCLUDES = -I$(top_builddir)/libksirtet -I$(top_srcdir)/libksirtet -I$(top_srcdir)/libkdegames/highscore -I$(top_srcdir)/libkdegames $(all_includes) + +KDE_CXXFLAGS = $(KDE_USE_FPIE) + +bin_PROGRAMS = klickety +klickety_LDADD = $(top_builddir)/libksirtet/base/libksirtetbase.la +klickety_DEPENDENCIES = $(LIB_KDEGAMES_DEP) +klickety_LDFLAGS = $(KDE_USE_PIE) $(LIB_KDEGAMES) $(all_libraries) $(KDE_RPATH) +klickety_SOURCES = piece.cpp board.cpp field.cpp highscores.cpp main.cpp +METASOURCES = board.moc field.moc main.moc + +rcdir = $(kde_datadir)/klickety +rc_DATA = klicketyui.rc + +xdg_apps_DATA = klickety.desktop + +appdatadir = $(kde_datadir)/klickety +appdata_DATA = eventsrc + +messages: rc.cpp + $(XGETTEXT) rc.cpp $(klickety_SOURCES) -o $(podir)/klickety.pot + +# for system-wide highscore file +DESTBIN = $(DESTDIR)$(bindir)/$(bin_PROGRAMS) +DESTHIGHSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY) +DESTSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY)/$(bin_PROGRAMS).scores + +install-data-local: + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && echo "********************************************************" \ + && echo "" \ + && echo "This game is installed sgid \"games\" to use the" \ + && echo "system-wide highscore file (in "$(HIGHSCORE_DIRECTORY)")." \ + && echo "" \ + && echo "If the system-wide highscore file does not exist, it is" \ + && echo "created with the correct ownership and permissions. See the" \ + && echo "INSTALL file in \"kdegames/libkdegames/highscore\" for details." \ + && echo "" \ + && echo "********************************************************" \ + ) || true + +install-exec-hook: + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((chown $(highscore_user):$(highscore_group) $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((mkdir -p $(DESTHIGHSCORES) && chown $(highscore_user):$(highscore_group) $(DESTHIGHSCORES) \ + && chmod 750 $(DESTHIGHSCORES)) \ + || echo "Error: Could not create the highscore directory with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((chown $(highscore_user):$(highscore_group) $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test ${setgid} = true \ + && ((chmod 2755 $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((touch $(DESTSCORES) && chown $(highscore_user):$(highscore_group) $(DESTSCORES) \ + && chmod 0660 $(DESTSCORES)) \ + || echo "Error: Could not create system-wide highscore file with correct permissions !!" \ + )) || true diff --git a/klickety/README b/klickety/README new file mode 100644 index 00000000..2a548cf6 --- /dev/null +++ b/klickety/README @@ -0,0 +1,12 @@ +KLICKETY : a clickomania-alike game +------------------------------------- +Copyright (c) 2001-2004 Nicolas HADACEK (hadacek@kde.org) +Distributed under the GNU General Public License + +Klickety is an adaptation of the (perharps) well-known Clickomania game ; it +is very similar to the "same" game. + +The code links to the ksirtet libraries. + + +Enjoy ! diff --git a/klickety/board.cpp b/klickety/board.cpp new file mode 100644 index 00000000..92969d6e --- /dev/null +++ b/klickety/board.cpp @@ -0,0 +1,163 @@ +#include "board.h" +#include "board.moc" + +#include "base/factory.h" + + +using namespace KGrid2D; + +void KLBoard::contentsMouseReleaseEvent(QMouseEvent *e) +{ + if ( e->button()!=LeftButton || blocked ) return; + QCanvasItemList list = canvas()->collisions(e->pos()); + if ( list.count()==0 ) return; + + QCanvasSprite *spr = static_cast(list.first()); + Coord c = findSprite(spr); + field.fill(0); + addRemoved = findGroup(field, c); + if ( addRemoved>=2 ) { + if ( state!=Normal ) { + state = Normal; + emit firstBlockClicked(); + } + blocked = true; + _beforeRemove(true); + } +} + +KLBoard::KLBoard(QWidget *parent) + : BaseBoard(true, parent), + field(matrix().width(), matrix().height()), + empty(matrix().width()), + blocked(false) +{} + +void KLBoard::start(const GTInitData &data) +{ + BaseBoard::start(data); + + updateScore(matrix().width() * matrix().height()); + state = GameOver; + sliding = false; + blocked = false; + for (uint i=0; isetValue(Piece::info().generateType(&randomSequence()), main); + Coord c(i, j); + setBlock(c, block); + } + computeInfos(); + showBoard(true); +} + +Coord KLBoard::findSprite(QCanvasSprite *spr) const +{ + for (uint i=0; isprite()==spr ) return c; + } + Q_ASSERT(false); + return Coord(); +} + +bool KLBoard::toBeRemoved(const Coord &c) const +{ + return ( field[c]==-1 ); +} + +void KLBoard::remove() +{ + BaseBoard::remove(); + updateRemoved(nbRemoved() + addRemoved); + updateScore(score() - addRemoved); +} + +bool KLBoard::toFall(const Coord &c) const +{ + Coord under(c.first, c.second-1); + return ( matrix()[under]==0 ); +} + +void KLBoard::computeInfos() +{ + BaseBoard::computeInfos(); + if ( graphic() ) computeNeighbours(); + empty.fill(true); + for (uint i=0; ibbi.nbFallStages); + + for (uint j=0; j heights(matrix().width()); + for (uint i=1; iupdate(); + if (final) computeInfos(); + return final; +} + +BaseBoard::AfterRemoveResult KLBoard::afterRemove(bool doAll, bool first) +{ + AfterRemoveResult res = Done; // dummy default + if (sliding) { + res = (doSlide(doAll, loop==bfactory->bbi.nbFallStages+1, false) ? Done + : NeedAfterRemove); + if ( res==Done ) sliding = false; + } else { + res = BaseBoard::afterRemove(doAll, first); + if ( res==Done ) { + res = NeedAfterRemove; + sliding = true; + loop++; + } + } + return res; +} + +bool KLBoard::afterAfterRemove() +{ + // check if there are remaining groups + field.fill(0); + QMemArray groups = findGroups(field, 2, true); + blocked = false; + return groups.size()!=0; +} diff --git a/klickety/board.h b/klickety/board.h new file mode 100644 index 00000000..d239f83d --- /dev/null +++ b/klickety/board.h @@ -0,0 +1,38 @@ +#ifndef KL_BOARD_H +#define KL_BOARD_H + +#include "base/board.h" +#include "base/piece.h" + +class KLBoard : public BaseBoard +{ + Q_OBJECT + public: + KLBoard(QWidget *parent); + + void start(const GTInitData &data); + + signals: + void firstBlockClicked(); + + private: + KGrid2D::Square field; + bool sliding; + QMemArray empty; + uint addRemoved; + bool blocked; + + KGrid2D::Coord findSprite(QCanvasSprite *) const; + AfterRemoveResult afterRemove(bool doAll, bool first); + bool afterAfterRemove(); + bool toBeRemoved(const KGrid2D::Coord &) const; + void remove(); + bool toFall(const KGrid2D::Coord &) const; + bool toSlide(const KGrid2D::Coord &) const; + bool doSlide(bool doAll, bool first, bool lineByLine); + void computeInfos(); + + void contentsMouseReleaseEvent(QMouseEvent *); +}; + +#endif diff --git a/klickety/eventsrc b/klickety/eventsrc new file mode 100644 index 00000000..912a83e6 --- /dev/null +++ b/klickety/eventsrc @@ -0,0 +1,249 @@ +[!Global!] +IconName=klickety +Comment=Klickety +Comment[ar]=لعبة Klickety +Comment[be]=Ðдбітак +Comment[bn]=কà§à¦²à¦¿à¦•à§‡à¦Ÿà¦¿ +Comment[hi]=के-लिकेटी +Comment[ne]=केलिकेटी +Comment[ta]=கேலிஙà¯à®•à®Ÿà¯à®Ÿà®¿ + +[removed] +Name=Line removed +Name[ar]=لقد أزيل الخط +Name[be]=Ð›Ñ–Ð½Ñ–Ñ Ð²Ñ‹Ð´Ð°Ð»ÐµÐ½Ð°Ñ +Name[bg]=Премахната е Ð»Ð¸Ð½Ð¸Ñ +Name[bn]=লাইন সরিয়ে ফেলা হয়েছে +Name[br]=Linenn lemet +Name[bs]=Uklonjena linija +Name[ca]=Línia eliminada +Name[cs]=OdstranÄ›n řádek +Name[cy]=Gwaredwyd llinell +Name[da]=Linje fjernet +Name[de]=Zeile entfernt +Name[el]=ΓÏαμμή αφαιÏέθηκε +Name[eo]=Linio forigita +Name[es]=Línea eliminada +Name[et]=Eemaldatud rida +Name[eu]=Lerroa kendu da +Name[fa]=خط حذ٠شد +Name[fi]=Rivi poistettu +Name[fr]=Ligne supprimée +Name[ga]=Líne bainte +Name[gl]=Liña eliminada +Name[he]=שורה הוסרה +Name[hi]=पंकà¥à¤¤à¤¿ हटाठ+Name[hr]=Uklonjena linija +Name[hu]=Sor eltávolítva +Name[is]=Lína fjarlægð +Name[it]=Riga rimossa +Name[ja]=線を消ã—ã¾ã—㟠+Name[km]=បន្ទាážáŸ‹â€‹ážŠáŸ‚ល​បាន​យក​ចáŸáž‰ +Name[lt]=Linija panaikinta +Name[lv]=Rinda noņemta +Name[mk]=ОтÑтранета е линија +Name[nb]=Linje fjernet +Name[nds]=Reeg wegdaan +Name[ne]=रेखा हटाइयो +Name[nl]=Regel verwijderd +Name[nn]=Linje fjerna +Name[pa]=ਸਤਰ ਹਟਾਈ +Name[pl]=Linia usuniÄ™ta +Name[pt]=Linha removida +Name[pt_BR]=Linha removida +Name[ro]=Linie eliminată +Name[ru]=Ð›Ð¸Ð½Ð¸Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð° +Name[se]=Linnjá lea eretváldon +Name[sk]=ÄŒiara odstránená +Name[sl]=Vrstica odstranjena +Name[sr]=Уклоњена линија +Name[sr@Latn]=Uklonjena linija +Name[sv]=Rad borttagen +Name[ta]=கோட௠நீகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Name[tg]=Сатр хориҷ карда шуд +Name[tr]=Silinen Çizgiler +Name[uk]=Ð›Ñ–Ð½Ñ–Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð° +Name[zh_CN]=消去的行数 +Name[zh_TW]=消去的行 +Comment=Line removed +Comment[ar]=لقد أزيل الخط +Comment[be]=Ð›Ñ–Ð½Ñ–Ñ Ð²Ñ‹Ð´Ð°Ð»ÐµÐ½Ð°Ñ +Comment[bg]=Премахната е Ð»Ð¸Ð½Ð¸Ñ +Comment[bn]=লাইন সরিয়ে ফেলা হয়েছে +Comment[br]=Linenn lemet +Comment[bs]=Uklonjena linija +Comment[ca]=Línia eliminada +Comment[cs]=OdstranÄ›n řádek +Comment[cy]=Gwaredwyd llinell +Comment[da]=Linje fjernet +Comment[de]=Zeile entfernt +Comment[el]=ΓÏαμμή αφαιÏέθηκε +Comment[eo]=Linio forigita +Comment[es]=Línea eliminada +Comment[et]=Eemaldatud rida +Comment[eu]=Lerroa kendu da +Comment[fa]=خط حذ٠شد +Comment[fi]=Rivi poistettu +Comment[fr]=Ligne supprimée +Comment[ga]=Líne bainte +Comment[gl]=Liña eliminada +Comment[he]=שורה הוסרה +Comment[hi]=पंकà¥à¤¤à¤¿ हटाठ+Comment[hr]=Uklonjena linija +Comment[hu]=Sor eltávolítva +Comment[is]=Lína fjarlægð +Comment[it]=Linea rimossa +Comment[ja]=線を消ã—ã¾ã—㟠+Comment[km]=បន្ទាážáŸ‹â€‹ážŠáŸ‚ល​បាន​យក​ចáŸáž‰ +Comment[lt]=Linija panaikinta +Comment[lv]=Rinda ir noņemta +Comment[mk]=ОтÑтранета е линија +Comment[nb]=Linje fjernet +Comment[nds]=Reeg wegdaan +Comment[ne]=रेखा हटाइयो +Comment[nl]=Regel verwijderd +Comment[nn]=Linje fjerna +Comment[pa]=ਸਤਰ ਹਟਾਈ +Comment[pl]=Linia usuniÄ™ta +Comment[pt]=Linha removida +Comment[pt_BR]=Linha removida +Comment[ro]=Linie eliminată +Comment[ru]=Ð›Ð¸Ð½Ð¸Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð° +Comment[se]=Linnjá lea eretváldon +Comment[sk]=ÄŒiara odstránená +Comment[sl]=Odstranjena vrstica +Comment[sr]=Уклоњена линија +Comment[sr@Latn]=Uklonjena linija +Comment[sv]=Rad borttagen +Comment[ta]=கோட௠நீகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=Сатр хориҷ карда шуд +Comment[tr]=Silinen çizgiler +Comment[uk]=Ð›Ñ–Ð½Ñ–Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð° +Comment[zh_CN]=消去的行数 +Comment[zh_TW]=消去的行 +default_presentation=0 + +[game over] +Name=Game Over +Name[af]=Speletjie Bo +Name[ar]=اللعبة انتهت +Name[az]=Oyun Qurtardı +Name[be]=Канец гульні +Name[bg]=Край на играта +Name[bn]=খেল খতম +Name[br]=Echu an abadenn +Name[bs]=Igra zavrÅ¡ena +Name[ca]=Fi de la partida +Name[cs]=Konec hry +Name[cy]=Gêm Drosodd +Name[da]=Spillet forbi +Name[de]=Spiel beendet +Name[el]=Τέλος Ï€Î±Î¹Ï‡Î½Î¹Î´Î¹Î¿Ï +Name[eo]=Ludo finita +Name[es]=Fin de la partida +Name[et]=Mäng läbi +Name[eu]=Jokoa amaitu da +Name[fa]=بازی تمام شد +Name[fi]=Peli loppu +Name[fr]=Fin de la partie +Name[ga]=Cluiche Thart +Name[gl]=Fin do Xogo +Name[he]=×¡×™×•× ×ž×©×—×§ +Name[hi]=खेल ख़तà¥à¤® +Name[hr]=Igra je zavrÅ¡ena +Name[hu]=Vége a játéknak +Name[id]=permainan berakhir +Name[is]=Leik lokið +Name[it]=Gioco terminato +Name[ja]=ゲームオーãƒãƒ¼ +Name[km]=ល្បែង​ចប់ +Name[ko]=SameGame +Name[lt]=Žaidimas baigtas +Name[lv]=SpÄ“les beigas +Name[mk]=Играта заврши +Name[mt]=Il-Logħba SpiÄ‹Ä‹at +Name[nb]=Spillet er slutt +Name[nds]=Speel vörbi +Name[ne]=खेल समापà¥à¤¤ +Name[nl]=Spel is afgelopen +Name[nn]=Spelet er slutt +Name[nso]=Papadi e Fedile +Name[pa]=ਖੇਡ ਖਤਮ +Name[pl]=Koniec gry +Name[pt]=Fim do jogo +Name[pt_BR]=Fim do jogo +Name[ro]=Joc terminat +Name[ru]=Конец игры +Name[se]=Speallu nogai +Name[sk]=Koniec hry +Name[sl]=Konec igre +Name[sr]=Крај игре +Name[sr@Latn]=Kraj igre +Name[sv]=Spelet är slut +Name[ta]=ஆடà¯à®Ÿà®®à¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯ +Name[tg]=Бозӣ ба итмом раÑид +Name[th]=จบเà¸à¸¡ +Name[tr]=Oyun Bitti +Name[uk]=Гру завершено +Name[uz]=OÊ»yin tugadi +Name[uz@cyrillic]=Ўйин тугади +Name[ven]=Muthambo wo Fhela +Name[vi]=Game kết thúc +Name[wa]=Li djeu est houte +Name[xh]=Uphelile Umdlalo +Name[zh_CN]=游æˆç»“æŸ +Name[zh_TW]=éŠæˆ²çµæŸ +Name[zu]=Umdlalo uphelile +Comment=Game over +Comment[be]=Канец гульні +Comment[bg]=Край на играта +Comment[bn]=খেল খতম +Comment[br]=Echu an abadenn +Comment[bs]=Kraj igre +Comment[ca]=Fi de la partida +Comment[cs]=Hra skonÄena +Comment[cy]=Gêm drosodd +Comment[da]=Spil forbi +Comment[de]=Das Spiel ist vorbei +Comment[el]=Τέλος Ï€Î±Î¹Ï‡Î½Î¹Î´Î¹Î¿Ï +Comment[eo]=Ludo finita +Comment[es]=Fin de la partida +Comment[et]=Mäng läbi +Comment[eu]=Jokoa amaitu da +Comment[fa]=بازی تمام شد +Comment[fi]=Peli loppui +Comment[fr]=Fin de la partie +Comment[ga]=Cluiche thart +Comment[he]=×¡×™×•× ×ž×©×—×§ +Comment[hr]=Kraj igre +Comment[hu]=Vége a játéknak +Comment[is]=Leik lokið +Comment[it]=Gioco terminato +Comment[ja]=ゲームオーãƒãƒ¼ +Comment[km]=ល្បែង​ចប់ +Comment[lt]=Žaidimas baigtas +Comment[lv]=SpÄ“le beigusies +Comment[mk]=Играта заврши +Comment[nb]=Spillet er slutt +Comment[nds]=Speel vörbi +Comment[ne]=खेल समापà¥à¤¤ +Comment[nl]=Het spel is afgelopen +Comment[nn]=Spelet er slutt +Comment[pa]=ਖੇਡ ਖਤਮ +Comment[pl]=Koniec gry +Comment[pt]=Fim do jogo +Comment[pt_BR]=Fim do Jogo +Comment[ru]=Конец игры +Comment[se]=Speallu nogai +Comment[sk]=Koniec hry +Comment[sl]=Konec igre +Comment[sr]=Крај игре +Comment[sr@Latn]=Kraj igre +Comment[sv]=Spelet slut +Comment[ta]=ஆடà¯à®Ÿà®®à¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯ +Comment[tr]=Oyun bitti +Comment[uk]=Кінець гри +Comment[wa]=Li djeu est houte +Comment[zh_CN]=游æˆç»“æŸ +Comment[zh_TW]=éŠæˆ²çµæŸ +default_presentation=0 diff --git a/klickety/field.cpp b/klickety/field.cpp new file mode 100644 index 00000000..4b85a3fe --- /dev/null +++ b/klickety/field.cpp @@ -0,0 +1,80 @@ +#include "field.h" +#include "field.moc" + +#include +#include + +#include +#include +#include + +#include "base/board.h" + + +Field::Field(QWidget *parent) + : QWidget(parent, "field"), BaseField(this) +{ + KGameLCDList *sc = new KGameLCDList(i18n("Remaining blocks"), this); + showScore = new KGameLCD(3, sc); + sc->append(showScore); + QWhatsThis::add(sc, i18n("Display the number of remaining " + "blocks.
" + "It turns blue" + " if it is a highscore " + "and red " + "if it is the best local score.
")); + lcds->addWidget(sc, 1, 0); + lcds->setRowStretch(2, 1); + + KGameLCDList *et = new KGameLCDList(i18n("Elapsed time"), this); + elapsedTime = new KGameLCDClock(et); + connect(board, SIGNAL(firstBlockClicked()), elapsedTime, SLOT(start())); + et->append(elapsedTime); + lcds->addWidget(et, 5, 0); + lcds->setRowStretch(6, 1); + + connect(board, SIGNAL(scoreUpdated()), SLOT(scoreUpdatedSlot())); + connect(board, SIGNAL(gameOverSignal()), SLOT(gameOver())); + + settingsChanged(); + connect(parent, SIGNAL(settingsChanged()), SLOT(settingsChanged())); + QTimer::singleShot(0, this, SLOT(start())); +} + +void Field::pause() +{ + if ( board->isGameOver() ) return; + bool paused = board->isPaused(); + if (paused) elapsedTime->start(); + else elapsedTime->stop(); + BaseField::pause(!paused); +} + +void Field::start() +{ + init(false, false, true, true, QString::null); + GTInitData data; + data.seed = kapp->random(); + BaseField::start(data); + elapsedTime->reset(); +} + +void Field::gameOver() +{ + elapsedTime->stop(); + stop(true); + BaseField::gameOver(currentScore(), this); +} + +KExtHighscore::Score Field::currentScore() const +{ + KExtHighscore::Score score(KExtHighscore::Won); + score.setScore(board->score()); + score.setData("time", 3600 - elapsedTime->seconds()); + return score; +} + +bool Field::_isPaused() const +{ + return board->isPaused(); +} diff --git a/klickety/field.h b/klickety/field.h new file mode 100644 index 00000000..67e6529b --- /dev/null +++ b/klickety/field.h @@ -0,0 +1,33 @@ +#ifndef KL_FIELD_H +#define KL_FIELD_H + +#include + +#include "base/field.h" +#include "base/inter.h" + +class KGameLCDClock; + +class Field : public QWidget, public BaseField, public BaseInterface +{ + Q_OBJECT + public: + Field(QWidget *parent); + + private slots: + void scoreUpdatedSlot() { scoreUpdated(); } + void start(); + void gameOver(); + void settingsChanged() { BaseField::settingsChanged(); } + + private: + KGameLCDClock *elapsedTime; + + void pause(); + KExtHighscore::Score currentScore() const; + void _start() { start(); } + void _pause() { pause(); } + bool _isPaused() const; +}; + +#endif diff --git a/klickety/highscores.cpp b/klickety/highscores.cpp new file mode 100644 index 00000000..6c49952e --- /dev/null +++ b/klickety/highscores.cpp @@ -0,0 +1,27 @@ +#include "highscores.h" + +#include "base/factory.h" +#include "base/board.h" + + +using namespace KExtHighscore; + +KLHighscores::KLHighscores() +{ + Item *item = createItem(ScoreDefault); + setScoreItem(bfactory->bbi.width * bfactory->bbi.height + 1, item); + addScoreItem("time", createItem(ElapsedTime)); +} + +bool KLHighscores::isStrictlyLess(const Score &s1, const Score &s2) const +{ + if ( s1.score()==s2.score() ) + return s1.data("time").toUInt()s2.score(); +} + +void KLHighscores::additionalQueryItems(KURL &url, const Score &s) const +{ + uint time = s.data("time").toUInt(); + addToQueryURL(url, "scoreTime", QString::number(time)); +} diff --git a/klickety/highscores.h b/klickety/highscores.h new file mode 100644 index 00000000..27548e69 --- /dev/null +++ b/klickety/highscores.h @@ -0,0 +1,18 @@ +#ifndef KL_HIGHSCORES_H +#define KL_HIGHSCORES_H + +#include "base/highscores.h" + + +class KLHighscores : public BaseHighscores +{ + public: + KLHighscores(); + + private: + bool isStrictlyLess(const KExtHighscore::Score &, + const KExtHighscore::Score &) const; + void additionalQueryItems(KURL &url, const KExtHighscore::Score &) const; +}; + +#endif diff --git a/klickety/klickety.desktop b/klickety/klickety.desktop new file mode 100644 index 00000000..dd93b3b2 --- /dev/null +++ b/klickety/klickety.desktop @@ -0,0 +1,84 @@ +[Desktop Entry] +Name=Klickety +Name[ar]=لعبة استراتيجية (Klickety) +Name[be]=Ðдбітак +Name[bn]=কà§à¦²à¦¿à¦•à§‡à¦Ÿà¦¿ +Name[hi]=के-लिकेटी +Name[ne]=केलिकेटि +Name[ta]=கேலிஙà¯à®•à®Ÿà¯à®Ÿà®¿ +Name[zu]=I-Klickety +GenericName=Board Game +GenericName[af]=Bord Speletjie +GenericName[ar]=لعبة لوح +GenericName[be]=ÐаÑÑ‚Ð¾Ð»ÑŒÐ½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ +GenericName[bg]=Табла +GenericName[bn]=ছককেনà§à¦¦à§à¦°à¦¿à¦• খেলা +GenericName[br]=Ur c'hoari taolenn +GenericName[bs]=Igra s ploÄom +GenericName[ca]=Joc de taula +GenericName[cs]=Desková Same +GenericName[cy]=Gêm Fwrdd +GenericName[da]=Brætspil +GenericName[de]=Brettspiel +GenericName[el]=ΕπιτÏαπέζιο παιχνίδι +GenericName[eo]=Tabuloludo +GenericName[es]=Juego de tablero +GenericName[et]=Lauamäng +GenericName[eu]=Mahai-jokoa +GenericName[fa]=بازی تخته +GenericName[fi]=Lautapeli +GenericName[fo]=Borðspæl +GenericName[fr]=Jeu de plateau +GenericName[ga]=Cluiche Chláir +GenericName[gl]=Xogo de Taboleiro +GenericName[he]=משחק לוח +GenericName[hi]=बिसात का खेल +GenericName[hr]=Igra na ploÄi +GenericName[hu]=Táblajáték +GenericName[is]=Borðleikur +GenericName[it]=Gioco da tavolo +GenericName[ja]=ボードゲーム +GenericName[km]=ល្បែង​ក្ដារ +GenericName[ko]=리버시 ë³´ë“œ 게임 +GenericName[lt]=Stalo žaidimas +GenericName[lv]=Galda spÄ“le +GenericName[mk]=Игра на табла +GenericName[mt]=Logħba tal-bord +GenericName[nb]=Brettspill +GenericName[nds]=Brettspeel +GenericName[ne]=बोरà¥à¤¡ खेल +GenericName[nl]=Bordspel +GenericName[nn]=Brettspel +GenericName[pa]=ਬੋਰਡ ਖੇਡ +GenericName[pl]=Gra planszowa +GenericName[pt]=Jogo de Tabuleiro +GenericName[pt_BR]=Jogo de Tabuleiro +GenericName[ro]=Un joc de table +GenericName[ru]=ÐаÑÑ‚Ð¾Ð»ÑŒÐ½Ð°Ñ Ð¸Ð³Ñ€Ð° +GenericName[rw]=Umukino w'Ikibaho +GenericName[se]=Duolbbášspeallu +GenericName[sk]=Stolová hra +GenericName[sl]=Namizna igra +GenericName[sr]=Игра на табли +GenericName[sr@Latn]=Igra na tabli +GenericName[sv]=Brädspel +GenericName[ta]=பலகை விளையாடà¯à®Ÿà¯ +GenericName[tg]=Бозии Рӯи Мизӣ +GenericName[th]=เà¸à¸¡à¸à¸£à¸°à¸”าน +GenericName[tr]=Tahta Oyunu +GenericName[uk]=Гра на дошці +GenericName[uz]=Stol oÊ»yini +GenericName[uz@cyrillic]=Стол ўйини +GenericName[ven]=Mutambo wa Bodo +GenericName[vi]=Game bàn +GenericName[wa]=Djeu d' platea +GenericName[xh]=Umdlalo Webhodi +GenericName[zh_CN]=æ£‹ç±»æ¸¸æˆ +GenericName[zh_TW]=棋盤éŠæˆ² +GenericName[zu]=Umdlalo webhodi +Icon=klickety +Exec=klickety -caption "%c" %i %m +Type=Application +DocPath=klickety/index.html +Comment= +Categories=Qt;KDE;Game;StrategyGame; diff --git a/klickety/klicketyui.rc b/klickety/klicketyui.rc new file mode 100644 index 00000000..2571e1ef --- /dev/null +++ b/klickety/klicketyui.rc @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/klickety/main.cpp b/klickety/main.cpp new file mode 100644 index 00000000..da6b0afe --- /dev/null +++ b/klickety/main.cpp @@ -0,0 +1,73 @@ +#include "main.h" +#include "main.moc" + +#include +#include +#include +#include + +#include "base/settings.h" +#include "piece.h" +#include "highscores.h" + +//----------------------------------------------------------------------------- +const MainData MAIN_DATA = { + "klickety", + I18N_NOOP("Klickety"), + I18N_NOOP("Klickety is an adaptation of the \"clickomania\" game"), + "http://klickety.sourceforge.net/", + I18N_NOOP("Removed blocks"), + "1.0.3", + "1.0.3 (5 August 2004)" +}; + +const uint HISTOGRAM_SIZE = 16; +const uint HISTOGRAM[HISTOGRAM_SIZE] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 20, 160 +}; + +const BaseBoardInfo BASE_BOARD_INFO = { + 10, 16, false, // width, height, with pieces + + 50, // before remove time + 10, // after removed time + 3, // nb toggles + 7, // nb partial fall stages + + 0, // nb arcade stages + + HISTOGRAM, HISTOGRAM_SIZE, true, // score is bound +}; + +KLFactory::KLFactory() + : BaseFactory(MAIN_DATA, BASE_BOARD_INFO) +{ + _aboutData->addCredit("Dan Hill", I18N_NOOP("Icons")); +} + + +//----------------------------------------------------------------------------- +KLMainWindow::KLMainWindow() +{ + Field *field = static_cast(_inter); + buildGUI(field); +} + +//----------------------------------------------------------------------------- +int main(int argc, char **argv) +{ + KHighscore::init(MAIN_DATA.appName); + KLFactory flf; + flf.init(argc, argv); + + KLPieceInfo pieceInfo; + KLHighscores highscores; + + if ( kapp->isRestored() ) RESTORE(KLMainWindow) + else { + KLMainWindow *mw = new KLMainWindow; + kapp->setMainWidget(mw); + mw->show(); + } + return kapp->exec(); +} diff --git a/klickety/main.h b/klickety/main.h new file mode 100644 index 00000000..e5b335f8 --- /dev/null +++ b/klickety/main.h @@ -0,0 +1,29 @@ +#ifndef KL_MAIN_H +#define KL_MAIN_H + +#include "base/main.h" +#include "base/factory.h" +#include "board.h" +#include "field.h" + + +class KLFactory : public BaseFactory +{ + public: + KLFactory(); + + protected: + virtual BaseBoard *createBoard(bool, QWidget *parent) + { return new KLBoard(parent); } + virtual BaseInterface *createInterface(QWidget *parent) + { return new Field(parent); } +}; + +class KLMainWindow : public BaseMainWindow +{ + Q_OBJECT + public: + KLMainWindow(); +}; + +#endif diff --git a/klickety/pics/Makefile.am b/klickety/pics/Makefile.am new file mode 100644 index 00000000..c471f62b --- /dev/null +++ b/klickety/pics/Makefile.am @@ -0,0 +1,3 @@ + +KDE_ICON= AUTO + diff --git a/klickety/pics/hi128-app-klickety.png b/klickety/pics/hi128-app-klickety.png new file mode 100644 index 00000000..7e9ee5a5 Binary files /dev/null and b/klickety/pics/hi128-app-klickety.png differ diff --git a/klickety/pics/hi16-app-klickety.png b/klickety/pics/hi16-app-klickety.png new file mode 100644 index 00000000..5f34ab07 Binary files /dev/null and b/klickety/pics/hi16-app-klickety.png differ diff --git a/klickety/pics/hi22-app-klickety.png b/klickety/pics/hi22-app-klickety.png new file mode 100644 index 00000000..f1c969a1 Binary files /dev/null and b/klickety/pics/hi22-app-klickety.png differ diff --git a/klickety/pics/hi32-app-klickety.png b/klickety/pics/hi32-app-klickety.png new file mode 100644 index 00000000..d2bec938 Binary files /dev/null and b/klickety/pics/hi32-app-klickety.png differ diff --git a/klickety/pics/hi48-app-klickety.png b/klickety/pics/hi48-app-klickety.png new file mode 100644 index 00000000..d4d61bac Binary files /dev/null and b/klickety/pics/hi48-app-klickety.png differ diff --git a/klickety/pics/hi64-app-klickety.png b/klickety/pics/hi64-app-klickety.png new file mode 100644 index 00000000..46e67906 Binary files /dev/null and b/klickety/pics/hi64-app-klickety.png differ diff --git a/klickety/piece.cpp b/klickety/piece.cpp new file mode 100644 index 00000000..4a33910b --- /dev/null +++ b/klickety/piece.cpp @@ -0,0 +1,52 @@ +#include "piece.h" + +#include +#include +#include "base/board.h" + + +const char *KLPieceInfo::DEFAULT_COLORS[NB_BLOCK_TYPES] = { + "#C86464", "#64C864", "#6464C8", "#C8C864", "#C864C8" +}; + +QColor KLPieceInfo::defaultColor(uint i) const +{ + if ( i>=nbColors() ) return QColor(); + return QColor(DEFAULT_COLORS[i]); +} + +QString KLPieceInfo::colorLabel(uint i) const +{ + return i18n("Color #%1:").arg(i+1); +} + +void KLPieceInfo::draw(QPixmap *pixmap, uint blockType, uint bMode, + bool lighted) const +{ + QColor col = color(blockType); + if (lighted) col = col.light(); + pixmap->fill(col); + + QPainter p(pixmap); + QRect r = pixmap->rect(); + + p.setPen(col.dark()); + if ( !(bMode & BaseBoard::Up) ) + p.drawLine(r.topLeft(), r.topRight()); + if ( !(bMode & BaseBoard::Down) ) + p.drawLine(r.bottomLeft(), r.bottomRight()); + if ( !(bMode & BaseBoard::Left) ) + p.drawLine(r.topLeft(), r.bottomLeft()); + if ( !(bMode & BaseBoard::Right) ) + p.drawLine(r.topRight(),r.bottomRight()); + + p.setPen(col.dark(110)); + if (bMode & BaseBoard::Up) + p.drawLine(r.topLeft()+QPoint(1,0), r.topRight()+QPoint(-1,0)); + if (bMode & BaseBoard::Down) + p.drawLine(r.bottomLeft()+QPoint(1,0), r.bottomRight()+QPoint(-1,0)); + if (bMode & BaseBoard::Left) + p.drawLine(r.topLeft()+QPoint(0,1), r.bottomLeft()+QPoint(0,-1)); + if (bMode & BaseBoard::Right) + p.drawLine(r.topRight()+QPoint(0,1), r.bottomRight()+QPoint(0,-1)); +} diff --git a/klickety/piece.h b/klickety/piece.h new file mode 100644 index 00000000..b7d192f0 --- /dev/null +++ b/klickety/piece.h @@ -0,0 +1,42 @@ +#ifndef KL_PIECE_H +#define KL_PIECE_H + +#include "base/piece.h" + + +class KLPieceInfo : public GPieceInfo +{ + public: + KLPieceInfo() {} + + virtual uint nbBlocks() const { return 0; } + virtual uint nbTypes() const { return NB_BLOCK_TYPES; } + virtual uint nbForms() const { return 0; } + + virtual const int *i(uint, uint) const { return 0; } + virtual const int *j(uint, uint) const { return 0; } + virtual uint value(uint, uint) const { return 0; } + virtual uint form(uint) const { return 0; } + virtual uint nbConfigurations(uint) const { return 0; } + + virtual uint nbNormalBlockTypes() const { return NB_BLOCK_TYPES; } + virtual uint nbGarbageBlockTypes() const { return 0; } + virtual uint nbBlockModes() const { return 1+4+6+4+1; } + + virtual uint nbColors() const { return NB_BLOCK_TYPES; } + virtual QString colorLabel(uint i) const; + virtual QColor defaultColor(uint i) const; + + protected: + void draw(QPixmap *, uint blockType, uint blockMode, + bool lighted) const; + + private: + static const uint NB_BLOCK_TYPES = 5; + static const char *DEFAULT_COLORS[NB_BLOCK_TYPES]; +}; + +#endif + + + diff --git a/klines/AUTHORS b/klines/AUTHORS new file mode 100644 index 00000000..99eadd5e --- /dev/null +++ b/klines/AUTHORS @@ -0,0 +1,2 @@ +Roman Merzlyakov +Roman Razilov diff --git a/klines/Makefile.am b/klines/Makefile.am new file mode 100644 index 00000000..37c8a6ec --- /dev/null +++ b/klines/Makefile.am @@ -0,0 +1,27 @@ + +INCLUDES= -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) +METASOURCES = AUTO + +bin_PROGRAMS = klines +klines_SOURCES = prompt.cpp mwidget.cpp linesboard.cpp field.cpp cell.cpp \ + ballpainter.cpp klines.cpp main.cpp prefs.kcfgc +klines_LDFLAGS = $(all_libraries) $(KDE_RPATH) +klines_LDADD = $(LIB_KDEGAMES) +klines_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +picsdir = $(kde_datadir)/klines/ +pics_DATA = balls.jpg field.jpg fire.jpg + +xdg_apps_DATA = klines.desktop +kde_kcfg_DATA = klines.kcfg + +EXTRA_DIST = $(pics_DATA) + +KDE_ICON = klines + +rcdir = $(kde_datadir)/klines +rc_DATA = klinesui.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/klines.pot; + diff --git a/klines/ballpainter.cpp b/klines/ballpainter.cpp new file mode 100644 index 00000000..06380a6f --- /dev/null +++ b/klines/ballpainter.cpp @@ -0,0 +1,140 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include +#include +//#include "shotcounter.h" +#include +#include "linesboard.h" +//#include +#include +#include +#include +#include + +#include "prefs.h" + +#define PIXSIZE (CELLSIZE - 2) + +int colorLinesArr[NCOLORS] = + {0x0000ff, 0x00ff00, 0xff0000, 0x00ffff, 0xff00ff, 0xffff00, 0x005080}; + // 0x00bbggrr + // red , green , blue , yellow , violet , cyan , brown + + + +BallPainter::BallPainter() + : QObject(), backgroundPix(0) +{ + createPix(); +} + +BallPainter::~BallPainter() +{ + deletePix(); +} + +void BallPainter::deletePix() +{ + delete backgroundPix; + for(int c=0; cisNull() ||backgroundPix->isNull() || fire->isNull() ) { + KMessageBox::error(0, i18n("Unable to find graphics. Check your installation."), i18n("Error")); + qApp->exit(1); + return; // Error + } + + for(int c=0; c=NCOLORS) || (animstep<0) || color == NOBALL ){ + return *backgroundPix; + } + if ( panim == ANIM_JUMP ) + { + if ( ( animstep < 0 ) || ( animstep >= PIXTIME ) ) + return *backgroundPix; + else + return *imgCash[color][animstep]; + } + else if ( panim == ANIM_BURN ) + { + if ( animstep < FIREBALLS ) + return *imgCash[color][animstep + PIXTIME + BOOMBALLS + 1]; + else if ( animstep < FIREBALLS + FIREPIX ) + return *firePix[animstep - FIREBALLS]; + } + else if ( panim == ANIM_BORN ) + { + if ( animstep < BOOMBALLS ) + return *imgCash[color][animstep + PIXTIME]; + else + return *imgCash[color][NORMALBALL]; + } + // rest is not imlemented yet + return *imgCash[color][NORMALBALL]; + +} + +#include "ballpainter.moc" diff --git a/klines/ballpainter.h b/klines/ballpainter.h new file mode 100644 index 00000000..a0ad2cbb --- /dev/null +++ b/klines/ballpainter.h @@ -0,0 +1,47 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef BALLPAINTER_H +#define BALLPAINTER_H + +#include +#include +#include "cfg.h" + +#define CELLSIZE 32 + +class BallPainter : public QObject +{ + Q_OBJECT + QPixmap* imgCash[NCOLORS][PIXTIME + FIREBALLS + BOOMBALLS + 1]; + QPixmap* backgroundPix; + QPixmap* firePix[FIREPIX]; + + +public: + BallPainter(); + ~BallPainter(); + + void deletePix(); + void createPix(); + + QPixmap GetBall( int color, int animstep, int panim ); + QPixmap GetNormalBall(int color) { return GetBall(color,0,ANIM_NO); } + QPixmap GetBackgroundPix() { return GetBall(NOBALL,0,ANIM_NO); } +}; + +#endif diff --git a/klines/balls.jpg b/klines/balls.jpg new file mode 100644 index 00000000..67a3aaf5 Binary files /dev/null and b/klines/balls.jpg differ diff --git a/klines/cell.cpp b/klines/cell.cpp new file mode 100644 index 00000000..1ed40e0b --- /dev/null +++ b/klines/cell.cpp @@ -0,0 +1,40 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "cell.h" + +Cell::Cell() +{ + clear(); +} + +void Cell::clear() +{ + color = NOBALL; + anim = ANIM_NO; +} + +Cell& Cell::moveBall(Cell& cell) +{ + *this = cell; + cell.clear(); + return *this; +} +Cell& Cell::operator= (const Cell& cell) +{ + color = cell.color; + anim = cell.anim; + return *this; +} diff --git a/klines/cell.h b/klines/cell.h new file mode 100644 index 00000000..48c8eb87 --- /dev/null +++ b/klines/cell.h @@ -0,0 +1,40 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef cell_h +#define cell_h + +#include "cfg.h" + +// enum int AnimType {ANIM_NO,ANIM_BORN,ANIM_JUMP,ANIM_RUN,ANIM_BURN,ANIM_BLOCK}; + +class Cell { + int color; + int anim; +public: + Cell(); + + int getColor() {return ( color );} + int getAnim() {return ( anim );} + bool isFree() { return (( color == NOBALL )? true:false);} + + void setColor(int c) {color = c;} + void setAnim(int a) {anim = a;} + void clear(); + Cell& operator = (const Cell& c); + + Cell& moveBall(Cell& cell); +}; +#endif diff --git a/klines/cfg.h b/klines/cfg.h new file mode 100644 index 00000000..d1a4b32b --- /dev/null +++ b/klines/cfg.h @@ -0,0 +1,48 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef CFG_H +#define CFG_H 1 + +#define LINESVERSION "1.1" + +#define NCOLORS 7 +#define BALLSDROP 3 + +#define NOBALL -1 +#define PIXTIME 10 +#define FIREBALLS 4 +#define BOOMBALLS 4 +#define FIREPIX 5 +#define STEPTIME 3 +#define NORMALBALL PIXTIME + BOOMBALLS +#define TIMERCLOCK 25 +#define DEMO_LEVEL -100 +#define DEMO_SEQUENCE 42 + +enum Anim +{ + ANIM_NO, + ANIM_JUMP, + ANIM_RUN, + ANIM_BORN, + ANIM_BURN, + ANIM_YES, + ANIM_BLOCK +}; + +//#define _DBG_ + +#endif //__CFG_H diff --git a/klines/field.cpp b/klines/field.cpp new file mode 100644 index 00000000..d7be22ca --- /dev/null +++ b/klines/field.cpp @@ -0,0 +1,194 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include "cfg.h" +#include "field.moc" + +Field::Field(QWidget* parent, const char* name) + : QWidget( parent, name ) +{ + clearField(); +} + +Field::~Field() +{ +} + +void Field::clearField() +{ + for(int y=0; y=0) && (x=0) && (y= NUMCELLSW) || (iy >= NUMCELLSH)) + continue; + int b = field[iy][ix].getColor(); +//printf("[%d,%d]%d", ix,iy,b); + if (b == NOBALL) + { + run++; + empty++; + } + else if (b == current_color) + { + run++; + score++; + empty = 0; + } + else + { + run = empty+1; + score = 1; + current_color = b; + empty = 0; + } + if (run > lpr) + { + lpr = run; + lprscore = score; + } + } + if (lpr < 5) + lprscore = -10; + else + lprscore = lprscore*lprscore; +// printf("= %d\n", lprscore); + return lprscore; +} + +int Field::calcPosScore(int x, int y, int whatIf) +{ + int score = -10; + int color = field[y][x].getColor(); + field[y][x].setColor(whatIf); + score = QMAX(score, calcRun(x, y-4, 0, 1)); + score = QMAX(score, calcRun(x-4, y-4, 1, 1)); + score = QMAX(score, calcRun(x-4, y, 1, 0)); + score = QMAX(score, calcRun(x-4, y+4, 1, -1)); + field[y][x].setColor(color); + return score; +} + diff --git a/klines/field.h b/klines/field.h new file mode 100644 index 00000000..de9ee8ce --- /dev/null +++ b/klines/field.h @@ -0,0 +1,65 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef FIELD_H +#define FIELD_H + +#include +#include +#include "cell.h" +// size of game field +#define NUMCELLSW 9 +#define NUMCELLSH 9 + +class Field: public QWidget +{ + Q_OBJECT +public: + void clearField(); + + int calcPosScore(int x, int y, int whatIf); + void saveUndo(); + +protected: + Field(QWidget* parent, const char* name); + ~Field(); + + void putBall(int x, int y, int color); + void putBallRun(int x, int y, int color); + void removeBall(int x, int y ); + int getBall(int x, int y); + int getAnim(int x, int y); + void setAnim(int x, int y, int anim ); + void clearAnim(); + int deleteAnimatedBalls(); + void moveBall(int xa, int ya, int xb, int yb); + int calcRun(int sx, int sy, int dx, int dy); + + + bool checkBounds( int x, int y ); +// virtual int erase5Balls(){ return 0;} + int freeSpace(); + void restoreUndo(); + +private: + Cell field[NUMCELLSH][NUMCELLSW]; + Cell field_undo[NUMCELLSH][NUMCELLSW]; +// void search5Balls(); + +}; + +#endif diff --git a/klines/field.jpg b/klines/field.jpg new file mode 100644 index 00000000..d021b6fa Binary files /dev/null and b/klines/field.jpg differ diff --git a/klines/fire.jpg b/klines/fire.jpg new file mode 100644 index 00000000..022f99ca Binary files /dev/null and b/klines/fire.jpg differ diff --git a/klines/hi128-app-klines.png b/klines/hi128-app-klines.png new file mode 100644 index 00000000..4784a9ec Binary files /dev/null and b/klines/hi128-app-klines.png differ diff --git a/klines/hi16-app-klines.png b/klines/hi16-app-klines.png new file mode 100644 index 00000000..ac3bf6b8 Binary files /dev/null and b/klines/hi16-app-klines.png differ diff --git a/klines/hi22-app-klines.png b/klines/hi22-app-klines.png new file mode 100644 index 00000000..cb456ae3 Binary files /dev/null and b/klines/hi22-app-klines.png differ diff --git a/klines/hi32-app-klines.png b/klines/hi32-app-klines.png new file mode 100644 index 00000000..e38cb1a5 Binary files /dev/null and b/klines/hi32-app-klines.png differ diff --git a/klines/hi48-app-klines.png b/klines/hi48-app-klines.png new file mode 100644 index 00000000..96b49a45 Binary files /dev/null and b/klines/hi48-app-klines.png differ diff --git a/klines/hi64-app-klines.png b/klines/hi64-app-klines.png new file mode 100644 index 00000000..0efed587 Binary files /dev/null and b/klines/hi64-app-klines.png differ diff --git a/klines/klines.cpp b/klines/klines.cpp new file mode 100644 index 00000000..d5f36e35 --- /dev/null +++ b/klines/klines.cpp @@ -0,0 +1,585 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +/* changes +21.05.2000 Roman Razilov Menu game/Next +*/ +// +// The implementation of the KLines widget +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cfg.h" +#include "prefs.h" +#include +#include "klines.moc" + +enum { Nb_Levels = 5 }; +static const char *LEVEL[Nb_Levels] = { + I18N_NOOP("Very Easy"), I18N_NOOP("Easy"), I18N_NOOP("Normal"), I18N_NOOP("Hard"), + I18N_NOOP("Very Hard") +}; + + +/* + Creates the KLines widget and sets saved options (if any). +*/ + +KLines::KLines() +{ + mwidget = new MainWidget(this); + setCentralWidget( mwidget ); + + lsb = mwidget->GetLsb(); + connect(lsb, SIGNAL(endTurn()), this, SLOT(makeTurn())); + connect(lsb, SIGNAL(eraseLine(int)), this, SLOT(addScore(int))); + connect(lsb, SIGNAL(endGame()), this, SLOT(endGame())); + connect(lsb, SIGNAL(userTurn()), this, SLOT(userTurn())); + + lPrompt = mwidget->GetPrompt(); + + score = 0; + score_undo = 0; + bDemo = false; + + statusBar()->insertItem(i18n(" Score:"), 1, 1); + statusBar()->setItemAlignment(1, AlignVCenter | AlignLeft); + statusBar()->insertItem(i18n(" Level: "), 0, 1); + statusBar()->setItemAlignment(0, AlignVCenter | AlignLeft); + + initKAction(); + + connect(&demoTimer, SIGNAL(timeout()), this, SLOT(slotDemo())); + + setFocusPolicy(StrongFocus); + setFocus(); + + startGame(); +} + +/* + Saves the options and destroys the KLines widget. +*/ +KLines::~KLines() +{ + Prefs::setLevel(levelAction->currentItem()-2); + Prefs::writeConfig(); +} + +/* + Init KAction objects (menubar, toolbar, shortcuts) +*/ +void KLines::initKAction() +{ + KStdGameAction::gameNew(this, SLOT(startGame()), actionCollection()); + act_demo = KStdGameAction::demo(this, SLOT(startDemo()), actionCollection()); + act_demo->setText(i18n("Start &Tutorial")); + KStdGameAction::highscores(this, SLOT(viewHighScore()), actionCollection()); + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + endTurnAction = KStdGameAction::endTurn(this, SLOT(makeTurn()), actionCollection()); + showNextAction = new KToggleAction(i18n("&Show Next"), KShortcut(CTRL+Key_P), + this, SLOT(switchPrompt()), actionCollection(), "options_show_next"); + showNextAction->setCheckedState(i18n("Hide Next")); + showNumberedAction = new KToggleAction(i18n("&Use Numbered Balls"), KShortcut(), + this, SLOT(switchNumbered()), actionCollection(), "options_show_numbered"); + undoAction = KStdGameAction::undo(this, SLOT(undo()), actionCollection()); + + levelAction = KStdGameAction::chooseGameType(0, 0, actionCollection()); + QStringList items; + for (uint i=0; isetItems(items); + + levelAction->setCurrentItem(Prefs::level()+2); + showNextAction->setChecked(Prefs::showNext()); + showNumberedAction->setChecked(Prefs::numberedBalls()); + lPrompt->setPrompt(Prefs::showNext()); + + (void)new KAction(i18n("Move Left"), Key_Left, lsb, SLOT(moveLeft()), actionCollection(), "left"); + (void)new KAction(i18n("Move Right"), Key_Right, lsb, SLOT(moveRight()), actionCollection(), "right"); + (void)new KAction(i18n("Move Up"), Key_Up, lsb, SLOT(moveUp()), actionCollection(), "up"); + (void)new KAction(i18n("Move Down"), Key_Down, lsb, SLOT(moveDown()), actionCollection(), "down"); + (void)new KAction(i18n("Move Ball"), Key_Space, lsb, SLOT(placePlayerBall()), actionCollection(), "place_ball"); + + setupGUI( KMainWindow::Save | Keys | StatusBar | Create ); +} + +void KLines::startGame() +{ + score = 0; + score_undo = 0; + bUndo = true; + bNewTurn = true; + if(bDemo) + stopDemo(); + + bFirst = true; + + int level = levelAction->currentItem()-2; + setLevel(level); + + lsb->setLevel(level); + lsb->setGameOver(false); + lsb->clearField(); + generateRandomBalls(); + placeBalls(); + generateRandomBalls(); + undoAction->setEnabled(false); + endTurnAction->setEnabled(true); + updateStat(); +} + +void KLines::setLevel(int level) { + levelStr = i18n(LEVEL[level+2]); + statusBar()->changeItem(i18n(" Level: %1").arg(levelStr), 0); +} + +void KLines::startDemo() +{ + if (bDemo) + { + stopDemo(); + return; + } + score = 0; + score_undo = 0; + bUndo = true; + bNewTurn = true; + bDemo = true; + act_demo->setText(i18n("Stop &Tutorial")); + bFirst = true; + + levelStr = i18n("Tutorial"); + statusBar()->changeItem(i18n(" Level: %1").arg(levelStr), 0); + + lsb->startDemoMode(); + lsb->setGameOver(false); + lsb->clearField(); + lsb->update(); + undoAction->setEnabled(false); + endTurnAction->setEnabled(false); + generateRandomBalls(); + + demoStep = 0; + demoTimer.start(1000, true); +} + +void KLines::stopDemo() +{ + bDemo = false; + lsb->hideDemoText(); + demoTimer.stop(); + statusBar()->changeItem(i18n(" Level: %1").arg(i18n("Tutorial - Stopped")), 0); + act_demo->setText(i18n("Start &Tutorial")); +} + +void KLines::slotDemo() +{ + bool newBalls = false; + int ballColors = -1; + int clickX = 0; + int clickY = 0; + QString msg; + demoStep++; + if ((demoStep % 2) == 0) + { + lsb->hideDemoText(); + demoTimer.start(1000, true); + return; + } + if (demoStep == 1) + { + msg = i18n("The goal of the game is to put\n" + "5 balls of the same color in line."); + } + else if (demoStep == 3) + { + newBalls = true; + } + else if (demoStep == 5) + { + msg = i18n("You can make horizontal, vertical\n" + "and diagonal lines."); + } + else if (demoStep == 7) + { + newBalls = true; + } + else if (demoStep == 9) + { + msg = i18n("Each turn, three new balls are placed on the board."); + } + else if (demoStep == 11) + { + newBalls = true; + } + else if (demoStep == 13) + { + msg = i18n("Every turn, you can move one ball."); + } + else if (demoStep == 15) + { + newBalls = true; + ballColors = 56; + } + else if (demoStep == 17) + { + msg = i18n("To move a ball, click on it with the mouse,\n" + "then click where you want the ball to go."); + } + else if (demoStep == 19) + { + clickX = 6; + clickY = 6; + } + else if (demoStep == 21) + { + clickX = 6; + clickY = 9; + } + else if (demoStep == 23) + { + msg = i18n("You just moved the blue ball!"); + } + else if (demoStep == 25) + { + newBalls = true; + } + else if (demoStep == 27) + { + msg = i18n("Balls can be moved to every position on the board,\n" + "as long as there are no other balls in their way."); + } + else if (demoStep == 29) + { + clickX = 4; + clickY = 3; + demoStep++; + } + else if (demoStep == 31) + { + clickX = 7; + clickY = 9; + } + else if (demoStep == 33) + { + msg = i18n("Now we only need one more blue ball."); + } + else if (demoStep == 35) + { + newBalls = true; + } + else if (demoStep == 37) + { + msg = i18n("It seems to be our lucky day!"); + } + else if (demoStep == 39) + { + clickX = 8; + clickY = 2; + demoStep++; + } + else if (demoStep == 41) + { + clickX = 8; + clickY = 9; + } + else if (demoStep == 43) + { + msg = i18n("Hurray! And away they go!\n" + "Now lets try the green balls."); + } + else if (demoStep == 45) + { + clickX = 8; + clickY = 7; + demoStep++; + } + else if (demoStep == 47) + { + clickX = 4; + clickY = 5; + lsb->demoAdjust(42); + } + else if (demoStep == 49) + { + newBalls = true; + } + else if (demoStep == 51) + { + msg = i18n("Now you try!\n" + "Click on the green ball and move it to the others!"); + demoStep++; + } + else if (demoStep == 53) + { + lsb->hideDemoText(); + lsb->adjustDemoMode(true, false); + demoStep++; + } + else if (demoStep == 55) + { + msg = i18n("Almost, try again!"); + demoStep -= 4; + } + else if (demoStep == 57) + { + msg = i18n("Very good!"); + } + else if (demoStep == 59) + { + msg = i18n("Whenever you complete a line you get an extra turn."); + } + else if (demoStep == 61) + { + msg = i18n("This is the end of this tutorial.\n" + "Feel free to finish the game!"); + demoStep++; + } + else if (demoStep == 63) + { + lsb->hideDemoText(); + lsb->adjustDemoMode(true, true); + bDemo = false; + act_demo->setText(i18n("Start &Tutorial")); + } + + if (!msg.isEmpty()) + { + lsb->showDemoText(msg); + demoTimer.start(3500 + msg.contains("\n")*1500, true); + return; + } + if (newBalls) + { + placeBalls(); + if (ballColors == -1) + { + generateRandomBalls(); + } + else + { + for( int i = 0 ; i < BALLSDROP ; i++ ) + { + nextBalls[i] = ballColors % 10; + ballColors = ballColors / 10; + lPrompt->SetBalls(nextBalls); + } + } + + updateStat(); + demoTimer.start(1000, true); + return; + } + if (clickX) + { + lsb->demoClick(clickX-1, clickY-1); + if (hasFocus()) + demoTimer.start(1000, true); + return; + } +} + +void KLines::focusOutEvent(QFocusEvent *ev) +{ + if (bDemo) + { + lsb->hideDemoText(); + demoTimer.stop(); + statusBar()->changeItem(i18n(" Level: %1").arg(i18n("Tutorial - Paused")), 0); + } + KMainWindow::focusOutEvent(ev); +} + +void KLines::focusInEvent(QFocusEvent *ev) +{ + if (bDemo) + { + statusBar()->changeItem(i18n(" Level: %1").arg(levelStr), 0); + slotDemo(); + } + KMainWindow::focusInEvent(ev); +} + +void KLines::stopGame() +{ + if (!lsb->gameOver()) + endGame(); + startGame(); +} + +void KLines::searchBallsLine() +{ +} + +void KLines::generateRandomBalls() +{ + score_undo = score; + for( int i = 0 ; i < BALLSDROP ; i++ ) + { + nextBalls_undo[i] = nextBalls[i]; + nextBalls[i] = bUndo ? + lsb->random(NCOLORS) : + nextBalls_redo[i]; + } + lPrompt->SetBalls(nextBalls); +} + +void KLines::placeBalls() +{ + lsb->placeBalls(nextBalls); +} + +void KLines::undo() +{ + if (lsb->gameOver()) + return; + if (!bUndo) + return; + for( int i = 0 ; i < BALLSDROP ; i++ ) + { + nextBalls_redo[i] = nextBalls[i]; + nextBalls[i] = nextBalls_undo[i]; + } + score = score_undo; + updateStat(); + lPrompt->SetBalls(nextBalls); + lsb->undo(); + switchUndo(FALSE); +} + +void KLines::makeTurn() +{ + if (bDemo) + { + lsb->adjustDemoMode(false, false); + demoTimer.start(100, true); + } + if (lsb->gameOver()) + return; + if (!bDemo){ + placeBalls(); + if(sender() != lsb) + lsb->saveUndo(); + } + bNewTurn = true; +} + +void KLines::userTurn() +{ + if(bNewTurn) + { + bNewTurn = false; + if (!bDemo) + generateRandomBalls(); + if (!bFirst && !bDemo) + switchUndo(true); + } + bFirst = false; +} + +void KLines::addScore(int ballsErased) +{ if(ballsErased >= 5){ + score += 2*ballsErased*ballsErased - 20*ballsErased + 60 ; + if( !lPrompt->getState() ) score+= 1; + updateStat(); + }; + if (bDemo) + { + lsb->adjustDemoMode(false, false); + demoStep += 2; + if (hasFocus()) + demoTimer.start(100, true); + } +} + +void KLines::updateStat() +{ + statusBar()->changeItem(i18n(" Score: %1").arg(score), 1); +} + +void KLines::viewHighScore() +{ + KScoreDialog d(KScoreDialog::Name | KScoreDialog::Score | KScoreDialog::Level, this); + d.exec(); +} + +void KLines::endGame() +{ + lsb->setGameOver(true); + lsb->repaint(false); + + if (bDemo) + return; + + KScoreDialog d(KScoreDialog::Name | KScoreDialog::Score | KScoreDialog::Level, this); + KScoreDialog::FieldInfo scoreInfo; + scoreInfo.insert(KScoreDialog::Level, levelStr); + if (d.addScore(score, scoreInfo, true)) + d.exec(); +} + +void KLines::switchPrompt() +{ + Prefs::setShowNext(!Prefs::showNext()); + lPrompt->setPrompt(Prefs::showNext()); + showNextAction->setChecked(Prefs::showNext()); +} + +void KLines::switchNumbered() +{ + Prefs::setNumberedBalls(!Prefs::numberedBalls()); + lPrompt->setPrompt(Prefs::showNext()); + showNumberedAction->setChecked(Prefs::numberedBalls()); + mwidget->updatePix(); +} + +void KLines::switchUndo(bool bu) +{ + bUndo = bu; + undoAction->setEnabled(bu); +} + +void KLines::keyPressEvent(QKeyEvent *e) +{ + if (lsb->gameOver() && (e->key() == Qt::Key_Space)) + { + e->accept(); + startGame(); + return; + } + KMainWindow::keyPressEvent(e); +} diff --git a/klines/klines.desktop b/klines/klines.desktop new file mode 100644 index 00000000..dba71f39 --- /dev/null +++ b/klines/klines.desktop @@ -0,0 +1,111 @@ +# KDE Config File +[Desktop Entry] +Type=Application +Exec=klines -caption "%c" %i %m +Icon=klines +DocPath=klines/index.html +GenericName=Tactical Game +GenericName[af]=Taktiese Speletjie +GenericName[ar]=لعبة تكتيكية +GenericName[az]=Taktik oyunu +GenericName[be]=Ð¢Ð°ÐºÑ‚Ñ‹Ñ‡Ð½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ +GenericName[bg]=ТактичеÑка игра +GenericName[bn]=কৌশলের খেলা +GenericName[br]=C'hoari a vrezelekaouriezh +GenericName[bs]=TaktiÄka igra +GenericName[ca]=Joc de tàctica +GenericName[cs]=Taktická hra +GenericName[cy]=Gêm Dactegol +GenericName[da]=Taktisk spil +GenericName[de]=Taktikspiel +GenericName[el]=Παιχνίδι τακτικής +GenericName[eo]=Taktika ludo +GenericName[es]=Juego de táctica +GenericName[et]=Taktikamäng +GenericName[eu]=Joko taktikoa +GenericName[fa]=بازی برنامه‌ریزی‌شده +GenericName[fi]=Taktiikkapeli +GenericName[fo]=Taktiskt spæl +GenericName[fr]=Jeu de tactique +GenericName[ga]=Cluiche Taicticiúil +GenericName[gl]=Xogo de Táctica +GenericName[he]=משחק טקטי +GenericName[hi]=रणनीतिक खेल +GenericName[hr]=TaktiÄka igra +GenericName[hu]=Logikai +GenericName[id]=Permainan Taktik +GenericName[is]=Herkænskuleikur +GenericName[it]=Gioco di tattica +GenericName[ja]=戦略的ゲーム +GenericName[km]=ល្បែង​ក្បួន​យុទ្ធសាស្ážáŸ’ážš +GenericName[ko]=ì „ëžµ 게임 +GenericName[lt]=Taktinis žaidimas +GenericName[lv]=TaktiskÄ spÄ“le +GenericName[mk]=Тактичка игра +GenericName[mt]=Logħba ta' tattika +GenericName[nb]=Taktikk-spill +GenericName[nds]=Taktikspeel +GenericName[ne]=यà¥à¤•à¥à¤¤à¤¿à¤¸à¤‚गत खेल +GenericName[nl]=Tactisch spel +GenericName[nn]=Taktisk spel +GenericName[pa]=ਟਾਕਟੀਕਲ ਖੇਡ +GenericName[pl]=Gra taktyczna +GenericName[pt]=Jogo de Estratégia +GenericName[pt_BR]=Jogo Tático +GenericName[ro]=Un joc de tactică +GenericName[ru]=ТактичеÑÐºÐ°Ñ Ð¸Ð³Ñ€Ð° +GenericName[rw]=Umukino Mugambi +GenericName[se]=Taktihkkaspeallu +GenericName[sk]=Taktická hra +GenericName[sl]=TaktiÄna igra +GenericName[sr]=Тактичка игра +GenericName[sr@Latn]=TaktiÄka igra +GenericName[sv]=Taktikspel +GenericName[ta]=தநà¯à®¤à®¿à®°à®®à®¾à®© விளையாடà¯à®Ÿà¯ +GenericName[tg]=Бозии Тактикӣ +GenericName[th]=เà¸à¸¡à¸§à¸²à¸‡à¹à¸œà¸™ +GenericName[tr]=Taktik oyunu +GenericName[uk]=Тактична гра +GenericName[ven]=Mutambo wa Tactical +GenericName[vi]=Trò chÆ¡i chiến thuật +GenericName[wa]=Djeu di tactike +GenericName[xh]=Umdlalo onamaqhinga +GenericName[zh_CN]=æˆ˜æœ¯æ¸¸æˆ +GenericName[zh_TW]=戰術éŠæˆ² +GenericName[zu]=Umdlalo wamasu +Terminal=false +Name=Kolor Lines +Name[af]=Kleur Lyne +Name[ar]=خطوط Kolor +Name[be]=КалÑÑ€Ð¾Ð²Ñ‹Ñ Ð»Ñ–Ð½Ñ–Ñ– +Name[bn]=কালার লাইনà§à¦¸ +Name[br]=Linenn Kolor +Name[eo]=Kolorlinioj +Name[fa]=خطوط Kolor +Name[fr]=KLines +Name[hi]=कलर लाइनà¥à¤¸ +Name[hr]=KNiz u boji +Name[hu]=Színes vonalak +Name[it]=KLines +Name[nb]=Klinjer +Name[ne]=रङ रेखा +Name[nso]=Methalo ya Mmala +Name[pa]=ਰੰਗ ਖੇਡ +Name[pl]=Kolorowe linie +Name[pt]=Linhas Coloridas +Name[pt_BR]=KLinhas de Cores +Name[ro]=Linii colorate +Name[ru]=Цветные линии +Name[sl]=Barvne Ärte +Name[sv]=Färglinjer +Name[ta]=வணà¯à®£à®•à¯à®•à¯‹à®Ÿà¯à®•à®³à¯ +Name[tg]=Хатҳои Рангӣ +Name[tr]=Renkli çizgiler +Name[uk]=Кольорові лінії +Name[ven]=Mitalo ya Kolor +Name[wa]=Royes di coleur +Name[xh]=Iilayini zeKolor +Name[zh_TW]=Kolor Lines å½©è‰²ç·šæ¢ +Name[zu]=Olayini bemibala +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;StrategyGame; diff --git a/klines/klines.h b/klines/klines.h new file mode 100644 index 00000000..73720f03 --- /dev/null +++ b/klines/klines.h @@ -0,0 +1,93 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KLINES_H +#define KLINES_H + +#include + +#include "linesboard.h" +#include "mwidget.h" +#include "prompt.h" + +class KSelectAction; +class KAction; +class KToggleAction; + +class KLines : public KMainWindow +{ + Q_OBJECT +public: + KLines(); + ~KLines(); + +protected: + void keyPressEvent(QKeyEvent *e); + void initKAction(); + void setLevel(int level); + + void focusOutEvent(QFocusEvent *); + void focusInEvent(QFocusEvent *); + +public slots: + void startGame(); + void startDemo(); + void stopGame(); + void endGame(); + void makeTurn(); + void userTurn(); + void addScore(int ballsErased); + void switchPrompt(); + void switchNumbered(); + void undo(); + void slotDemo(); + +private slots: + void viewHighScore(); + +private: + LinesBoard* lsb; + MainWidget *mwidget; + LinesPrompt *lPrompt; + KAction *act_demo, *undoAction, *endTurnAction; + KSelectAction *levelAction; + KToggleAction *showNextAction; + KToggleAction *showNumberedAction; + QString levelStr; + + bool bNewTurn; + + int score; + int score_undo; + + int nextBalls[BALLSDROP]; + int nextBalls_undo[BALLSDROP]; + int nextBalls_redo[BALLSDROP]; + bool bUndo; + bool bFirst; + bool bDemo; + + int demoStep; + QTimer demoTimer; + + void searchBallsLine(); + void generateRandomBalls(); + void placeBalls(); + void updateStat(); + void switchUndo( bool bu ); + void stopDemo(); +}; + +#endif diff --git a/klines/klines.kcfg b/klines/klines.kcfg new file mode 100644 index 00000000..bc5b6f63 --- /dev/null +++ b/klines/klines.kcfg @@ -0,0 +1,23 @@ + + + + + + + 0 + -2 + 2 + + + + true + + + + false + + + diff --git a/klines/klinesui.rc b/klines/klinesui.rc new file mode 100644 index 00000000..13af3734 --- /dev/null +++ b/klines/klinesui.rc @@ -0,0 +1,11 @@ + + + + + &Settings + + + + + + diff --git a/klines/linesboard.cpp b/klines/linesboard.cpp new file mode 100644 index 00000000..5078b8f9 --- /dev/null +++ b/klines/linesboard.cpp @@ -0,0 +1,754 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "linesboard.h" +#include "linesboard.moc" + +/* + Constructs a LinesBoard widget. +*/ + +LinesBoard::LinesBoard( BallPainter * abPainter, QWidget* parent, const char* name ) + : Field( parent, name ) +{ + demoLabel = 0; + bGameOver = false; + anim = ANIM_NO; + focusX = -1; + focusY = -1; +// waypos = 0; +// waylen = 0; +// jumpingRow = -1; +// jumpingCol = -1; + painting = 0; + way = new Waypoints[NUMCELLSW*NUMCELLSH]; + + bPainter = abPainter; + + setFocusPolicy( NoFocus ); + setBackgroundColor( gray ); + + setMouseTracking( FALSE ); + setFixedSize(wHint(), hHint()); + + timer = new QTimer(this); + connect( timer, SIGNAL(timeout()), SLOT(timerSlot()) ); + timer->start( TIMERCLOCK, FALSE ); + +} + +/* + Destructor: deallocates memory for contents +*/ + +LinesBoard::~LinesBoard() +{ + timer->stop(); + delete timer; + delete [] way; +} + +void LinesBoard::startDemoMode() +{ + level = DEMO_LEVEL; + rnd_demo = KRandomSequence(DEMO_SEQUENCE); + bAllowMove = false; +} + +void LinesBoard::adjustDemoMode(bool allowMove, bool off) +{ + bAllowMove = allowMove; + if (off) + level = -2; +} + +void LinesBoard::demoAdjust(int a) +{ + rnd_demo.modulate(a); +} + + +void LinesBoard::placeBalls(int pnextBalls[BALLSDROP]) +{ + for(int i=0; i < BALLSDROP; i++) + nextBalls[i] = pnextBalls[i]; + + nextBallToPlace = 0; + placeBall(); +} + +void LinesBoard::placeBall( ) +{ + char* xx = (char*)malloc( sizeof(char)*NUMCELLSW*NUMCELLSH ); + char* yy = (char*)malloc( sizeof(char)*NUMCELLSW*NUMCELLSH ); + int empty = 0; + for(int y=0; y 0 ? 1000: -1000; + int maxtry = level > 0 ? level+1 : 1-level; + for(int i=0;i 0) && (score < best_score)) || + ((level < 0) && (score > best_score))) + { + best_pos = pos; + best_score = score; + } + } + pos = best_pos; + } + + putBall( xx[pos], yy[pos], color ); + clearAnim(); + setAnim( xx[pos], yy[pos], ANIM_BORN ); + nextBallToPlace++; + AnimStart(ANIM_BORN); + free(xx); + free(yy); + } + else + { + free(xx); + free(yy); + emit endGame(); + } +} + + +/*id LinesBoard::doAfterBalls() { + erase5Balls(); + repaint(FALSE); +} +*/ +/* + Sets the size of the table +*/ + +int LinesBoard::width() { return CELLSIZE * NUMCELLSW; } +int LinesBoard::height() { return CELLSIZE * NUMCELLSH; } +int LinesBoard::wHint() { return width(); } +int LinesBoard::hHint() { return height(); } + +void LinesBoard::setGameOver(bool b) +{ + bGameOver = b; + focusX = -1; + focusY = -1; +} + + +void LinesBoard::paintEvent( QPaintEvent* ) +{ + QPainter *paint; + KPixmap *pixmap = 0; + if (bGameOver) + { + pixmap = new KPixmap(); + pixmap->resize(width(), height()); + paint = new QPainter( pixmap ); + } + else + { + paint = new QPainter( this ); + } + + for( int y=0; y < NUMCELLSH; y++ ){ + for( int x=0; x < NUMCELLSW; x++ ){ + if( getBall(x,y) == NOBALL ) + { + paint->drawPixmap(x*CELLSIZE, y*CELLSIZE, bPainter->GetBackgroundPix() ); + } + else + { + paint->drawPixmap(x*CELLSIZE, y*CELLSIZE, + bPainter->GetBall(getBall(x,y),animstep,getAnim(x,y))); + } + } + } + if (bGameOver) + { + paint->end(); + + KPixmapEffect::fade(*pixmap, 0.5, Qt::black); + + QPainter p(this); + p.drawPixmap(0,0, *pixmap); + delete pixmap; + + QFont gameover_font = font(); + gameover_font.setPointSize(48); + gameover_font.setBold(true); + p.setFont(gameover_font); + p.setPen(Qt::white); + QString gameover_text = i18n("Game Over"); + p.drawText(0, 0, width(), height(), AlignCenter|Qt::WordBreak, gameover_text); + } + else + { + if ((focusX >= 0) && (focusX < NUMCELLSW) && + (focusY >= 0) && (focusY < NUMCELLSH)) + { + QRect r; + r.setX(focusX*CELLSIZE+2); + r.setY(focusY*CELLSIZE+2); + r.setWidth(CELLSIZE-4); + r.setHeight(CELLSIZE-4); + paint->setPen(QPen(Qt::DotLine)); + paint->drawRect(r); + } + } + delete paint; +} + +/* + Handles mouse press events for the LinesBoard widget. +*/ +void LinesBoard::mousePressEvent( QMouseEvent* e ) +{ + if (bGameOver) return; + if ((level == DEMO_LEVEL) && (!bAllowMove) && e->spontaneous()) return; + + int curRow = e->y() / CELLSIZE; + int curCol = e->x() / CELLSIZE; + + placePlayerBall(curCol, curRow); +} + +void LinesBoard::placePlayerBall(int curCol, int curRow) +{ + //check range + if (!checkBounds( curCol, curRow ) ) + return; +// if( running || anim != ANIM_NO ) return; + if(anim != ANIM_JUMP && anim != ANIM_NO) return; + if ( anim == ANIM_JUMP ) + { + if ( getBall(curCol,curRow) == NOBALL ) + { + if(existPath(jumpingCol, jumpingRow, curCol, curRow)) + { + saveRandomState(); + rnd.modulate(jumpingCol); + rnd.modulate(jumpingRow); + rnd.modulate(curCol); + rnd.modulate(curRow); + saveUndo(); + AnimStart(ANIM_RUN); + } + } + else + AnimJump(curCol,curRow); + } + else + AnimJump(curCol,curRow); +} + +/* + Move focus thingy +*/ +void LinesBoard::moveFocus(int dx, int dy) +{ + if (bGameOver) return; + if ((level == DEMO_LEVEL) && (!bAllowMove)) return; + if (focusX == -1) + { + focusX = 0; + focusY = 0; + } + else + { + focusX = (focusX + dx + NUMCELLSW) % NUMCELLSW; + focusY = (focusY + dy + NUMCELLSH) % NUMCELLSH; + } + repaint(FALSE); +} + +void LinesBoard::moveLeft() +{ + moveFocus(-1, 0); +} + +void LinesBoard::moveRight() +{ + moveFocus(1, 0); +} + +void LinesBoard::moveUp() +{ + moveFocus(0, -1); +} + +void LinesBoard::moveDown() +{ + moveFocus(0, 1); +} + +void LinesBoard::placePlayerBall() +{ + if (bGameOver) return; + if ((level == DEMO_LEVEL) && (!bAllowMove)) return; + placePlayerBall(focusX, focusY); +} + +void LinesBoard::AnimJump(int x, int y ) { + if ( getBall(x,y) != NOBALL ) + if (!( anim == ANIM_JUMP && jumpingCol == x && jumpingRow == y )) + if ( AnimEnd() ) + { + clearAnim(); + setAnim(x,y,ANIM_JUMP); + jumpingCol = x; + jumpingRow = y; + AnimStart(ANIM_JUMP); + } +} +void LinesBoard::AnimStart(int panim) { + if (anim != ANIM_NO) + AnimEnd(); + animstep = 0; + animdelaystart = 1; + switch(panim) { + case ANIM_NO: + break; + case ANIM_BORN: + animdelaystart=1; + animmax = BOOMBALLS; + break; + case ANIM_JUMP: + direction = -1; + animstep = 4; + animmax = PIXTIME -1; + break; + case ANIM_RUN: + animdelaystart=3; + // do first step on next timer; + animdelaycount = 0; + // animmax already set + break; + case ANIM_BURN: + animdelaystart=1; + animmax = FIREBALLS + FIREPIX - 1; + break; + default: + ; + } + anim = panim; + animdelaycount = animdelaystart; +} + +int LinesBoard::AnimEnd( ) +{ + if (anim == ANIM_NO ) return true; + int oldanim = anim; + anim = ANIM_NO; + if (oldanim == ANIM_RUN) { + if (animstep != animmax) { + moveBall(way[animstep].x,way[animstep].y,way[animmax].x,way[animmax].y); + } + if ( erase5Balls() == 0 ) { + emit endTurn(); + return true; + } + else + return false; + } + else if ( oldanim == ANIM_BURN ) + { + emit eraseLine( deleteAnimatedBalls() ); + repaint(FALSE); + if ( nextBallToPlace < BALLSDROP ) + { + placeBall(); + // continue with born + return false; + } + else + { + emit userTurn(); + return true; + } + } + else if ( oldanim == ANIM_BORN ) + { + if ( erase5Balls() == 0 ) + { + if ( freeSpace() > 0) + { + if ( nextBallToPlace < BALLSDROP ) + { + placeBall(); + return false; + } + else + { + emit userTurn(); + return true; + } + } + else + { + emit endGame(); + return false; + } + } + else + { + // wait for user input + emit userTurn(); + return true; + } + } + emit userTurn(); + return true; +} + +void LinesBoard::AnimNext() { + if ( anim != ANIM_NO ) + { + if ( anim == ANIM_JUMP ) { + if ( (direction > 0 && animstep == animmax) || ( direction < 0 && animstep == 0)) + direction = -direction; + animstep += direction; + repaint(FALSE); + } else { + if ( animstep >= animmax ) + AnimEnd(); + else { + animdelaycount--; + if (animdelaycount <= 0) { + if ( anim == ANIM_RUN ) + moveBall(way[animstep].x,way[animstep].y,way[animstep+1].x,way[animstep+1].y); + animstep++; + animdelaycount = animdelaystart; + repaint( FALSE ); + } + } + } + } +} +int LinesBoard::getAnim( int x, int y ) +{ + return (( Field::getAnim(x,y) != ANIM_NO )? anim : ANIM_NO); +} + +void LinesBoard::timerSlot() +{ + AnimNext(); +} + +int LinesBoard::erase5Balls() +{ + int nb=5; // minimum balls for erasure + + bool bit_erase[NUMCELLSH][NUMCELLSW]; //bool array for balls, that must be erased + for(int y=0; y= nb ) + if ( count == nb ) + for (int i = 0; i < nb; i++) + bit_erase[y][x-i] = true; + else bit_erase[y][x] = true; + } + } else { + color = newcolor; + count = 1; + } + } + } + + //for vertical + for(int x=0; x= nb ) + if ( count == nb ) + for (int i = 0; i < nb; i++) + bit_erase[y-i][x] = true; + else bit_erase[y][x] = true; + } + } else { + color = newcolor; + count = 1; + } + } + } + + + //for left-down to rigth-up diagonal + for ( int xs = NUMCELLSW-1,ys = NUMCELLSH-nb; xs >= nb-1; ) { + count = 0; + color = NOBALL; + for ( int x = xs, y = ys; x >= 0 && y < NUMCELLSH; x--, y++ ) { + if ( (newcolor = getBall(x,y)) == color) { + if ( color != NOBALL) { + count++; + if ( count >= nb ) + if ( count == nb ) + for (int i = 0; i < nb; i++) + bit_erase[y-i][x+i] = true; + else bit_erase[y][x] = true; + } + } else { + color = newcolor; + count = 1; + } + } + if ( ys > 0 ) ys--; else xs--; + } + + + //for left-up to rigth-down diagonal + for ( int xs = 0,ys = NUMCELLSH-nb; xs <= NUMCELLSW-nb; ) + { + count = 0; + color = NOBALL; + for ( int x = xs, y = ys; x < NUMCELLSW && y < NUMCELLSH; x++, y++ ) + { + if ( (newcolor = getBall(x,y)) == color) + { + if ( color != NOBALL) + { + count++; + if ( count >= nb ) + if ( count == nb ) + for (int i = 0; i < nb; i++) + bit_erase[y-i][x-i] = true; + else + bit_erase[y][x] = true; + } + } + else + { + color = newcolor; + count = 1; + } + } + if ( ys > 0 ) ys--; else xs++; + } + + //remove all lines balls, that more than nb + int cb=0; + for(int y=0; y < NUMCELLSH; y++) + for(int x=0; x < NUMCELLSW; x++) + { + if (bit_erase[y][x]) + { + setAnim(x,y,ANIM_YES); + cb++; + } + else + { + setAnim(x,y,ANIM_NO); + } + } + if ( cb > 0 ) + { + AnimStart(ANIM_BURN); + //emit eraseLine(cb); + } + + return cb; +} + + +#define GO_EMPTY 4 +#define GO_A 5 +#define GO_B 6 +#define GO_BALL 7 + +bool LinesBoard::existPath(int bx, int by, int ax, int ay) +{ + int dx[4]={1,-1, 0, 0}; + int dy[4]={0, 0, 1,-1}; + + if (getBall(ax,ay) != NOBALL) + return false; + + char pf[NUMCELLSH][NUMCELLSW]; + for(int y=0; y=0) && (nx=0) && (nysetMargin(1); + demoLabel->setIndent(0); + demoLabel->setAutoMask( FALSE ); + demoLabel->setFrameStyle( QFrame::Plain | QFrame::Box ); + demoLabel->setLineWidth( 1 ); + demoLabel->setAlignment( AlignHCenter | AlignTop ); + demoLabel->setPalette(QToolTip::palette()); + demoLabel->polish(); + } + demoLabel->setText(text); + demoLabel->adjustSize(); + QSize s = demoLabel->sizeHint(); + QPoint p = QPoint(x() + (width()-s.width())/2, y() + (height()-s.height())/2); + demoLabel->move(mapToGlobal(p)); + demoLabel->show(); +} + +void LinesBoard::hideDemoText() +{ + if (demoLabel) + demoLabel->hide(); +} + +void LinesBoard::demoClick(int x, int y) +{ + QPoint lDest = QPoint(x*CELLSIZE+(CELLSIZE/2), y*CELLSIZE+(CELLSIZE/2)); + QPoint dest = mapToGlobal(lDest); + QPoint cur = QCursor::pos(); + for(int i = 0; i < 25;) + { + i++; + QPoint p = cur + i*(dest-cur) / 25; + QCursor::setPos(p); + QApplication::flushX(); + QTimer::singleShot(80, this, SLOT(demoClickStep())); + kapp->enter_loop(); + } + QCursor::setPos(dest); + QMouseEvent ev(QEvent::MouseButtonPress, lDest, dest, LeftButton, LeftButton); + mousePressEvent(&ev); +} + +void LinesBoard::demoClickStep() +{ + kapp->exit_loop(); +} diff --git a/klines/linesboard.h b/klines/linesboard.h new file mode 100644 index 00000000..044887b4 --- /dev/null +++ b/klines/linesboard.h @@ -0,0 +1,130 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef linesboard_h +#define linesboard_h + +#include +#include +#include +#include + +#include + +#include "field.h" +//#include "shotcounter.h" +#include "ballpainter.h" + +class LinesBoard : public Field +{ + Q_OBJECT +public: + LinesBoard( BallPainter * abPainter, QWidget* parent=0, const char* name=0 ); + ~LinesBoard(); + + int width(); + int height(); + int wHint(); + int hHint(); +// void doAfterBalls(); + void placeBalls(int nextBalls[BALLSDROP]); + void undo(); + bool gameOver() { return bGameOver; } + void setGameOver(bool b); + int random(int max) { return (level == DEMO_LEVEL) ? rnd_demo.getLong(max) : rnd.getLong(max); } + void saveRandomState() { rnd_saved = rnd; } + void restoreRandomState() { rnd = rnd_saved; } + void setLevel(int _level) { level = _level; } + void startDemoMode(); + void adjustDemoMode(bool allowMove, bool off); + void showDemoText(const QString &); + void hideDemoText(); + void demoClick(int x, int y); + void demoAdjust(int a); + +signals: + void endTurn(); + void endGame(); + void eraseLine(int nb); + void userTurn(); + +private: + int anim; + + struct Waypoints { + int x,y; + } *way; + int nextBalls[BALLSDROP]; + + int animmax; + + //used for running and animation phase + int painting; + int animstep; + int animdelaycount; + int animdelaystart; + int direction; + + + int nextBallToPlace; + int jumpingCol; + int jumpingRow; + + int focusX; + int focusY; + + int level; + + QTimer* timer; +// ShotCounter* shCounter; + BallPainter* bPainter; + bool bGameOver; + KRandomSequence rnd; + KRandomSequence rnd_saved; + KRandomSequence rnd_demo; + + QLabel *demoLabel; + bool bAllowMove; + + void paintEvent( QPaintEvent* ); + void mousePressEvent( QMouseEvent* ); + + void AnimStart(int panim); + void AnimNext(); + int AnimEnd(); + int getAnim(int x, int y ); // returns if the specifyed cell is animated.. + void AnimJump( int col, int row ); + + int erase5Balls(); + bool existPath(int ax, int ay, int bx, int by); + void placeBall(); + void placePlayerBall(int col, int row); + void moveFocus(int dx, int dy); + +public slots: + void moveLeft(); + void moveRight(); + void moveUp(); + void moveDown(); + void placePlayerBall(); + +protected slots: + void timerSlot(); + void demoClickStep(); +}; + +#endif diff --git a/klines/main.cpp b/klines/main.cpp new file mode 100644 index 00000000..66f5959c --- /dev/null +++ b/klines/main.cpp @@ -0,0 +1,56 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + /* + * Roman Razilov 2000-05-19 debug dummmy + * Roman Razilov 2000-05-21 qimgio +*/ + + +#include +#include +#include +#include + +#include "klines.h" + +static const char description[] = I18N_NOOP("Kolor Lines - a little game about balls and how to get rid of them"); + +// A hack to circumvent tricky i18n issue, not used later on in the code. +// Both context and contents must be exactly the same as for the entry in +// kdelibs/kdeui/ui_standards.rc +static const char dummy[] = I18N_NOOP2("Menu title", "&Move"); + +int main( int argc, char **argv ) +{ + KAboutData aboutData("klines", I18N_NOOP("Kolor Lines"), LINESVERSION, + description, KAboutData::License_GPL); + aboutData.addAuthor("Roman Merzlyakov", I18N_NOOP("Original author"), "roman@sbrf.barrt.ru"); + aboutData.addAuthor("Roman Razilov", I18N_NOOP("Rewrite and Extension"), "Roman.Razilov@gmx.de"); + KCmdLineArgs::init(argc, argv, &aboutData); + + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + + if (a.isRestored()) + RESTORE(KLines) + else { + KLines *w = new KLines; + a.setMainWidget(w); + w->show(); + } + return a.exec(); +} diff --git a/klines/mwidget.cpp b/klines/mwidget.cpp new file mode 100644 index 00000000..a6555835 --- /dev/null +++ b/klines/mwidget.cpp @@ -0,0 +1,77 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "mwidget.moc" + +#include + +#include +#include + +#include "ballpainter.h" + +MainWidget::MainWidget( QWidget* parent, const char* name ) + : QFrame( parent, name ) +{ + QBoxLayout *grid = new QHBoxLayout( this, 5 ); //(rows,col) + bPainter = new BallPainter(); + + lsb = new LinesBoard(bPainter, this); + grid->addWidget( lsb ); + + QBoxLayout *right = new QVBoxLayout(grid, 2); + QLabel *label = new QLabel(i18n("Next balls:"), this); + lPrompt = new LinesPrompt(bPainter, this); + connect(lPrompt, SIGNAL(PromptPressed()), parent, SLOT(switchPrompt())); + + right->addWidget( label, 0, Qt::AlignBottom | Qt::AlignHCenter ); + right->addWidget( lPrompt, 0, Qt::AlignTop | Qt::AlignHCenter ); + + grid->activate(); + grid->freeze(0,0); + +// warning("width: %i height: %i", width(), height() ); + +// warning("wh: %i hh: %i", sizeHint().width(), sizeHint().height() ); + +} + +/* + Destructor: deallocates memory for contents +*/ + +MainWidget::~MainWidget() +{ +} + +LinesBoard * MainWidget::GetLsb() +{ + return lsb; +} + +LinesPrompt * MainWidget::GetPrompt() +{ + return lPrompt; +} + +void MainWidget::updatePix() +{ + bPainter->deletePix(); + bPainter->createPix(); + lPrompt->update(); + lsb->update(); +} diff --git a/klines/mwidget.h b/klines/mwidget.h new file mode 100644 index 00000000..140ccd51 --- /dev/null +++ b/klines/mwidget.h @@ -0,0 +1,45 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef MWIDGET_H +#define MWIDGET_H + +#include +#include +#include +#include +#include "linesboard.h" +#include "prompt.h" + +class BallPainter; + +class MainWidget : public QFrame +{ + Q_OBJECT + LinesBoard * lsb; + LinesPrompt * lPrompt; + BallPainter *bPainter; + +public: + MainWidget( QWidget* parent=0, const char* name=0 ); + ~MainWidget(); + LinesBoard * GetLsb(); + LinesPrompt * GetPrompt(); + void updatePix(); +}; + +#endif diff --git a/klines/povray/ball.pov b/klines/povray/ball.pov new file mode 100644 index 00000000..16d577ed --- /dev/null +++ b/klines/povray/ball.pov @@ -0,0 +1,289 @@ +/*************************************************************************** + ball.pov - script for rendering Kolor Lines graphics + ------------------- + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#version 3.0 + +// Objectname = Main View +// Objecttype = view + +// This camera is build for ratio 451:277 +#include "colors.inc" +#include "textures.inc" +#declare BallColor = Green +#include "clr.inc" + +#declare CELLSIZE = 32 +#declare PIXTIME = 10 +#declare BALLDOWN = 3 +#declare BALLSPACE = 4 + +#warning concat("XXXX clock:",str(clock,5,2),"\n") + +#switch (clock) +// --------------------burning ball------------------- + #range (0,1.001) + + #declare explosion = 1 + #declare jump = 0 + #declare n = 0 + + #declare t0 = 0 + #declare t1 = 0.2 + #declare t2 = 0.5 + #declare t3 = 0.8 + #declare t4 = 1 + #declare tt = clock + #if ( tt = t0 ) + //turbulence + #declare tur = 0 + #declare trb = 0 + #declare trf = 0 + #declare pf = 0 + #end + #if ( tt > t0 & tt <= t1 ) + #declare tl = (tt - t0)/(t1-t0) + #declare tur = 3+(0.2-3)*tl + #declare trb = 0+(0-0)*tl + #declare trf = 1+(3-1)*tl + #declare pf = 0+(0.3-0)*t1 + #end + #if ( tt > t1 & tt <= t2 ) + #declare tl = (tt - t1)/(t2-t1) + #declare tur = 0.2+(0.5-0.1)*tl + #declare trb = 0+(1-0)*tl + #declare trf = 3+(1-3)*tl + #declare pf = 0.3+(0.2-0.3)*t1 + #end + #if ( tt > t2 & tt <= t3 ) + #declare tl = (tt - t2)/(t3-t2) + #declare tur = 0.5+(2-0.5)*tl + #declare trb = 1+(1-1)*tl + #declare trf = 1+(1-1)*tl + #declare pf = 0.2-(0.0-0.2)*t1 + #end + #if ( tt > t3 ) + #declare tl = (tt - t3)/(t4-t3) + #declare tur = 2+(5-2)*tl + #declare trb = 1+(1-1)*tl + #declare trf = 1+(0-1)*tl + #declare pf = 0.0-(0.0-0.0)*t1 + #end + // ball position / size + #declare by = 0 + #declare bh = CELLSIZE-2-BALLSPACE + #declare bw = CELLSIZE-2-BALLSPACE + #break +// ----------------borning ball---------------------- + #range (5,6.001) + + #declare explosion = 0 + #declare jump = 0 + + // fire turbulence + #declare tur = 0 + // trancperancy ball + #declare trb = 0 + // trancperancy fire + #declare trf = 0 + // lambda + #declare pf = 0 + // relative clock + #declare tt = clock - 5 + // y position + #declare by = 0 + #declare r=sqrt(tt) + #declare bh = (CELLSIZE-2-BALLSPACE)*r + #declare bw = (CELLSIZE-2-BALLSPACE)*r + // normal + #declare n = (1-tt)*r*2 + + #break + + // -------------jumping ball---------------------- + #range (10,11.001) + #declare explosion = 0 + #declare jump = 1 + #declare tur = 0 + #declare trb = 0 + #declare trf = 0 + #declare pf = 0 + #declare n = 0 + #declare tt = clock - 10 + // ball free fall: y = -(g * t^2)/2 + // ball jums on X*sin( sqrt(k)*(t0 - t1 + t ) ) + // t whole motion time + // g - gravity acceleration + // A - frei movement amplitude + // BB way with deflation + #declare A = BALLDOWN + #declare B = BALLSPACE + #declare ta = PIXTIME + #declare k = (pi + 2*sqrt( B*(A+B))/A - acos( A/(A+2*B)) )/ta + #declare k = k*k + #warning concat("XXXX k:",str(k,5,2),"\n") + #declare g = k*A*A/(2*(A+B)) + #warning concat("XXXX g:",str(g,5,2),"\n") + #declare t1 = sqrt(2*B/g) + #warning concat("XXXX t1:",str(t1,5,2),"\n") + #declare X = A*(A+2*B)/(2*(A+B)) + #warning concat("XXXX X:",str(X,5,2),"\n") + + #declare tc = PIXTIME - tt*PIXTIME + + #warning concat("XXXX tc:",str(tc,5,2),"\n") + // ball position + #if (tc < t1) + #declare by = B/2 - g*tc*tc/2 + #else + #declare by = -B/2 - g/k - X*cos( sqrt(k)*(ta-tc)) + #end + #if ( by < -2 ) + #declare bh=CELLSIZE-2+2*by + #declare bw = CELLSIZE-2-BALLSPACE+(-by-2) + #else + #declare bh = CELLSIZE-2-BALLSPACE + #declare bw = CELLSIZE-2-BALLSPACE + #end + + #break + + #else + #warning " ERROR, clock out of range !!! \n" + +#end + + +#warning concat("XXXX clock:",str(clock,5,2),"\n") + +#warning concat("XXXX tt:",str(tt,5,2),"\n") + + + + + +camera +{ + orthographic + location <0,0,-100> + look_at <0,0,0> + up (CELLSIZE-2)*y + right (CELLSIZE)*x +} + +// Global settings +global_settings +{ + ambient_light rgb<0.5,0.5,0.5> +} +light_source +{ + <-400, 600, -600> + color White + cylinder + radius 200 + falloff 1000 + tightness 1 + point_at <0,0,0> +} +// Background +background +{ + color rgb<.5,.5,.5> +} + +// fire +#if (explosion = 1) +sphere { 0, 1 + pigment { color rgbt<0,0,0,1> + } + halo { + emitting + spherical_mapping + poly + max_value 40 + exponent 0.1 + linear + turbulence tur +// phase pf + lambda 2-pf + frequency 1 + octaves 6 + color_map { + [ 0 color rgbt <1, 0, 0, 1> ] + [ 0.2 color rgbt <1, 0, 0, 1-1*trf> ] + [ 0.5 color rgbt <1, 0, 0, 1-3*trf> ] + [ 0.9 color rgbt <1, 1, 0, 1-4*trf> ] + [ 1 color rgbt <1, 1, 0.5, 1-6*trf> ] + } + samples 30 + } + hollow + scale<15,15,15> + translate <-0.5,-0.5,0> + } +#end +// Ball +#if ( trb < 1 & bh > 0 ) +sphere +{ + <0,0,0>,1 + pigment { + + wood +// agate_turb 0.5 + turbulence .1 + frequency 2 +// lambda + rotate <30,10,15> + color_map { + [0.5 BallColor transmit trb] + [0.3 BallColor*0.95 transmit trb] + [0.2 BallColor*0.9 transmit trb] + } + } + no_shadow + #if ( n > 0 ) + normal + { + //wood 0.5 + //frequency 2 + //turbulence .1 + //rotate <30,10,15> + bumps n + scale 0.3 + } + #end + finish { + ambient 0.3 + diffuse 0.8 + specular 1 + roughness 0.001 + reflection .1 + phong 0.9 * ( 1-trb) + phong_size 60 + + } + #if (trb > 0) + hollow + #end + scale + translate <-0.5,by - 0.5,0> + +#warning concat("XXXX by:",str(by,5,2),"\n") +#warning concat("XXXX bh:",str(bh,5,2),"\n") +#warning concat("XXXX bw:",str(bw,5,2),"\n") +} +#end diff --git a/klines/povray/balls.sh b/klines/povray/balls.sh new file mode 100755 index 00000000..1897afaa --- /dev/null +++ b/klines/povray/balls.sh @@ -0,0 +1,13 @@ +#!/bin/sh +echo "#declare BallColor = $2" >clr.inc +#### jumping balls +povray -w30 -h30 -V1 -P +A +KI10 +KF11 +KFF10 -Iball.pov +Oa$1.tga +#### borning balls +povray -w30 -h30 -V1 -P +A +KI5 +KF6 +KFF6 +SF2 +EF5 -Iball.pov +Ob$1.tga +#### burning balls balls +povray -w30 -h30 -V1 -P +A +KI0 +KF1 +KFF11 +SF1 +EF5 -Iball.pov +Oe$1.tga + +montage -geometry 30x30 -tile 100x1 -page 570x30 a$1??.tga b$1?.tga e$1??.tga bl_$1.tga +mogrify -crop 570x30+0+0 bl_$1.tga + + diff --git a/klines/povray/clean.sh b/klines/povray/clean.sh new file mode 100755 index 00000000..df78c905 --- /dev/null +++ b/klines/povray/clean.sh @@ -0,0 +1,2 @@ +#!/bin/sh +rm *.tga *.jpg \ No newline at end of file diff --git a/klines/povray/field.pov b/klines/povray/field.pov new file mode 100644 index 00000000..1256c733 --- /dev/null +++ b/klines/povray/field.pov @@ -0,0 +1,31 @@ +#declare CELLSIZE = 32 +#include "colors.inc" + +camera +{ + orthographic + location <0,0,-100> + look_at <0,0,0> + up (CELLSIZE-2)*y + right (CELLSIZE)*x +} +// Global settings +global_settings +{ + ambient_light rgb<0.5,0.5,0.5> +} +light_source +{ + <-400, 600, -600> + color White + cylinder + radius 100 + falloff 1000 + tightness 1 + point_at <0,0,0> +} +background +{ + color rgb<.5,.5,.5> +} + diff --git a/klines/povray/field.sh b/klines/povray/field.sh new file mode 100755 index 00000000..f4a784d2 --- /dev/null +++ b/klines/povray/field.sh @@ -0,0 +1,5 @@ +#!/bin/sh +if (povray -w32 -h32 -V1 -P +A +K10 -Ifield.pov +Ofield.tga) +then + mogrify -raise 1x1 field.tga +fi diff --git a/klines/povray/fire.sh b/klines/povray/fire.sh new file mode 100755 index 00000000..2b6b5f9a --- /dev/null +++ b/klines/povray/fire.sh @@ -0,0 +1,9 @@ +#! /bin/sh +echo "#declare BallColor = White" >clr.inc +if (povray -w30 -h30 -V1 -P +A +KI0 +KF1 +KFF11 +SF6 +EF10 -Iball.pov \ ++Ofire.tga) +then # mogrify -raise 1x1 e$1??.tga + montage -geometry 30x30 -tile 100x1 -page 150x30 fire*.tga fire.tga + mogrify -crop 150x30+0+0 fire.tga +# animate -delay 7 $1??.tga +fi diff --git a/klines/povray/makeballs.sh b/klines/povray/makeballs.sh new file mode 100755 index 00000000..62769605 --- /dev/null +++ b/klines/povray/makeballs.sh @@ -0,0 +1,36 @@ +#! /bin/sh +#### clean + +clean.sh + +#### render + +field.sh +fire.sh +balls.sh Red "rgb<1,0,0>" +balls.sh Green "rgb<0,1,0>" +balls.sh Blue "rgb<0,0,1>" +balls.sh Yellow "rgb<1,1,0>" +balls.sh Violet "rgb<1,0,1>" +balls.sh Cyan "rgb<0,1,1>" +balls.sh Brown "rgb<1/2,1/3,0>" + +#### montage balls ( 20x7 cells each 30x30) + +montage -geometry 570x30 -tile 1x10 bl_*.tga balls.tga +mogrify -crop 570x210+0+0 balls.tga + +#### convert to jpeg + +convert -quality 95 balls.tga balls.jpg +convert -quality 95 fire.tga fire.jpg +convert -quality 95 field.tga field.jpg + + +#### copy to data + +cp *jpg ../ + +#### clean + +#clean.sh \ No newline at end of file diff --git a/klines/povray/povray.ini b/klines/povray/povray.ini new file mode 100644 index 00000000..22ddf8ec --- /dev/null +++ b/klines/povray/povray.ini @@ -0,0 +1,59 @@ +; PERSISTENCE OF VISION RAY TRACER +; +; POV-Ray VERSION 3.0 +; +; SAMPLE POVRAY.INI FILE +; +; This file contains options which control how POV-Ray does its job. +; The file should be placed in the same directory as POVRAY.EXE and +; it will automaticaly read when POVRAY is run. +; +; The general form of the options is "Variable=value". Everything +; between the equals sign and the end of the line is considered part +; of the value. The spacing and layout is free-form but only one option +; per line is allowed. Variables and values are not case-sensitive. +; +; Note: characters after a semi-colon are treated as a comment +; +; Traditional POV-Ray switches beginning with + or - are also allowed +; and they may be given with more than one switch per line. +; +; These options have been put in this file because they will help +; you get up and running quickly. This file assumes you have installed +; the program in the /usr/local/lib directory. If you have installed +; the program on a directory you must edit the Library_Path lines below. +; See the general and Linux specific documentation for full instructions +; on how to use INI options. +; +; Add your own options at the bottom and/or edit these to suit. +; +; +; Width of image in pixels. Accepts integer values. +; +Width = 320 +; +; +; Height of image in pixels. Accepts integer values. +; +Height = 240 +; +; +; Turns pause when done feature off/on. Accepts boolean values. +; +Pause_when_Done = on +; +; +; Sets minimum number of objects before auto bounding kicks in. +; +Bounding_Threshold = 3 +; +; +; Test for user abort with any keypress every 100 pixels. +; +Test_Abort=On +Test_Abort_Count=100 +; +; Specify path to search for any files not found in current +; directory. Up to 25 such paths may be specified. +Library_Path=/usr/lib/povray3 +Library_Path=/usr/lib/povray3/include diff --git a/klines/prefs.kcfgc b/klines/prefs.kcfgc new file mode 100644 index 00000000..6ed42a2f --- /dev/null +++ b/klines/prefs.kcfgc @@ -0,0 +1,7 @@ +# Code generation options for kconfig_compiler +File=klines.kcfg +#IncludeFiles=defines.h +ClassName=Prefs +Singleton=true +#CustomAdditions=true +Mutators=true diff --git a/klines/prompt.cpp b/klines/prompt.cpp new file mode 100644 index 00000000..09c17e2a --- /dev/null +++ b/klines/prompt.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include "prompt.h" +#include "prompt.moc" + +LinesPrompt::LinesPrompt( BallPainter * abPainter, QWidget* parent, const char* name ) + : QWidget( parent, name ) +{ + + bPainter = abPainter; + + setFocusPolicy( NoFocus ); + setBackgroundColor( gray ); + + setMouseTracking( FALSE ); + setFixedSize(wPrompt(), hPrompt()); + + PromptEnabled = true; + cb[0]=NOBALL; + cb[1]=NOBALL; + cb[2]=NOBALL; +} + +LinesPrompt::~LinesPrompt() +{ +} + +int LinesPrompt::width() { return CELLSIZE * 3 ; } +int LinesPrompt::height() { return CELLSIZE ; } + +int LinesPrompt::wPrompt() { return CELLSIZE * 3 ; } +int LinesPrompt::hPrompt() { return CELLSIZE ; } + +void LinesPrompt::paintEvent( QPaintEvent* ) +{ + QPainter paint( this ); + if(PromptEnabled){ + paint.drawPixmap(0, 0, bPainter->GetNormalBall(cb[0]) ); + paint.drawPixmap(CELLSIZE, 0, bPainter->GetNormalBall(cb[1]) ); + paint.drawPixmap(2*CELLSIZE, 0, bPainter->GetNormalBall(cb[2]) ); + } + else{ + paint.drawPixmap(0, 0, bPainter->GetBackgroundPix() ); + paint.drawPixmap(CELLSIZE, 0, bPainter->GetBackgroundPix() ); + paint.drawPixmap(2*CELLSIZE, 0, bPainter->GetBackgroundPix() ); + } +} + +/* + Handles mouse press events for the LinesPrompt widget. +*/ +void LinesPrompt::mousePressEvent( QMouseEvent* ) +{ + emit PromptPressed(); +} + +void LinesPrompt::SetBalls( int *pcb ) +{ + for (int i = 0; i +#include "ballpainter.h" + +class LinesPrompt : public QWidget +{ + Q_OBJECT + + BallPainter* bPainter; + bool PromptEnabled; + int cb[BALLSDROP]; + + void paintEvent( QPaintEvent* ); + void mousePressEvent( QMouseEvent* ); + +public: + LinesPrompt( BallPainter * abPainter, QWidget * parent=0, const char * name=0 ); + ~LinesPrompt(); + + void setPrompt(bool enabled); + bool getState(); // enabled = true + void SetBalls( int *pcb ); + + int width(); + int height(); + int wPrompt(); + int hPrompt(); + +signals: + void PromptPressed(); + +}; + +#endif diff --git a/klines/templates/cpp_template b/klines/templates/cpp_template new file mode 100644 index 00000000..6afef5d4 --- /dev/null +++ b/klines/templates/cpp_template @@ -0,0 +1,16 @@ +/*************************************************************************** + |FILENAME| - description + ------------------- + begin : |DATE| + copyright : (C) |YEAR| by |AUTHOR| + email : |EMAIL| + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ diff --git a/klines/templates/header_template b/klines/templates/header_template new file mode 100644 index 00000000..6afef5d4 --- /dev/null +++ b/klines/templates/header_template @@ -0,0 +1,16 @@ +/*************************************************************************** + |FILENAME| - description + ------------------- + begin : |DATE| + copyright : (C) |YEAR| by |AUTHOR| + email : |EMAIL| + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ diff --git a/kmahjongg/Background.cpp b/kmahjongg/Background.cpp new file mode 100644 index 00000000..749607b3 --- /dev/null +++ b/kmahjongg/Background.cpp @@ -0,0 +1,115 @@ + +#include "Background.h" +#include + +Background::Background(): tile(true) { + + sourceImage = 0; + backgroundImage = 0; + backgroundPixmap = 0; + backgroundShadowPixmap = 0; +} + +Background::~Background() { + delete sourceImage; + delete backgroundImage; + delete backgroundPixmap; + delete backgroundShadowPixmap; +} + +bool Background::load(const QString &file, short width, short height) { + w=width; + h=height; + + if (file == filename) { + return true; + } + sourceImage = new QImage(); + backgroundImage = new QImage(); + backgroundPixmap = new QPixmap(); + backgroundShadowPixmap = new QPixmap(); + + // try to load the image, return on failure + if(!sourceImage->load(file )) + return false; + + // Just in case the image is loaded 8 bit + if (sourceImage->depth() != 32) + *sourceImage = sourceImage->convertDepth(32); + + // call out to scale/tile the source image into the background image + sourceToBackground(); + filename = file; + + return true; +} + +// slot used when tile/scale option changes +void Background::scaleModeChanged() { + sourceToBackground(); +} + +void Background::sizeChanged(int newW, int newH) { + if (newW == w && newH == h) + return; + w = newW; + h = newH; + sourceToBackground(); +} + +void Background::sourceToBackground() { + + // Deallocate the old image and create the new one + if (!backgroundImage->isNull()) + backgroundImage->reset(); + + // the new version of kmahjongg uses true color images + // to avoid the old color limitation. + backgroundImage->create(w, h, 32); + + // Now we decide if we should scale the incoming image + // or if we tile. First we check for an exact match which + // should be true for all images created specifically for us. + if ((sourceImage->width() == w) && (sourceImage->height() == h)) { + *backgroundImage = *sourceImage; + return; + } + + if (tile) { + // copy new to background wrapping on w and height + for (int y=0; yheight(); y++) { + QRgb *dest = (QRgb *) backgroundImage->scanLine(y); + QRgb *src = (QRgb *) sourceImage->scanLine(y % sourceImage->height()); + for (int x=0; x< backgroundImage->width(); x++) { + *dest = *(src + (x % sourceImage->width())); + dest++; + } + } + } else { + *backgroundImage = sourceImage->smoothScale(w, h); + // Just incase the image is loaded 8 bit + if (backgroundImage->depth() != 32) + *backgroundImage = backgroundImage->convertDepth(32); + } + + // Save a copy of the background as a pixmap for easy and quick + // blitting. + backgroundPixmap->convertFromImage(*backgroundImage); + + QImage tmp; + tmp.create(backgroundImage->width(), backgroundImage->height(), 32); + for (int ys=0; ys < tmp.height(); ys++) { + QRgb *src = (QRgb *) backgroundImage->scanLine(ys); + QRgb *dst = (QRgb *) tmp.scanLine(ys); + for (int xs=0; xs < tmp.width(); xs++) { + *dst=QColor(*src).dark(133).rgb(); + src++; + dst++; + } + } + + backgroundShadowPixmap->convertFromImage(tmp); + + return; +} + diff --git a/kmahjongg/Background.h b/kmahjongg/Background.h new file mode 100644 index 00000000..095f0f6e --- /dev/null +++ b/kmahjongg/Background.h @@ -0,0 +1,36 @@ +#ifndef _BACKGROUND_H +#define _BACKGROUND_H +#include + +class QPixmap; +class QImage; + + +class Background +{ + + + public: + Background(); + ~Background(); + bool tile; + + bool load(const QString &file, short width, short height); + void sizeChanged(int newW, int newH); + void scaleModeChanged(); + QPixmap *getBackground() {return backgroundPixmap;} + QPixmap *getShadowBackground() {return backgroundShadowPixmap;} + private: + void sourceToBackground(); + + int tileMode; // scale background = 0, tile = 1 + QImage *backgroundImage; + QImage *sourceImage; + QPixmap *backgroundPixmap; + QPixmap *backgroundShadowPixmap; + QString filename; + short w; + short h; +}; + +#endif diff --git a/kmahjongg/BoardLayout.cpp b/kmahjongg/BoardLayout.cpp new file mode 100644 index 00000000..56659611 --- /dev/null +++ b/kmahjongg/BoardLayout.cpp @@ -0,0 +1,300 @@ + +#include "BoardLayout.h" +#include +#include +#include + + + + + +BoardLayout::BoardLayout() +{ + filename=""; + clearBoardLayout(); +} + +BoardLayout::~BoardLayout() +{ +} + +void BoardLayout::clearBoardLayout() { + loadedBoard=""; + initialiseBoard(); +} + +bool BoardLayout::saveBoardLayout(const QString where) { + QFile f(where); + if (!f.open(IO_ReadWrite)) { + return false; + } + + QCString tmp = layoutMagic1_0.utf8(); + if (f.writeBlock(tmp, tmp.length()) == -1) { + return(false); + } + + for (int z=0; z0; x--) { + board[z][y][x] = board[z][y][x-1]; + } + board[z][y][0] = 0; + } + } +} +void BoardLayout::shiftUp() { + for (int z=0; z0; y--) { + for (int x=0; x= depth -1) + return false; + if( board[z+1][y][x] || board[z+1][y+1][x] || board[z+1][y][x+1] || board[z+1][y+1][x+1] ) { + return true; + } + return false; +} + + +bool BoardLayout::blockedLeftOrRight(short z, short y, short x) { + return( (board[z][y][x-1] || board[z][y+1][x-1]) && + (board[z][y][x+2] || board[z][y+1][x+2]) ); +} + +void BoardLayout::deleteTile(POSITION &p) { + if ( p.e +#include "KmTypes.h" + +const QString layoutMagic1_0 = "kmahjongg-layout-v1.0"; + +class BoardLayout { + +public: + BoardLayout(); + ~BoardLayout(); + + bool loadBoardLayout(const QString from); + bool saveBoardLayout(const QString where); + UCHAR getBoardData(short z, short y, short x) {return board[z][y][x];} + + // is there a tile anywhere above here (top left to bot right quarter) + bool tileAbove(short z, short y, short x); + bool tileAbove(POSITION &p) { return(tileAbove(p.e, p.y, p.x)); } + + // is this tile blocked to the left or right + bool blockedLeftOrRight(short z, short y, short x); + + void deleteTile(POSITION &p); + + bool anyFilled(POSITION &p); + bool allFilled(POSITION &p); + void insertTile(POSITION &p); + bool isTileAt(POSITION &p) { return board[p.e][p.y][p.x] == '1'; } + + + + const char *getBoardLayout(); + void copyBoardLayout(UCHAR *to , unsigned short &numTiles); + void clearBoardLayout(); + void shiftLeft(); + void shiftRight(); + void shiftUp(); + void shiftDown(); + + + enum { width = 32, + height = 16, + depth = 5 }; + enum { maxTiles = (depth*width*height)/4 }; + + QString &getFilename() {return filename;} + +protected: + + void initialiseBoard(); + +private: + QString filename; + QString loadedBoard; + UCHAR board[depth][height][width]; + unsigned short maxTileNum; +}; + +#endif diff --git a/kmahjongg/ChangeLog b/kmahjongg/ChangeLog new file mode 100644 index 00000000..dae64050 --- /dev/null +++ b/kmahjongg/ChangeLog @@ -0,0 +1,97 @@ + +This is the change log for kmahjongg. + +Personel + Albert Astals Cid: Some bug fixes and wishlists + Michael Haertjens: Solvable game generation + David Black: Fold, spindle and mutilate 0.4.1 to 0.5.0 + Osvaldo Stark: Tileset creator, doc guinepig and tester 0.5.0 + Robert Williams: Bug fixes 0.4.0 to 0.4.1 + Mathias Muller: Implementor of the original kmahjongg. + +0.7.4 to 0.7.6 ++ Fix bug #73944 ++ Implement wishlist #63171 ++ Implement wishlist #56607 ++ Fix bug #26595 + +0.7.3 to 0.7.4 ++ Fixed bug #31639 Kmahjongg flashes wrong tiles. ++ Fixed bug #26872 Kmahjongg timer not reset. + +0.7.2 to 0.7.3 ++ Added ability to generate solvable games. + This includes a fair amount of code in kmahjongg.cpp, plus support + for a new entry in the Preferences dialog. ++ Place checkmark on Game menu when Demo Mode is active. ++ Change "Show Hiscores" to "Show High Scores" in Game Menu. + +0.7.0 to 0.7.2 + ++ Fixed bug in show matching tiles so that tiles on the top row and + the left -most column are detected in matched (the dreaded off by + one error. Fix provided by Robert Schroeter. ++ Fixed bug in board generation where tiles went missing. Fix + provided by Robert Schroeter. ++ Fixed bug where the default tileset was listed twice in the + preview dialog ++ Fixed bug in the board editor, which stopped you putting + tiles in completely legal places. + +Version 0.4.1 to 0.7.0 (beta) + ++ Took over sources from Mathias Muller. ++ Changed tileset highlighting which previousely used a swap of two + fixed pallete colours, to a new tileset format where the designer + supplies the selected and unselected tile backgrounds. ++ Changed the main game engine to a 24 bit colour system. This removed + the old problem where the tileset and background combined could + only use a maximum of 128 colours. No limitations now exist. For + low color depths the game screen is dithered. ++ converted the rendering method to use pixmaps and blitting. ++ added a tool bar for commonly used features. ++ Added a tileset load feature (Yeah!!). ++ Added preferences dialog ++ Added preference to disable shadow generation (after complaints + about 3D visualisation. ++ Configuration and preferences now persist across sessions. ++ Tidied up transparency for tilesets. Now the top left colour of + the background tile determines the transparency colour, not a + fixed value of 0. ++ Added a hiscore system based on time taken, tiles removed and + penalties for using _cheats_ ++ Added a game timer to the tool bar. ++ added a hiscore dialog. ++ Added a pause mode for hiscore play (blanks the screen because lets face it + people cheat). ++ Background images may be tiled or scalled. Saved as a preference ++ The main rendering functions are now independant of the tileset + metric. In future this will allow for variable size tiles. ++ Added preview dialogs for load background, tileset and game board, + now you can see what you will get. ++ Added a redo to compliment the undo. ++ Fixed up the menu system and added more accelerators. ++ Added a play with mini-tiles option. Not necessarily for everyone, + but some people (well at least one, and thats me, so there) use + kmahjongg on an 800x600 lcd laptop display. So this helps! ++ Added a show removed tiles option. This allows you to determine if + it is safe to remove a pair etc. Nice aid to game play. ++ Moved the file selectors over to the kde style. This should + allow urls to be supplied for tilesets, backgrounds and boards etc. In + future I hope to maintain a web page with new tilesets and layout etc. + Ultimately it would be nice to have a per-boardlayout internet hiscore + table (mayhaps in version 0.6) ++ Started documenting how to design tilesets etc. ++ Fixed a few bugs and introduced many (probably) ++ Added an embryonic board editor ++ Rewrote game generation for new board layout possibilities ++ Speed improvements for tile set load using fixed masks ++ High score now displays time as well as score (requested feature) ++ Plus lots of bits I forgot :-) + + + +Version original to 0.4.1 +* [Robert Williams] New games now start at 10 +* [Robert Williams] Added kapp->getHelpMenu() +* [Robert Williams] Added -caption "%c" to kmahjongg.kdelnk diff --git a/kmahjongg/Editor.cpp b/kmahjongg/Editor.cpp new file mode 100644 index 00000000..4e141259 --- /dev/null +++ b/kmahjongg/Editor.cpp @@ -0,0 +1,675 @@ + +#include +#include +#include +#include + +#include "Editor.h" +#include "prefs.h" + +#include +#include // Needed to use KLocale +#include // +#include +#include + +#define ID_TOOL_NEW 100 +#define ID_TOOL_LOAD 101 +#define ID_TOOL_SAVE 102 +#define ID_TOOL_ADD 103 +#define ID_TOOL_DEL 104 +#define ID_TOOL_MOVE 105 +#define ID_TOOL_SELECT 106 +#define ID_TOOL_CUT 107 +#define ID_TOOL_COPY 108 +#define ID_TOOL_PASTE 109 +#define ID_TOOL_LEFT 110 +#define ID_TOOL_RIGHT 111 +#define ID_TOOL_UP 112 +#define ID_TOOL_DOWN 113 + +#define ID_TOOL_STATUS 199 + +#define ID_META_EXIT 201 + + + +// When we assign a tile to draw in a slot we do it in order from te following +// table, wrapping on the tile number. It makes the tile layout look more +// random. + + +Editor::Editor +( + QWidget* parent, + const char* name +) + : + QDialog( parent, name, true, 0 ), tiles(false) +{ + + clean= true; + numTiles=0; + mode = insert; + + int sWidth = (BoardLayout::width+2)*(tiles.qWidth()); + int sHeight =( BoardLayout::height+2)*tiles.qHeight(); + + sWidth += 4*tiles.shadowSize(); + + drawFrame = new FrameImage( this, "drawFrame" ); + drawFrame->setGeometry( 10, 40 ,sWidth ,sHeight); + drawFrame->setMinimumSize( 0, 0 ); + drawFrame->setMaximumSize( 32767, 32767 ); + drawFrame->setFocusPolicy( QWidget::NoFocus ); + drawFrame->setBackgroundMode( QWidget::PaletteBackground ); + drawFrame->setFrameStyle( 49 ); + drawFrame->setMouseTracking(true); + + // setup the tool bar + setupToolbar(); + + QVBoxLayout *layout = new QVBoxLayout(this, 1); + layout->addWidget(topToolbar,0); + layout->addWidget(drawFrame,1); + layout->activate(); + + resize( sWidth+60, sHeight+60); + setMinimumSize( sWidth+60, sHeight+60); + setMaximumSize( sWidth+60, sHeight+60); + + QString tile = Prefs::tileSet(); + tiles.loadTileset(tile); + + // tell the user what we do + setCaption(kapp->makeStdCaption(i18n("Edit Board Layout"))); + + + connect( drawFrame, SIGNAL(mousePressed(QMouseEvent *) ), + SLOT(drawFrameMousePressEvent(QMouseEvent *))); + connect( drawFrame, SIGNAL(mouseMoved(QMouseEvent *) ), + SLOT(drawFrameMouseMovedEvent(QMouseEvent *))); + + statusChanged(); + + update(); +} + + + +Editor::~Editor() +{ +} + +// --------------------------------------------------------- +void Editor::setupToolbar() +{ + + KIconLoader *loader = KGlobal::iconLoader(); + topToolbar = new KToolBar( this, "editToolBar" ); + KToolBarRadioGroup *radio = new KToolBarRadioGroup(topToolbar); + + // new game + topToolbar->insertButton(loader->loadIcon("filenew", KIcon::Toolbar), + ID_TOOL_NEW, true, i18n("New board")); + // open game + topToolbar->insertButton(loader->loadIcon("fileopen", KIcon::Toolbar), + ID_TOOL_LOAD, true, i18n("Open board")); + // save game + topToolbar->insertButton(loader->loadIcon("filesave", KIcon::Toolbar), + ID_TOOL_SAVE, true, i18n("Save board")); + topToolbar->setButtonIconSet(ID_TOOL_SAVE,loader->loadIconSet("filesave", KIcon::Toolbar)); + +#ifdef FUTURE_OPTIONS + // Select + topToolbar->insertSeparator(); + topToolbar->insertButton(loader->loadIcon("rectangle_select", KIcon::Toolbar), + ID_TOOL_SELECT, true, i18n("Select")); + topToolbar->insertButton(loader->loadIcon("editcut", KIcon::Toolbar), + ID_TOOL_CUT, true, i18n("Cut")); + topToolbar->insertButton(loader->loadIcon("editcopy", KIcon::Toolbar), + ID_TOOL_COPY, true, i18n("Copy")); + topToolbar->insertButton(loader->loadIcon("editpaste", KIcon::Toolbar), + ID_TOOL_PASTE, true, i18n("Paste")); + + topToolbar->insertSeparator(); + topToolbar->insertButton(loader->loadIcon("move", KIcon::Toolbar), + ID_TOOL_MOVE, true, i18n("Move tiles")); +#endif + topToolbar->insertButton(loader->loadIcon("pencil", KIcon::Toolbar), + ID_TOOL_ADD, true, i18n("Add tiles")); + topToolbar->insertButton(loader->loadIcon("editdelete", KIcon::Toolbar), + ID_TOOL_DEL, true, i18n("Remove tiles")); + + topToolbar->setToggle(ID_TOOL_ADD); + topToolbar->setToggle(ID_TOOL_MOVE); + topToolbar->setToggle(ID_TOOL_DEL); + topToolbar->toggleButton(ID_TOOL_ADD); + radio->addButton(ID_TOOL_ADD); +#ifdef FUTURE_OPTIONS + radio->addButton(ID_TOOL_MOVE); +#endif + radio->addButton(ID_TOOL_DEL); + + // board shift + + topToolbar->insertSeparator(); + topToolbar->insertButton(loader->loadIcon("back", KIcon::Toolbar), + ID_TOOL_LEFT, true, i18n("Shift left")); + topToolbar->insertButton(loader->loadIcon("up", KIcon::Toolbar), + ID_TOOL_UP, true, i18n("Shift up")); + topToolbar->insertButton(loader->loadIcon("down", KIcon::Toolbar), + ID_TOOL_DOWN, true, i18n("Shift down")); + topToolbar->insertButton(loader->loadIcon("forward", KIcon::Toolbar), + ID_TOOL_RIGHT, true, i18n("Shift right")); + + topToolbar->insertSeparator(); + topToolbar->insertButton(loader->loadIcon("exit", KIcon::Toolbar), + ID_META_EXIT, true, i18n("Exit")); + + // status in the toolbar for now (ick) + + theLabel = new QLabel(statusText(), topToolbar); + int lWidth = theLabel->sizeHint().width(); + + topToolbar->insertWidget(ID_TOOL_STATUS,lWidth, theLabel ); + topToolbar->alignItemRight( ID_TOOL_STATUS, true ); + + //addToolBar(topToolbar); + connect( topToolbar, SIGNAL(clicked(int) ), SLOT( topToolbarOption(int) ) ); + + topToolbar->updateRects(0); + topToolbar->setFullSize(true); + topToolbar->setBarPos(KToolBar::Top); +// topToolbar->enableMoving(false); + topToolbar->adjustSize(); + setMinimumWidth(topToolbar->width()); + + +} + +void Editor::statusChanged() { + bool canSave = ((numTiles !=0) && ((numTiles & 1) == 0)); + theLabel->setText(statusText()); + topToolbar->setItemEnabled( ID_TOOL_SAVE, canSave); +} + + +void Editor::topToolbarOption(int option) { + + switch(option) { + case ID_TOOL_NEW: + newBoard(); + break; + case ID_TOOL_LOAD: + loadBoard(); + break; + case ID_TOOL_SAVE: + saveBoard(); + break; + case ID_TOOL_LEFT: + theBoard.shiftLeft(); + repaint(false); + break; + case ID_TOOL_RIGHT: + theBoard.shiftRight(); + repaint(false); + break; + case ID_TOOL_UP: + theBoard.shiftUp(); + repaint(false); + break; + case ID_TOOL_DOWN: + theBoard.shiftDown(); + repaint(false); + break; + case ID_TOOL_DEL: + mode=remove; + break; + + case ID_TOOL_MOVE: + mode=move; + break; + + case ID_TOOL_ADD: + mode = insert; + break; + case ID_META_EXIT: + close(); + break; + + default: + + break; + } + +} + +QString Editor::statusText() { + QString buf; + + int x=currPos.x; + int y=currPos.y; + int z= currPos.e; + + if (z == 100) + z = 0; + else + z=z+1; + + if (x >=BoardLayout::width || x <0 || y >=BoardLayout::height || y <0) + x = y = z = 0; + + buf = i18n("Tiles: %1 Pos: %2,%3,%4").arg(numTiles).arg(x).arg(y).arg(z); + return buf; +} + + +void Editor::loadBoard() { + + if ( !testSave() ) + return; + + KURL url = KFileDialog::getOpenURL( + NULL, + i18n("*.layout|Board Layout (*.layout)\n" + "*|All Files"), + this, + i18n("Open Board Layout" )); + + if ( url.isEmpty() ) + return; + + + theBoard.loadBoardLayout( url.path() ); + + repaint(false); +} + + +// Clear out the contents of the board. Repaint the screen +// set values to their defaults. +void Editor::newBoard() { + if (!testSave()) + return; + + + + theBoard.clearBoardLayout(); + + + + clean=true; + numTiles=0; + statusChanged(); + repaint(false); +} + +bool Editor::saveBoard() { + // get a save file name + KURL url = KFileDialog::getSaveURL( + NULL, + i18n("*.layout|Board Layout (*.layout)\n" + "*|All Files"), + this, + i18n("Save Board Layout" )); + if( url.isEmpty() ) return false; + if( !url.isLocalFile() ) + { + KMessageBox::sorry( this, i18n( "Only saving to local files currently supported." ) ); + return false; + } + + if ( url.isEmpty() ) + return false; + + QFileInfo f( url.path() ); + if ( f.exists() ) { + // if it already exists, querie the user for replacement + int res=KMessageBox::warningContinueCancel(this, + i18n("A file with that name " + "already exists. Do you " + "wish to overwrite it?"), + i18n("Save Board Layout" ), KStdGuiItem::save()); + if (res != KMessageBox::Continue) + return false; + } + + bool result = theBoard.saveBoardLayout( url.path() ); + if (result==true){ + clean = true; + return true; + } else { + return false; + } +} + + +// test if a save is required and return true if the app is to continue +// false if cancel is selected. (if ok then call out to save the board +bool Editor::testSave() +{ + + if (clean) + return(true); + + int res; + res=KMessageBox::warningYesNoCancel(this, + i18n("The board has been modified. Would you " + "like to save the changes?"),QString::null,KStdGuiItem::save(),KStdGuiItem::dontSave()); + + if (res == KMessageBox::Yes) { + // yes to save + if (saveBoard()) { + return true; + } else { + KMessageBox::sorry(this, i18n("Save failed. Aborting operation.")); + } + } else { + return (res != KMessageBox::Cancel); + } + return(true); +} + + +// The main paint event, draw in the grid and blit in +// the tiles as specified by the layout. + +void Editor::paintEvent( QPaintEvent* ) { + + + // first we layer on a background grid + QPixmap buff; + QPixmap *dest=drawFrame->getPreviewPixmap(); + buff.resize(dest->width(), dest->height()); + drawBackground(&buff); + drawTiles(&buff); + bitBlt(dest, 0,0,&buff, 0,0,buff.width(), buff.height(), CopyROP); + + drawFrame->repaint(false); +} + +void Editor::drawBackground(QPixmap *pixmap) { + + QPainter p(pixmap); + + // blast in a white background + p.fillRect(0,0,pixmap->width(), pixmap->height(), QColor(white)); + + + // now put in a grid of tile quater width squares + int sy = (tiles.height()/2)+tiles.shadowSize(); + int sx = (tiles.width()/2); + + for (int y=0; y<=BoardLayout::height; y++) { + int nextY=sy+(y*tiles.qHeight()); + p.drawLine(sx, nextY,sx+(BoardLayout::width*tiles.qWidth()), nextY); + } + + for (int x=0; x<=BoardLayout::width; x++) { + int nextX=sx+(x*tiles.qWidth()); + p.drawLine(nextX, sy, nextX, sy+BoardLayout::height*tiles.qHeight()); + } +} + +void Editor::drawTiles(QPixmap *dest) { + + QPainter p(dest); + + QString tile1 = Prefs::tileSet(); + tiles.loadTileset(tile1); + + + int xOffset = tiles.width()/2; + int yOffset = tiles.height()/2; + short tile = 0; + + // we iterate over the depth stacking order. Each successive level is + // drawn one indent up and to the right. The indent is the width + // of the 3d relief on the tile left (tile shadow width) + for (int z=0; z=0; x--) { + int sx = x*(tiles.qWidth() )+xOffset; + int sy = y*(tiles.qHeight() )+yOffset; + if (theBoard.getBoardData(z, y, x) != '1') { + continue; + } + QPixmap *t; + tile=(z*BoardLayout::depth)+ + (y*BoardLayout::height)+ + (x*BoardLayout::width); +// if (mode==remove && currPos.x==x && currPos.y==y && currPos.e==z) { +// t = tiles.selectedPixmaps(44)); +// } else { + t = tiles.unselectedPixmaps(43); +// } + + // Only one compilcation. Since we render top to bottom , left + // to right situations arise where...: + // there exists a tile one q height above and to the left + // in this situation we would draw our top left border over it + // we simply split the tile draw so the top half is drawn + // minus border + if ((x>1) && (y>0) && theBoard.getBoardData(z,y-1,x-2)=='1'){ + + bitBlt( dest, + sx+tiles.shadowSize(), sy, + t, tiles.shadowSize() ,0, + t->width()-tiles.shadowSize(), + t->height()/2, CopyROP ); + + + bitBlt( dest, sx, sy+t->height()/2, + t, 0,t->height()/2,t->width(),t->height()/2,CopyROP); + } else { + + bitBlt( dest, sx, sy, + t, 0,0, t->width(), t->height(), CopyROP ); + } + + + tile++; + tile = tile % 143; + } + } + xOffset +=tiles.shadowSize(); + yOffset -=tiles.shadowSize(); + } +} + + +// convert mouse position on screen to a tile z y x coord +// different to the one in kmahjongg.cpp since if we hit ground +// we return a result too. + +void Editor::transformPointToPosition( + const QPoint& point, + POSITION& MouseClickPos, + bool align) +{ + + short z = 0; // shut the compiler up about maybe uninitialised errors + short y = 0; + short x = 0; + MouseClickPos.e = 100; + + // iterate over z coordinate from top to bottom + for( z=BoardLayout::depth-1; z>=0; z-- ) + { + // calculate mouse coordiantes --> position in game board + // the factor -theTiles.width()/2 must keep track with the + // offset for blitting in the print zvent (FIX ME) + x = ((point.x()-tiles.width()/2)-(z+1)*tiles.shadowSize())/ tiles.qWidth(); + y = ((point.y()-tiles.height()/2)+ z*tiles.shadowSize()) / tiles.qHeight(); + + + // skip when position is illegal + if (x<0 || x>=BoardLayout::width || y<0 || y>=BoardLayout::height) + continue; + + // + switch( theBoard.getBoardData(z,y,x) ) + { + case (UCHAR)'3': if (align) { + x--; + y--; + } + break; + + case (UCHAR)'2': if (align) + x--; + break; + + case (UCHAR)'4': if (align) + y--; + break; + + case (UCHAR)'1': break; + + default : continue; + } + // if gameboard is empty, skip + if ( ! theBoard.getBoardData(z,y,x) ) + continue; + + // here, position is legal + MouseClickPos.e = z; + MouseClickPos.y = y; + MouseClickPos.x = x; + MouseClickPos.f = theBoard.getBoardData(z,y,x); + break; + } + if (MouseClickPos.e == 100) { + MouseClickPos.x = x; + MouseClickPos.y = y; + MouseClickPos.f=0; + } +} + + +// we swallow the draw frames mouse clicks and process here +void Editor::drawFrameMousePressEvent( QMouseEvent* e ) +{ + + POSITION mPos; + transformPointToPosition(e->pos(), mPos, (mode == remove)); + + switch (mode) { + case remove: + if (!theBoard.tileAbove(mPos) && mPos.e < BoardLayout::depth && theBoard.isTileAt(mPos) ) { + theBoard.deleteTile(mPos); + numTiles--; + statusChanged(); + drawFrameMouseMovedEvent(e); + repaint(false); + } + break; + case insert: { + POSITION n = mPos; + if (n.e == 100) + n.e = 0; + else + n.e += 1; + if (canInsert(n)) { + theBoard.insertTile(n); + numTiles++; + statusChanged(); + repaint(false); + } + } + break; + default: + break; + } + +} + + +void Editor::drawCursor(POSITION &p, bool visible) +{ + int x = (tiles.width()/2)+(p.e*tiles.shadowSize())+(p.x * tiles.qWidth()); + int y = (tiles.height()/2)-(p.e*tiles.shadowSize())+(p.y * tiles.qHeight()); + int w = tiles.width(); + int h = tiles.height(); + + + if (p.e==100 || !visible) + x = -1; + drawFrame->setRect(x,y,w,h, tiles.shadowSize(), mode-remove); + drawFrame->repaint(false); + + + +} + + + +// we swallow the draw frames mouse moves and process here +void Editor::drawFrameMouseMovedEvent( QMouseEvent* e ){ + + + POSITION mPos; + transformPointToPosition(e->pos(), mPos, (mode == remove)); + + if ((mPos.x==currPos.x) && (mPos.y==currPos.y) && (mPos.e==currPos.e)) + return; + currPos = mPos; + + statusChanged(); + + switch(mode) { + case insert: { + POSITION next; + next = currPos; + if (next.e == 100) + next.e = 0; + else + next.e += 1; + + drawCursor(next, canInsert(next)); + break; + } + case remove: + drawCursor(currPos, 1); + break; + + case move: + + break; + + } + +} + +// can we inser a tile here. We can iff +// there are tiles in all positions below us (or we are a ground level) +// there are no tiles intersecting with us on this level + +bool Editor::canInsert(POSITION &p) { + + + if (p.e >= BoardLayout::depth) + return (false); + if (p.y >BoardLayout::height-2) + return false; + if (p.x >BoardLayout::width-2) + return false; + + POSITION n = p; + if (p.e != 0) { + n.e -= 1; + if (!theBoard.allFilled(n)) { + return(false); + } + } + int any = theBoard.anyFilled(p); + return(!any); + +} + + + +#include "Editor.moc" diff --git a/kmahjongg/Editor.h b/kmahjongg/Editor.h new file mode 100644 index 00000000..5ced0daf --- /dev/null +++ b/kmahjongg/Editor.h @@ -0,0 +1,68 @@ +#ifndef _EditorLoadBase_H +#define _EditorLoadBase_H + +#include +#include +#include +#include +#include + +#include "Tileset.h" +#include "BoardLayout.h" +#include "Background.h" + +#include "Preview.h" + +class Editor: public QDialog +{ + Q_OBJECT + +public: + + + Editor + ( + QWidget* parent = NULL, + const char* name = NULL + ); + + virtual ~Editor(); + + + +protected slots: + void topToolbarOption(int w); + void drawFrameMousePressEvent ( QMouseEvent* ); + void drawFrameMouseMovedEvent ( QMouseEvent *); + + +protected: + enum {remove=98, insert=99, move=100}; + void paintEvent( QPaintEvent* pa ); + void setupToolbar(); + void loadBoard(); + bool saveBoard(); + void newBoard(); + void drawBackground(QPixmap *to); + void drawTiles(QPixmap *to); + bool testSave(); + void transformPointToPosition(const QPoint &, POSITION &, bool align); + void drawCursor(POSITION &p, bool visible); + bool canInsert(POSITION &p); + void statusChanged(); + QString statusText(); +private: + int mode; + int numTiles; + KToolBar *topToolbar; + FrameImage * drawFrame; + Tileset tiles; + BoardLayout theBoard; + bool clean; + POSITION currPos; + QLabel *theLabel; +private: + +}; + +#endif diff --git a/kmahjongg/GameTimer.cpp b/kmahjongg/GameTimer.cpp new file mode 100644 index 00000000..5eb827d9 --- /dev/null +++ b/kmahjongg/GameTimer.cpp @@ -0,0 +1,94 @@ +/* + * Based upon the QT example dclock + */ + +#include +#include "GameTimer.h" +#include "GameTimer.moc" + + +// +// Constructs a GameTimer widget with a parent and a name. +// + +GameTimer::GameTimer( QWidget *parent, const char *name ) + : QLCDNumber( parent, name ) +{ + showingColon = false; + setNumDigits(7); + setFrameStyle(QFrame::Panel | QFrame::Sunken); + setFrameStyle(QFrame::NoFrame); + timerMode = stopped; + showTime(); // display the current time1 + startTimer( 500 ); // 1/2 second timer events +} + +// QObject timer call back implementation +void GameTimer::timerEvent( QTimerEvent * ) +{ + if (timerMode == running) + theTimer=theTimer.addMSecs(500); + showTime(); +} + + +// +// Shows the current time in the internal lcd widget. +// + +void GameTimer::showTime() +{ + QString s; + showingColon = !showingColon; // toggle/blink colon + + switch(timerMode) { + case paused: + case running: + s = theTimer.toString(); + break; + case stopped: + s = "00:00:00"; + break; + } + + if ( !showingColon ) + s[2] = s[5] = ' '; + display( s ); // set LCD number/text +} + +void GameTimer::start() { + theTimer.setHMS(0,0,0); + timerMode = running; +} + + +void GameTimer::fromString(const char*tim) { + int h,m,s; + sscanf(tim, "%2d:%2d:%2d\n", &h, &m, &s); + theTimer.setHMS(h,m,s); + timerMode = running; +} + + +void GameTimer::stop() { + timerMode = stopped; +} + +void GameTimer::pause() { + + if (timerMode == stopped) + return; + + if (timerMode == paused) { + timerMode = running; + } else { + timerMode = paused; + } +} + +int GameTimer::toInt() { + + return (theTimer.second()+ + theTimer.minute()*60+ + theTimer.hour()*360); +} diff --git a/kmahjongg/GameTimer.h b/kmahjongg/GameTimer.h new file mode 100644 index 00000000..6cde5d11 --- /dev/null +++ b/kmahjongg/GameTimer.h @@ -0,0 +1,53 @@ +/* ------------------------------------------------------------------------- + -- kmahjongg timer. Based on a slightly modified verion of the QT demo -- + -- program dclock. Copyright as shown below. -- + ------------------------------------------------------------------------- */ + +/**************************************************************************** +** $Id$ +** +** Copyright (C) 1992-1998 Troll Tech AS. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#ifndef KM_GAME_TIMER +#define KM_GAME_TIMER + +#include +#include + +enum TimerMode {running = -53 , stopped= -54 , paused = -55}; + +class GameTimer: public QLCDNumber +{ + Q_OBJECT +public: + GameTimer( QWidget *parent=0, const char *name=0 ); + + int toInt(); + QString toString() {return theTimer.toString();} + void fromString(const char *); + +protected: // event handlers + void timerEvent( QTimerEvent * ); + +public slots: + void start(); + void stop(); + void pause(); + + +private slots: // internal slots + void showTime(); + +private: // internal data + bool showingColon; + QTime theTimer; + TimerMode timerMode; +}; + + +#endif diff --git a/kmahjongg/HighScore.cpp b/kmahjongg/HighScore.cpp new file mode 100644 index 00000000..2e55c15a --- /dev/null +++ b/kmahjongg/HighScore.cpp @@ -0,0 +1,541 @@ +#include +#include "HighScore.h" +#include "HighScore.moc" + + +#include +#include +#include +#include "klocale.h" +#include +#include +#include +#include +#include +#include +#include + +static const QString highScoreMagic1_0 = "kmahjongg-scores-v1.0"; +static const QString highScoreMagic1_1 = "kmahjongg-scores-v1.1"; + +static const char * highScoreFilename = "/kmahjonggHiscores"; + +const char * defNames[numScores] = { + "David Black", + "Mathias Mueller", + "Osvaldo Stark", + "Steve Taylor", + "Clare Brizzolara", + "Angela Simpson", + "Michael O'Brien", + "Kelvin Bell", + "Jenifferi O'Keeffe", + "Phil Lamdin" +}; + +int defScores[numScores] = + {400, 350, 300, 250, 200, 150, 100, 50, 20, 10}; + +const int ages = 59+(59*60)+(2*60*60); +int defTimes[numScores] = {ages, ages-1, ages-2, ages-3, + ages-4, ages-5, ages-6, ages-7, ages-8, ages-9}; + + +HighScore::HighScore +( + QWidget* parent, + const char* name +) + : + QDialog( parent, name, true, 0 ) +{ + + // form the target name + + + filename = locateLocal("appdata", highScoreFilename); + + QFont fnt; + // Number + QLabel* qtarch_Label_3; + qtarch_Label_3 = new QLabel( this, "Label_3" ); + qtarch_Label_3->setGeometry( 10, 45, 30, 30 ); + qtarch_Label_3->setFrameStyle( 50 ); + qtarch_Label_3->setText( i18n("Pos") ); + qtarch_Label_3->setAlignment( AlignCenter ); + fnt = qtarch_Label_3->font(); + fnt.setBold(true); + qtarch_Label_3->setFont(fnt); + + + // name + + QLabel* qtarch_Label_4; + qtarch_Label_4 = new QLabel( this, "Label_4" ); + qtarch_Label_4->setGeometry( 40, 45, 150, 30 ); + qtarch_Label_4->setFrameStyle( 50 ); + qtarch_Label_4->setText( i18n("Name") ); + qtarch_Label_4->setFont(fnt); + + + // board number + QLabel* boardTitle; + boardTitle= new QLabel( this, "" ); + boardTitle->setGeometry( 190, 45, 80, 30 ); + boardTitle->setFrameStyle( 50 ); + boardTitle->setText( i18n("Board") ); + boardTitle->setFont(fnt); + + // score + QLabel* qtarch_Label_5; + qtarch_Label_5 = new QLabel( this, "Label_5" ); + qtarch_Label_5->setGeometry( 270, 45, 70, 30 ); + qtarch_Label_5->setFrameStyle( 50 ); + qtarch_Label_5->setText( i18n("Score") ); + qtarch_Label_5->setFont(fnt); + + // time + QLabel* qtarch_Label_6; + qtarch_Label_6 = new QLabel( this, "Label_6" ); + qtarch_Label_6->setGeometry( 340, 45, 70, 30 ); + qtarch_Label_6->setFrameStyle( 50 ); + qtarch_Label_6->setText( i18n("Time") ); + qtarch_Label_6->setFont(fnt); + + + + for (int row=0; rowsetGeometry( 110+35, 340+50, 100, 30 ); + qtarch_PushButton_1->setMinimumSize( 0, 0 ); + qtarch_PushButton_1->setMaximumSize( 32767, 32767 ); + qtarch_PushButton_1->setFocusPolicy( QWidget::TabFocus ); + qtarch_PushButton_1->setAutoRepeat( false ); + qtarch_PushButton_1->setAutoResize( false ); + qtarch_PushButton_1->setDefault(true); + + QPushButton* resetBtn; + resetBtn= new QPushButton( this, "resetBtn" ); + resetBtn->setGeometry( 10, 5, 25, 25); + resetBtn->setMinimumSize( 0, 0 ); + resetBtn->setMaximumSize( 32767, 32767 ); + resetBtn->setFocusPolicy( QWidget::TabFocus ); + //resetBtn->setText(i18n( "Reset" )); + resetBtn->setAutoRepeat( false ); + resetBtn->setAutoResize( false ); + + KIconLoader *loader = KGlobal::iconLoader(); + resetBtn->setPixmap(loader->loadIcon("editdelete", KIcon::Toolbar)); + + + + /* We create the ediat area for the hi score name entry and move it */ + /* off screen. it is moved over and placed in position when a */ + /* new name is added */ + + lineEdit = new QLineEdit(this, ""); + lineEdit->setGeometry( 50, 40+(20*30), 190, 30 ); + lineEdit->setFocusPolicy(QWidget::StrongFocus); + lineEdit->setFrame(true); + lineEdit->setEchoMode(QLineEdit::Normal); + lineEdit->setText(""); + + // the drop down for the board names + + combo = new QComboBox( false, this, "combo" ); + combo->setGeometry( 65, 5, 220, 25 ); + combo->setMinimumSize( 0, 0 ); + combo->setMaximumSize( 32767, 32767 ); + combo->setFocusPolicy( QWidget::StrongFocus ); + combo->setSizeLimit( 10 ); + combo->setAutoResize( false ); + connect( combo, SIGNAL(activated(int)), SLOT(selectionChanged(int)) ); + + + resize( 350+70,390+45 ); + setFixedSize(350+70,390+45); + + tables = NULL; + loadTables(); + currTable = tables; + + setCaption(kapp->makeStdCaption(i18n("Scores"))); + + selectedLine = -1; + + connect(lineEdit, SIGNAL( textChanged(const QString &)), + SLOT( nameChanged(const QString &))); + + + connect(qtarch_PushButton_1, SIGNAL(clicked()), SLOT(reject())); + connect(resetBtn, SIGNAL(clicked()), SLOT(reset())); +} + +// free up the table structures + +HighScore::~HighScore() +{ + TableInstance *t, *t1; + + if (tables != NULL) { + t = tables; + while(t != NULL) + { + t1 = t; + t = t -> next; + delete t1; + } + } + tables = NULL; +} + + + +// return a pointer to a linked list of table entries from the +// saved hi score tables file. If no such file exists then +// return a single default hiscore table. + +void HighScore::loadTables() { + char buff[1024]; + + // open the file, on error set up the default table + FILE *fp = fopen( QFile::encodeName(highScoreFile()), "r"); + if (fp == NULL) + goto error; + + // check magic + fscanf(fp, "%1023s\n", buff); + if (highScoreMagic1_1 != buff) { + goto error; + } + + int num; + fscanf(fp, "%d\n", &num); + + for (int p=0; pnext = tables; + + + tables = t; + + fgets(buff, sizeof(buff), fp); + if (buff[strlen(buff)-1] == '\n') + buff[strlen(buff)-1] = '\0'; + t->name = buff; + combo->insertItem(t->name); + setComboTo(t->name); + for (int e=0; eentries[e].score)); + fscanf(fp, "%ld\n", &(t->entries[e].elapsed)); + fscanf(fp, "%ld\n", &(t->entries[e].board)); + fgets(buff, sizeof(buff), fp); + if (buff[strlen(buff)-1] == '\n') + buff[strlen(buff)-1] = '\0'; + t->entries[e].name=QString::fromUtf8(buff,-1); + } + } + + + fclose(fp); + return; + + +error: + selectTable("default"); + saveTables(); + return; +} + +void HighScore::saveTables() { + + TableInstance *p; + int num = 0; + + // Nothing to do + if (tables == NULL) + return; + + + // open the outrput file, with naff error handling + FILE *fp = fopen( QFile::encodeName(highScoreFile()), "w"); + if (fp == NULL) + return; + + // count up the number of tables to save + for (p=tables; p != NULL; p = p->next) + num++; + + // output the file magic + fprintf(fp,"%s\n", highScoreMagic1_1.utf8().data()); + + // output the count of tables to process + fprintf(fp, "%d\n", num); + + // output each table + for (p=tables; p != NULL; p = p->next) { + fprintf(fp, "%s\n", p->name.utf8().data()); + for (int e=0; eentries[e].score, + p->entries[e].elapsed, + p->entries[e].board, + p->entries[e].name.utf8().data()); + } + } + fclose(fp); + +} + +// traverse the list of hi score tables and set the +// current table to the specified board. Create it if it does not +// exist. + +void HighScore::selectTable(const QString &board) { + + TableInstance *pos = tables; + + + while (pos != NULL) { + if (pos->name == board) + break; + pos = pos->next; + } + + + + if (pos == NULL) { + // not found, add new board to the front of the list + TableInstance *n = new TableInstance; + n->next = tables; + n->name = board; + + + + for (int p =0; p < numScores; p ++) { + n->entries[p].name = defNames[p]; + n->entries[p].score = defScores[p]; + n->entries[p].board = 928364243l+(p *3); + n->entries[p].elapsed = defTimes[p]; + } + tables = n; + currTable = n; + combo->insertItem(board); + setComboTo(board); + return; + } + currTable = pos; + setComboTo(board); + return; +} + + + +void HighScore::addRow(int num) { + QFont tmp; + + // game number + numbersWidgets[num] = new QLabel( this); + numbersWidgets[num]->setGeometry( 10, 75+(num*30), 30, 30 ); + numbersWidgets[num]->setFrameStyle( 50 ); + numbersWidgets[num]->setAlignment( AlignRight | AlignVCenter ); + numbersWidgets[num]->setNum(num+1); + + + // name + namesWidgets[num] = new QLabel( this); + namesWidgets[num]->setGeometry( 40, 75+(num*30), 150, 30 ); + namesWidgets[num]->setFrameStyle( 50 ); + namesWidgets[num]->setAlignment( 289 ); + + // board + boardWidgets[num] = new QLabel( this); + boardWidgets[num]->setGeometry( 190, 75+(num*30), 80, 30 ); + boardWidgets[num]->setFrameStyle( 50 ); + boardWidgets[num]->setAlignment( 289 ); + + // score + scoresWidgets[num] = new QLabel( this); + scoresWidgets[num]->setGeometry( 270, 75+(num*30), 70, 30 ); + scoresWidgets[num]->setFrameStyle( 50 ); + tmp = scoresWidgets[num]->font(); + tmp.setItalic(true); + scoresWidgets[num]->setFont(tmp); + + // elapsed time + elapsedWidgets[num] = new QLabel( this); + elapsedWidgets[num]->setGeometry( 270+70, 75+(num*30), 70, 30 ); + elapsedWidgets[num]->setFrameStyle( 50 ); + tmp = elapsedWidgets[num]->font(); + tmp.setItalic(true); + elapsedWidgets[num]->setFont(tmp); + + +} + +void HighScore::copyTableToScreen(const QString &name) { + char buff[256]; + QString base; + getBoardName(name, base); + selectTable(base); + for (int p=0; psetNum((int)currTable->entries[p].score); + namesWidgets[p]->setText(currTable->entries[p].name); + boardWidgets[p]->setNum((int)currTable->entries[p].board); + + int e = currTable->entries[p].elapsed; + int s = e % 60; + e = e-s; + int m = (e % (60*60)) / 60; + e = e - (e % (60*60)); + int h = (e % (60*60*60)) / (60*60); + sprintf(buff, "%2.2d:%2.2d:%2.2d", h, m , s); + elapsedWidgets[p]->setText(buff); + + } + repaint(false); +} + +int HighScore::exec(QString &layout) { + copyTableToScreen(layout); + return(QDialog::exec()); +} + +void HighScore::checkHighScore(int s, int e, long gameNum, QString &name) { + int pos; + + QString board; + getBoardName(name, board); + + // make this board name the current one! + // creates it if it does not exist + selectTable(board); + + + for (pos=0; pos currTable->entries[pos].score) { + break; + } + } + if (pos >= numScores) { + return; + } + for (int move= numScores-1; move >pos; move--) { + currTable->entries[move].score = currTable->entries[move-1].score; + currTable->entries[move].name = currTable->entries[move-1].name; + currTable->entries[move].board = currTable->entries[move-1].board; + currTable->entries[move].elapsed = currTable->entries[move-1].elapsed; + } + + currTable->entries[pos].score = s; + currTable->entries[pos].board = gameNum; + currTable->entries[pos].elapsed = e; + + lineEdit->setEnabled(true); + lineEdit->setGeometry( 40, 75+(pos*30), 150, 30 ); + lineEdit->setFocus(); + lineEdit->setText(""); + selectedLine = pos; + nameChanged(""); + + // no board change when entering data + combo->setEnabled(false); + exec(board); + combo->setEnabled(true); + + selectedLine = -1; + lineEdit->setGeometry( 40, 75+(20*30), 150, 30); + lineEdit->setEnabled(false); + + // sync the hiscore table to disk now + saveTables(); + +} + +void HighScore::nameChanged(const QString &s) { + + if (selectedLine == -1) + return; + + if (s.isEmpty()) + currTable->entries[selectedLine].name = + i18n("Anonymous"); + else + currTable->entries[selectedLine].name = s; +} + + +void HighScore::getBoardName(QString in, QString &out) { + + QFileInfo fi( in ); + out = fi.baseName(); +} + +void HighScore::setComboTo(const QString &to) { + for (int p=0; pcount(); p++) { + if (combo->text(p) == to) + combo->setCurrentItem(p); + } +} + + +void HighScore::selectionChanged(int ) { + copyTableToScreen(combo->currentText()); + +} + + +// reset the high score table. Caution the user +// before acting + + +void HighScore::reset() { + + int res=KMessageBox::warningContinueCancel(this, + i18n("Resetting the high scores will " + "remove all high score entries " + "both in memory and on disk. Do " + "you wish to proceed?"), + i18n("Reset High Scores"),i18n("Reset")); + if (res != KMessageBox::Continue) + return ; + + // delete the file + res = unlink( QFile::encodeName(highScoreFile())); + + // wipe ou the in memory list of tables + TableInstance *t, *d; + + if (tables != NULL) { + t = tables; + while (t != NULL) { + d = t; + t = t->next; + d->next=0; + delete d; + } + + } + + // set the list empty + tables = NULL; + currTable=NULL; + + // clear out the combobox + combo->clear(); + + // stick in a default + selectTable("default"); + + // make sure tha the on screen data does not + // point to deleted data + copyTableToScreen("default"); +} + +QString &HighScore::highScoreFile() { + return filename; + +} diff --git a/kmahjongg/HighScore.h b/kmahjongg/HighScore.h new file mode 100644 index 00000000..5db36819 --- /dev/null +++ b/kmahjongg/HighScore.h @@ -0,0 +1,77 @@ + +#ifndef HighScore_included +#define HighScore_included + +#include + + +class QLineEdit; +class QComboBox; +class QLabel; + +const int numScores = 10; + +typedef struct HiScoreEntry { + QString name; + long board; + long score; + long elapsed; + +}; + +typedef struct TableInstance { + QString name; + HiScoreEntry entries[numScores]; + TableInstance *next; +}; + + +class HighScore : public QDialog +{ + Q_OBJECT + +public: + + HighScore + ( + QWidget* parent = NULL, + const char* name = NULL + ); + + virtual ~HighScore(); + + int exec(QString &layout); + + + void checkHighScore(int score, int elapsed, long game, QString &board); +public slots: + void selectionChanged(int); + +protected slots: + void nameChanged(const QString &s); + void reset(); +private: + void addRow(int num); // generate one table row + void loadTables(); // initialise from saved + void saveTables(); // save to disc. + void getBoardName(QString in, QString &out); + void selectTable(const QString &name); + void setComboTo(const QString &to); + void copyTableToScreen(const QString &name); + QString &highScoreFile(); + + int selectedLine; + QLineEdit *lineEdit; + QLabel* numbersWidgets[numScores]; + QLabel* boardWidgets[numScores]; + QLabel* namesWidgets[numScores]; + QLabel* scoresWidgets[numScores]; + QLabel* elapsedWidgets[numScores]; + QComboBox* combo; + QString filename; + + TableInstance *tables; + TableInstance *currTable; +}; + +#endif // HighScore_included diff --git a/kmahjongg/KmTypes.h b/kmahjongg/KmTypes.h new file mode 100644 index 00000000..8f85978a --- /dev/null +++ b/kmahjongg/KmTypes.h @@ -0,0 +1,27 @@ +#ifndef _KM_TYPES_ +#define _KM_TYPES_ + +//---------------------------------------------------------- +// TYPEDEFS +//---------------------------------------------------------- +typedef unsigned char UCHAR; +typedef unsigned char BYTE; +typedef unsigned short USHORT; +typedef unsigned long ULONG; + + +typedef struct pos { + pos() : e(0), y(0), x(0), f(0) { } + USHORT e,y,x,f; +} POSITION; + +typedef struct dep { + int turn_dep[4]; // Turn dependencies + int place_dep[4]; // Placing dependencies + int lhs_dep[2]; // Left side dependencies, same level + int rhs_dep[2]; // Right side dependencies, same level + bool filled; // True if this tile has been placed. + bool free; // True if this tile can be removed? +} DEPENDENCY; + +#endif diff --git a/kmahjongg/Makefile.am b/kmahjongg/Makefile.am new file mode 100644 index 00000000..7eba27b1 --- /dev/null +++ b/kmahjongg/Makefile.am @@ -0,0 +1,25 @@ +SUBDIRS = pics + +INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes) +KDE_ICON = kmahjongg + +bin_PROGRAMS = kmahjongg +kmahjongg_SOURCES = main.cpp kmahjongg.cpp boardwidget.cpp \ + Tileset.cpp BoardLayout.cpp GameTimer.cpp \ + Background.cpp Preview.cpp prefs.kcfgc \ + Editor.cpp HighScore.cpp settings.ui +kmahjongg_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kmahjongg_LDADD = $(LIB_KDEGAMES) $(LIB_KFILE) +kmahjongg_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +METASOURCES = AUTO + +xdg_apps_DATA = kmahjongg.desktop +kde_kcfg_DATA = kmahjongg.kcfg + +rcdir = $(kde_datadir)/kmahjongg +rc_DATA = kmahjonggui.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kmahjongg.pot + diff --git a/kmahjongg/Preview.cpp b/kmahjongg/Preview.cpp new file mode 100644 index 00000000..edd8ef79 --- /dev/null +++ b/kmahjongg/Preview.cpp @@ -0,0 +1,513 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "prefs.h" +#include "Preview.h" + +static const char * themeMagicV1_0= "kmahjongg-theme-v1.0"; + +Preview::Preview(QWidget* parent) : KDialogBase(parent), m_tiles(true) +{ + KPushButton *loadButton; + QGroupBox *group; + QVBox *page; + + page = new QVBox(this); + + group = new QHGroupBox(page); + + m_combo = new QComboBox(false, group); + connect(m_combo, SIGNAL(activated(int)), SLOT(selectionChanged(int))); + + loadButton = new KPushButton(i18n("Load..."), group); + connect( loadButton, SIGNAL(clicked()), SLOT(load()) ); + + m_drawFrame = new FrameImage(page); + m_drawFrame->setFixedSize(310, 236); + + m_changed = false; + + setMainWidget(page); + setFixedSize(sizeHint()); +} + +Preview::~Preview() +{ +} + +void Preview::selectionChanged(int which) +{ + m_selectedFile = m_fileList[which]; + drawPreview(); + m_drawFrame->repaint(0,0,-1,-1,false); + markChanged(); +} + +bool Preview::isChanged() +{ + return m_changed; +} + +void Preview::markChanged() +{ + m_changed = true; +} + +void Preview::markUnchanged() +{ + m_changed = false; +} + +void Preview::initialise(const PreviewType type) +{ + QString extension; + QString tile = Prefs::tileSet(); + QString back = Prefs::background(); + QString layout = Prefs::layout(); + + // set up the concept of the current file. Initialised to the preferences + // value initially. Set the caption to indicate what we are doing + switch (type) + { + case background: + setCaption(i18n("Change Background Image")); + m_selectedFile = back; + m_fileSelector = i18n("*.bgnd|Background Image (*.bgnd)\n"); + m_fileSelector += KImageIO::pattern()+"\n"; + extension = "*.bgnd"; + break; + + case tileset: + setCaption(i18n("Change Tile Set")); + m_fileSelector = i18n("*.tileset|Tile Set File (*.tileset)\n"); + m_selectedFile = tile; + extension = "*.tileset"; + break; + + case board: + m_fileSelector = i18n("*.layout|Board Layout File (*.layout)\n"); + setCaption(i18n("Change Board Layout")); + m_selectedFile = layout; + extension = "*.layout"; + break; + + case theme: + m_fileSelector = i18n("*.theme|KMahjongg Theme File (*.theme)\n"); + setCaption(i18n("Choose Theme")); + m_selectedFile=""; + extension = "*.theme"; + + m_themeLayout=""; + m_themeBack=""; + m_themeTileset=""; + + default: + break; + } + + m_fileSelector += i18n("*|All Files"); + enableButtonApply(type != board); + + m_previewType = type; + // we start with no change indicated + markUnchanged(); + + m_fileList = kapp->dirs()->findAllResources("appdata", "pics/*"+extension, false, true); + + // get rid of files from the last invocation + m_combo->clear(); + + QStringList names; + QStringList::const_iterator it, itEnd; + it = m_fileList.begin(); + itEnd = m_fileList.end(); + for ( ; it != itEnd; ++it) + { + QFileInfo fi(*it); + names << fi.baseName(); + } + + m_combo->insertStringList(names); + m_combo->setEnabled(m_fileList.count()); + drawPreview(); +} + +void Preview::slotApply() { + if (isChanged()) { + applyChange(); + markUnchanged(); + } +} + +void Preview::slotOk() { + slotApply(); + accept(); +} + +void Preview::load() { + KURL url = KFileDialog::getOpenURL(QString::null, m_fileSelector, this, i18n("Open Board Layout" )); + if ( !url.isEmpty() ) { + m_selectedFile = url.path(); + drawPreview(); + m_drawFrame->repaint(0,0,-1,-1,false); + markChanged(); + } +} + +// Top level preview drawing method. Background, tileset and layout +// are initialised from the preferences. Depending on the type +// of preview dialog we pick up the selected file for one of these +// chaps. + +void Preview::drawPreview() +{ + QString tile = Prefs::tileSet(); + QString back = Prefs::background(); + QString layout = Prefs::layout(); + + switch (m_previewType) + { + case background: + back = m_selectedFile; + break; + + case tileset: + tile = m_selectedFile; + break; + + case board: + layout = m_selectedFile; + break; + + case theme: + // a theme is quite a bit of work. We load the + // specified bits in (layout, background and tileset + if (!m_selectedFile.isEmpty()) + { + QString backRaw, layoutRaw, tilesetRaw, magic; + + QFile in(m_selectedFile); + if (in.open(IO_ReadOnly)) + { + QTextStream stream(&in); + magic = stream.readLine(); + if (magic != themeMagicV1_0) + { + in.close(); + KMessageBox::sorry(this, i18n("That is not a valid theme file.")); + break; + } + tilesetRaw = stream.readLine(); + backRaw = stream.readLine(); + layoutRaw = stream.readLine(); + in.close(); + + tile = tilesetRaw; + tile.replace(":", "/kmahjongg/pics/"); + if (!QFile::exists(tile)) + { + tile = tilesetRaw; + tile = "pics/" + tile.right(tile.length() - tile.find(":") - 1 ); + tile = locate("appdata", tile); + } + + back = backRaw; + back.replace(":", "/kmahjongg/pics/"); + if (!QFile::exists(back)) + { + back = backRaw; + back = "pics/" + back.right(back.length() - back.find(":") - 1); + back = locate("appdata", back); + } + + layout = layoutRaw; + layout.replace(":", "/kmahjongg/pics/"); + if (!QFile::exists(layout)) + { + layout = layoutRaw; + layout = "pics/" + layout.right(layout.length() - layout.find(":") - 1); + layout = locate("appdata", layout); + } + + m_themeBack=back; + m_themeLayout=layout; + m_themeTileset=tile; + } + } + break; + } + + renderBackground(back); + renderTiles(tile, layout); +} + +void Preview::paintEvent( QPaintEvent* ){ + m_drawFrame->repaint(false); +} + +// the user selected ok, or apply. This method passes the changes +// across to the game widget and if necessary forces a board redraw +// (unnecessary on layout changes since it only effects the next game) +void Preview::applyChange() +{ + switch (m_previewType) + { + case background: + loadBackground(m_selectedFile, false); + break; + + case tileset: + loadTileset(m_selectedFile); + break; + + case board: + loadBoard(m_selectedFile); + break; + + case theme: + if (!m_themeLayout.isEmpty() && !m_themeBack.isEmpty() && !m_themeTileset.isEmpty()) + { + loadBackground(m_themeBack, false); + loadTileset(m_themeTileset); + loadBoard(m_themeLayout); + } + break; + } + + // don't redraw for a layout change + if (m_previewType == board || m_previewType == theme) layoutChange(); + else boardRedraw(true); + + // either way we mark the current value as unchanged + markUnchanged(); +} + +// Render the background to the pixmap. +void Preview::renderBackground(const QString &bg) { + QImage img; + QImage tmp; + QPixmap *p; + QPixmap *b; + p = m_drawFrame->getPreviewPixmap(); + m_back.load(bg, p->width(), p->height()); + b = m_back.getBackground(); + bitBlt( p, 0,0, + b,0,0, b->width(), b->height(), CopyROP ); +} + +// This method draws a mini-tiled board with no tiles missing. + +void Preview::renderTiles(const QString &file, const QString &layout) { + m_tiles.loadTileset(file, true); + m_boardLayout.loadBoardLayout(layout); + + QPixmap *dest = m_drawFrame->getPreviewPixmap(); + int xOffset = m_tiles.width()/2; + int yOffset = m_tiles.height()/2; + short tile = 0; + + // we iterate over the depth stacking order. Each successive level is + // drawn one indent up and to the right. The indent is the width + // of the 3d relief on the tile left (tile shadow width) + for (int z=0; z=0; x--) { + int sx = x*(m_tiles.qWidth() )+xOffset; + int sy = y*(m_tiles.qHeight() )+yOffset; + if (m_boardLayout.getBoardData(z, y, x) != '1') { + continue; + } + QPixmap *t = m_tiles.unselectedPixmaps(tile); + + // Only one compilcation. Since we render top to bottom , left + // to right situations arise where...: + // there exists a tile one q height above and to the left + // in this situation we would draw our top left border over it + // we simply split the tile draw so the top half is drawn + // minus border + + if ((x>1) && (y>0) && m_boardLayout.getBoardData(z,y-1,x-2)=='1'){ + bitBlt( dest, sx+2, sy, + t, 2,0, t->width(), t->height()/2, CopyROP ); + bitBlt( dest, sx, sy+t->height()/2, + t, 0,t->height()/2,t->width(),t->height()/2,CopyROP); + } else { + + bitBlt( dest, sx, sy, + t, 0,0, t->width(), t->height(), CopyROP ); + } + tile++; + if (tile == 35) + tile++; + tile = tile % 43; + } + } + xOffset +=m_tiles.shadowSize(); + yOffset -=m_tiles.shadowSize(); + } +} + +// this really does not belong here. It will be fixed in v1.1 onwards +void Preview::saveTheme() { + QString tile = Prefs::tileSet(); + QString back = Prefs::background(); + QString layout = Prefs::layout(); + + QString with = ":"; + // we want to replace any path in the default store + // with a + + QRegExp p(locate("data_dir", "/kmahjongg/pics/")); + + back.replace(p,with); + tile.replace(p,with); + layout.replace(p,with); + + + // Get the name of the file to save + KURL url = KFileDialog::getSaveURL( + NULL, + "*.theme", + parentWidget(), + i18n("Save Theme" )); + if ( url.isEmpty() ) + return; + + if( !url.isLocalFile() ) + { + KMessageBox::sorry( this, i18n( "Only saving to local files currently supported." ) ); + return; + } + + // Are we over writing an existin file, or was a directory selected? + QFileInfo f( url.path() ); + if( f.isDir() ) + return; + if (f.exists()) { + // if it already exists, querie the user for replacement + int res=KMessageBox::warningContinueCancel(this, + i18n("A file with that name " + "already exists. Do you " + "wish to overwrite it?"),QString::null,i18n("Overwrite")); + if (res != KMessageBox::Continue) + return ; + } + FILE *outFile = fopen( QFile::encodeName(url.path()), "w" ); + if (outFile == NULL) { + KMessageBox::sorry(this, + i18n("Could not write to file. Aborting.")); + return; + } + + fprintf(outFile,"%s\n%s\n%s\n%s\n", + themeMagicV1_0, + tile.utf8().data(), + back.utf8().data(), + layout.utf8().data()); + fclose(outFile); +} + +FrameImage::FrameImage (QWidget *parent, const char *name) + : QFrame(parent, name) +{ + rx = -1; + thePixmap = new QPixmap(); +} + +FrameImage::~FrameImage() +{ + delete thePixmap; +} + +void FrameImage::setGeometry(int x, int y, int w, int h) { + QFrame::setGeometry(x,y,w,h); + + thePixmap->resize(size()); + +} + +void FrameImage::paintEvent( QPaintEvent* pa ) +{ + QFrame::paintEvent(pa); + + QPainter p(this); + + + QPen line; + line.setStyle(DotLine); + line.setWidth(2); + line.setColor(yellow); + p.setPen(line); + p.setBackgroundMode(OpaqueMode); + p.setBackgroundColor(black); + + int x = pa->rect().left(); + int y = pa->rect().top(); + int h = pa->rect().height(); + int w = pa->rect().width(); + + p.drawPixmap(x+frameWidth(),y+frameWidth(),*thePixmap,x+frameWidth(),y+frameWidth(),w-(2*frameWidth()),h-(2*frameWidth())); + if (rx >=0) { + + p.drawRect(rx, ry, rw, rh); + p.drawRect(rx+rs, ry, rw-rs, rh-rs); + p.drawLine(rx, ry+rh, rx+rs, ry+rh-rs); + + int midX = rx+rs+((rw-rs)/2); + int midY = ry+((rh-rs)/2); + switch (rt) { + case 0: // delete mode cursor + p.drawLine(rx+rs, ry, rx+rw, ry+rh-rs); + p.drawLine(rx+rw, ry, rx+rs, ry+rh-rs); + break; + case 1: // insert cursor + p.drawLine(midX, ry, midX, ry+rh-rs); + p.drawLine(rx+rs, midY, rx+rw, midY); + break; + case 2: // move mode cursor + p.drawLine(midX, ry, rx+rw, midY); + p.drawLine(rx+rw, midY, midX, ry+rh-rs); + p.drawLine(midX, ry+rh-rs, rx+rs, midY); + p.drawLine(rx+rs, midY, midX, ry); + + break; + } + + } +} + +void FrameImage::setRect(int x,int y,int w,int h, int s, int t) +{ + rx = x; + ry = y; + rw = w; + rh = h; + rt = t; + rs = s; +} + +// Pass on the mouse presed event to our owner + +void FrameImage::mousePressEvent(QMouseEvent *m) { + mousePressed(m); +} + +void FrameImage::mouseMoveEvent(QMouseEvent *e) { + mouseMoved(e); +} + +#include "Preview.moc" diff --git a/kmahjongg/Preview.h b/kmahjongg/Preview.h new file mode 100644 index 00000000..4f58e2cd --- /dev/null +++ b/kmahjongg/Preview.h @@ -0,0 +1,104 @@ +#ifndef _PreviewLoadBase_H +#define _PreviewLoadBase_H + +#include + +#include + +#include "Tileset.h" +#include "BoardLayout.h" +#include "Background.h" + +class QComboBox; +class QPixmap; + +class FrameImage: public QFrame +{ + Q_OBJECT +public: + FrameImage(QWidget *parent=NULL, const char *name = NULL); + ~FrameImage(); + void setGeometry(int x, int y, int w, int h); + QPixmap *getPreviewPixmap() {return thePixmap;} + void setRect(int x, int y, int w, int h, int ss, int type); +signals: + void mousePressed(QMouseEvent *e); + void mouseMoved(QMouseEvent *e); +protected: + void mousePressEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void paintEvent( QPaintEvent* pa ); +private: + QPixmap *thePixmap; + int rx; + int ry; + int rw; + int rh; + int rs; + int rt; +}; + + + +class Preview: public KDialogBase +{ + Q_OBJECT + +public: + enum PreviewType {background, tileset, board, theme}; + + Preview(QWidget* parent); + ~Preview(); + + void initialise(const PreviewType type); + void saveTheme(); + +protected: + void markUnchanged(); + void markChanged(); + bool isChanged(); + QPixmap *getPreviewPixmap() {return m_drawFrame->getPreviewPixmap(); } + virtual void drawPreview(); + void applyChange() ; + void renderBackground(const QString &bg); + void renderTiles(const QString &file, const QString &layout); + void paintEvent( QPaintEvent* pa ); + +signals: + void boardRedraw(bool); + void loadTileset(const QString &); + void loadBackground(const QString &, bool); + void loadBoard(const QString &); + void layoutChange(); + +public slots: + void selectionChanged(int which); + +protected slots: + void slotApply(); + void slotOk(); + +private slots: + void load(); + +protected: + FrameImage *m_drawFrame; + QComboBox *m_combo; + + QString m_selectedFile; + Tileset m_tiles; + BoardLayout m_boardLayout; + Background m_back; + +private: + QString m_fileSelector; + bool m_changed; + QStringList m_fileList; + PreviewType m_previewType; + + QString m_themeBack; + QString m_themeLayout; + QString m_themeTileset; +}; + +#endif diff --git a/kmahjongg/Tileset.cpp b/kmahjongg/Tileset.cpp new file mode 100644 index 00000000..c29f9701 --- /dev/null +++ b/kmahjongg/Tileset.cpp @@ -0,0 +1,281 @@ + +#include +#include "Tileset.h" +#include + + +#define mini_width 20 +#define mini_height 28 +static unsigned char mini_bits[] = { + 0xfc, 0xff, 0x0f, 0xfe, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, + 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, + 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, + 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, + 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, + 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, + 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x07, 0xff, 0xff, 0x03, + }; + +#define mask_width 40 +#define mask_height 56 +static unsigned char mask_bits[] = { + 0xf0, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7f, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, + 0xff, 0xff, 0xff, 0x0f, }; + + +// --------------------------------------------------------- + +Tileset::Tileset(bool scale): + maskBits(mask_width, mask_height, mask_bits, true), + maskBitsMini(mini_width, mini_height, mini_bits, true) +{ + isScaled = scale; + divisor = (isScaled) ? 2 : 1; + + // set up tile metrics (fixed for now) + ss = 4; // left/bottom shadow width + bs = 1; // tile boarder width + w = 40; // tile width (inc boarder & shadow) + h = 56; // tile height (inc boarder and shadow) + s = w*h; // RGBA's required per tile + + // Allocate memory for the 9*5 tile arrays + tiles = new QRgb [9*5*s]; + selectedTiles = new QRgb [9*5*s]; + + // allocate memory for single tile storage + selectedFace = new QRgb [s]; + unselectedFace = new QRgb [s]; + + // quarter widths are used as an offset when + // overlaying tiles in 3 dimensions. + qw = ((w-ss)/2) ; + qh = ((h-ss)/2) ; + + filename = ""; +} + + +// --------------------------------------------------------- + +Tileset::~Tileset() { + + // deallocate all memory + delete [] tiles; + delete [] selectedTiles; + delete [] selectedFace; + delete [] unselectedFace; +} + +// --------------------------------------------------------- +// copy a tile from a qimage into a linear array of bytes. This +// method returns the address of the byte after the copied image +// and can be used to fill a larger array of tiles. + +QRgb *Tileset::copyTileImage(short tileX, short tileY, QRgb *to, QImage &from) { + QRgb *dest = to; + QRgb *src; + + src = (QRgb *) from.scanLine(tileY * h) + +(tileX * w); + for (short pos=0; pos < h; pos++) { + memcpy(dest, src, w*sizeof(QRgb)); + dest+=w; + src += from.width(); + } + return(dest); +} + +// ---------------------------------------------------------- +// Create a tile. Take a specified tile background a tile face +// (specified as an x,y coord) and a destination buffer (location +// in which is calculated from the x,y) and copy in the +// tile background, overlaying the foreground with transparency +// (the foregrounds top/left pixel is taken as the transparent +// color). + + +QRgb *Tileset::createTile(short x, short y, + QRgb *det, QImage &allTiles , QRgb *face) { + QRgb *image ; + QRgb *to = det; + + // Alloc the space + image = new QRgb[s]; + + // copy in the background + memcpy(to, face, s*sizeof(QRgb)); + + // get the tile gylph + copyTileImage(x, y , image, allTiles); + + // copy the image over the background using the + // top left colour as the transparency. We step over + // the shadow and the boarder + + QRgb* src = image+ // image + ss+ // past the left shadow + bs+ // then the tile border + (bs * w); // then step past the top border + + + to += (((ss+bs))+(bs*w)); + + + QRgb trans = *src; + + // start after the top border rows and go through all rows + for( short YP=0; YP < h-ss - (2*bs); YP++) { + // start after the shadow and border and iterate over x + for (short xpos=0; xpos < w-ss -(2*bs) ; xpos++) { + // skip transparent pixels + if (*src != trans) + *to = *src; + src++; + to++; + } + // step over the border to get to the next row + src += ss + (2 * bs); + to += ss + (2 * bs); + } + + // Free allocated space + delete [] image; + + // calculate the address of the next tile + return(det+s); +} + +// -------------------------------------------------------- +// create a pixmap for a tile. Optionally create a scalled +// version, which can be used for mini tile requirements. +// this gives us a small tile for previews and showing +// removed tiles. +void Tileset::createPixmap(QRgb *src, QPixmap &dest, bool scale, bool shadow) +{ + + QImage buff; + QRgb *line; + + buff.create(w, h, 32); + + for (int y=0; y + +class Tileset { + public: + Tileset(bool scaled=false); + ~Tileset(); + + bool loadTileset(const QString &filesetPath, const bool isPreview = false); + QRgb *createTile(short x, short y, QRgb *dst, QImage &src , QRgb *face); + QRgb *copyTileImage(short tileX, short tileY, QRgb *to, QImage &from); + + void setScaled(bool sc) {isScaled = sc; divisor = (sc) ? 2 : 1;} + + + QRgb *tile(short tnum); + QRgb *selectedTile(short tnum); + short width() {return w/divisor;} + short height() {return h/divisor;} + short shadowSize() {return ss/divisor;} + short size() {return s;} + short qWidth() {return qw/divisor;} + short qHeight() {return qh/divisor;} + + + QPixmap *selectedPixmaps(int num) { + if (!isScaled) + return &(selectedPix[num]); + else + return &(selectedMiniPix[num]); + } + + QPixmap *unselectedPixmaps(int num) { + if (!isScaled) + return &(unselectedPix[num]); + else + return &(unselectedMiniPix[num]); + } + + QPixmap *selectedShadowPixmaps(int num) { + if (!isScaled) + return &(selectedShadowPix[num]); + else + return &(selectedShadowMiniPix[num]); + } + + QPixmap *unselectedShadowPixmaps(int num) { + if (!isScaled) + return &(unselectedShadowPix[num]); + else + return &(unselectedShadowMiniPix[num]); + } + + protected: + + enum { maxTiles=45 }; + void createPixmap(QRgb *src, QPixmap &dest, bool scale, bool shadow); + + + private: + QBitmap maskBits; // xbm mask for the tile + QBitmap maskBitsMini; // xbm mask for the tile + QRgb* tiles; // Buffer containing all tiles (unselected glyphs) + QRgb* selectedTiles; // Buffer containing all tiles (selected glyphs) + + + // in version 0.5 we have moved ftom using images and calculating + // masks etc, to using pixmaps and letting the blt do the hard work, + // in hardware. + QPixmap selectedPix[maxTiles]; // selected tiles + QPixmap unselectedPix[maxTiles]; // unselected tiles + QPixmap selectedMiniPix[maxTiles]; // selected tiles + QPixmap unselectedMiniPix[maxTiles]; // unselected tiles + + QPixmap selectedShadowPix[maxTiles]; // selected tiles as above in shadow + QPixmap unselectedShadowPix[maxTiles]; // unselected tiles + QPixmap selectedShadowMiniPix[maxTiles]; // selected tiles + QPixmap unselectedShadowMiniPix[maxTiles]; // unselected tiles + + + + + QRgb* selectedFace; // The tile background face for a selected tile + QRgb* unselectedFace;// The tile background face for an unselected tile + + QRgb tr; // transparenct color for tile bg + + short ss; // left/bottom shadow width + short bs; // width of the border around a tile + short w; // tile width ( +border +shadow) + short h; // tile height ( +border +shadow) + short qw; // quarter tile width used in 3d rendering + short qh; // quarter tile height used in 3d rendering + short s; // buffer size for tile (width*height) + + QString filename; // cache the last file loaded to save reloading it + bool isScaled; + int divisor; +}; + + +#endif + + diff --git a/kmahjongg/boardwidget.cpp b/kmahjongg/boardwidget.cpp new file mode 100644 index 00000000..9c3355ea --- /dev/null +++ b/kmahjongg/boardwidget.cpp @@ -0,0 +1,2023 @@ +#include "boardwidget.h" +#include "prefs.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Constructor. + * Loads tileset and background bitmaps. + */ +BoardWidget::BoardWidget( QWidget* parent, const char *name ) + : QWidget( parent, name ), theTiles(false) +{ + setBackgroundColor( QColor( 0,0,0 ) ); + + timer = new QTimer(this); + connect( timer, SIGNAL(timeout()), + this, SLOT(helpMoveTimeout()) ); + + TimerState = Stop; + gamePaused = false; + iTimerStep = 0; + matchCount = 0; + showMatch = false; + showHelp = false; + MouseClickPos1.e = BoardLayout::depth; // mark tile position as invalid + MouseClickPos2.e = BoardLayout::depth; + memset( &Game.Mask, 0, sizeof( Game.Mask ) ); + Game.MaxTileNum = 0; + gameGenerationNum = 0; + + // initially we force a redraw + updateBackBuffer=true; + + // Load tileset. First try to load the last use tileset + QString tFile; + getFileOrDefault(Prefs::tileSet(), "tileset", tFile); + + if (!loadTileset(tFile)){ + KMessageBox::error(this, + i18n("An error occurred when loading the tileset file %1\n" + "KMahjongg will now terminate.").arg(tFile)); + kapp->quit(); + } + + getFileOrDefault(Prefs::background(), "bgnd", tFile); + + // Load background + if( ! loadBackground(tFile, false ) ) + { + KMessageBox::error(this, + i18n("An error occurred when loading the background image\n%1").arg(tFile)+ + i18n("KMahjongg will now terminate.")); + kapp->quit(); + } + + getFileOrDefault(Prefs::layout(), "layout", tFile); + if( ! loadBoardLayout(tFile) ) + { + KMessageBox::error(this, + i18n("An error occurred when loading the board layout %1\n" + "KMahjongg will now terminate.").arg(tFile)); + kapp->quit(); + } + setDisplayedWidth(); + loadSettings(); +} + +BoardWidget::~BoardWidget(){ + saveSettings(); +} + +void BoardWidget::loadSettings(){ + theBackground.tile = Prefs::tiledBackground(); + + setDisplayedWidth(); + tileSizeChanged(); + updateScaleMode(); + drawBoard(true); +} + +void BoardWidget::saveSettings(){ + // Preview can't handle this. TODO + //KConfig *config=kapp->config(); + //config->setGroup("General"); + + //config->writePathEntry("Tileset_file", tileFile); + //config->writePathEntry("Background_file", backgroundFile); + //config->writePathEntry("Layout_file", layout); +} + +void BoardWidget::getFileOrDefault(QString filename, QString type, QString &res) +{ + QString picsPos = "pics/"; + picsPos += "default."; + picsPos += type; + + if (QFile::exists(filename)) { + res = filename; + } + else { + res = locate("appdata", picsPos); + } + + if (res.isEmpty()) { + KMessageBox::error(this, i18n("KMahjongg could not locate the file: %1\n" + "or the default file of type: %2\n" + "KMahjongg will now terminate").arg(filename).arg(type) ); + kapp->quit(); + } +} + +void BoardWidget::setDisplayedWidth() { + if (Prefs::showRemoved()) + setFixedSize( requiredWidth() , requiredHeight()); + else + setFixedSize( requiredWidth() - ((theTiles.width())*4) + , requiredHeight()); +} + +// for a given cell x y calc how that cell is shadowed +// returnd left = width of left hand side shadow +// t = height of top shadow +// c = width and height of corner shadow + +void BoardWidget::calcShadow(int e, int y, int x, int &l, int &t, int &c) { + + l = t = c = 0; + if ((Game.shadowHeight(e,y,x) != 0) || + (Game.shadowHeight(e,y-1,x) != 0) || + (Game.shadowHeight(e,y,x-1) != 0)) { + return; + } + int a,b; + + a=Game.shadowHeight(e,y,x-2); + b=Game.shadowHeight(e,y-1,x-2); + if (a != 0 || b != 0) + l = (a>b) ? a : b; + a=Game.shadowHeight(e,y-2,x); + b=Game.shadowHeight(e,y-2,x-1); + if (a != 0 || b != 0) + t = (a>b) ? a : b; + + c = Game.shadowHeight(e, y-2, x-2); +} + +// draw a triangular shadow from the top right to the bottom left. +// one such shadow is a right hand edge of a shadow line. +// if a second shadow botton left to top right is rendered over it +// then the shadow becomes a box (ie in the middle of the run) + +void BoardWidget::shadowTopLeft(int depth, int sx, int sy, int rx, int ry, QPixmap *src, bool flag) { + if (depth) { + int shadowPixels= (depth+1) * theTiles.shadowSize(); + int xOffset=theTiles.qWidth()-shadowPixels; + for (int p=0; p 0)) + bitBlt( &backBuffer, + sx, sy, + src, + rx, ry, + theTiles.qWidth() - shadowPixels, + shadowPixels, CopyROP ); + } +} + +// Second triangular shadow generator see above +void BoardWidget::shadowBotRight(int depth, int sx, int sy, int rx, int ry, QPixmap *src, bool flag) { + if (depth) { + int shadowPixels= (depth+1) * theTiles.shadowSize(); + int xOffset=theTiles.qWidth(); + for (int p=0; p0)) + bitBlt( &backBuffer, + sx+xOffset-shadowPixels, + sy+shadowPixels, + src, + rx+xOffset-shadowPixels, + ry+shadowPixels, + shadowPixels, + theTiles.qHeight()-shadowPixels, CopyROP ); + + } +} + + + + +void BoardWidget::shadowArea(int z, int y, int x, int sx, int sy,int rx, int ry, QPixmap *src) +{ + // quick check to see if we are obscured + if (z < BoardLayout::depth-1) { + if ((x >= 0) && (yrect().left(); + int xheight = pa->rect().height(); + int xwidth = pa->rect().width(); + + back = theBackground.getBackground(); + + if (gamePaused) { + // If the game is paused, then blank out the board. + // We tolerate no cheats around here folks.. + bitBlt( this, xx, pa->rect().top(), + back, xx, pa->rect().top(), xwidth, xheight, CopyROP ); + return; + } + + // if the repaint is because of a window redraw after a move + // or a menu roll up, then just blit in the last rendered image + if (!updateBackBuffer) { + bitBlt(this, xx,pa->rect().top(), + &backBuffer, xx, pa->rect().top(), xwidth, xheight, CopyROP); + return; + } + + // update the complete drawArea + + backBuffer.resize(back->width(), back->height()); + + // erase out with the background + bitBlt( &backBuffer, xx, pa->rect().top(), + back, xx,pa->rect().top(), back->width(), back->height(), CopyROP ); + + // initial offset on the screen of tile 0,0 + int xOffset = theTiles.width()/2; + int yOffset = theTiles.height()/2; + //short tile = 0; + + // shadow the background first + if (Prefs::showShadows()) { + for (int by=0; by =0; x--) { + int sx = x*(theTiles.qWidth() )+xOffset; + int sy = y*(theTiles.qHeight() )+yOffset; + + + + // skip if no tile to display + if (!Game.tilePresent(z,y,x)) + continue; + + QPixmap *t; + QPixmap *s; + if (Game.hilighted[z][y][x]) { + t= theTiles.selectedPixmaps( + Game.Board[z][y][x]-TILE_OFFSET); + s= theTiles.selectedShadowPixmaps( + Game.Board[z][y][x]-TILE_OFFSET); + } else { + t= theTiles.unselectedPixmaps( + Game.Board[z][y][x]-TILE_OFFSET); + s= theTiles.unselectedShadowPixmaps( + Game.Board[z][y][x]-TILE_OFFSET); + } + + // Only one compilcation. Since we render top to bottom , left + // to right situations arise where...: + // there exists a tile one q height above and to the left + // in this situation we would draw our top left border over it + // we simply split the tile draw so the top half is drawn + // minus border + + if (x > 1 && y > 0 && Game.tilePresent(z, y-1, x-2)){ + bitBlt( &backBuffer, + sx+theTiles.shadowSize(), sy, + t, theTiles.shadowSize() ,0, + t->width()-theTiles.shadowSize(), + t->height()/2, CopyROP ); + bitBlt( &backBuffer, sx, sy+t->height()/2, + t, 0,t->height()/2,t->width(),t->height()/2,CopyROP); + } else { + + bitBlt( &backBuffer, sx, sy, + t, 0,0, t->width(), t->height(), CopyROP ); + } + + + if (Prefs::showShadows() && z= 0 && pos < 3) { + last = removedDragon[pos]; + tile = TILE_DRAGON+pos; + } else { + //Wind? + if (pos >= 3 && pos < 7) { + last = removedWind[pos-3]; + tile = TILE_WIND+pos-3; + } else { + if (pos == 7) { + for (int t=0; t<4;t++) { + if (removedFlower[t]) { + last++; + tile=TILE_FLOWER+t; + } + } + } else { + for (int t=0; t<4;t++) { + if (removedSeason[t]) { + last++; + tile=TILE_SEASON+t; + } + } + } + } + } + + stackTiles(tile, last, xPos, yPos); + stackTiles(TILE_ROD+pos, removedRod[pos], + xPos - (1*(theTiles.width() - theTiles.shadowSize())) , yPos); + stackTiles(TILE_BAMBOO+pos, removedBamboo[pos], + xPos - (2*(theTiles.width() - theTiles.shadowSize())) , yPos); + stackTiles(TILE_CHARACTER+pos, removedCharacter[pos], + xPos - (3*(theTiles.width() - theTiles.shadowSize())) , yPos); + + + + yPos += theTiles.height()-theTiles.shadowSize(); + } + + updateBackBuffer=false; + bitBlt(this, xx,pa->rect().top(), &backBuffer, xx, pa->rect().top(), xwidth, xheight, CopyROP); + + +} + +void BoardWidget::stackTiles(unsigned char t, unsigned short h, unsigned short x,unsigned short y) +{ + + int ss = theTiles.shadowSize(); + QPainter p(&backBuffer); + QPen line; + p.setBackgroundMode(OpaqueMode); + p.setBackgroundColor(black); + + + + + line.setWidth(1); + line.setColor(white); + p.setPen(line); + int x2 = x+theTiles.width()-ss-1; + int y2 = y+theTiles.height()-1; + p.drawLine(x, y+ss, x2, y+ss); + p.drawLine(x, y+ss, x, y2); + p.drawLine(x2, y+ss, x2, y2); + p.drawLine(x+1, y2, x2, y2); + + // p.fillRect(x+1, y+ss+1, theTiles.width()-ss-2, theTiles.height()-ss-2, QBrush(lightGray)); + + for (unsigned short pos=0; pos < h; pos++) { + QPixmap *p = theTiles.unselectedPixmaps(t-TILE_OFFSET); + bitBlt( &backBuffer, x+(pos*ss), y-(pos*ss), + p, 0,0, p->width(), p->height(), CopyROP ); + } + +} + + +void BoardWidget::pause() { + gamePaused = !gamePaused; + drawBoard(true); +} + +void BoardWidget::gameLoaded() +{ + int i; + initialiseRemovedTiles(); + i = Game.TileNum; + // use the history of moves to put in the removed tiles area the correct tiles + while (i < Game.MaxTileNum ) + { + setRemovedTilePair(Game.MoveList[i], Game.MoveList[i+1]); + i +=2; + } + drawBoard(); +} + +// --------------------------------------------------------- +int BoardWidget::undoMove() +{ + cancelUserSelectedTiles(); + + if( Game.TileNum < Game.MaxTileNum ) + { + + clearRemovedTilePair(Game.MoveList[Game.TileNum], Game.MoveList[Game.TileNum+1]); + putTile( Game.MoveList[Game.TileNum], false ); + Game.TileNum++; + putTile( Game.MoveList[Game.TileNum] ); + Game.TileNum++; + drawTileNumber(); + setStatusText( i18n("Undo operation done successfully.") ); + return 1; + } + else { + setStatusText(i18n("What do you want to undo? You have done nothing!")); + return 0; + } +} + +// --------------------------------------------------------- +void BoardWidget::helpMove() +{ + cancelUserSelectedTiles(); + if (showHelp) helpMoveStop(); + + if( findMove( TimerPos1, TimerPos2 ) ) + { + cheatsUsed++; + iTimerStep = 1; + showHelp = true; + helpMoveTimeout(); + } + else + setStatusText( i18n("Sorry, you have lost the game.") ); +} +// --------------------------------------------------------- +void BoardWidget::helpMoveTimeout() +{ + if( iTimerStep & 1 ) + { + hilightTile( TimerPos1, true, false ); + hilightTile( TimerPos2, true ); + } + else + { + hilightTile( TimerPos1, false, false ); + hilightTile( TimerPos2, false ); + } + // restart timer + if( iTimerStep++ < 8 ) + timer->start( ANIMSPEED , true ); + else + showHelp = false; +} +// --------------------------------------------------------- + +void BoardWidget::helpMoveStop() +{ + timer->stop(); + iTimerStep = 8; + hilightTile( TimerPos1, false, false ); + hilightTile( TimerPos2, false ); + showHelp = false; +} + +// --------------------------------------------------------- +void BoardWidget::startDemoMode() +{ + calculateNewGame(); + + if( TimerState == Stop ) + { + TimerState = Demo; + iTimerStep = 0; + emit demoModeChanged( true ); + setStatusText( i18n("Demo mode. Click mousebutton to stop.") ); + demoMoveTimeout(); + } +} +// --------------------------------------------------------- +void BoardWidget::stopDemoMode() +{ + TimerState = Stop; // stop demo + calculateNewGame(); + setStatusText( i18n("Now it's you again.") ); + emit demoModeChanged( false ); + emit gameCalculated(); +} +// --------------------------------------------------------- +void BoardWidget::demoMoveTimeout() +{ + if( TimerState == Demo ) + { + switch( iTimerStep++ % 6 ) + { + // at firts, find new matching tiles + case 0: + if( ! findMove( TimerPos1, TimerPos2 ) ) + { + // if computer has won + if( Game.TileNum == 0 ) + { + animateMoveList(); + } + // else computer has lost + else + { + setStatusText( i18n("Your computer has lost the game.") ); + while( Game.TileNum < Game.MaxTileNum ) + { + putTile( Game.MoveList[Game.TileNum], false ); + Game.TileNum++; + putTile( Game.MoveList[Game.TileNum] ); + Game.TileNum++; + drawTileNumber(); + } + } + TimerState = Stop; + startDemoMode(); + return; + } + break; + // hilight matching tiles two times + case 1: + case 3: + hilightTile( TimerPos1, true, false ); + hilightTile( TimerPos2, true ); + break; + + case 2: + case 4: + hilightTile( TimerPos1, false, false ); + hilightTile( TimerPos2, false ); + break; + // remove matching tiles from game board + case 5: + setRemovedTilePair(TimerPos1, TimerPos2); + removeTile( TimerPos1, false ); + removeTile( TimerPos2 ); + drawTileNumber(); + break; + } + // restart timer + QTimer::singleShot( ANIMSPEED, this, SLOT( demoMoveTimeout() ) ); + } +} + +// --------------------------------------------------------- +void BoardWidget::setShowMatch( bool show ) +{ + if( showMatch ) + stopMatchAnimation(); + showMatch = show; +} +// --------------------------------------------------------- +void BoardWidget::matchAnimationTimeout() +{ + if (matchCount == 0) + return; + + if( iTimerStep++ & 1 ) + { + for(short Pos = 0; Pos < matchCount; Pos++) + { + + + hilightTile(PosTable[Pos], true); + } + } + else + { + for(short Pos = 0; Pos < matchCount; Pos++) + { + hilightTile(PosTable[Pos], false); + } + } + if( TimerState == Match ) + QTimer::singleShot( ANIMSPEED, this, SLOT( matchAnimationTimeout() ) ); +} +// --------------------------------------------------------- +void BoardWidget::stopMatchAnimation() +{ + for(short Pos = 0; Pos < matchCount; Pos++) + { + hilightTile(PosTable[Pos], false); + } + TimerState = Stop; + matchCount = 0; +} + +void BoardWidget::redoMove() +{ + + setRemovedTilePair(Game.MoveList[Game.TileNum-1],Game.MoveList[Game.TileNum-2]); + removeTile(Game.MoveList[Game.TileNum-1], false); + removeTile(Game.MoveList[Game.TileNum-1]); + drawTileNumber(); +} + +// --------------------------------------------------------- +void BoardWidget::animateMoveList() +{ + setStatusText( i18n("Congratulations. You have won!") ); + + if (Prefs::playAnimation()) + { + while( Game.TileNum < Game.MaxTileNum ) + { + // put back all tiles + putTile(Game.MoveList[Game.TileNum]); + Game.TileNum++; + putTile(Game.MoveList[Game.TileNum], false); + Game.TileNum++; + drawTileNumber(); + } + while( Game.TileNum > 0 ) + { + // remove all tiles + removeTile(Game.MoveList[Game.TileNum-1], false); + removeTile(Game.MoveList[Game.TileNum-1]); + drawTileNumber(); + } + } + + calculateNewGame(); +} + +// --------------------------------------------------------- +void BoardWidget::calculateNewGame( int gNumber) +{ + cancelUserSelectedTiles(); + stopMatchAnimation(); + initialiseRemovedTiles(); + setStatusText( i18n("Calculating new game...") ); + + + if( !loadBoard()) + { + setStatusText( i18n("Error converting board information!") ); + return; + } + + if (gNumber == -1) { + gameGenerationNum = kapp->random(); + } else { + gameGenerationNum = gNumber; + } + + random.setSeed(gameGenerationNum); + + // Translate Game.Map to an array of POSITION data. We only need to + // do this once for each new game. + memset(tilePositions, 0, sizeof(tilePositions)); + generateTilePositions(); + + // Now use the tile position data to generate tile dependency data. + // We only need to do this once for each new game. + generatePositionDepends(); + + // Now try to position tiles on the board, 64 tries max. + for( short nr=0; nr<64; nr++ ) + { + if( generateStartPosition2() ) + { + drawBoard(); + setStatusText( i18n("Ready. Now it is your turn.") ); + cheatsUsed=0; + return; + } + } + + drawBoard(); + setStatusText( i18n("Error generating new game!") ); +} + +// --------------------------------------------------------- +// Generate the position data for the layout from contents of Game.Map. +void BoardWidget::generateTilePositions() { + + numTiles = 0; + + for (int z=0; z< BoardLayout::depth; z++) { + for (int y=0; y (numTiles*numTiles)) { + return false; // bail + } + } while (tilePositions[position].e != 0); + + // If there are no other free positions on the same apparent + // horizontal line, we can mark that position as free. + if (onlyFreeInLine(position)) { + positionDepends[position].free = true; + } + } + + // Check to make sure we really got them all. Very important for + // this algorithm. + for (int i = 0; i < numTiles; i++) { + if (tilePositions[i].e == 0 && onlyFreeInLine(i)) { + positionDepends[i].free = true; + } + } + + // Get ready to place the tiles + int lastPosition = -1; + int position = -1; + int position2 = -1; + + // For each position, + for (int i = 0; i < numTiles; i++) { + + // If this is the first tile in a 144 tile set, + if ((i % 144) == 0) { + + // Initialise the faces to allocate. For the classic + // dragon board there are 144 tiles. So we allocate and + // randomise the assignment of 144 tiles. If there are > 144 + // tiles we will reallocate and re-randomise as we run out. + // One advantage of this method is that the pairs to assign are + // non-linear. In kmahjongg 0.4, If there were > 144 the same + // allocation series was followed. So 154 = 144 + 10 rods. + // 184 = 144 + 40 rods (20 pairs) which overwhemed the board + // with rods and made deadlock games more likely. + randomiseFaces(); + } + + // If this is the first half of a pair, there is no previous + // position for the pair. + if ((i & 1) == 0) { + lastPosition = -1; + } + + // Select a position for the tile, relative to the position of + // the last tile placed. + if ((position = selectPosition(lastPosition)) < 0) { + return false; // bail + } + if (i < numTiles-1) { + if ((position2 = selectPosition(lastPosition)) < 0) { + return false; // bail + } + if (tilePositions[position2].e > tilePositions[position].e) { + position = position2; // higher is better + } + } + + // Place the tile. + placeTile(position, tilePair[i % 144]); + + // Remember the position + lastPosition = position; + } + + // The game is solvable. + return true; +} + +// --------------------------------------------------------- +// Determines whether it is ok to mark this position as "free" because +// there are no other positions marked "free" in its apparent horizontal +// line. +bool BoardWidget::onlyFreeInLine(int position) { + + int i, i0, w; + int lin, rin, out; + static int nextLeft[BoardLayout::maxTiles]; + static int nextRight[BoardLayout::maxTiles]; + + /* Check left, starting at position */ + lin = 0; + out = 0; + nextLeft[lin++] = position; + do { + w = nextLeft[out++]; + if (positionDepends[w].free || positionDepends[w].filled) { + return false; + } + if ((i = positionDepends[w].lhs_dep[0]) != -1) { + nextLeft[lin++] = i; + } + i0 = i; + if ((i = positionDepends[w].lhs_dep[1]) != -1 && i0 != i) { + nextLeft[lin++] = i; + } + } + while (lin > out) ; + + /* Check right, starting at position */ + rin = 0; + out = 0; + nextRight[rin++] = position; + do { + w = nextRight[out++]; + if (positionDepends[w].free || positionDepends[w].filled) { + return false; + } + if ((i = positionDepends[w].rhs_dep[0]) != -1) { + nextRight[rin++] = i; + } + i0 = i; + if ((i = positionDepends[w].rhs_dep[1]) != -1 && i0 != i) { + nextRight[rin++] = i; + } + } + while (rin > out) ; + + // Here, the position can be marked "free" + return true; +} + +// --------------------------------------------------------- +int BoardWidget::selectPosition(int lastPosition) { + + int position, cnt = 0; + bool goodPosition = false; + + // while a good position has not been found, + while (!goodPosition) { + + // Select a random, but free, position. + do { + position = random.getLong(numTiles); + if (cnt++ > (numTiles*numTiles)) { + return -1; // bail + } + } while (!positionDepends[position].free); + + // Found one. + goodPosition = true; + + // If there is a previous position to take into account, + if (lastPosition != -1) { + + // Check the new position against the last one. + for (int i = 0; i < 4; i++) { + if (positionDepends[position].place_dep[i] == lastPosition) { + goodPosition = false; // not such a good position + } + } + for (int i = 0; i < 2; i++) { + if ((positionDepends[position].lhs_dep[i] == lastPosition) || + (positionDepends[position].rhs_dep[i] == lastPosition)) { + goodPosition = false; // not such a good position + } + } + } + } + + return position; +} + +// --------------------------------------------------------- +void BoardWidget::placeTile(int position, int tile) { + + // Install the tile in the specified position + tilePositions[position].f = tile; + Game.putTile(tilePositions[position]); + + // Update position dependency data + positionDepends[position].filled = true; + positionDepends[position].free = false; + + // Now examine the tiles near this to see if this makes them "free". + int depend; + for (int i = 0; i < 4; i++) { + if ((depend = positionDepends[position].turn_dep[i]) != -1) { + updateDepend(depend); + } + } + for (int i = 0; i < 2; i++) { + if ((depend = positionDepends[position].lhs_dep[i]) != -1) { + updateDepend(depend); + } + if ((depend = positionDepends[position].rhs_dep[i]) != -1) { + updateDepend(depend); + } + } +} + +// --------------------------------------------------------- +// Updates the free indicator in the dependency data for a position +// based on whether the positions on which it depends are filled. +void BoardWidget::updateDepend(int position) { + + // If the position is valid and not filled + if (position >= 0 && !positionDepends[position].filled) { + + // Check placement depends. If they are not filled, the + // position cannot become free. + int depend; + for (int i = 0; i < 4; i++) { + if ((depend = positionDepends[position].place_dep[i]) != -1) { + if (!positionDepends[depend].filled) { + return ; + } + } + } + + // If position is first free on apparent horizontal, it is + // now free to be filled. + if (onlyFreeInLine(position)) { + positionDepends[position].free = true; + return; + } + + // Assume no LHS positions to fill + bool lfilled = false; + + // If positions to LHS + if ((positionDepends[position].lhs_dep[0] != -1) || + (positionDepends[position].lhs_dep[1] != -1)) { + + // Assume LHS positions filled + lfilled = true; + + for (int i = 0; i < 2; i++) { + if ((depend = positionDepends[position].lhs_dep[i]) != -1) { + if (!positionDepends[depend].filled) { + lfilled = false; + } + } + } + } + + // Assume no RHS positions to fill + bool rfilled = false; + + // If positions to RHS + if ((positionDepends[position].rhs_dep[0] != -1) || + (positionDepends[position].rhs_dep[1] != -1)) { + + // Assume LHS positions filled + rfilled = true; + + for (int i = 0; i < 2; i++) { + if ((depend = positionDepends[position].rhs_dep[i]) != -1) { + if (!positionDepends[depend].filled) { + rfilled = false; + } + } + } + } + + // If positions to left or right are filled, this position + // is now free to be filled. + positionDepends[position].free = (lfilled || rfilled); + } +} + +// --------------------------------------------------------- +bool BoardWidget::generateStartPosition2() { + + // For each tile, + for (int i = 0; i < numTiles; i++) { + + // Get its basic position data + int x = tilePositions[i].x; + int y = tilePositions[i].y; + int z = tilePositions[i].e; + + // Clear Game.Board at that position + Game.Board[z][y][x] = 0; + + // Clear tile placed/free indicator(s). + positionDepends[i].filled = false; + positionDepends[i].free = false; + + // Set tile face blank + tilePositions[i].f = 254; + } + + // If solvable games should be generated, + if (Prefs::solvableGames()) { + + if (generateSolvableGame()) { + Game.TileNum = Game.MaxTileNum; + return true; + } else { + return false; + } + } + + // Initialise the faces to allocate. For the classic + // dragon board there are 144 tiles. So we allocate and + // randomise the assignment of 144 tiles. If there are > 144 + // tiles we will reallocate and re-randomise as we run out. + // One advantage of this method is that the pairs to assign are + // non-linear. In kmahjongg 0.4, If there were > 144 the same + // allocation series was followed. So 154 = 144 + 10 rods. + // 184 = 144 + 40 rods (20 pairs) which overwhemed the board + // with rods and made deadlock games more likely. + + int remaining = numTiles; + randomiseFaces(); + + for (int tile=0; tile 2) { + p2 = p1 = random.getLong(remaining-2); + int bail = 0; + while (p1 == p2) { + p2 = random.getLong(remaining-2); + + if (bail >= 100) { + if (p1 != p2) { + break; + } + } + if ((tilePositions[p1].y == tilePositions[p2].y) && + (tilePositions[p1].e == tilePositions[p2].e)) { + // skip if on same y line + bail++; + p2=p1; + continue; + } + } + } else { + p1 = 0; + p2 = 1; + } + POSITION a, b; + a = tilePositions[p1]; + b = tilePositions[p2]; + tilePositions[p1] = tilePositions[remaining - 1]; + tilePositions[p2] = tilePositions[remaining - 2]; + remaining -= 2; + + getFaces(a, b); + Game.putTile(a); + Game.putTile(b); + } + + Game.TileNum = Game.MaxTileNum; + return 1; +} + +void BoardWidget::getFaces(POSITION &a, POSITION &b) { + a.f = tilePair[tilesUsed]; + b.f = tilePair[tilesUsed+1]; + tilesUsed += 2; + + if (tilesUsed >= 144) { + randomiseFaces(); + } +} + +void BoardWidget::randomiseFaces() { + int nr; + int numAlloced=0; + // stick in 144 tiles in pairsa. + + for( nr=0; nr<9*4; nr++) + tilePair[numAlloced++] = TILE_CHARACTER+(nr/4); // 4*9 Tiles + for( nr=0; nr<9*4; nr++) + tilePair[numAlloced++] = TILE_BAMBOO+(nr/4); // 4*9 Tiles + for( nr=0; nr<9*4; nr++) + tilePair[numAlloced++] = TILE_ROD+(nr/4); // 4*9 Tiles + for( nr=0; nr<4; nr++) + tilePair[numAlloced++] = TILE_FLOWER+nr; // 4 Tiles + for( nr=0; nr<4; nr++) + tilePair[numAlloced++] = TILE_SEASON+nr; // 4 Tiles + for( nr=0; nr<4*4; nr++) + tilePair[numAlloced++] = TILE_WIND+(nr/4); // 4*4 Tiles + for( nr=0; nr<3*4; nr++) + tilePair[numAlloced++] = TILE_DRAGON+(nr/4); // 3*4 Tiles + + + //randomise. Keep pairs together. Ie take two random + //odd numbers (n,x) and swap n, n+1 with x, x+1 + + int at=0; + int to=0; + for (int r=0; r<200; r++) { + + + to=at; + while (to==at) { + to = random.getLong(144); + + if ((to & 1) != 0) + to--; + + } + UCHAR tmp = tilePair[at]; + tilePair[at] = tilePair[to]; + tilePair[to] = tmp; + tmp = tilePair[at+1]; + tilePair[at+1] = tilePair[to+1]; + tilePair[to+1] = tmp; + + + at+=2; + if (at >= 144) + at =0; + } + + tilesAllocated = numAlloced; + tilesUsed = 0; +} + + +// --------------------------------------------------------- +bool isFlower( UCHAR Tile ) +{ + return( Tile >= TILE_FLOWER && Tile <=TILE_FLOWER+3 ); +} +bool isSeason( UCHAR Tile ) +{ + return( Tile >= TILE_SEASON && Tile <=TILE_SEASON+3 ); +} +bool isBamboo(UCHAR t) { + return( t >= TILE_BAMBOO && t = TILE_CHARACTER && t = TILE_ROD && t = TILE_DRAGON && t < TILE_DRAGON +3); +} +bool isWind(UCHAR t) { + return( t >= TILE_WIND && t < TILE_WIND +4); +} + + +bool BoardWidget::isMatchingTile( POSITION& Pos1, POSITION& Pos2 ) +{ + // don't compare 'equal' positions + if( memcmp( &Pos1, &Pos2, sizeof(POSITION) ) ) + { + UCHAR FA = Pos1.f; + UCHAR FB = Pos2.f; + + if( (FA == FB) + || ( isFlower( FA ) && isFlower( FB ) ) + || ( isSeason( FA ) && isSeason( FB ) ) ) + return( true ); + } + return( false ); +} + +// --------------------------------------------------------- +bool BoardWidget::findMove( POSITION& posA, POSITION& posB ) +{ + short Pos_Ende = Game.MaxTileNum; // Ende der PosTable + + for( short E=0; E=2 ) + { + random.setSeed(0); // WABA: Why is the seed reset? + short Pos = random.getLong(iPosCount) & -2; // Gerader Wert + posA = PosTable[Pos]; + posB = PosTable[Pos+1]; + + return( true ); + } + else + return( false ); +} + +int BoardWidget::moveCount( ) +{ + short Pos_Ende = Game.MaxTileNum; // end of PosTable + + for( short E=0; Ebutton() == LeftButton ) + { + if( TimerState == Demo ) + { + stopDemoMode(); + } + else if( showMatch ) + { + stopMatchAnimation(); + } + + if( showHelp ) // stop hilighting tiles + helpMoveStop(); + + if( MouseClickPos1.e == BoardLayout::depth ) // first tile + { + transformPointToPosition( event->pos(), MouseClickPos1 ); + + if( MouseClickPos1.e != BoardLayout::depth && showMatch ) + { + matchCount = findAllMatchingTiles( MouseClickPos1 ); + TimerState = Match; + iTimerStep = 1; + matchAnimationTimeout(); + cheatsUsed++; + } + } + else // second tile + { + transformPointToPosition( event->pos(), MouseClickPos2 ); + if( MouseClickPos2.e == BoardLayout::depth ) + { + cancelUserSelectedTiles(); + } + else + { + if( isMatchingTile( MouseClickPos1, MouseClickPos2 ) ) + { + // update the removed tiles (we do this before the remove below + // so that we only require 1 screen paint for both actions) + setRemovedTilePair(MouseClickPos1, MouseClickPos2); + + // now we remove the tiles from the board + removeTile(MouseClickPos1, false); + removeTile(MouseClickPos2); + + // removing a tile means redo is impossible without + // a further undo. + Game.allow_redo=false; + demoModeChanged(false); + drawTileNumber(); + + // if no tiles are left, the player has `won`, so celebrate + if( Game.TileNum == 0 ) + { + gameOver(Game.MaxTileNum,cheatsUsed); + } + // else if no more moves are possible, display the sour grapes dialog + else if( ! findMove( TimerPos1, TimerPos2 ) ) + { + KMessageBox::information(this, i18n("Game over: You have no moves left.")); + } + } + else + { + // redraw tiles in normal state + hilightTile( MouseClickPos1, false, false ); + hilightTile( MouseClickPos2, false ); + } + MouseClickPos1.e = BoardLayout::depth; // mark tile position as invalid + MouseClickPos2.e = BoardLayout::depth; + } + } + } +} + + +// ---------------------------------------------------------- +/** + Transform window point to board position. + + @param point Input: Point in window coordinates + @param MouseClickPos Output: Position in game board +*/ +void BoardWidget::transformPointToPosition( + const QPoint& point, + POSITION& MouseClickPos + ) +{ + short E,X,Y; + + // iterate over E coordinate from top to bottom + for( E=BoardLayout::depth-1; E>=0; E-- ) + { + // calculate mouse coordiantes --> position in game board + // the factor -theTiles.width()/2 must keep track with the + // offset for blitting in the print Event (FIX ME) + X = ((point.x()-theTiles.width()/2)- (E+1)*theTiles.shadowSize()) / theTiles.qWidth(); + Y = ((point.y()-theTiles.height()/2) + E*theTiles.shadowSize()) / theTiles.qHeight(); + + + // changed to allow x == 0 + // skip when position is illegal + if (X<0 || X>=BoardLayout::width || Y<0 || Y>=BoardLayout::height) + continue; + + // + switch( Game.Mask[E][Y][X] ) + { + case (UCHAR)'3': X--;Y--; + break; + + case (UCHAR)'2': X--; + break; + + case (UCHAR)'4': Y--; + break; + + case (UCHAR)'1': break; + + default : continue; + } + // if gameboard is empty, skip + if ( ! Game.Board[E][Y][X] ) continue; + // tile must be 'free' (nothing left, right or above it) + if( E < 4 ) + { + if( Game.Board[E+1][Y][X] || Game.Board[E+1][Y+1][X] || + (X 0) && (Game.Board[E][Y][X-1] || Game.Board[E][Y+1][X-1])) { + if ((X +#include +#include + + +#include "KmTypes.h" +#include "Tileset.h" +#include "Background.h" +#include "BoardLayout.h" + +typedef struct gamedata { + int allow_undo; + int allow_redo; + UCHAR Board[BoardLayout::depth][BoardLayout::height][BoardLayout::width]; + USHORT TileNum; + USHORT MaxTileNum; + UCHAR Mask[BoardLayout::depth][BoardLayout::height][BoardLayout::width]; + UCHAR hilighted[BoardLayout::depth][BoardLayout::height][BoardLayout::width]; + POSITION MoveList[BoardLayout::maxTiles]; + void putTile( short e, short y, short x, UCHAR f ) + { + + + Board[e][y][x] = Board[e][y+1][x] = + Board[e][y+1][x+1] = Board[e][y][x+1] = f; + } + void putTile( POSITION& pos ) + { + putTile( pos.e, pos.y, pos.x, pos.f ); + } + + + bool tilePresent(int z, int y, int x) { + return(Board[z][y][x]!=0 && Mask[z][y][x] == '1'); + } + + bool partTile(int z, int y, int x) { + return (Board[z][y][x] != 0); + } + + int shadowHeight(int z, int y, int x) { + + + if ((z>=BoardLayout::depth||y>=BoardLayout::height||x>=BoardLayout::width)) + return 0; + + + if ((y < 0) || (x < 0)) + return 0; + + int h=0; + for (int e=z; e + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "prefs.h" +#include "kmahjongg.h" +#include "settings.h" +#include "GameTimer.h" +#include "Editor.h" + +static const char *gameMagic = "kmahjongg-game-v1.0"; + +//---------------------------------------------------------- +// Defines +//---------------------------------------------------------- +#define ID_STATUS_TILENUMBER 1 +#define ID_STATUS_MESSAGE 2 +#define ID_STATUS_GAME 3 + +int is_paused = 0; + +/** + Constructor. +*/ +KMahjongg::KMahjongg( QWidget* parent, const char *name) + : KMainWindow(parent, name) +{ + boardEditor = 0; + + // init board widget + bw = new BoardWidget( this ); + setCentralWidget( bw ); + + previewLoad = new Preview(this); + + setupStatusBar(); + setupKAction(); + + gameTimer = new GameTimer(toolBar()); + toolBar()->insertWidget(ID_GAME_TIMER, gameTimer->width() , gameTimer); + toolBar()->alignItemRight( ID_GAME_TIMER, true ); + + theHighScores = new HighScore(this); + + + bDemoModeActive = false; + + connect( bw, SIGNAL( statusTextChanged(const QString&, long) ), + SLOT( showStatusText(const QString&, long) ) ); + + connect( bw, SIGNAL( tileNumberChanged(int,int,int) ), + SLOT( showTileNumber(int,int,int) ) ); + + connect( bw, SIGNAL( demoModeChanged(bool) ), + SLOT( demoModeChanged(bool) ) ); + + connect( bw, SIGNAL( gameOver(unsigned short , unsigned short)), this, + SLOT( gameOver(unsigned short , unsigned short))); + + + connect(bw, SIGNAL(gameCalculated()), + this, SLOT(timerReset())); + + // Make connections for the preview load dialog + connect( previewLoad, SIGNAL( boardRedraw(bool) ), + bw, SLOT( drawBoard(bool) ) ); + + connect( previewLoad, SIGNAL( layoutChange() ), + this, SLOT( newGame() ) ); + + + connect( previewLoad, SIGNAL( loadBackground(const QString&, bool) ), + bw, SLOT(loadBackground(const QString&, bool) ) ); + + connect( previewLoad, SIGNAL( loadTileset(const QString &) ), + bw, SLOT(loadTileset(const QString&) ) ); + connect( previewLoad, SIGNAL( loadBoard(const QString&) ), + SLOT(loadBoardLayout(const QString&) ) ); + + startNewGame( ); + +} + +// --------------------------------------------------------- +KMahjongg::~KMahjongg() +{ + delete previewLoad; + delete theHighScores; + delete bw; +} + +// --------------------------------------------------------- +void KMahjongg::setupKAction() +{ + // game + KStdGameAction::gameNew(this, SLOT(newGame()), actionCollection()); + KStdGameAction::load(this, SLOT(loadGame()), actionCollection()); + KStdGameAction::save(this, SLOT(saveGame()), actionCollection()); + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + KStdGameAction::restart(this, SLOT(restartGame()), actionCollection()); + new KAction(i18n("New Numbered Game..."), "newnum", 0, this, SLOT(startNewNumeric()), actionCollection(), "game_new_numeric"); + new KAction(i18n("Open Th&eme..."), 0, this, SLOT(openTheme()), actionCollection(), "game_open_theme"); + new KAction(i18n("Open &Tileset..."), 0, this, SLOT(openTileset()), actionCollection(), "game_open_tileset"); + new KAction(i18n("Open &Background..."), 0, this, SLOT(openBackground()), actionCollection(), "game_open_background"); + new KAction(i18n("Open La&yout..."), 0, this, SLOT(openLayout()), actionCollection(), "game_open_layout"); + new KAction(i18n("Sa&ve Theme..."), 0, this, SLOT(saveTheme()), actionCollection(), "game_save_theme"); + // originally "file" ends here + KStdGameAction::hint(bw, SLOT(helpMove()), actionCollection()); + new KAction(i18n("Shu&ffle"), "reload", 0, bw, SLOT(shuffle()), actionCollection(), "move_shuffle"); + demoAction = KStdGameAction::demo(this, SLOT(demoMode()), actionCollection()); + showMatchingTilesAction = new KToggleAction(i18n("Show &Matching Tiles"), 0, this, SLOT(showMatchingTiles()), actionCollection(), "options_show_matching_tiles"); + showMatchingTilesAction->setCheckedState(i18n("Hide &Matching Tiles")); + showMatchingTilesAction->setChecked(Prefs::showMatchingTiles()); + bw->setShowMatch( Prefs::showMatchingTiles() ); + KStdGameAction::highscores(this, SLOT(showHighscores()), actionCollection()); + pauseAction = KStdGameAction::pause(this, SLOT(pause()), actionCollection()); + + // TODO: store the background ; open on startup + // TODO: same about layout + // TODO: same about theme + + // move + undoAction = KStdGameAction::undo(this, SLOT(undo()), actionCollection()); + redoAction = KStdGameAction::redo(this, SLOT(redo()), actionCollection()); + + // edit + new KAction(i18n("&Board Editor"), 0, this, SLOT(slotBoardEditor()), actionCollection(), "edit_board_editor"); + + // settings + KStdAction::preferences(this, SLOT(showSettings()), actionCollection()); + + setupGUI(); +} + +// --------------------------------------------------------- +void KMahjongg::setupStatusBar() +{ + // The following isn't possible with the new KStatusBar anymore. + // The correct fix is probably to reverse the order of adding the + // widgets. :/ + // Just commenting it out for now (order is not as important + // as compilation), in case someone comes up with a better fix. + // pStatusBar->setInsertOrder( KStatusBar::RightToLeft ); + + tilesLeftLabel= new QLabel("Removed: 0000/0000", statusBar()); + tilesLeftLabel->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + statusBar()->addWidget(tilesLeftLabel, tilesLeftLabel->sizeHint().width(), ID_STATUS_GAME); + + + gameNumLabel = new QLabel("Game: 000000000000000000000", statusBar()); + gameNumLabel->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + statusBar()->addWidget(gameNumLabel, gameNumLabel->sizeHint().width(), ID_STATUS_TILENUMBER); + + + statusLabel= new QLabel("Kmahjongg", statusBar()); + statusLabel->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + statusBar()->addWidget(statusLabel, statusLabel->sizeHint().width(), ID_STATUS_MESSAGE); + + // pStatusBar->setAlignment( ID_STATUS_TILENUMBER, AlignCenter ); +} + +void KMahjongg::setDisplayedWidth() +{ + bw->setDisplayedWidth(); +/* setFixedSize( bw->size() + + QSize( 2, (!statusBar()->isHidden() ? statusBar()->height() : 0) + + 2 + menuBar()->height() ) ); + toolBar()->setFixedWidth(bw->width());*/ + toolBar()->alignItemRight( ID_GAME_TIMER, true ); + bw->drawBoard(); +} + + +// --------------------------------------------------------- +void KMahjongg::startNewNumeric() +{ + bool ok; + int s = KInputDialog::getInteger(i18n("New Game"),i18n("Enter game number:"),0,0,INT_MAX,1,&ok,this); + if (ok) startNewGame(s); +} + +void KMahjongg::undo() +{ + bw->Game.allow_redo += bw->undoMove(); + demoModeChanged(false); +} + +void KMahjongg::redo() +{ + if (bw->Game.allow_redo >0) { + bw->Game.allow_redo--; + bw->redoMove(); + demoModeChanged(false); + } +} + +/** + * Show Configure dialog. + */ +void KMahjongg::showSettings(){ + if(KConfigDialog::showDialog("settings")) + return; + + KConfigDialog *dialog = new KConfigDialog(this, "settings", Prefs::self(), KDialogBase::Swallow); + dialog->addPage(new Settings(0, "General"), i18n("General"), "package_settings"); + connect(dialog, SIGNAL(settingsChanged()), bw, SLOT(loadSettings())); + connect(dialog, SIGNAL(settingsChanged()), this, SLOT(setDisplayedWidth())); + dialog->show(); +} + +void KMahjongg::demoMode() +{ + if( bDemoModeActive ) { + bw->stopDemoMode(); + } else { + // we assume demo mode removes tiles so we can + // disbale redo here. + bw->Game.allow_redo=false; + bw->startDemoMode(); + } + +} + +void KMahjongg::pause() +{ + is_paused = !is_paused; + demoModeChanged(false); + gameTimer->pause(); + bw->pause(); +} + +void KMahjongg::showMatchingTiles() +{ + Prefs::setShowMatchingTiles(!Prefs::showMatchingTiles()); + bw->setShowMatch( Prefs::showMatchingTiles() ); + showMatchingTilesAction->setChecked(Prefs::showMatchingTiles()); + Prefs::writeConfig(); +} + +void KMahjongg::showHighscores() +{ + theHighScores->exec(bw->getLayoutName()); +} + +void KMahjongg::openTheme() +{ + previewLoad->initialise(Preview::theme); + previewLoad->exec(); +} + +void KMahjongg::saveTheme() +{ + previewLoad->initialise(Preview::theme); + previewLoad->saveTheme(); +} + +void KMahjongg::openLayout() +{ + previewLoad->initialise(Preview::board); + previewLoad->exec(); +} + +void KMahjongg::openBackground() +{ + previewLoad->initialise(Preview::background); + previewLoad->exec(); +} + +void KMahjongg::openTileset() +{ + previewLoad->initialise(Preview::tileset); + previewLoad->exec(); +} + +void KMahjongg::slotBoardEditor() +{ + if (!boardEditor) + boardEditor = new Editor(this); + boardEditor->exec(); +} + +//---------------------------------------------------------- +// signalled from the prieview dialog to generate a new game +// we don't make startNewGame a slot because it has a default +// param. + +void KMahjongg::newGame() +{ + startNewGame(); +} + + + +// --------------------------------------------------------- +void KMahjongg::startNewGame( int item ) +{ + if( ! bDemoModeActive ) { + bw->calculateNewGame(item); + + // initialise button states + bw->Game.allow_redo = bw->Game.allow_undo = 0; + + timerReset(); + + // update the initial enabled/disabled state for + // the menu and the tool bar. + demoModeChanged(false); + } +} + +// --------------------------------------------------------- +void KMahjongg::timerReset() { + + // initialise the scoring system + gameElapsedTime = 0; + + // start the game timer + gameTimer->start(); + +} + + +// --------------------------------------------------------- + +void KMahjongg::gameOver( + unsigned short numRemoved, + unsigned short cheats) +{ + int time; + int score; + + gameTimer->pause(); + long gameNum = bw->getGameNum(); + KMessageBox::information(this, i18n("You have won!")); + bw->animateMoveList(); + int elapsed = gameTimer->toInt(); + + time = score = 0; + + // get the time in milli secs + // subtract from 20 minutes to get bonus. if longer than 20 then ignore + time = (60*20) - gameTimer->toInt(); + if (time <0) + time =0; + // conv back to secs (max bonus = 60*20 = 1200 + + // points per removed tile bonus (for deragon max = 144*10 = 1440 + score += (numRemoved * 20); + // time bonus one point per second under one hour + score += time; + // points per cheat penalty (max penalty = 1440 for dragon) + score -= (cheats *20); + if (score < 0) + score = 0; + + theHighScores->checkHighScore(score, elapsed, gameNum, bw->getBoardName()); + + timerReset(); +} + +// --------------------------------------------------------- +void KMahjongg::showStatusText( const QString &msg, long board ) +{ + statusLabel->setText(msg); + QString str = i18n("Game number: %1").arg(board); + gameNumLabel->setText(str); + +} + +// --------------------------------------------------------- +void KMahjongg::showTileNumber( int iMaximum, int iCurrent, int iLeft ) +{ + // Hmm... seems iCurrent is the number of remaining tiles, not removed ... + //QString szBuffer = i18n("Removed: %1/%2").arg(iCurrent).arg(iMaximum); + QString szBuffer = i18n("Removed: %1/%2 Combinations left: %3").arg(iMaximum-iCurrent).arg(iMaximum).arg(iLeft); + tilesLeftLabel->setText(szBuffer); + + // Update here since undo allow is effected by demo mode + // removal. However we only change the enabled state of the + // items when not in demo mode + bw->Game.allow_undo = iMaximum != iCurrent; + + // update undo menu item, if demomode is inactive + if( ! bDemoModeActive && !is_paused) + { + +// pMenuBar->setItemEnabled( ID_EDIT_UNDO, bw->Game.allow_undo); +// toolBar->setItemEnabled( ID_EDIT_UNDO, bw->Game.allow_undo); + undoAction->setEnabled(bw->Game.allow_undo); + } +} + + +// --------------------------------------------------------- +void KMahjongg::demoModeChanged( bool bActive) +{ + bDemoModeActive = bActive; + + pauseAction->setChecked(is_paused); + demoAction->setChecked(bActive || is_paused); + + if (is_paused) + stateChanged("paused"); + else if (bActive) + stateChanged("active"); + else { + stateChanged("inactive"); + undoAction->setEnabled(bw->Game.allow_undo); + redoAction->setEnabled(bw->Game.allow_redo); + } +} + +void KMahjongg::loadBoardLayout(const QString &file) { + bw->loadBoardLayout(file); +} + +void KMahjongg::tileSizeChanged() { + bw->tileSizeChanged(); + setDisplayedWidth(); +} + + +void KMahjongg::loadGame() { + GAMEDATA in; + char buffer[1024]; + QString fname; + + // Get the name of the file to load + KURL url = KFileDialog::getOpenURL( NULL, "*.kmgame", this, i18n("Load Game" ) ); + + if ( url.isEmpty() ) + return; + + KIO::NetAccess::download( url, fname, this ); + + // open the file for reading + FILE *outFile = fopen( QFile::encodeName(fname), "r"); + if (outFile == NULL) { + KMessageBox::sorry(this, + i18n("Could not read from file. Aborting.")); + return; + } + + // verify the magic + fscanf(outFile, "%1023s\n", buffer); + if (strcmp(buffer, gameMagic) != 0) { + KMessageBox::sorry(this, + i18n("File format not recognized.")); + fclose(outFile); + return; + } + + //ed the elapsed time + fscanf(outFile, "%1023s\n", buffer); + gameTimer->fromString(buffer); + + // suck out all the game data + fread(&in, sizeof(GAMEDATA), 1, outFile); + memcpy(&bw->Game, &in, sizeof(GAMEDATA)); + + // close the file before exit + fclose(outFile); + + KIO::NetAccess::removeTempFile( fname ); + + // refresh the board + bw->gameLoaded(); +} + +void KMahjongg::restartGame() { + if( ! bDemoModeActive ) { + bw->calculateNewGame(bw->getGameNum()); + + // initialise button states + bw->Game.allow_redo = bw->Game.allow_undo = 0; + + timerReset(); + + // update the initial enabled/disabled state for + // the menu and the tool bar. + demoModeChanged(false); + if (is_paused) + { + pauseAction->setChecked(false); + is_paused = false; + bw->pause(); + } + } +} + +void KMahjongg::saveGame() { + + // Get the name of the file to save + KURL url = KFileDialog::getSaveURL( NULL, "*.kmgame", this, i18n("Save Game" ) ); + + if ( url.isEmpty() ) + return; + + if( !url.isLocalFile() ) + { + KMessageBox::sorry( this, i18n( "Only saving to local files currently supported." ) ); + return; + } + + FILE *outFile = fopen( QFile::encodeName(url.path()), "w"); + if (outFile == NULL) { + KMessageBox::sorry(this, + i18n("Could not write to file. Aborting.")); + return; + } + + // stick in the magic id string + fprintf(outFile, "%s\n", gameMagic); + + // Now stick in the elapsed time for the game + fprintf(outFile, "%s\n", gameTimer->toString().utf8().data()); + + + // chuck in all the game data + fwrite(&bw->Game, sizeof(GAMEDATA), 1, outFile); + + // close the file before exit + fclose(outFile); +} + + +#include "kmahjongg.moc" diff --git a/kmahjongg/kmahjongg.desktop b/kmahjongg/kmahjongg.desktop new file mode 100644 index 00000000..4ffb95bc --- /dev/null +++ b/kmahjongg/kmahjongg.desktop @@ -0,0 +1,72 @@ +[Desktop Entry] +Name=KMahjongg +Name[af]=Kmahjong +Name[ar]=لعبة KMahjongg +Name[be]=Маджонг +Name[bn]=কে-মাহজং +Name[eo]=Mahjongo +Name[hi]=के-महजोंग +Name[ne]=केडीई माहजोङ +Name[pa]=ਕੇ-ਮਹਿਜੋਂਗ +Name[pl]=Mahjongg +Name[sv]=Kmahjongg +Name[tg]=KМаҷонг +Name[th]=มาจง - K +Name[zh_TW]=KMahjongg 麻將 +Exec=kmahjongg %i %m -caption "%c" +Type=Application +GenericName=Mahjongg-like Tile Game +GenericName[be]=Ð“ÑƒÐ»ÑŒÐ½Ñ Ñž маджонг +GenericName[bg]=Игра Ñ Ð¿Ð»Ð¾Ñ‡ÐºÐ¸ +GenericName[bn]=মাহজং-জাতীয় টালির খেলা +GenericName[br]=Ur c'hoari teol a seurt gant Mahjongg +GenericName[bs]=Igra nalik na Mahjongg +GenericName[ca]=Joc de mosaics a l'estil Mahjongg +GenericName[cs]=Hra s dlaždicemi podobná Mahjongg +GenericName[cy]=Gêm Deiliau sy'n debyg i Mahjongg +GenericName[da]=Mahjongg-lignende flisespil +GenericName[de]=Mahjongg-ähnliches Spiel mit Steinen +GenericName[el]=Παιχνίδι παÏόμοιο με το Mahjongg +GenericName[eo]=Mahjongg-simila ludo +GenericName[es]=Juego de fichas similar al Mahjongg +GenericName[et]=Mahjonggi moodi klotsimäng +GenericName[eu]=Mahjongg-en antzeko fitxa-jokoa +GenericName[fa]=بازی کاشی شبیه Mahjongg +GenericName[fi]=Mahjonggin kaltainen peli +GenericName[fr]=Jeu de tuiles dans le style du Mahjongg +GenericName[ga]=Cluiche Tíleanna Mar Mahjongg +GenericName[he]=חיקוי Mahjongg, משחק ××‘× ×™× (קלפי×) +GenericName[hr]=Igra s ploÄicama poput Mahjongga +GenericName[hu]=Mahjongg +GenericName[is]=Leikur sem líkist Mahjongg +GenericName[it]=Gioco di tessere simile a Mahjongg +GenericName[ja]=上海マージャン牌ゲーム +GenericName[km]=ល្បែង​ក្បឿងដូច Mahjongg +GenericName[ko]=시센-쇼 마작과 ê°™ì€ íƒ€ì¼ ê²Œìž„ +GenericName[lt]=Mahjongg primenantis žaidimas +GenericName[lv]=Mahjongg lÄ«dzÄ«ga spÄ“le +GenericName[mk]=Игра Ñо плочки Ñлична на Mahjongg +GenericName[nb]=Mahjongg-aktig brikkespill +GenericName[nds]=Mahjongg-liek Speel +GenericName[ne]=माहजोङ जसà¥à¤¤à¥ˆ टायल खेल +GenericName[nl]=Mahjongg-achtig stenenspel +GenericName[nn]=Mahjongg-aktig brikkespel +GenericName[pl]=Gra typu Mahjonng +GenericName[pt]=Jogo de Padrões tipo Mahjongg +GenericName[pt_BR]=Jogo de Ladrilhos parecido com Mahjongg +GenericName[ru]=Маджонг +GenericName[se]=Mahjongg-lágan bihttáspeallu +GenericName[sk]=Hra typu Mahjongg +GenericName[sl]=Igra s ploÅ¡Äicami, podobna Mahjonggu +GenericName[sr]=Игра Ñа пољима налик на Mahjongg +GenericName[sr@Latn]=Igra sa poljima nalik na Mahjongg +GenericName[sv]=Mahjongg-liknande brickspel +GenericName[ta]=மாஹà¯à®œà¯‹à®™à¯-போனà¯à®± ஓட௠விளையாடà¯à®Ÿà¯ +GenericName[uk]=Гра з плитками подібна до Mahjongg +GenericName[zh_CN]=对对碰 +GenericName[zh_TW]=麻將牌éŠæˆ² +Terminal=false +Icon=kmahjongg +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;BoardGame; diff --git a/kmahjongg/kmahjongg.h b/kmahjongg/kmahjongg.h new file mode 100644 index 00000000..a4b3ac3f --- /dev/null +++ b/kmahjongg/kmahjongg.h @@ -0,0 +1,119 @@ +/* + + $Id$ + + kmahjongg, the classic mahjongg game for KDE project + + Requires the Qt widget libraries, available at no cost at + http://www.troll.no + + Copyright (C) 1997 Mathias Mueller + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#ifndef _KMAHJONGG_H +#define _KMAHJONGG_H + +#include + +#include "KmTypes.h" +#include "Tileset.h" +#include "Background.h" +#include "BoardLayout.h" +#include "Preview.h" +#include "HighScore.h" +#include "boardwidget.h" + +class GameTimer; +class Editor; + +class KToggleAction; +class QLabel; + +/** + ... + @author Mathias +*/ +class KMahjongg : public KMainWindow +{ + Q_OBJECT + + public: + KMahjongg( QWidget* parent = 0, const char *name = 0); + ~KMahjongg(); + + public slots: + void startNewGame( int num = -1 ); + void showStatusText ( const QString& , long); + void showTileNumber( int iMaximum, int iCurrent, int iLeft ); + void demoModeChanged( bool bActive ); + void gameOver( unsigned short removed, unsigned short cheats); + void loadBoardLayout(const QString&); + void setDisplayedWidth(); + void newGame(); + void timerReset(); + + void tileSizeChanged(); + + +private slots: + void showSettings(); + + void startNewNumeric(); + void saveGame(); + void loadGame(); + void restartGame(); + void undo(); + void redo(); + void pause(); + void demoMode(); + void showMatchingTiles(); + void showHighscores(); + void slotBoardEditor(); + void openTheme(); + void saveTheme(); + void openLayout(); + void openBackground(); + void openTileset(); + +protected: + void setupKAction(); + void setupStatusBar(); + +private: + // number of seconds since the start of the game + unsigned long gameElapsedTime; + BoardWidget* bw; + + QLabel *gameNumLabel; + QLabel *tilesLeftLabel; + QLabel *statusLabel; + + GameTimer *gameTimer; + HighScore *theHighScores; + Preview *previewLoad; + Editor* boardEditor; + + bool bDemoModeActive; + + KToggleAction *showMatchingTilesAction, *pauseAction, *demoAction; + KAction *undoAction, *redoAction; + +}; + +#endif + diff --git a/kmahjongg/kmahjongg.kcfg b/kmahjongg/kmahjongg.kcfg new file mode 100644 index 00000000..427b4474 --- /dev/null +++ b/kmahjongg/kmahjongg.kcfg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + false + + + + false + + + + false + + + + true + + + + true + + + + true + + + + false + + + diff --git a/kmahjongg/kmahjonggui.rc b/kmahjongg/kmahjonggui.rc new file mode 100644 index 00000000..e8c4bd5b --- /dev/null +++ b/kmahjongg/kmahjonggui.rc @@ -0,0 +1,102 @@ + + + + + &Game + + + + + + + + &Edit + + + &Move + + + &Settings + + + + +Main Toolbar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kmahjongg/main.cpp b/kmahjongg/main.cpp new file mode 100644 index 00000000..6564a842 --- /dev/null +++ b/kmahjongg/main.cpp @@ -0,0 +1,38 @@ +#include "kmahjongg.h" +#include "version.h" + +#include +#include +#include +#include + +static const char description[] = I18N_NOOP("Mahjongg for KDE"); + +int main( int argc, char** argv ) +{ + KAboutData aboutData( "kmahjongg", I18N_NOOP("KMahjongg"), + KMAHJONGG_VERSION, description, KAboutData::License_GPL, + "(c) 1997, Mathias Mueller"); + aboutData.addAuthor("Mathias Mueller", I18N_NOOP("Original Author"), "in5y158@public.uni-hamburg.de"); + aboutData.addAuthor("Albert Astals Cid", I18N_NOOP("Current maintainer"), "astals11@terra.es"); + aboutData.addAuthor("David Black", I18N_NOOP("Rewrite and Extension"), "david.black@lutris.com"); + aboutData.addAuthor("Michael Haertjens", I18N_NOOP("Solvable game generation\nbased on algorithm by Michael Meeks in GNOME mahjongg"), "mhaertjens@modusoperandi.com"); + aboutData.addAuthor("Osvaldo Stark", I18N_NOOP("Tile set contributor and web page maintainer"), "starko@dnet.it"); + aboutData.addCredit("Benjamin Meyer", I18N_NOOP("Code cleanup"), "ben+kmahjongg@meyerhome.net"); + + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + KImageIO::registerFormats(); + + if (a.isRestored()) + RESTORE(KMahjongg) + else { + KMahjongg *app = new KMahjongg; + a.setMainWidget(app); + app->show(); + } + return a.exec(); +} + diff --git a/kmahjongg/pics/Makefile.am b/kmahjongg/pics/Makefile.am new file mode 100644 index 00000000..1b879ee0 --- /dev/null +++ b/kmahjongg/pics/Makefile.am @@ -0,0 +1,19 @@ +theme_DATA = default.theme pirates.theme +themedir = $(kde_datadir)/kmahjongg/pics + +pics_DATA = kmahjongg.png kmahjongg_bgnd.png splash.png newnum.xpm +picsdir = $(kde_datadir)/kmahjongg/pics + +layout_DATA = default.layout cross.layout pirates.layout \ + pyramid.layout stax.layout test.layout test2.layout \ + tower.layout triangle.layout +layoutdir = $(kde_datadir)/kmahjongg/pics + +bgnd_DATA = default.bgnd haze.bgnd pirates.bgnd slate.bgnd \ + wood.bgnd +bgnddir = $(kde_datadir)/kmahjongg/pics + +tileset_DATA = default.tileset pirates.tileset runes.tileset traditional.tileset +tilesetdir = $(kde_datadir)/kmahjongg/pics + +EXTRA_DIST = $(pics_DATA) $(layout_DATA) $(bgnd_DATA) $(tileset_DATA) $(theme_DATA) diff --git a/kmahjongg/pics/cross.layout b/kmahjongg/pics/cross.layout new file mode 100644 index 00000000..39c58f1e --- /dev/null +++ b/kmahjongg/pics/cross.layout @@ -0,0 +1,81 @@ +kmahjongg-layout-v1.0 +12....12121212121212121212....12 +4312..43434343434343434343..1243 +124312........1212........124312 +43124312......4343......12431243 +..43124312....1212....12431243.. +....43124312..4343..12431243.... +......43124312121212431243...... +........4312434343431243........ +........1243121212124312........ +......12431243434343124312...... +....12431243..1212..43124312.... +..12431243....4343....43124312.. +12431243......1212......43124312 +431243........4343........431243 +1243..12121212121212121212..4312 +43....43434343434343434343....43 +12......1212121212121212......12 +4312....4343434343434343....1243 +124312........1212........124312 +43124312......4343......12431243 +..43124312....1212....12431243.. +....431243....4343....431243.... +......4312....1212....1243...... +........43....4343....43........ +........12....1212....12........ +......1243....4343....4312...... +....124312....1212....124312.... +..12431243....4343....43124312.. +12431243......1212......43124312 +431243........4343........431243 +1243....1212121212121212....4312 +43......4343434343434343......43 +12........121212121212........12 +4312......434343434343......1243 +124312........1212........124312 +43124312......4343......12431243 +..43124312....1212....12431243.. +....431243....4343....431243.... +......4312....1212....1243...... +........43....4343....43........ +........12....1212....12........ +......1243....4343....4312...... +....124312....1212....124312.... +..12431243....4343....43124312.. +12431243......1212......43124312 +431243........4343........431243 +1243......121212121212......4312 +43........434343434343........43 +...............12............... +...............43............... +...............12............... +...............43............... +...............12............... +...............43............... +...............12............... +...............43............... +...............12............... +...............43............... +...............12............... +...............43............... +...............12............... +...............43............... +...............12............... +...............43............... +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ diff --git a/kmahjongg/pics/default.bgnd b/kmahjongg/pics/default.bgnd new file mode 100644 index 00000000..2f0e4612 Binary files /dev/null and b/kmahjongg/pics/default.bgnd differ diff --git a/kmahjongg/pics/default.layout b/kmahjongg/pics/default.layout new file mode 100644 index 00000000..f72ed794 --- /dev/null +++ b/kmahjongg/pics/default.layout @@ -0,0 +1,86 @@ +kmahjongg-layout-v1.0 +# Level 0 ------------------------- +...121212121212121212121212..... +...434343434343434343434343..... +.......1212121212121212......... +.......4343434343434343......... +.....12121212121212121212....... +.....43434343434343434343....... +...121212121212121212121212..... +.124343434343434343434343431212. +.431212121212121212121212124343. +...434343434343434343434343..... +.....12121212121212121212....... +.....43434343434343434343....... +.......1212121212121212......... +.......4343434343434343......... +...121212121212121212121212..... +...434343434343434343434343..... +# Level 1 ------------------------- +................................ +................................ +.........121212121212........... +.........434343434343........... +.........121212121212........... +.........434343434343........... +.........121212121212........... +.........434343434343........... +.........121212121212........... +.........434343434343........... +.........121212121212........... +.........434343434343........... +.........121212121212........... +.........434343434343........... +................................ +................................ +# Level 2 ------------------------- +................................ +................................ +................................ +................................ +...........12121212............. +...........43434343............. +...........12121212............. +...........43434343............. +...........12121212............. +...........43434343............. +...........12121212............. +...........43434343............. +................................ +................................ +................................ +................................ +# Level 3 ------------------------- +................................ +................................ +................................ +................................ +................................ +................................ +.............1212............... +.............4343............... +.............1212............... +.............4343............... +................................ +................................ +................................ +................................ +................................ +................................ +# Level 4 ------------------------- +................................ +................................ +................................ +................................ +................................ +................................ +................................ +..............12................ +..............43................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ diff --git a/kmahjongg/pics/default.theme b/kmahjongg/pics/default.theme new file mode 100644 index 00000000..9ba994bc --- /dev/null +++ b/kmahjongg/pics/default.theme @@ -0,0 +1,4 @@ +kmahjongg-theme-v1.0 +:default.tileset +:default.bgnd +:default.layout diff --git a/kmahjongg/pics/default.tileset b/kmahjongg/pics/default.tileset new file mode 100644 index 00000000..b35cda6c Binary files /dev/null and b/kmahjongg/pics/default.tileset differ diff --git a/kmahjongg/pics/haze.bgnd b/kmahjongg/pics/haze.bgnd new file mode 100644 index 00000000..f32784ff Binary files /dev/null and b/kmahjongg/pics/haze.bgnd differ diff --git a/kmahjongg/pics/kmahjongg.png b/kmahjongg/pics/kmahjongg.png new file mode 100644 index 00000000..5e0ac37c Binary files /dev/null and b/kmahjongg/pics/kmahjongg.png differ diff --git a/kmahjongg/pics/kmahjongg_bgnd.png b/kmahjongg/pics/kmahjongg_bgnd.png new file mode 100644 index 00000000..14d90033 Binary files /dev/null and b/kmahjongg/pics/kmahjongg_bgnd.png differ diff --git a/kmahjongg/pics/newnum.xpm b/kmahjongg/pics/newnum.xpm new file mode 100644 index 00000000..bc0b96ee --- /dev/null +++ b/kmahjongg/pics/newnum.xpm @@ -0,0 +1,30 @@ +/* XPM */ +static char*numnew[]={ +"22 22 5 1", +"c c #c0c0c0", +"# c #000000", +". c None", +"b c #dcdcdc", +"a c #ffffff", +"......................", +"......................", +"......................", +"......................", +"......#######.........", +"......#aaaabb#........", +"......#aaaacab#.......", +"......#aaaacaab#......", +"......#aaaac####......", +"......#aaaaaccc#......", +"......#aaaaaaaa#......", +"......#a#a##a#a#......", +"......#a#aa#a#a#......", +"......#a#a##a#a#......", +"......#a#a##a#a#......", +"......#a#a##a#a#......", +"......#aaaaaaaa#......", +"......##########......", +"......................", +"......................", +"......................", +"......................"}; diff --git a/kmahjongg/pics/pirates.bgnd b/kmahjongg/pics/pirates.bgnd new file mode 100644 index 00000000..2e96211f Binary files /dev/null and b/kmahjongg/pics/pirates.bgnd differ diff --git a/kmahjongg/pics/pirates.layout b/kmahjongg/pics/pirates.layout new file mode 100644 index 00000000..c5bb895c --- /dev/null +++ b/kmahjongg/pics/pirates.layout @@ -0,0 +1,81 @@ +kmahjongg-layout-v1.0 +.............121212.......12..12 +.............434343.......43..43 +.............12.............12.. +.........1212431212.........43.. +.........4343124343.......12..12 +.......12121243121212.....43..43 +.......43434312434343........... +...1212121212431212121212....... +...4343434343124343434343....12. +12...........43..............43. +43...........12..........121212. +.12..........43..........434343. +.431212121212121212121212121212. +...4343434343434343434343434343. +.......1212121212121212121212... +.......4343434343434343434343... +.............12................. +.............43................. +.............12.............12.. +.........12124312...........43.. +.........43431243............... +.........1212431212............. +.........4343124343............. +.......12121243121212........... +.......43434312434343........12. +.............43..............43. +.............12..........121212. +.............43..........434343. +.....121212121212121212121212... +.....434343434343434343434343... +.........1212121212121212....... +.........4343434343434343....... +................................ +................................ +................................ +...........12..12............... +...........43..43............... +.........1212..1212............. +.........4343..4343............. +.........1212..1212............. +.........4343..4343............. +................................ +.............................12. +...........................1243. +......121212121212121212.1243... +......434343434343434343.43..... +.........1212121212121212....... +.........4343434343434343....... +................................ +................................ +................................ +...........12..12............... +...........43..43............... +...........12..12............... +...........43..43............... +.........12......12............. +.........43......43............. +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ diff --git a/kmahjongg/pics/pirates.theme b/kmahjongg/pics/pirates.theme new file mode 100644 index 00000000..748b3ffa --- /dev/null +++ b/kmahjongg/pics/pirates.theme @@ -0,0 +1,5 @@ +kmahjongg-theme-v1.0 +:pirates.tileset +:pirates.bgnd +:pirates.layout + diff --git a/kmahjongg/pics/pirates.tileset b/kmahjongg/pics/pirates.tileset new file mode 100644 index 00000000..77bab899 Binary files /dev/null and b/kmahjongg/pics/pirates.tileset differ diff --git a/kmahjongg/pics/pyramid.layout b/kmahjongg/pics/pyramid.layout new file mode 100644 index 00000000..edd634dc --- /dev/null +++ b/kmahjongg/pics/pyramid.layout @@ -0,0 +1,86 @@ +kmahjongg-layout-v1.0 +# Level 0 ------------------------- +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +# Level 1 ------------------------- +................................ +................................ +......12121212121212121212...... +......43434343434343434343...... +......12121212121212121212...... +......43434343434343434343...... +......12121212121212121212...... +......43434343434343434343...... +......12121212121212121212...... +......43434343434343434343...... +......12121212121212121212...... +......43434343434343434343...... +......12121212121212121212...... +......43434343434343434343...... +................................ +................................ +# Level 2 ------------------------- +................................ +................................ +................................ +................................ +........1212121212121212........ +........4343434343434343........ +........1212121212121212........ +........4343434343434343........ +........1212121212121212........ +........4343434343434343........ +........1212121212121212........ +........4343434343434343........ +................................ +................................ +................................ +................................ +# Level 3 ------------------------- +................................ +................................ +................................ +................................ +................................ +................................ +..........121212121212.......... +..........434343434343.......... +..........121212121212.......... +..........434343434343.......... +................................ +................................ +................................ +................................ +................................ +................................ +# Level 4 ------------------------- +................................ +................................ +................................ +................................ +................................ +................................ +................................ +............12121212............ +............43434343............ +................................ +................................ +................................ +................................ +................................ +................................ +................................ diff --git a/kmahjongg/pics/runes.tileset b/kmahjongg/pics/runes.tileset new file mode 100644 index 00000000..30106ae2 Binary files /dev/null and b/kmahjongg/pics/runes.tileset differ diff --git a/kmahjongg/pics/slate.bgnd b/kmahjongg/pics/slate.bgnd new file mode 100644 index 00000000..07d3f73e Binary files /dev/null and b/kmahjongg/pics/slate.bgnd differ diff --git a/kmahjongg/pics/splash.png b/kmahjongg/pics/splash.png new file mode 100644 index 00000000..dba4cf67 Binary files /dev/null and b/kmahjongg/pics/splash.png differ diff --git a/kmahjongg/pics/stax.layout b/kmahjongg/pics/stax.layout new file mode 100644 index 00000000..f5595dca --- /dev/null +++ b/kmahjongg/pics/stax.layout @@ -0,0 +1,81 @@ +kmahjongg-layout-v1.0 +................................ +12121212..121212121212..12121212 +43434343..434343434343..43434343 +12............1212............12 +43............4343............43 +12121212......1212......12121212 +43434343......4343......43434343 +12............1212............12 +43............4343............43 +12121212......1212......12121212 +43434343......4343......43434343 +12............1212............12 +43............4343............43 +12121212..121212121212..12121212 +43434343..434343434343..43434343 +................................ +................................ +121212.....1212121212.....121212 +434343.....4343434343.....434343 +12.............12.............12 +43.............43.............43 +121212.........12.........121212 +434343.........43.........434343 +12.............12.............12 +43.............43.............43 +121212.........12.........121212 +434343.........43.........434343 +12.............12.............12 +43.............43.............43 +121212.....1212121212.....121212 +434343.....4343434343.....434343 +................................ +................................ +1212.........121212.........1212 +4343.........434343.........4343 +12.............12.............12 +43.............43.............43 +1212...........12...........1212 +4343...........43...........4343 +12.............12.............12 +43.............43.............43 +1212...........12...........1212 +4343...........43...........4343 +12.............12.............12 +43.............43.............43 +1212.........121212.........1212 +4343.........434343.........4343 +................................ +................................ +12.............12.............12 +43.............43.............43 +12.............12.............12 +43.............43.............43 +12.............12.............12 +43.............43.............43 +12.............12.............12 +43.............43.............43 +12.............12.............12 +43.............43.............43 +12.............12.............12 +43.............43.............43 +12.............12.............12 +43.............43.............43 +................................ +................................ +................................ +................................ +...............12............... +...............43............... +...............12............... +...............43............... +...............12............... +...............43............... +...............12............... +...............43............... +...............12............... +...............43............... +................................ +................................ +................................ diff --git a/kmahjongg/pics/test.layout b/kmahjongg/pics/test.layout new file mode 100644 index 00000000..d46a4e21 --- /dev/null +++ b/kmahjongg/pics/test.layout @@ -0,0 +1,81 @@ +kmahjongg-layout-v1.0 +.1212121212121212121212121212... +.4343434343434343434343434343... +.1212121212121212121212121212... +.4343434343434343434343434343... +.1212121212121212121212121212... +.4343434343434343434343434343... +.1212121212121212121212121212... +.4343434343434343434343434343... +.1212121212121212121212121212... +.4343434343434343434343434343... +.1212121212121212121212121212... +.4343434343434343434343434343... +.1212121212121212121212121212... +.4343434343434343434343434343... +.1212121212121212121212121212... +.4343434343434343434343434343... +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ diff --git a/kmahjongg/pics/test2.layout b/kmahjongg/pics/test2.layout new file mode 100644 index 00000000..aedc6290 --- /dev/null +++ b/kmahjongg/pics/test2.layout @@ -0,0 +1,81 @@ +kmahjongg-layout-v1.0 +................................ +................................ +.121212121212121212............. +.434343434343434343............. +.121212121212121212............. +.434343434343434343............. +.121212121212121212............. +.434343434343434343............. +.121212121212121212............. +.434343434343434343............. +.121212121212121212............. +.434343434343434343............. +.121212121212121212............. +.434343434343434343............. +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ diff --git a/kmahjongg/pics/tower.layout b/kmahjongg/pics/tower.layout new file mode 100644 index 00000000..76fe96a0 --- /dev/null +++ b/kmahjongg/pics/tower.layout @@ -0,0 +1,86 @@ +kmahjongg-layout-v1.0 +# Level 0 ------------------------- +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +# Level 1 ------------------------- +................................ +................................ +......12121212121212121212...... +......43434343434343434343...... +......12121212121212121212...... +......43434343434343434343...... +......12121212121212121212...... +......43434343434343434343...... +......12121212121212121212...... +......43434343434343434343...... +......12121212121212121212...... +......43434343434343434343...... +......12121212121212121212...... +......43434343434343434343...... +................................ +................................ +# Level 2 ------------------------- +................................ +................................ +......121212........121212...... +......434343........434343...... +......121212........121212...... +......434343........434343...... +................................ +................................ +................................ +................................ +......121212........121212...... +......434343........434343...... +......121212........121212...... +......434343........434343...... +................................ +................................ +# Level 3 ------------------------- +................................ +................................ +......121212........121212...... +......434343........434343...... +......121212........121212...... +......434343........434343...... +................................ +................................ +................................ +................................ +......121212........121212...... +......434343........434343...... +......121212........121212...... +......434343........434343...... +................................ +................................ +# Level 4 ------------------------- +................................ +................................ +......121212........121212...... +......434343........434343...... +......121212........121212...... +......434343........434343...... +................................ +................................ +................................ +................................ +......121212........121212...... +......434343........434343...... +......121212........121212...... +......434343........434343...... +................................ +................................ diff --git a/kmahjongg/pics/traditional.tileset b/kmahjongg/pics/traditional.tileset new file mode 100644 index 00000000..e4c7a93a Binary files /dev/null and b/kmahjongg/pics/traditional.tileset differ diff --git a/kmahjongg/pics/triangle.layout b/kmahjongg/pics/triangle.layout new file mode 100644 index 00000000..e1dd4891 --- /dev/null +++ b/kmahjongg/pics/triangle.layout @@ -0,0 +1,86 @@ +kmahjongg-layout-v1.0 +# Level 0 ------------------------- +.121212121212121212121212121212. +.434343434343434343434343434343. +...12121212121212121212121212... +...43434343434343434343434343... +.....1212121212121212121212..... +.....4343434343434343434343..... +.......121212121212121212....... +.......434343434343434343....... +.........12121212121212......... +.........43434343434343......... +...........1212121212........... +...........4343434343........... +.............121212............. +.............434343............. +...............12............... +...............43............... +# Level 1 ------------------------- +...12121212121212121212121212... +...43434343434343434343434343... +.....1212121212121212121212..... +.....4343434343434343434343..... +.......121212121212121212....... +.......434343434343434343....... +.........12121212121212......... +.........43434343434343......... +...........1212121212........... +...........4343434343........... +.............121212............. +.............434343............. +...............12............... +...............43............... +................................ +................................ +# Level 2 ------------------------- +.....1212121212121212121212..... +.....4343434343434343434343..... +.......121212121212121212....... +.......434343434343434343....... +.........12121212121212......... +.........43434343434343......... +...........1212121212........... +...........4343434343........... +.............121212............. +.............434343............. +...............12............... +...............43............... +................................ +................................ +................................ +................................ +# Level 3 ------------------------- +.......121212121212121212....... +.......434343434343434343....... +.........12121212121212......... +.........43434343434343......... +...........1212121212........... +...........4343434343........... +.............121212............. +.............434343............. +...............12............... +...............43............... +................................ +................................ +................................ +................................ +................................ +................................ +# Level 4 ------------------------- +.........12121212121212......... +.........43434343434343......... +...........1212121212........... +...........4343434343........... +.............121212............. +.............434343............. +...............12............... +...............43............... +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ diff --git a/kmahjongg/pics/wood.bgnd b/kmahjongg/pics/wood.bgnd new file mode 100644 index 00000000..1433b16c Binary files /dev/null and b/kmahjongg/pics/wood.bgnd differ diff --git a/kmahjongg/prefs.kcfgc b/kmahjongg/prefs.kcfgc new file mode 100644 index 00000000..1498936f --- /dev/null +++ b/kmahjongg/prefs.kcfgc @@ -0,0 +1,7 @@ +# Code generation options for kconfig_compiler +File=kmahjongg.kcfg +#IncludeFiles=defines.h +ClassName=Prefs +Singleton=true +Mutators=true +#CustomAdditions=true diff --git a/kmahjongg/settings.ui b/kmahjongg/settings.ui new file mode 100644 index 00000000..e53c6e37 --- /dev/null +++ b/kmahjongg/settings.ui @@ -0,0 +1,171 @@ + +Settings + + + Settings + + + + 0 + 0 + 274 + 200 + + + + + unnamed + + + 0 + + + 0 + + + + frame3 + + + StyledPanel + + + Raised + + + 0 + + + + unnamed + + + + groupBox1 + + + General + + + + unnamed + + + + kcfg_ShowRemoved + + + Show removed tiles + + + + + kcfg_SolvableGames + + + Generate solvable games + + + true + + + + + kcfg_PlayAnimation + + + Play winning animation + + + true + + + + + + + spacer1 + + + Vertical + + + Expanding + + + + 20 + 16 + + + + + + kcfg_TiledBackground + + + Background + + + + unnamed + + + + Background_scale + + + Scale + + + + + Background_tiled + + + Tiled + + + true + + + + + + + groupBox2 + + + Tiles + + + + unnamed + + + + kcfg_ShowShadows + + + Draw shadows + + + false + + + + + kcfg_MiniTiles + + + Use mini-tiles + + + + + + + + + + diff --git a/kmahjongg/version.h b/kmahjongg/version.h new file mode 100644 index 00000000..fb1c1583 --- /dev/null +++ b/kmahjongg/version.h @@ -0,0 +1 @@ +#define KMAHJONGG_VERSION "0.7.9" diff --git a/kmines/CHANGELOG b/kmines/CHANGELOG new file mode 100644 index 00000000..a6d3aac0 --- /dev/null +++ b/kmines/CHANGELOG @@ -0,0 +1,325 @@ +2.1.10 (25 Aug 2005) [KDE 3.5 devel] + +2.1.9a (25 Aug 2005) [KDE 3.4.3 stable] + * fix constness in solver [reported by Garrett Kajmowicz] + * fix bug: cannot load log [reported by Tobias Meyer] + +2.1.9 (27 Jun 2004) [KDE 3.3 stable] + * fix compilation of solver debugging code. + * add missing events + * replace case size option by zoom in/zoom out actions + +2.1.8c (31 May 2004) [KDE 3.2.3 stable] + * fix bug: hint not displayed [reported by Daniel Schepler] + * fix bug: log file restarted when game paused [reported by Astharoth] + +2.1.8b (26 February 2004) [KDE 3.2.1 stable] + * fix solver crash when "magic reveal" on. + * fix longstanding crash in solver dialog. + +2.1.8 (17 January 2003) [KDE 3.2 stable] + * configure button in highscores dialog + * notifications + +2.1.7e (11 May 2003) [KDE 3.1.3 stable] + * fix score trends display + +2.1.7d (30 April 2003) [KDE 3.1.2 stable] + * fix assert in custom dialog [reported by Albert Astals Cid] + +2.1.7c (17 January 2003) [KDE 3.1.1 stable] + * fix score lcd colors for custom games + +2.1.7b (3 December 2002) [KDE 3.1 stable] + * fix bug in highscores dialog in statistics and histogram tabs + * fix icons in adviser menu and in configuration dialog + * fix bug in XML configuration that was leading to a crash at program + end [report by connyosis and "Quel Qun"] + * fix bug in mean score computation + * fix bug that prevents logging solver actions + +2.1.7 (27 July 2002) + * XML configuration is working !! + * separate keyboard and general shortcuts in configuration dialog + * add "advanced" tab to highscores configuration + possibility to remove + registration + * export highscores to text file + * add (optionnal) statistics and histogram to highscores dialog + * track lost games and black marks for kmines + * histogram for kmines + * some cosmetic fixes in configuration dialog + * view/save/replay/load game log + * fix a bug in KMultiConfigItem [reported by ...] + * at game end and in case of victory, do not show mines but add flags [bug + reported by ...] + +2.1.6 (24 April 2002) + * mark/unmark actions on mouse release [patch by Thomas Capricelli] + * "magic reveal" : a new option that leave only the non-trivial cases to + solve [patch by Thomas Capricelli] + * fix obscure flicker condition : keyboard play and moving pressed mouse + outside field :) + * add home/end/pageup/pagedown keyboard actions + * fix pause menu entry state in some situation + * fix resize when case size changed + * first step for game logs + +2.1.5 (14 February 2002) + * now include a solver/adviser ! [contributed by Mikhail Kourinny] + * revamped game state management + * slightly changed settings API + +2.1.4c (1 May 2002) [KDE 3.0.1 stable] + * fixed case drawing for all styles [bug reported by kanthoney and fixed by + Maksim Orlovich] + +2.1.4 (23 January 2002) + * completely revamped API for highscores + dynamic library + * revamped settings (now includes Custom minefield) + * fixed repaint of LCDs in inactive mode + +2.1.3 (29 November 2001) + * use KConfigGroupSaver everywhere + * config entry for wwhs server url (just in case ...) + +2.1.2 (19 November 2001) + * date for each highscores and for best highscore [proposed by Jenne] + * some more reorganisation + * better custom dialog + * slightly modify the init/repaint at game start : more clean & repaint could + leak some infos about mines position (?) + * forward port the bug fix from 2.1.0b (in 2.2.x branch) + * do not bother people who want to stay anonymous with message boxes + [suggestion from Bernhard Berger] + * autoreveal does not take "?" flag into account anymore + * changing nickname will change nickname in highscores list too [suggestion + from Juliette] + +2.1.1 (16 October 2001) + * port to Qt 3.0 completed (hopefully) + * some changes in highscores (nicer dialog) + * i18n error messages for wwhs + * new smiley pixmap for pause + * clicking smiley resumes when game paused + * changes in keyboard accelators since Ctrl and Shift seems to be + forbidden as keys now ... + * count nb of clicks (reveal, autoreveal, mark) and display it in highscores. + +2.1.0b (19 November 2001) [SF 2.2 stable] + * fix a nasty bug : uncorrect flag was not shown at game over in certain + cases [thanx for the bug reports from Viira and from an anonymous admin :)] + +2.1.0 (17 July 2001) [KDE 2.2 stable] + * fixed checked entry in "show highscores" submenu + +2.0.13 (10 June 2001) [SF 2.2 devel] + * enable world-wide highscores !! + +2.0.12 (09 June 2001) [SF 2.2 devel] + * option to put the game in pause mode if the window loses focus [proposed by + Bernhard Berger] + * better default keys + fixed autoreveal display for keyboard game + * use KStdGameAction + * big revamping of highscores + use of KHighscores + * removed status bar (ugly and not very useful) + +2.0.11 (14 March 2001) + * changed highscores accelerator to CTRL + H (more standard) + * better use of session-management + +2.0.10 (23 October 2000) [KDE 2.1 stable] + * fixed mouse action in settings (autoreveal and toggle mark were inversed) + +2.0.9 (21 September 2000) [KDE 2.0 stable] + * fix obscure bug reported by Tobias Oed (was crashing version 1.0.1a !) + : mouse buttons were messing things when pressed simultaneously. + * in the same move : simplified some code and correct a small buglet due to + rounding error (with left mouse button pressed, the first line and first + column case was pressed when the mouse was moved just outside the top and + left side of the field). + +2.0.8 (7 September 2000) + * fix bug that was pausing a stopped game when calling highscores + +2.0.7 (4 September 2000) + * fixed bug in mine field drawing with some styles [found by Gerard Delafond + and Tobias Kretschmar] that induces a complete rewrite of the drawing code. + now it honors the global style (beautiful in marble style) and it even + leads to code simplification ! + * the keyboard cursor uses now the focus drawing method. + * fixed bug when field.width != field.height when exploding + * fixed font in the pause button (with non standard case size) + * fixed behaviour of button in highscores dialog when entering the winner name + [proposed by Lotta Inkovaara for ksirtet] + * keyboard is disabled by default + +2.0.6 (23 August 2000) + * use of KMainWindow (replace KTMainWindow) but this does not solve the + resizing problems :( + * pause game when high scores requested + * custom game settings are saved [feature suggested by Toan Nguyen, + Williaw Barnes and François-Xavier Duranceau] + * solve the resizing problems by intercepting the Layout Hint event ... + (due to limitations in K/QMainWindow IMHO) + +2.0.5 (14 June 2000) + * default for mouse binding changed + * added color configuration for numbers, flag and explosion (for B&W monitors + and few-colors themes) [feature suggested by Hume Smith and François-Xavier + Duranceau] + * moved around some things about configuration (cleaned up "defines.h") + * case state is now more clean (but some bugs might have crept in) + * properly mark all the flagged cases with no mine to error on game end + * versioning the XMLGUI file + +2.0.4 (11 April 2000) + * hack to resize correctly when menu is hidden (due to bugs + in KTMainWindow) + * nicer (and simpler) custom level dialog with KNumIntInput + * kstatusbar in place of the label + * remove the title in dialogs (more consistent with other apps) + +2.0.3 (6 April 2000) + * use kkeydialog for actions + * rationalize settings (much better now) + * animate autoreveal with keyboard + * enable/disable and change text for pause in menu + * configuration of mouse bindings [idea of FX Duranceau] + +2.0.2 (28 February 2000) + * XMLify the GUI + +2.0.1 (19 February 2000) + * KAction/KAccel integrated + * keyboard play + * can choose case size (font is scaled) + * less flicker in repainting (+ fixed a strange divide negative int by + uint thing) + * fix the pixmaps drawing so that they are not too bad in custom case sizes + +2.0.0 (14 December 1999) + * use of KDialogBase and KAboutData/KAboutDialog + * unflagged mines are shown at game's end + * hack for focus handling in highscores dialog ... + * slightly better LCDs, message label and smiley button + * fixed a bug when clicking on the frame outside the mine field + * small fixes for custom games + * "What's This" added. + +1.0.6 (21 July 1999) + * slightly better highscores dialog + * fixed a bug in flagged mines display (cannot be negative) + * the LCDs gets red when there are more flagged cases than mines + and when you have used more time than the better player. + * use of a status bar. + * the smiley moods are now XPMs [made by Andreas Zehender] + +1.0.5 (6 July 1999) + * fixed layout handling (all this "updateGeometry" and "LayoutHint" stuff) + * space bar hit cannot restart game anymore + * clean code (config) + level is saved + +1.0.4 (12 March 1999) + * porting to QT 2.0 + +1.0.3 (10 March 1999) +Mario Weilguni + * fixed the bug in the level selection (showed up with Qt 2.0) + * fixed layout for Qt 2.0 + * fixed signal/slot handling for Qt 2.0 + * fixed layout with floating menubar + * level menu items are now checked + +1.0.2 (23 February 1999) + * bug fix : argh! a "brown paper bag" one [bug report by Szokovacs Robert] + +1.0.1 (2 Junuary 1999) [KDE 1.1 stable] + * bug fix : when paused a game can be continued by changing desktops or + iconifying/deiconifying without time consumption ... [bug report by + François-Xavier Duranceau, Frank Pieczynski & Oliver Eiden] + * add printing facility : well it seems hacky to me and certainly not perfect + but it works ... [feature suggested by Tomislav Marsic] + +1.0.0 (18 June 1998) [KDE 1.0 stable] + * final cleanup before 1.0 ! + +0.6.12 (9 June 1998) + * use QLayout for "custom level dialog" and "highscore dialog" + * some cleanup + * and a fix ! (yes there was a bug still lurking : when only two + uncovered cases remain, it was possible to win on clicking on the mine) + +0.6.11 + * Added kapp->getHelpMenu() & setCaption() [Robert Williams] + +0.6.10 + * included in the CVS tree + +0.6.9 + * bugfix from Anders Widell (doesn't allow anymore to middle click on a flag) + +0.6.8 + * some polishing (game over message do not overwrite mines number & marked + mines stay marked even when the game is lost : so you can completely analyse + why you have lost :) [thanx to Christoph Rummel for pointing these to me] + +0.6.7 + * internationalization + +0.6.6 + * no more NULLs (for the sake of 64bits) + * nicer dialog boxes + * no mine on first click + +0.6.5 + * minor changes + * "kexample 0.31" compliant + +0.6.4 + * adapted to libkdecore 0.7 + * hide/show menubar and popup + * more compliant to the Style Guide + +0.6.3 + * use kdehelp + * updating of Makefile (use KDEDIR) + * use kmsgbox + +0.62 + * use KKeyCode to manage some keyboard shortcuts + +0.6 + * change the class name "Status" to "KStatus" as an include file present on my + system defines something called "Status"... + +0.5 + * use KApp for configuration and highscoring save (via KConfig) + * better look of option & highscores windows + +0.4 + * minor bug fix + * 4 spaces tabs indentation + * slight change in the "pause code" + +0.3 + * the random configuration seems to be a bit too repetitive ... + (fixed : silly me !) + * a red cross must show where the marked cases have not contained mines + * uncover case on button release, not on button push and also allow moving + the mouse with the left button pressed + * clear function with the mid*button : it should be kool :) + * a pause entry in the menu + * QLCD for timer and mines left + * cleaning of the highscore stuff (the highscore file is now in the home dir) + * option : "'?' mark" on/off (the default behaviour is on : you can change + it by editing the defines.h file) + +0.2 + * some code reorganisation + * timer starts at first click now + * it is no more possible to click on a marked or uncertain case now + * a small help + * colored pixmaps + * a better look (?) + * highscoring + * custom level diff --git a/kmines/LICENSE b/kmines/LICENSE new file mode 100644 index 00000000..6b772e1c --- /dev/null +++ b/kmines/LICENSE @@ -0,0 +1,19 @@ +KMINES : the KDE minesweeper +---------------------------- +Copyright (c) 1996-2004 Nicolas HADACEK (hadacek@kde.org) +Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. diff --git a/kmines/Makefile.am b/kmines/Makefile.am new file mode 100644 index 00000000..e49f6501 --- /dev/null +++ b/kmines/Makefile.am @@ -0,0 +1,66 @@ +SUBDIRS = data bitmaps solver +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) + +KDE_CXXFLAGS = $(KDE_USE_FPIE) + +bin_PROGRAMS = kmines +kmines_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(KDE_USE_PIE) +kmines_LDADD = ./solver/libsolver.la $(LIB_KDEGAMES) +kmines_DEPENDENCIES = $(LIB_KDEGAMES_DEP) +kmines_SOURCES = kzoommainwindow.cpp defines.cpp highscores.cpp settings.kcfgc \ + dialogs.cpp frame.cpp field.cpp status.cpp main.cpp +kmines_METASOURCES = AUTO + +rcdir = $(kde_datadir)/kmines +rc_DATA = kminesui.rc + +messages: rc.cpp + $(XGETTEXT) rc.cpp *.cpp solver/*.cpp -o $(podir)/kmines.pot + +# for system-wide highscore file +DESTBIN = $(DESTDIR)$(bindir)/$(bin_PROGRAMS) +DESTHIGHSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY) +DESTSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY)/$(bin_PROGRAMS).scores + +install-data-local: + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && echo "********************************************************" \ + && echo "" \ + && echo "This game is installed sgid \"games\" to use the" \ + && echo "system-wide highscore file (in "$(HIGHSCORE_DIRECTORY)")." \ + && echo "" \ + && echo "If the system-wide highscore file does not exist, it is" \ + && echo "created with the correct ownership and permissions. See the" \ + && echo "INSTALL file in \"kdegames/libkdegames/highscore\" for details." \ + && echo "" \ + && echo "********************************************************" \ + ) || true + +install-exec-hook: + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((chown $(highscore_user):$(highscore_group) $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((mkdir -p $(DESTHIGHSCORES) && chown $(highscore_user):$(highscore_group) $(DESTHIGHSCORES) \ + && chmod 750 $(DESTHIGHSCORES)) \ + || echo "Error: Could not create the highscore directory with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((chown $(highscore_user):$(highscore_group) $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test ${setgid} = true \ + && ((chmod 2755 $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((touch $(DESTSCORES) && chown $(highscore_user):$(highscore_group) $(DESTSCORES) \ + && chmod 0660 $(DESTSCORES)) \ + || echo "Error: Could not create system-wide highscore file with correct permissions !!" \ + )) || true + diff --git a/kmines/README b/kmines/README new file mode 100644 index 00000000..3371a474 --- /dev/null +++ b/kmines/README @@ -0,0 +1,16 @@ +KMINES : the KDE minesweeper +---------------------------- +Copyright (c) 1996-2004 Nicolas HADACEK (hadacek@kde.org) +Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) +Distributed under the GNU General Public License + + +This is a very classical minesweeper written from scratch +with three predefined levels and custom levels. + + Easy : 8x8 with 10 mines + Normal : 16x16 with 40 mines + Expert : 30x16 with 99 mines + + +Requirements : up to date KDE and QT libraries. diff --git a/kmines/TODO b/kmines/TODO new file mode 100644 index 00000000..655b7b07 --- /dev/null +++ b/kmines/TODO @@ -0,0 +1,14 @@ +TODO: + + * messages from/to a named pipe for external AI [yawn...] + * icons for easy/normal/expert + * new levels ... + * flower / star shaped levels + * option for only solvable games + + * do you have any idea ? + + +KNOWN BUGS: + + * please find one ! diff --git a/kmines/bitmaps/Makefile.am b/kmines/bitmaps/Makefile.am new file mode 100644 index 00000000..3266d0c7 --- /dev/null +++ b/kmines/bitmaps/Makefile.am @@ -0,0 +1,2 @@ +EXTRA_DIST = smile smile_happy smile_ohno smile_stress + diff --git a/kmines/bitmaps/README b/kmines/bitmaps/README new file mode 100644 index 00000000..368803c1 --- /dev/null +++ b/kmines/bitmaps/README @@ -0,0 +1 @@ +These XPMs are a contribution from Andreas Zehender. Many thanks! diff --git a/kmines/bitmaps/smile b/kmines/bitmaps/smile new file mode 100644 index 00000000..9977db89 --- /dev/null +++ b/kmines/bitmaps/smile @@ -0,0 +1,37 @@ +/* XPM */ +const char * smile_xpm[] = { +"25 25 9 1", +" c None", +". c #FFFF00", +"+ c #C0C000", +"@ c #808000", +"# c #404000", +"$ c #303030", +"% c #000000", +"& c #585858", +"* c #A0A0A0", +".........................", +".........................", +"........+@#$%$#@+........", +"......+&$@++.++@$&+......", +".....+$@.........@$+.....", +"....+$+...........+$+....", +"...+$+.............+$+...", +"...&@..+@@+...+@@+..@&...", +"..+$...@%%@...@%%@...$+..", +"..@@...@%%@...@%%@...@@..", +"..#+...+@@+...+@@+...+#..", +"..$+.................+$..", +"..%...................%..", +"..$+..+...........+..+$..", +"..#+.+$...........$+.+#..", +"..@@.@#+.........+#@.@@..", +"..+$...#*.......*#...$+..", +"...&@...@#@+++@#@...@&...", +"...+$+...+@$#$@+...+$+...", +"....+$+...........+$+....", +".....+$@.........@$+.....", +"......+&$@++.++@$&+......", +"........+@#$%$#@+........", +".........................", +"........................."}; diff --git a/kmines/bitmaps/smile_happy b/kmines/bitmaps/smile_happy new file mode 100644 index 00000000..475ac3eb --- /dev/null +++ b/kmines/bitmaps/smile_happy @@ -0,0 +1,37 @@ +/* XPM */ +const char * smile_happy_xpm[] = { +"25 25 9 1", +" c None", +". c #FFFF00", +"+ c #C0C000", +"@ c #808000", +"# c #404000", +"$ c #303030", +"% c #000000", +"& c #585858", +"* c #A0A0A0", +".........................", +".........................", +"........+@#$%$#@+........", +"......+&$@++.++@$&+......", +".....+$@.........@$+.....", +"....+$+...........+$+....", +"...+$+..+++...+++..+$+...", +"...&@#+@#%%+.+%%#@+#@&...", +"..+$.+#%%%%%#%%%%%#+.$+..", +"..@@..+%%%%%+%%%%%+..@@..", +"..#+...@#%%@.@%%#@...+#..", +"..$+....+++...+++....+$..", +"..%...................%..", +"..$+..+...........+..+$..", +"..#+.+$...........$+.+#..", +"..@@.@#+.........+#@.@@..", +"..+$...#*.......*#...$+..", +"...&@...@#@+++@#@...@&...", +"...+$+...+@$#$@+...+$+...", +"....+$+...........+$+....", +".....+$@.........@$+.....", +"......+&$@++.++@$&+......", +"........+@#$%$#@+........", +".........................", +"........................."}; diff --git a/kmines/bitmaps/smile_ohno b/kmines/bitmaps/smile_ohno new file mode 100644 index 00000000..fb8d0784 --- /dev/null +++ b/kmines/bitmaps/smile_ohno @@ -0,0 +1,37 @@ +/* XPM */ +const char * smile_ohno_xpm[] = { +"25 25 9 1", +" c None", +". c #FFFF00", +"+ c #C0C000", +"@ c #808000", +"# c #404000", +"$ c #303030", +"% c #000000", +"& c #585858", +"* c #A0A0A0", +".........................", +".........................", +"........+@#$%$#@+........", +"......+&$@++.++@$&+......", +".....+$@.........@$+.....", +"....+$+...........+$+....", +"...+$+.............+$+...", +"...&@..+@@+...+@@+..@&...", +"..+$...@%%@...@%%@...$+..", +"..@@...@%%@...@%%@...@@..", +"..#+...+@@+...+@@+...+#..", +"..$+.................+$..", +"..%...................%..", +"..$+.................+$..", +"..#+.................+#..", +"..@@.....+@$#$@+.....@@..", +"..+$....@#@+++@#@....$+..", +"...&@..#*.......*#..@&...", +"...+$+.+.........+.+$+...", +"....+$+...........+$+....", +".....+$@.........@$+.....", +"......+&$@++.++@$&+......", +"........+@#$%$#@+........", +".........................", +"........................."}; diff --git a/kmines/bitmaps/smile_sleep b/kmines/bitmaps/smile_sleep new file mode 100644 index 00000000..695cf9bd --- /dev/null +++ b/kmines/bitmaps/smile_sleep @@ -0,0 +1,36 @@ +/* XPM */ +const char * smile_sleep_xpm[] = { +"25 25 8 1", +" c None", +". c #FFFF00", +"+ c #C0C000", +"@ c #808000", +"# c #404000", +"$ c #303030", +"% c #000000", +"& c #585858", +".........................", +".........................", +"........+@#$%$#@+........", +"......+&$@++.++@$&+......", +".....+$@.........@$+.....", +"....+$+...........+$+....", +"...+$+.............+$+...", +"...&@...............@&...", +"..+$.................$+..", +"..@@...@%%.....%%@...@@..", +"..#+.................+#..", +"..$+.................+$..", +"..%...................%..", +"..$+.................+$..", +"..#+.................+#..", +"..@@.................@@..", +"..+$.....%%%%%%#.....$+..", +"...&@...............@&...", +"...+$+.............+$+...", +"....+$+...........+$+....", +".....+$@.........@$+.....", +"......+&$@++.++@$&+......", +"........+@#$%$#@+........", +".........................", +"........................."}; diff --git a/kmines/bitmaps/smile_stress b/kmines/bitmaps/smile_stress new file mode 100644 index 00000000..57b973da --- /dev/null +++ b/kmines/bitmaps/smile_stress @@ -0,0 +1,36 @@ +/* XPM */ +const char * smile_stress_xpm[] = { +"25 25 8 1", +" c None", +". c #FFFF00", +"+ c #C0C000", +"@ c #808000", +"# c #404000", +"$ c #303030", +"% c #000000", +"& c #585858", +".........................", +".........................", +"........+@#$%$#@+........", +"......+&$@++.++@$&+......", +".....+$@.........@$+.....", +"....+$+...........+$+....", +"...+$+.............+$+...", +"...&@..+@@+...+@@+..@&...", +"..+$...@%%@...@%%@...$+..", +"..@@...@%%@...@%%@...@@..", +"..#+...+@@+...+@@+...+#..", +"..$+.................+$..", +"..%...................%..", +"..$+.......+++.......+$..", +"..#+......@#$#@......+#..", +"..@@.....@#...#@.....@@..", +"..+$.....#+...+#.....$+..", +"...&@....#+...+#....@&...", +"...+$+...@&...&@...+$+...", +"....+$+...&#&#&...+$+....", +".....+$@...+@+...@$+.....", +"......+&$@++.++@$&+......", +"........+@#$%$#@+........", +".........................", +"........................."}; diff --git a/kmines/data/Makefile.am b/kmines/data/Makefile.am new file mode 100644 index 00000000..b3c82d50 --- /dev/null +++ b/kmines/data/Makefile.am @@ -0,0 +1,7 @@ +KDE_ICON = kmines + +xdg_apps_DATA = kmines.desktop + +appdatadir = $(kde_datadir)/kmines +appdata_DATA = eventsrc + diff --git a/kmines/data/eventsrc b/kmines/data/eventsrc new file mode 100644 index 00000000..867865db --- /dev/null +++ b/kmines/data/eventsrc @@ -0,0 +1,988 @@ +[!Global!] +IconName=kmines +Comment=KMines +Comment[ar]=لعبة الألغام (KMines) +Comment[be]=Сапёр +Comment[bn]=কে-মাইনà§à¦¸ +Comment[hi]=के-माइनà¥à¤¸ +Comment[hr]=KMine +Comment[ne]=केडीई माइन +Comment[pa]=ਕੇ-ਸਰà©à©°à¨— +Comment[pt_BR]=KMinas +Comment[sv]=Minröjning +Comment[ta]=கேகனà¯à®©à®¿à®µà¯†à®Ÿà®¿à®•à®³à¯ +Comment[tg]=KСапёр +Comment[tr]=Mayın tarlası +Comment[wa]=KMenes +Comment[zh_TW]=KMine 踩地雷 + +[reveal] +Name=Reveal case +Name[ar]=أظهر القضية +Name[be]=Ðдкрыццё Ð¿Ð¾Ð»Ñ +Name[bg]=Разкриване +Name[bn]=মাইন পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করো +Name[bs]=Otkrij polja +Name[ca]=Descobreix casella +Name[cs]=Odkrýt pole +Name[cy]=Dangos cas +Name[da]=Vis felt +Name[de]=Aufdecken +Name[el]=Αποκάλυψη +Name[eo]=MalkaÅi kazon +Name[es]=Revelar el caso +Name[et]=Avab välja +Name[eu]=Erakutsi kasua +Name[fa]=آشکار شدن موقعیت +Name[fi]=Paljasta peli +Name[fr]=Révéler la case +Name[gl]=Amosar cadrado +Name[he]=גלה ריבוע +Name[hi]=केस पà¥à¤°à¤•à¤Ÿ करें +Name[hr]=Otkrij sluÄaj +Name[hu]=MezÅ‘ felfedése +Name[is]=Sýna tösku +Name[it]=Rivela casella +Name[ja]=é–‹ã„ãŸã¨ã +Name[lt]=Atverti langelį +Name[lv]=AtrisinÄt +Name[mk]=Отворено е поле +Name[nb]=Avslør +Name[nds]=Opmaken +Name[ne]=केस पà¥à¤°à¤•à¤Ÿ +Name[nl]=Vak openen +Name[nn]=Avslør +Name[pa]=ਰੀਵਲ ਕੇਸ +Name[pl]=Pole odsÅ‚oniÄ™te +Name[pt]=Quadrado revelado +Name[pt_BR]=Revelar quadrado +Name[ru]=Открытие Ð¿Ð¾Ð»Ñ +Name[se]=Čájet +Name[sk]=OdkryÅ¥ pole +Name[sl]=Odkrij ploÅ¡Äico +Name[sr]=Откриј Ñлучај +Name[sr@Latn]=Otkrij sluÄaj +Name[sv]=Avslöja ruta +Name[ta]=நிகழà¯à®šà¯à®šà®¿à®¯à¯ˆ வெளிபà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯ +Name[tg]=Кушодани ҳолат +Name[tr]=Kutu aç +Name[uk]=Ð’Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ ÐºÐ¾Ð¼Ñ–Ñ€ÐºÐ¸ +Name[zh_CN]=æ­å¼€ç›’ç›– +Name[zh_TW]=開挖方格 +Comment=Reveal case +Comment[be]=Ðдкрыццё Ð¿Ð¾Ð»Ñ +Comment[bg]=Разкриване +Comment[bn]=মাইন পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করো +Comment[bs]=Otkrij polja +Comment[ca]=Descobreix casella +Comment[cs]=Odkrýt pole +Comment[cy]=Dangos cas +Comment[da]=Vis felt +Comment[de]=Aufdecken +Comment[el]=Αποκάλυψη +Comment[eo]=MalkaÅi kazon +Comment[es]=Revelar el caso +Comment[et]=Avab välja +Comment[eu]=Erakutsi kasua +Comment[fa]=آشکار شدن موقعیت +Comment[fi]=Paljasta peli +Comment[fr]=Révéler la case +Comment[gl]=Amosar cadrado +Comment[he]=גלה ריבוע +Comment[hi]=केस पà¥à¤°à¤•à¤Ÿ करें +Comment[hr]=Otkrij sluÄaj +Comment[hu]=MezÅ‘ felfedése +Comment[is]=Sýna tösku +Comment[it]=Rivela casella +Comment[ja]=é–‹ã„ãŸã¨ã +Comment[km]=ករណី​បក +Comment[lt]=Atverti langelį +Comment[lv]=AtrisinÄt +Comment[mk]=Отворено е поле +Comment[nb]=Avslør +Comment[nds]=Opmaken +Comment[ne]=केस पà¥à¤°à¤•à¤Ÿ +Comment[nl]=Vak openen +Comment[nn]=Avslør +Comment[pl]=Pole zaznaczone +Comment[pt]=Quadrado revelado +Comment[pt_BR]=Revelar quadrado +Comment[ru]=Открытие Ð¿Ð¾Ð»Ñ +Comment[se]=Čájet +Comment[sk]=OdkryÅ¥ pole +Comment[sl]=Odkrij ploÅ¡Äico +Comment[sr]=Откриј Ñлучај +Comment[sr@Latn]=Otkrij sluÄaj +Comment[sv]=Avslöja ruta +Comment[ta]=நிகழà¯à®šà¯à®šà®¿à®¯à¯ˆ வெளிபà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯ +Comment[tg]=Кушодани ҳолат +Comment[tr]=Kutuyu aç +Comment[uk]=Відкрити комірку +Comment[zh_CN]=æ­å¼€ç›’ç›– +Comment[zh_TW]=開挖方格 +default_presentation=0 + +[autoreveal] +Name=Autoreveal case +Name[be]=Ðдкрыццё пуÑÑ‚Ñ‹Ñ… палёў +Name[bg]=Ðвтоматично разкриване +Name[bn]=সà§à¦¬à§Ÿà¦‚কà§à¦°à¦¿à§Ÿà¦­à¦¾à¦¬à§‡ মাইন পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করো +Name[bs]=Automatski otkrij polja +Name[ca]=Descobreix automàticament casella +Name[cs]=Automaticky odkrýt pole +Name[cy]=Dangos cas yn ymysgogol +Name[da]=Vis felt automatisk +Name[de]=Automatisch aufdecken +Name[el]=Αυτόματη αποκάλυψη +Name[eo]=AÅ­tomalkaÅi kazon +Name[es]=Autorevelar el caso +Name[et]=Välja automaatne avamine +Name[eu]=Auto-erakutsi kasua +Name[fa]=آشکار شدن موقعیت به طور خودکار +Name[fi]=Automaattisesti paljasta peli +Name[fr]=Révéler automatiquement la case +Name[gl]=Auto-amosar cadrado +Name[he]=גלה ריבוע ×וטומטית +Name[hi]=केस सà¥à¤µà¤¯à¤‚ पà¥à¤°à¤•à¤Ÿ करें +Name[hr]=Automatski otkrij sluÄaj +Name[hu]=MezÅ‘ automatikus felfedése +Name[is]=Sýna tösku sjálfkrafa +Name[it]=Rivela automaticamente caselle +Name[ja]=自動ã§é–‹ã„ãŸã¨ã +Name[km]=ករណី​បក​ដោយ​ស្វáŸáž™â€‹áž”្រវážáŸ’ážáž· +Name[lt]=Automatinis atvÄ—rimas +Name[lv]=AutomÄtiski atrisinÄt +Name[mk]=ÐвтоматÑки е отворено поле +Name[nb]=Automatisk avsløring +Name[nds]=Automaatsch opmaken +Name[ne]=सà¥à¤µà¤¤: पà¥à¤°à¤•à¤Ÿ केस +Name[nl]=Vak automatisch openen +Name[nn]=Automatisk avsløring +Name[pa]=ਆਟੋਰੀਵਲ ਕੇਸ +Name[pl]=Pole odsÅ‚oniÄ™te automatycznie +Name[pt]=Auto-revelar quadrado +Name[pt_BR]=Auto-revelar quadrado +Name[ru]=Открытие пуÑÑ‚Ñ‹Ñ… полей +Name[se]=AutomáhtalaÄÄat Äájet +Name[sk]=Automaticky odkryÅ¥ pole +Name[sl]=Samodejno odkrij ploÅ¡Äico +Name[sr]=ÐутоматÑки откриј Ñлучај +Name[sr@Latn]=Automatski otkrij sluÄaj +Name[sv]=Avslöja ruta automatiskt +Name[ta]=நிகழà¯à®šà¯à®šà®¿à®¯à¯ˆ தானே வெளிபà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯ +Name[tg]=Худкушодашудани ҳолат +Name[tr]=Otomatik kutu aç +Name[uk]=Ðвтоматичне відкрити комірки +Name[zh_CN]=自动æ­å¼€ç›’ç›– +Name[zh_TW]=周åœè‡ªå‹•é–‹æŒ–方格 +Comment=Autoreveal case +Comment[be]=Ðдкрыццё пуÑÑ‚Ñ‹Ñ… палёў +Comment[bg]=Ðвтоматично разкриване +Comment[bn]=সà§à¦¬à§Ÿà¦‚কà§à¦°à¦¿à§Ÿà¦­à¦¾à¦¬à§‡ মাইন পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করো +Comment[bs]=Automatski otkrij polja +Comment[ca]=Descobreix automàticament casella +Comment[cs]=Automaticky odkrýt pole +Comment[cy]=Dangos cas yn ymysgogol +Comment[da]=Vis felt automatisk +Comment[de]=Automatisch aufdecken +Comment[el]=Αυτόματη αποκάλυψη +Comment[eo]=AÅ­tomalkaÅi kazon +Comment[es]=Autorevelar el caso +Comment[et]=Välja automaatne avamine +Comment[eu]=Auto-erakutsi kasua +Comment[fa]=آشکار شدن موقعیت به طور خودکار +Comment[fi]=Automaattisesti paljasta peli +Comment[fr]=Révéler automatiquement la case +Comment[gl]=Auto-amosar cadrado +Comment[he]=גלה ריבוע ×וטומטית +Comment[hi]=केस सà¥à¤µà¤¯à¤‚ पà¥à¤°à¤•à¤Ÿ करें +Comment[hr]=Automatski otkrij sluÄaj +Comment[hu]=MezÅ‘ automatikus felfedése +Comment[is]=Sýna tösku skjakrafa +Comment[it]=Rivela automaticamente caselle +Comment[ja]=自動ã§é–‹ã„ãŸã¨ã +Comment[km]=ករណី​បក​ដោយ​ស្វáŸáž™â€‹áž”្រវážáŸ’ážáž· +Comment[lt]=Automatinis atvÄ—rimas +Comment[lv]=AutomÄtiski atrisinÄt +Comment[mk]=ÐвтоматÑки е отворено поле +Comment[nb]=Automatisk avsløring +Comment[nds]=Automaatsch opmaken +Comment[ne]=सà¥à¤µà¤¤: पà¥à¤°à¤•à¤Ÿ केस +Comment[nl]=Vak automatisch openen +Comment[nn]=Automatisk avsløring +Comment[pl]=Pole odsÅ‚oniÄ™te automatycznie +Comment[pt]=Quadrado auto-revelado +Comment[pt_BR]=Auto-revelar quadrado +Comment[ru]=Открытие пуÑÑ‚Ñ‹Ñ… полей +Comment[se]=AutomáhtalaÄÄat Äájet +Comment[sk]=Automaticky odkryÅ¥ pole +Comment[sl]=Samodejno odkrij ploÅ¡Äico +Comment[sr]=ÐутоматÑки откриј Ñлучај +Comment[sr@Latn]=Automatski otkrij sluÄaj +Comment[sv]=Avslöja ruta automatiskt +Comment[ta]=நிகழà¯à®šà¯à®šà®¿à®¯à¯ˆ தானே வெளிபà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯ +Comment[tg]=Худкушодани ҳолат +Comment[tr]=Kutuyu otomatik aç +Comment[uk]=Ðвтоматично відкрити комірку +Comment[zh_CN]=自动æ­å¼€ç›’ç›– +Comment[zh_TW]=周åœè‡ªå‹•é–‹æŒ–方格 +default_presentation=0 + +[mark] +Name=Mark case +Name[be]=Пазнака міны +Name[bg]=ПоÑтавÑне на флаг +Name[bn]=মাইন-ঠচিহà§à¦¨ দাও +Name[bs]=OznaÄi polje +Name[ca]=Marca casella +Name[cs]=OznaÄit pole +Name[cy]=Marcio cas +Name[da]=Markér felt +Name[de]=Markieren +Name[el]=Σημείωση ÎºÎ¿Ï…Ï„Î¹Î¿Ï +Name[eo]=Marki kazon +Name[es]=Marcar el caso +Name[et]=Märgib välja +Name[eu]=Markatu kasua +Name[fa]=مشخص کردن موقعیت +Name[fi]=Merkkaa peli +Name[fr]=Marquer la case +Name[gl]=Marcar cadrado +Name[he]=סמן ריבוע +Name[hi]=केस चिहà¥à¤¨à¤¿à¤¤ करें +Name[hr]=Obilježi sluÄaj +Name[hu]=MezÅ‘ megjelölése +Name[is]=Merkja tösku +Name[it]=Segna casella +Name[ja]=マークã—ãŸã¨ã +Name[km]=ករណី​សម្គាល់ +Name[lt]=PažymÄ—ti langelį +Name[lv]=MarÄ·Ä“t +Name[mk]=Обележано е поле +Name[nb]=Merk +Name[nds]=Markeren +Name[ne]=चिनà¥à¤¹ केस +Name[nl]=Vak markeren +Name[nn]=Merk +Name[pa]=ਮਾਰਕ ਕੇਸ +Name[pl]=Pole zaznaczone +Name[pt]=Marcar quadrado +Name[pt_BR]=Marcar quadrado +Name[ru]=Отметка мины +Name[se]=Merke +Name[sk]=OznaÄiÅ¥ pole +Name[sl]=OznaÄi ploÅ¡Äico +Name[sr]=Обележи Ñлучај +Name[sr@Latn]=Obeleži sluÄaj +Name[sv]=Markera ruta +Name[ta]=நிகழà¯à®šà¯à®šà®¿à®¯à¯ˆ கà¯à®±à®¿ +Name[tg]=Ðишонакунии ҳолат +Name[tr]=Kutuyu iÅŸaretle +Name[uk]=ÐŸÐ¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ€ÐºÐ¸ +Name[zh_CN]=æ ‡è®°ç›’å­ +Name[zh_TW]=標記方格 +Comment=Mark case +Comment[be]=Пазнака міны +Comment[bg]=ПоÑтавÑне на флаг +Comment[bn]=সমà§à¦­à¦¾à¦¬à§à¦¯ মাইন-ঠচিহà§à¦¨ দাও +Comment[bs]=OznaÄi polje +Comment[ca]=Marca casella +Comment[cs]=OznaÄit pole +Comment[cy]=Marcio cas +Comment[da]=Markér felt +Comment[de]=Markieren +Comment[el]=Σημείωση ÎºÎ¿Ï…Ï„Î¹Î¿Ï +Comment[eo]=Marki kazon +Comment[es]=Marcar el caso +Comment[et]=Märgib välja +Comment[eu]=Markatu kasua +Comment[fa]=مشخص کردن موقعیت +Comment[fi]=Merkkaa peli +Comment[fr]=Marquer la case +Comment[gl]=Marcar cadrado +Comment[he]=סמן ריבוע +Comment[hi]=केस चिहà¥à¤¨à¤¿à¤¤ करें +Comment[hr]=Obilježi sluÄaj +Comment[hu]=MezÅ‘ megjelölése +Comment[is]=Merkja tösku +Comment[it]=Segna casella +Comment[ja]=マークã—ãŸã¨ã +Comment[km]=ករណី​សម្គាល់ +Comment[lt]=PažymÄ—ti langelį +Comment[lv]=MarÄ·Ä“t +Comment[mk]=Обележано е поле +Comment[nb]=Merk +Comment[nds]=Markeren +Comment[ne]=केसमा चिनà¥à¤¹ लगाउनà¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Vak markeren +Comment[nn]=Merk +Comment[pl]=Zaznaczenie pola +Comment[pt]=Quadrado marcado +Comment[pt_BR]=Marcar quadrado +Comment[ru]=Отметка мины +Comment[se]=Merke +Comment[sk]=OznaÄiÅ¥ pole +Comment[sl]=OznaÄi ploÅ¡Äico +Comment[sr]=Обележи Ñлучај +Comment[sr@Latn]=Obeleži sluÄaj +Comment[sv]=Markera ruta +Comment[ta]=நிகழà¯à®šà¯à®šà®¿à®¯à¯ˆ கà¯à®±à®¿ +Comment[tg]=Ðишонакунии ҳолат +Comment[tr]=Kutuyu iÅŸaretle +Comment[uk]=Помітити комірку +Comment[zh_CN]=æ ‡è®°ç›’å­ +Comment[zh_TW]=標記方格 +default_presentation=0 + +[unmark] +Name=Unmark case +Name[be]=Здыманне пазнакі +Name[bg]=Премахване на флаг +Name[bn]=মাইন-ঠদেওয়া চিহà§à¦¨ মà§à¦›à§‡ ফেল +Name[bs]=OdznaÄi polje +Name[ca]=Desmarca casella +Name[cs]=ZruÅ¡it oznaÄení pole +Name[cy]=Dadmarcio cas +Name[da]=Afmarkér felt +Name[de]=Markierung entfernen +Name[el]=ΑναίÏεση σημείωσης ÎºÎ¿Ï…Ï„Î¹Î¿Ï +Name[eo]=Malmarki kazon +Name[es]=Desmarcar el caso +Name[et]=Eemaldab väljalt märgi +Name[eu]=Desmarkatu kasua +Name[fa]=نامشخص کردن موقعیت +Name[fi]=Poista merkki pelistä +Name[fr]=Ne plus marquer la case +Name[gl]=Desmarcar cadrado +Name[he]=הורד סימון מריבוע +Name[hi]=केस अचिहà¥à¤¨à¤¿à¤¤ करें +Name[hr]=Skini obilježje sa sluÄaja +Name[hu]=MezÅ‘ kijelölésének megszüntetése +Name[is]=Afmerkja tösku +Name[it]=Togli segno su casella +Name[ja]=マークを外ã—ãŸã¨ã +Name[km]=ករណី​មិន​សម្គាល់ +Name[lt]=AtžymÄ—ti langelį +Name[lv]=Noņemt marÄ·Ä“jumu +Name[mk]=Одобележано е поле +Name[nb]=Fjern merket +Name[nds]=Markeren wegdoon +Name[ne]=केसको चिनà¥à¤¹ हटाउनà¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Vakmarkering verwijderen +Name[nn]=Fjern merke +Name[pa]=ਅਣ-ਮਾਰਕ ਕੇਸ +Name[pl]=Pole odznaczone +Name[pt]=Desmarcar quadrado +Name[pt_BR]=Desmarcar quadrado +Name[ru]=СнÑтие отметки +Name[se]=Váldde mearkka eret +Name[sk]=OdstrániÅ¥ znaÄku poľa +Name[sl]=Odstrani oznako +Name[sr]=Скини обележје Ñа Ñлучаја +Name[sr@Latn]=Skini obeležje sa sluÄaja +Name[sv]=Avmarkera ruta +Name[ta]=நிகழà¯à®šà¯à®šà®¿à®¯à¯ˆ கà¯à®±à®¿à®•à¯à®•à®¾à®¤à¯‡ +Name[tg]=Гирифтани нишонаи ҳолат +Name[tr]=Kutudaki iÅŸareti kaldır +Name[uk]=ЗнÑÑ‚Ñ‚Ñ Ð¿Ð¾Ð¼Ñ–Ñ‚ÐºÐ¸ з комірки +Name[zh_CN]=ä¸æ ‡è®°ç›’å­ +Name[zh_TW]=去標記方格 +Comment=Unmark case +Comment[be]=Здыманне пазнакі +Comment[bg]=Премахване на флаг +Comment[bn]=মাইন-ঠদেওয়া চিহà§à¦¨ মà§à¦›à§‡ ফেল +Comment[bs]=OdznaÄi polje +Comment[ca]=Desmarca casella +Comment[cs]=ZruÅ¡it oznaÄení pole +Comment[cy]=Dadmarcio cas +Comment[da]=Afmarkér felt +Comment[de]=Markierung entfernen +Comment[el]=ΑναίÏεση σημείωσης ÎºÎ¿Ï…Ï„Î¹Î¿Ï +Comment[eo]=Malmarki kazon +Comment[es]=Desmarcar el caso +Comment[et]=Eemaldab väljalt märgi +Comment[eu]=Desmarkatu kasua +Comment[fa]=نامشخص کردن موقعیت +Comment[fi]=Poista merkki pelistä +Comment[fr]=Ne plus marquer la case +Comment[gl]=Desmarcar cadrado +Comment[he]=הורד סימון מריבוע +Comment[hi]=केस अचिहà¥à¤¨à¤¿à¤¤ करें +Comment[hr]=Skini obilježje sa sluÄaja +Comment[hu]=MezÅ‘ megjelölésének megszüntetése +Comment[is]=Afmerkja tösku +Comment[it]=Togli segno a casella +Comment[ja]=マークを外ã—ãŸã¨ã +Comment[km]=ករណី​មិន​សម្គាល់ +Comment[lt]=AtžymÄ—ti langelį +Comment[lv]=Noņemt marÄ·Ä“jumu +Comment[mk]=Одобележано е поле +Comment[nb]=Fjern merket +Comment[nds]=Markeren wegdoon +Comment[ne]=केसको चिनà¥à¤¹ हटाउनà¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Vakmarkering verwijderen +Comment[nn]=Fjern merke +Comment[pl]=Odznaczenie pola +Comment[pt]=Quadrado desmarcado +Comment[pt_BR]=Desmarcar quadrado +Comment[ru]=СнÑтие отметки +Comment[se]=Váldde eret mearkka +Comment[sk]=OdstrániÅ¥ znaÄku poľa +Comment[sl]=Odstrani oznako +Comment[sr]=Скини обележје Ñа Ñлучаја +Comment[sr@Latn]=Skini obeležje sa sluÄaja +Comment[sv]=Avmarkera ruta +Comment[ta]=நிகழà¯à®šà¯à®šà®¿à®¯à¯ˆ கà¯à®±à®¿à®•à¯à®•à®¾à®¤à¯‡ +Comment[tg]=Гирифтани нишонаи ҳолат +Comment[tr]=Kutudaki iÅŸareti kaldır +Comment[uk]=ЗнÑти помітку з комірки +Comment[zh_CN]=ä¸æ ‡è®°ç›’å­ +Comment[zh_TW]=去標記方格 +default_presentation=0 + +[explosion] +Name=Explosion +Name[ar]=انÙجار +Name[be]=Выбух +Name[bg]=ЕкÑÐ¿Ð»Ð¾Ð·Ð¸Ñ +Name[bn]=বিসà§à¦«à§‹à¦°à¦£ +Name[br]=Tarzhad +Name[bs]=Eksplozija +Name[ca]=Explosió +Name[cs]=Exploze +Name[cy]=Ffrwydriad +Name[da]=Eksplosion +Name[el]=ΈκÏηξη +Name[eo]=Eksplodo +Name[es]=Explosión +Name[et]=Plahvatus +Name[eu]=Eztanda +Name[fa]=انÙجار +Name[fi]=Räjähdys +Name[gl]=Estoupido +Name[he]=פיצוץ +Name[hi]=धमाका +Name[hr]=Eksplozija +Name[hu]=Robbanás +Name[is]=Sprenging +Name[it]=Esplosione +Name[ja]=爆発 +Name[km]=ការ​ផ្ទុះ +Name[ko]=í­ë°œ +Name[lt]=Sprogimas +Name[lv]=Eksplozija +Name[mk]=ЕкÑплозија +Name[nb]=Eksplosjon +Name[nds]=Exploschoon +Name[ne]=विसà¥à¤«à¥‹à¤Ÿ +Name[nl]=Explosie +Name[nn]=Eksplosjon +Name[pa]=ਧਮਾਕੇ +Name[pl]=Wybuch +Name[pt]=Explosão +Name[pt_BR]=Explosão +Name[ro]=Explozie +Name[ru]=Взрыв +Name[se]=EksploÅ¡uvdna +Name[sk]=Výbuch +Name[sl]=Eksplozija +Name[sr]=ЕкÑплозија +Name[sr@Latn]=Eksplozija +Name[ta]=அதிரà¯à®µà¯†à®Ÿà®¿ +Name[tg]=Таркиш +Name[tr]=Patlama +Name[uk]=Вибух +Name[uz]=Portlash +Name[uz@cyrillic]=Портлаш +Name[wa]=Esplôzion +Name[zh_CN]=爆炸 +Name[zh_TW]=爆炸 +Comment=Explosion +Comment[be]=Выбух +Comment[bg]=ЕкÑÐ¿Ð»Ð¾Ð·Ð¸Ñ +Comment[bn]=বিসà§à¦«à§‹à¦°à¦£ +Comment[br]=Tarzhad +Comment[bs]=Eksplozija +Comment[ca]=Explosió +Comment[cs]=Exploze +Comment[cy]=Ffrwydriad +Comment[da]=Eksplosion +Comment[el]=ΈκÏηξη +Comment[eo]=Eksplodo +Comment[es]=Explosión +Comment[et]=Plahvatus +Comment[eu]=Eztanda +Comment[fa]=انÙجار +Comment[fi]=Räjähdys +Comment[gl]=Estoupido +Comment[he]=פיצוץ +Comment[hi]=धमाका +Comment[hr]=Eksplozija +Comment[hu]=Robbanás +Comment[is]=Sprenging +Comment[it]=Esplosione +Comment[ja]=爆発 +Comment[km]=ការ​ផ្ទុះ +Comment[ko]=í­ë°œ +Comment[lt]=Sprogimas +Comment[lv]=Eksplozija +Comment[mk]=ЕкÑплозија +Comment[nb]=Eksplosjon +Comment[nds]=Exploschoon +Comment[ne]=विसà¥à¤«à¥‹à¤Ÿ +Comment[nl]=Explosie +Comment[nn]=Eksplosjon +Comment[pa]=ਧਮਾਕਾਖੇਜ਼ +Comment[pl]=Wybuch +Comment[pt]=Explosão +Comment[pt_BR]=Explosão +Comment[ro]=Explozie +Comment[ru]=Взрыв +Comment[se]=EksploÅ¡uvdna +Comment[sk]=Výbuch +Comment[sl]=Eksplozija +Comment[sr]=ЕкÑплозија +Comment[sr@Latn]=Eksplozija +Comment[ta]=அதிரà¯à®µà¯†à®Ÿà®¿ +Comment[tg]=Таркиш +Comment[tr]=Patlama +Comment[uk]=Вибух +Comment[uz]=Portlash +Comment[uz@cyrillic]=Портлаш +Comment[wa]=Esplôzion +Comment[zh_CN]=爆炸 +Comment[zh_TW]=爆炸 +default_presentation=0 + +[won] +Name=Game won +Name[ar]=ربحت اللعبة +Name[be]=Перамога +Name[bg]=Спечелихте +Name[bn]=খেলা জিতেছেন +Name[br]=Gounezet eo ar c'hoari +Name[bs]=Pobjeda +Name[ca]=Partida guanyada +Name[cs]=Vyhraná hra +Name[cy]=Gêm wedi ei ennill +Name[da]=Spillet vundet +Name[de]=Spiel gewonnen +Name[el]=Παιχνίδι κεÏδήθηκε +Name[eo]=Ludo venkita +Name[es]=Partida ganada +Name[et]=Mäng läbi, sina võitsid +Name[eu]=Jokoa irabazi da +Name[fa]=برد بازی +Name[fi]=Peli voitettu +Name[fr]=Partie gagnée +Name[gl]=Xogo gañado +Name[he]=ניצחת! +Name[hi]=खेल में जीत हà¥à¤ˆ +Name[hr]=Igra je dobivena +Name[hu]=GyÅ‘zelem +Name[is]=Leikur unninn +Name[it]=Partita vinta +Name[ja]=ゲームã«å‹ã¡ +Name[km]=ល្បែង​បាន​ឈ្នះ +Name[ko]=게임ì—ì„œ ì´ê¹€ +Name[lt]=Žaidimas laimÄ—tas +Name[lv]=SpÄ“le uzvarÄ“ta +Name[mk]=Играта е добиена +Name[nb]=Du vant +Name[nds]=Speel wunnen +Name[ne]=खेल जितà¥à¤¨à¥ भयो +Name[nl]=Spel gewonnen +Name[nn]=Du vann +Name[pa]=ਖੇਡ ਜਿੱਤੀ +Name[pl]=Gra wygrana +Name[pt]=Jogo ganho +Name[pt_BR]=Jogo ganho +Name[ro]=Joc cîştigat +Name[ru]=Победа +Name[se]=Don vuitet +Name[sk]=Vyhraná hra +Name[sl]=Igra je dobljena +Name[sr]=Игра је добијена +Name[sr@Latn]=Igra je dobijena +Name[sv]=Du vann spelet +Name[ta]=ஆடà¯à®Ÿà®®à¯ ஜெயிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Name[tg]=Дар бозӣ ғолиб омадед +Name[tr]=Oyun kazanıldı +Name[uk]=Гру виграно +Name[wa]=Djeu wangnî +Name[zh_CN]=æ‚¨èµ¢äº†æ¸¸æˆ +Name[zh_TW]=éŠæˆ²ç²å‹ +Comment=Game won +Comment[ar]=ربحت اللعبة +Comment[be]=Перамога +Comment[bg]=Спечелихте +Comment[bn]=খেল খতম +Comment[br]=Gounezet eo ar c'hoari +Comment[bs]=Pobjeda +Comment[ca]=Partida guanyada +Comment[cs]=Vyhraná hra +Comment[cy]=Gêm wedi ei ennill +Comment[da]=Spil vundet +Comment[de]=Spiel gewonnen +Comment[el]=Παιχνίδι κεÏδήθηκε +Comment[eo]=Ludo venkita +Comment[es]=Partida ganada +Comment[et]=Mäng läbi, sina võitsid +Comment[eu]=Jokoa irabazi da +Comment[fa]=برد بازی +Comment[fi]=Peli voitettu +Comment[fr]=Partie gagnée +Comment[gl]=Xogo gañado +Comment[he]=ניצחת! +Comment[hi]=खेल में जीत हà¥à¤ˆ +Comment[hr]=Igra je dobivena +Comment[hu]=GyÅ‘zelem +Comment[is]=Leikur unninn +Comment[it]=Partita vinta +Comment[ja]=ゲームã«å‹ã¡ +Comment[km]=ល្បែង​បាន​ឈ្នះ +Comment[ko]=게임ì—ì„œ ì´ê¹€ +Comment[lt]=Žaidimas laimÄ—tas +Comment[lv]=SpÄ“le ir uzvarÄ“ta +Comment[mk]=Играта е добиена +Comment[nb]=Du vant! +Comment[nds]=Speel wunnen +Comment[ne]=खेल जितà¥à¤¨à¥ भयो +Comment[nl]=Spel gewonnen +Comment[nn]=Du vann +Comment[pa]=ਖੇਡ ਜਿੱਤੀ +Comment[pl]=Gra wygrana +Comment[pt]=Jogo ganho +Comment[pt_BR]=Jogo ganho +Comment[ro]=Joc cîştigat +Comment[ru]=Победа +Comment[se]=Don vuitet +Comment[sk]=Vyhraná hra +Comment[sl]=Igra je dobljena +Comment[sr]=Игра је добијена +Comment[sr@Latn]=Igra je dobijena +Comment[sv]=Du vann spelet +Comment[ta]=ஆடà¯à®Ÿà®®à¯ ஜெயிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=Дар бозӣ ғолиб омадед +Comment[tr]=Oyun kazanıldı +Comment[uk]=Гру виграно +Comment[wa]=Djeu wangnî +Comment[zh_CN]=æ‚¨èµ¢äº†æ¸¸æˆ +Comment[zh_TW]=éŠæˆ²ç²å‹ +default_presentation=0 + +[lost] +Name=Game lost +Name[ar]=خسرت اللعبة +Name[be]=Параза +Name[bg]=Загубихте +Name[bn]=খেলায় হেরে গিয়েছেন +Name[br]=Kollet eo ar c'hoari +Name[bs]=Poraz +Name[ca]=Partida perduda +Name[cs]=Prohraná hra +Name[cy]=Gêm wedi ei golli +Name[da]=Spil tabt +Name[de]=Spiel verloren +Name[el]=Παιχνίδι χάθηκε +Name[eo]=Ludo malvenkita +Name[es]=Partida perdida +Name[et]=Mäng läbi, sina kaotasid +Name[eu]=Jokoa galdu da +Name[fa]=باخت بازی +Name[fi]=Peli hävitty +Name[fr]=Partie perdue +Name[gl]=Xogo perdido +Name[he]=המשחק הסתיי×, הפסדת +Name[hi]=खेल में हार हà¥à¤ˆ +Name[hr]=Igra je izgubljena +Name[hu]=Vereség +Name[is]=Leik tapað +Name[it]=Partita persa +Name[ja]=ゲームã«è² ã‘ +Name[km]=ល្បែង​បាន​ចាញ់ +Name[ko]=게임ì—ì„œ ì§ +Name[lt]=Žaidimas pralaimÄ—tas +Name[lv]=SpÄ“le zaudÄ“ta +Name[mk]=Играта е изгубена +Name[nb]=Du tapte +Name[nds]=Speel verloren +Name[ne]=खेल हारà¥à¤¨à¥ भयो +Name[nl]=Spel verloren +Name[nn]=Du tapte +Name[pa]=ਖੇਡ ਹਾਰੀ +Name[pl]=Koniec gry, przegraÅ‚eÅ› +Name[pt]=Jogo perdido +Name[pt_BR]=Jogo perdido +Name[ro]=Joc pierdut +Name[ru]=Поражение +Name[se]=Don vuoittohallet +Name[sk]=Prehraná hra +Name[sl]=Igra je izgubljena +Name[sr]=Игра је изгубљена +Name[sr@Latn]=Igra je izgubljena +Name[sv]=Du förlorade spelet +Name[ta]=ஆடà¯à®Ÿà®®à¯ இழகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Name[tg]=Дар бозӣ мағлуб шудед +Name[tr]=Oyun kaybedildi +Name[uk]=Гра програна +Name[wa]=Djeu pierdou +Name[zh_CN]=æ‚¨è¾“äº†æ¸¸æˆ +Name[zh_TW]=éŠæˆ²å¤±æ•— +Comment=Game lost +Comment[ar]=خسرت اللعبة +Comment[be]=Параза +Comment[bg]=Загубихте +Comment[bn]=খেলায় হেরে গিয়েছেন +Comment[br]=Koll eo ar c'hoari +Comment[bs]=Poraz +Comment[ca]=Partida perduda +Comment[cs]=Prohraná hra +Comment[cy]=Gêm wedi ei golli +Comment[da]=Spil tabt +Comment[de]=Spiel verloren +Comment[el]=Παιχνίδι χάθηκε +Comment[eo]=Ludo malvenkita +Comment[es]=Partida perdida +Comment[et]=Mäng läbi, sina kaotasid +Comment[eu]=Jokoa galdu da +Comment[fa]=باخت بازی +Comment[fi]=Peli hävitty +Comment[fr]=Partie perdue +Comment[gl]=Xogo perdido +Comment[he]=המשחק הסתיי×, הפסדת +Comment[hi]=खेल में हार हà¥à¤ˆ +Comment[hr]=Igra je izgubljena +Comment[hu]=Vereség +Comment[is]=Leik tapað +Comment[it]=Partita persa +Comment[ja]=ゲームã«è² ã‘ +Comment[km]=ល្បែង​បាន​ចាញ់ +Comment[ko]=게임ì—ì„œ ì§ +Comment[lt]=Žaidimas pralaimÄ—tas +Comment[lv]=SpÄ“le ir zaudÄ“ta +Comment[mk]=Играта е изубена +Comment[nb]=Du tapte +Comment[nds]=Speel verloren +Comment[ne]=खेल हारà¥à¤¨à¥ भयो +Comment[nl]=Spel verloren +Comment[nn]=Du tapte +Comment[pa]=ਖੇਡ ਹਾਰੀ +Comment[pl]=Koniec gry, przegraÅ‚eÅ› +Comment[pt]=Jogo perdido +Comment[pt_BR]=Jogo perdido +Comment[ro]=Joc pierdut +Comment[ru]=Поражение +Comment[se]=Don vuoittohallet +Comment[sk]=Prehraná hra +Comment[sl]=Igra je izgubljena +Comment[sr]=Игра је изгубљена +Comment[sr@Latn]=Igra je izgubljena +Comment[sv]=Du förlorade spelet +Comment[ta]=ஆடà¯à®Ÿà®®à¯ இழகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=Дар бозӣ мағлуб шудед +Comment[tr]=Oyun kaybedildi +Comment[uk]=Гра програна +Comment[wa]=Djeu pierdou +Comment[zh_CN]=æ‚¨è¾“äº†æ¸¸æˆ +Comment[zh_TW]=éŠæˆ²å¤±æ•— +default_presentation=0 + +[set_uncertain] +Name=Set question mark +Name[be]=Пазначыць пытальнікам +Name[bg]=ПоÑтавÑне на въпроÑителна +Name[bn]=পà§à¦°à¦¶à§à¦¨à¦¬à§‹à¦§à¦• চিহà§à¦¨ দিন +Name[bs]=Postavi upitnik +Name[ca]=Marca amb un interrogant +Name[cs]=Nastavit otazník +Name[cy]=Gosod gofynnod +Name[da]=Sæt spørgsmÃ¥lstegn +Name[de]=Markierung setzen +Name[el]=ΠÏοσθήκη εÏÏ‰Ï„Î·Î¼Î±Ï„Î¹ÎºÎ¿Ï +Name[eo]=Meti demandsignon +Name[es]=Establecer signo de interrogación +Name[et]=Pane küsimärk +Name[eu]=Ezarri galdera-marka +Name[fa]=گذاردن علامت سؤال +Name[fi]=Aseta kysymysmerkki +Name[fr]=Ajout d'un point d'interrogation +Name[he]=הצב סימן ש×לה +Name[hr]=Postavi oznaku pitanja +Name[hu]=KérdÅ‘jel beállítása +Name[is]=Setja spurningamerkið +Name[it]=Metti punto interrogativo +Name[ja]=クエスãƒãƒ§ãƒ³ãƒžãƒ¼ã‚¯ã‚’セットã™ã‚‹ +Name[km]=កំណážáŸ‹â€‹ážŸáž‰áŸ’ញា​សួរ +Name[lt]=UždÄ—ti klaustukÄ… +Name[lv]=Novietot jautÄjuma zÄ«mi +Name[mk]=ПоÑтавен е прашалник +Name[nb]=Angi spørsmÃ¥ltegn +Name[nds]=Fraagteken setten +Name[ne]=पà¥à¤°à¤¶à¥à¤¨ चिनà¥à¤¹ सेट गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Vraagteken instellen +Name[nn]=Set spørjeteikn +Name[pa]=ਸਵਾਲੀਆ ਨਿਸ਼ਾਨ ਬਣਾਓ +Name[pl]=Wstaw znak zapytania +Name[pt]=Colocar um ponto de interrogação +Name[pt_BR]=Marcar como ponto de interrogação +Name[ru]=Отметка вопроÑом +Name[se]=Bija jearaldatmearkka +Name[sk]=NastaviÅ¥ otáznik +Name[sl]=Postavi vpraÅ¡aj +Name[sr]=ПоÑтави знак питања +Name[sr@Latn]=Postavi znak pitanja +Name[sv]=Placerade frÃ¥getecken +Name[ta]=கேளà¯à®µà®¿à®•à¯à®•à¯à®±à®¿à®¯à¯ˆ அமை +Name[uk]=ПоÑтавити знак Ð¿Ð¸Ñ‚Ð°Ð½Ð½Ñ +Name[zh_CN]=è®¾ç½®äº†é—®å· +Name[zh_TW]=設定å•è™Ÿ +Comment=Set question mark +Comment[be]=Пазначыць пытальнікам +Comment[bg]=ПоÑтавÑне на въпроÑителна +Comment[bn]=পà§à¦°à¦¶à§à¦¨à¦¬à§‹à¦§à¦• চিহà§à¦¨ দিন +Comment[bs]=Postavi upitnik +Comment[ca]=Marca amb interrogant +Comment[cs]=Nastavit otazník +Comment[cy]=Gosod gofynnod +Comment[da]=Sæt spørgsmÃ¥lstegn +Comment[de]=Markierung setzen +Comment[el]=ΠÏοσθήκη εÏÏ‰Ï„Î·Î¼Î±Ï„Î¹ÎºÎ¿Ï +Comment[eo]=Meti demandsignon +Comment[es]=Establecer signo de interrogación +Comment[et]=Pane küsimärk +Comment[eu]=Ezarri galdera-marka +Comment[fa]=گذاردن علامت سؤال +Comment[fi]=Aseta kysymysmerkki +Comment[fr]=Ajout d'un point d'interrogation +Comment[he]=הצב סימן ש×לה +Comment[hr]=Postavi oznaku pitanja +Comment[hu]=KérdÅ‘jel beállítása +Comment[is]=Setja spurningamerkið +Comment[it]=Metti punto interrogativo +Comment[ja]=クエスãƒãƒ§ãƒ³ãƒžãƒ¼ã‚¯ã‚’セットã™ã‚‹ +Comment[km]=កំណážáŸ‹â€‹ážŸáž‰áŸ’ញា​សួរ +Comment[lt]=UždÄ—ti klaustukÄ… +Comment[lv]=Novieto jautÄjuma zÄ«mi +Comment[mk]=ПоÑтавен е прашалник +Comment[nb]=Angi spørsmÃ¥ltegn +Comment[nds]=Fraagteken setten +Comment[ne]=पà¥à¤°à¤¶à¥à¤¨ चिनà¥à¤¹ सेट गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Vraagteken instellen +Comment[nn]=Set spørjeteikn +Comment[pa]=ਸਵਾਲੀਆ ਨਿਸ਼ਾਨ ਦਿਓ +Comment[pl]=Wstawia znak zapytania +Comment[pt]=Colocar um ponto de interrogação +Comment[pt_BR]=Marcar como ponto de interrogação +Comment[ru]=Отметка вопроÑом +Comment[se]=Bija jearaldatmearkka +Comment[sk]=NastaviÅ¥ otáznik +Comment[sl]=Postavi vpraÅ¡aj +Comment[sr]=ПоÑтави знак питања +Comment[sr@Latn]=Postavi znak pitanja +Comment[sv]=Placerade frÃ¥getecken +Comment[ta]=கேளà¯à®µà®¿à®•à¯à®•à¯à®±à®¿à®¯à¯ˆ அமை +Comment[uk]=ПоÑтавити знак Ð¿Ð¸Ñ‚Ð°Ð½Ð½Ñ +Comment[zh_CN]=è®¾ç½®äº†é—®å· +Comment[zh_TW]=設定å•è™Ÿ +default_presentation=0 + +[unset_uncertain] +Name=Unset question mark +Name[be]=ЗнÑць пытальнік +Name[bg]=Премахване на въпроÑителна +Name[bn]=পà§à¦°à¦¶à§à¦¨à¦¬à§‹à¦§à¦• চিহà§à¦¨ সরিয়ে ফেলà§à¦¨ +Name[bs]=IskljuÄi upitnik +Name[ca]=Desmarca l'interrogant +Name[cs]=ZruÅ¡it nastavení otazníku +Name[cy]=Dadosod gofynnod +Name[da]=Fjern spørgsmÃ¥lstegn +Name[de]=Markierung entfernen +Name[el]=ΑφαίÏεση του εÏÏ‰Ï„Î·Î¼Î±Ï„Î¹ÎºÎ¿Ï +Name[eo]=Malmeti demandsignon +Name[es]=Quitar signo de interrogación +Name[et]=Eemalda küsimärk +Name[eu]=Kendu galdera-marka +Name[fa]=برداشتن علامت سؤال +Name[fi]=Poista kysymysmerkki +Name[fr]=Retrait d'un point d'interrogation +Name[he]=הורד סימן ש×לה +Name[hr]=Ukloni oznaku pitanja +Name[hu]=KérdÅ‘jel megszüntetése +Name[is]=Afsetja spurningamerkið +Name[it]=Togli punto interrogativo +Name[ja]=クエスãƒãƒ§ãƒ³ãƒžãƒ¼ã‚¯ã‚’外㙠+Name[km]=ដោះ​កំណážáŸ‹â€‹ážŸáž‰áŸ’ញា​សួរ +Name[lt]=Nuimti klaustukÄ… +Name[lv]=Noņemt jautajuma zÄ«mi +Name[mk]=ОтÑтранет е прашалник +Name[nb]=Fjern spørsmÃ¥ltegn +Name[nds]=Fraagteken wegmaken +Name[ne]=पà¥à¤°à¤¶à¥à¤¨ चिनà¥à¤¹ अनसेट गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Vraagteken weghalen +Name[nn]=Fjern spørjeteikn +Name[pa]=ਸਵਾਲੀਆ ਨਿਸ਼ਾਨ ਹਟਾਓ +Name[pl]=UsuÅ„ znak zapytania +Name[pt]=Retirar um ponto de interrogação +Name[pt_BR]=Desmarcar ponto de interrogação +Name[ru]=СнÑтие знака вопроÑа +Name[se]=Váldde jearaldatmearkka eret +Name[sk]=OdstrániÅ¥ otáznik +Name[sl]=Odstrani vpraÅ¡aj +Name[sr]=Скини знак питања +Name[sr@Latn]=Skini znak pitanja +Name[sv]=Tog bort frÃ¥getecken +Name[ta]=கேளà¯à®µà®¿à®•à¯ கà¯à®±à®¿ அமைகà¯à®•à®µà®¿à®²à¯à®²à¯ˆ +Name[tr]=Soru iÅŸaretini kaldır +Name[uk]=ЗнÑти знак Ð¿Ð¸Ñ‚Ð°Ð½Ð½Ñ +Name[zh_CN]=å–æ¶ˆäº†é—®å· +Name[zh_TW]=å–消å•è™Ÿ +Comment=Unset question mark +Comment[be]=ЗнÑць пытальнік +Comment[bg]=Премахване на въпроÑителна +Comment[bn]=পà§à¦°à¦¶à§à¦¨à¦¬à§‹à¦§à¦• চিহà§à¦¨ সরিয়ে ফেলà§à¦¨ +Comment[bs]=IskljuÄi upitnik +Comment[ca]=Desmarca l'interrogant +Comment[cs]=ZruÅ¡it nastavení otazníku +Comment[cy]=Dadosod gofynnod +Comment[da]=Fjern spørgsmÃ¥lstegn +Comment[de]=Markierung entfernen +Comment[el]=ΑφαίÏεση του εÏÏ‰Ï„Î·Î¼Î±Ï„Î¹ÎºÎ¿Ï +Comment[eo]=Malmeti demandsignon +Comment[es]=Quitar signo de interrogación +Comment[et]=Eemalda küsimärk +Comment[eu]=Kendu galdera-marka +Comment[fa]=برداشتن علامت سؤال +Comment[fi]=Poista kysymysmerkki +Comment[fr]=Retrait d'un point d'interrogation +Comment[he]=הורד סימן ש×לה +Comment[hr]=Ukloni oznaku pitanja +Comment[hu]=KérdÅ‘jel megszüntetése +Comment[is]=Afsetja spurningamerkið +Comment[it]=Togli punto interrogativo +Comment[ja]=クエスãƒãƒ§ãƒ³ãƒžãƒ¼ã‚¯ã‚’外㙠+Comment[km]=ដោះ​កំណážáŸ‹â€‹ážŸáž‰áŸ’ញា​សួរ +Comment[lt]=Nuimti klaustukÄ… +Comment[lv]=Noņem jautÄjuma zÄ«mi +Comment[mk]=ОтÑтранет е прашалник +Comment[nb]=Fjern spørsmÃ¥ltegn +Comment[nds]=Fraagteken wegmaken +Comment[ne]=पà¥à¤°à¤¶à¥à¤¨ चिनà¥à¤¹ अनसेट गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Vraagteken weghalen +Comment[nn]=Fjern spørjeteikn +Comment[pl]=Usuwa znak zapytania +Comment[pt]=Retirar um ponto de interrogação +Comment[pt_BR]=Desmarcar ponto de interrogação +Comment[ru]=СнÑтие знака вопроÑа +Comment[se]=Váldde jearaldatmearkka eret +Comment[sk]=OdstrániÅ¥ otáznik +Comment[sl]=Odstrani vpraÅ¡aj +Comment[sr]=Скини знак питања +Comment[sr@Latn]=Skini znak pitanja +Comment[sv]=Tog bort frÃ¥getecken +Comment[ta]=கேளà¯à®µà®¿à®•à¯à®•à¯à®±à®¿ நீகà¯à®•à¯ +Comment[tr]=Soru iÅŸaretini kaldır +Comment[uk]=ЗнÑти знак Ð¿Ð¸Ñ‚Ð°Ð½Ð½Ñ +Comment[zh_CN]=å–æ¶ˆäº†é—®å· +Comment[zh_TW]=å–消å•è™Ÿ +default_presentation=0 diff --git a/kmines/data/hi128-app-kmines.png b/kmines/data/hi128-app-kmines.png new file mode 100644 index 00000000..faaab0c2 Binary files /dev/null and b/kmines/data/hi128-app-kmines.png differ diff --git a/kmines/data/hi16-app-kmines.png b/kmines/data/hi16-app-kmines.png new file mode 100644 index 00000000..06585ce6 Binary files /dev/null and b/kmines/data/hi16-app-kmines.png differ diff --git a/kmines/data/hi22-app-kmines.png b/kmines/data/hi22-app-kmines.png new file mode 100644 index 00000000..fc1ae801 Binary files /dev/null and b/kmines/data/hi22-app-kmines.png differ diff --git a/kmines/data/hi32-app-kmines.png b/kmines/data/hi32-app-kmines.png new file mode 100644 index 00000000..10740280 Binary files /dev/null and b/kmines/data/hi32-app-kmines.png differ diff --git a/kmines/data/hi48-app-kmines.png b/kmines/data/hi48-app-kmines.png new file mode 100644 index 00000000..a1905746 Binary files /dev/null and b/kmines/data/hi48-app-kmines.png differ diff --git a/kmines/data/hi64-app-kmines.png b/kmines/data/hi64-app-kmines.png new file mode 100644 index 00000000..b4f5d27f Binary files /dev/null and b/kmines/data/hi64-app-kmines.png differ diff --git a/kmines/data/kmines.desktop b/kmines/data/kmines.desktop new file mode 100644 index 00000000..a74b3333 --- /dev/null +++ b/kmines/data/kmines.desktop @@ -0,0 +1,75 @@ +[Desktop Entry] +Name=KMines +Name[af]=Kmyne +Name[ar]=لعبة الألغام (KMines) +Name[be]=Сапёр +Name[bn]=কে-মাইনà§à¦¸ +Name[hi]=के-माइनà¥à¤¸ +Name[hr]=KMine +Name[ne]=केडीई बारूद +Name[pa]=ਕੇ-ਸਰà©à©°à¨— +Name[pl]=Miny +Name[pt_BR]=KMinas +Name[sv]=Kmines +Name[ta]=கேகனà¯à®©à®¿à®µà¯†à®Ÿà®¿à®•à®³à¯ +Name[tg]=KСапёр +Name[th]=à¸à¸¹à¹‰à¸£à¸°à¹€à¸šà¸´à¸” - K +Name[wa]=KMenes +Name[zh_TW]=KMines 踩地雷 +Icon=kmines +Exec=kmines -caption "%c" %i %m +Type=Application +DocPath=kmines/index.html +GenericName=Minesweeper-like Game +GenericName[be]=Ð“ÑƒÐ»ÑŒÐ½Ñ Ñž Ñапёра +GenericName[bg]=Мини +GenericName[bn]=মাইনসà§à¦‡à¦ªà¦¾à¦°-জাতীয় খেলা +GenericName[br]=C'hoari doare Minesweeper +GenericName[bs]=Igra minskog polja +GenericName[ca]=Joc a l'estil del buscamines +GenericName[cs]=Hra s minovým polem +GenericName[cy]=Gêm tebyg i Minesweeper +GenericName[da]=Minesøger-lignende spil +GenericName[de]=Minesweeper-ähnliches Spiel +GenericName[el]=Παιχνίδι παÏόμοιο με το ναÏκαλιευτή +GenericName[eo]="Minesweeper"-simila ludo +GenericName[es]=Juego similar al buscaminas +GenericName[et]=Miiniväljamäng +GenericName[eu]=Mina bilatzailearen antzeko jokoa +GenericName[fa]=بازی Minesweeper-like +GenericName[fi]=Kaivostyylinen peli +GenericName[fr]=Jeu dans le style du démineur +GenericName[he]=חיקוי שולה ×ž×•×§×©×™× +GenericName[hr]=Igra s poput Minesweepera +GenericName[hu]=AknakeresÅ‘ +GenericName[is]=Leikur sem líkist Minesweeper +GenericName[it]=Gioco simile a Mine +GenericName[ja]=地雷ゲーム +GenericName[km]=ល្បែង​ដូច Minesweeper +GenericName[ko]=지뢰찾기 게임 +GenericName[lt]=Minesweeper primenantis žaidimas +GenericName[lv]=Minesweeper lÄ«dzÄ«ga spÄ“le +GenericName[mk]=Игра Ñлична на Minesweeper +GenericName[nb]=Minesveiper-lignende spill +GenericName[nds]=Minesweeper-liek Speel +GenericName[ne]=बारूद हटाउने जसà¥à¤¤à¥ˆ खेल +GenericName[nl]=Mijnenveger-achtig spel +GenericName[nn]=Minesveipar-liknande spel +GenericName[pa]=ਸà©à¨°à©°à¨— ਹਟਾਓਣ ਵਰਗੀ ਖੇਡ +GenericName[pl]=Gra typu saper +GenericName[pt]=Jogo tipo Minas +GenericName[pt_BR]=Jogo parecido com Campo Minado +GenericName[ru]=Сапёр +GenericName[sk]=Hra typu Minesweeper +GenericName[sl]=Igra, podobna Minesweeperju +GenericName[sr]=Игра налик на миноловац +GenericName[sr@Latn]=Igra nalik na minolovac +GenericName[sv]=Minröjarliknande spel +GenericName[ta]=சà¯à®°à®™à¯à®•à®®à¯à®µà¯†à®Ÿà¯à®Ÿà¯à®®à¯ விளையாடà¯à®Ÿà¯ +GenericName[uk]=Гра в Ñапера +GenericName[wa]=On djeu di tchamp d' menes +GenericName[zh_CN]=扫雷 +GenericName[zh_TW]=類似踩地雷的éŠæˆ² +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;StrategyGame; diff --git a/kmines/defines.cpp b/kmines/defines.cpp new file mode 100644 index 00000000..b008de00 --- /dev/null +++ b/kmines/defines.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "defines.h" + +#include + +const char *Level::LABELS[NB_TYPES+1] = { + I18N_NOOP("Easy"), I18N_NOOP("Normal"), I18N_NOOP("Expert"), + I18N_NOOP("Custom") +}; + +const Level::Data Level::DATA[NB_TYPES] = { + { 8, 8, 10, "easy", "8x8x10", }, + {16, 16, 40, "normal", "16x16x40", }, + {30, 16, 99, "expert", "30x16x99", } +}; + +Level::Level(Type type) +{ + Q_ASSERT( type!=Custom ); + _width = DATA[type].width; + _height = DATA[type].height; + _nbMines = DATA[type].nbMines; +} + +Level::Level(uint width, uint height, uint nbMines) + : _width(width), _height(height), _nbMines(nbMines) +{ + Q_ASSERT( width>=2 && height>=2 ); + if (_nbMines > maxNbMines(width, height) ) + _nbMines = maxNbMines(width, height); +} + +Level::Type Level::type() const +{ + for (uint i=0; i + + +class Level +{ + public: + enum Type { Easy = 0, Normal, Expert, NB_TYPES, Custom = NB_TYPES }; + static const char *LABELS[NB_TYPES+1]; + struct Data { + uint width, height, nbMines; + const char *label, *wwLabel; + }; + static const Data DATA[NB_TYPES]; + + Level(Type); + Level(uint width, uint height, uint nbMines); + + uint width() const { return _width; } + uint height() const { return _height; } + uint nbMines() const { return _nbMines; } + Type type() const; + static uint maxNbMines(uint width, uint height) { return width*height - 2;} + + bool operator ==(const Level &level) const { + return ( _width==level._width && _height==level._height && + _nbMines==level._nbMines ); + } + + private: + uint _width, _height, _nbMines; +}; + +class KMines +{ + public: + enum GameState { Playing = 0, Paused, GameOver, Stopped, Replaying, + Init, NB_STATES }; + static const char *STATES[NB_STATES]; + enum SolvingState { Regular, Advised, Solved }; + + enum CaseState { Covered, Uncovered, Uncertain, Marked, Exploded, Error }; + struct Case { + bool mine; + CaseState state; + }; + + enum NumberColor { NB_N_COLORS = 8 }; + enum Mood { Normal = 0, Stressed, Happy, Sad, Sleeping, NbMoods }; +}; + +#endif diff --git a/kmines/dialogs.cpp b/kmines/dialogs.cpp new file mode 100644 index 00000000..d02b2eea --- /dev/null +++ b/kmines/dialogs.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (c) 1996-2003 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "dialogs.h" +#include "dialogs.moc" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" + +#include "bitmaps/smile" +#include "bitmaps/smile_happy" +#include "bitmaps/smile_ohno" +#include "bitmaps/smile_stress" +#include "bitmaps/smile_sleep" + + +//----------------------------------------------------------------------------- +const char **Smiley::XPM_NAMES[NbMoods] = { + smile_xpm, smile_stress_xpm, smile_happy_xpm, smile_ohno_xpm, + smile_sleep_xpm +}; + +void Smiley::setMood(Mood mood) +{ + QPixmap p(XPM_NAMES[mood]); + setPixmap(p); +} + +//----------------------------------------------------------------------------- +DigitalClock::DigitalClock(QWidget *parent) +: KGameLCDClock(parent, "digital_clock") +{ + setFrameStyle(Panel | Sunken); + setDefaultBackgroundColor(black); + setDefaultColor(white); +} + +KExtHighscore::Score DigitalClock::score() const +{ + KExtHighscore::Score score(KExtHighscore::Won); + score.setScore(3600 - seconds()); + score.setData("nb_actions", _nbActions); + return score; +} + +void DigitalClock::timeoutClock() +{ + KGameLCDClock::timeoutClock(); + + if ( _cheating || _customGame ) setColor(white); + else if ( _firstsetLabel(i18n("Width:")); + _width->setRange(minWidth, maxWidth); + connect(_width, SIGNAL(valueChanged(int)), SLOT(updateNbMines())); + top->addWidget(_width); + + _height = new KIntNumInput(this, "kcfg_CustomHeight"); + _height->setLabel(i18n("Height:")); + _height->setRange(minWidth, maxWidth); + connect(_height, SIGNAL(valueChanged(int)), SLOT(updateNbMines())); + top->addWidget(_height); + + _mines = new KIntNumInput(this, "kcfg_CustomMines"); + _mines->setLabel(i18n("No. of mines:")); + _mines->setRange(1, Level::maxNbMines(maxWidth, maxHeight)); + connect(_mines, SIGNAL(valueChanged(int)), SLOT(updateNbMines())); + top->addWidget(_mines); + + top->addSpacing(2 * KDialog::spacingHint()); + + // combo to choose level + QHBoxLayout *hbox = new QHBoxLayout(top); + QLabel *label = new QLabel(i18n("Choose level:"), this); + hbox->addWidget(label); + _gameType = new KComboBox(false, this); + connect(_gameType, SIGNAL(activated(int)), SLOT(typeChosen(int))); + for (uint i=0; i<=Level::NB_TYPES; i++) + _gameType->insertItem(i18n(Level::LABELS[i])); + hbox->addWidget(_gameType); + hbox->addWidget(new QWidget(this), 1); + + top->addStretch(1); +} + +void CustomConfig::updateNbMines() +{ + if (_block) return; + _block = true; + Level l(_width->value(), _height->value(), _mines->value()); + _mines->setRange(1, Level::maxNbMines(l.width(), l.height())); + _mines->setLabel(i18n("Mines (%1%):") + .arg( (100*l.nbMines()) / (l.width() * l.height()) )); + _gameType->setCurrentItem(l.type()); + _block = false; +} + +void CustomConfig::typeChosen(int i) +{ + if (_block) return; + _block = true; + Level::Type type = (Level::Type)i; + if ( type==Level::Custom ) { + Level level = Settings::customLevel(); + _width->setValue(level.width()); + _height->setValue(level.height()); + _mines->setRange(1, Level::maxNbMines(level.width(), level.height())); + _mines->setValue(level.nbMines()); + } else { + Level level(type); + _width->setValue(level.width()); + _height->setValue(level.height()); + _mines->setRange(1, Level::maxNbMines(level.width(), level.height())); + _mines->setValue(level.nbMines()); + } + _block = false; + updateNbMines(); +} + +//----------------------------------------------------------------------------- +static const char *MOUSE_BUTTON_LABELS[Settings::EnumButton::COUNT] = { + I18N_NOOP("Left button:"), I18N_NOOP("Middle button:"), + I18N_NOOP("Right button:") +}; + +static const char *MOUSE_CONFIG_NAMES[Settings::EnumButton::COUNT] = { + "kcfg_leftMouseAction", "kcfg_midMouseAction", + "kcfg_rightMouseAction" +}; + +static const char *MOUSE_ACTION_LABELS[Settings::EnumMouseAction::COUNT-1] = { + I18N_NOOP("Reveal"), I18N_NOOP("Autoreveal"), + I18N_NOOP("Toggle Flag"), I18N_NOOP("Toggle ? Flag") +}; + +GameConfig::GameConfig() + : QWidget(0, "game_config_widget"), _magicDialogEnabled(false) +{ + QVBoxLayout *top = new QVBoxLayout(this, KDialog::spacingHint()); + + QCheckBox *cb = new QCheckBox(i18n("Enable ? mark"), this, "kcfg_UncertainMark"); + top->addWidget(cb); + + cb = new QCheckBox(i18n("Enable keyboard"), this, "kcfg_KeyboardGame"); + top->addWidget(cb); + + cb = new QCheckBox(i18n("Pause if window loses focus"), this, "kcfg_PauseFocus"); + top->addWidget(cb); + + cb = new QCheckBox(i18n("\"Magic\" reveal"), this, "kcfg_MagicReveal"); + QWhatsThis::add(cb, i18n("Set flags and reveal squares where they are trivial.")); + connect(cb, SIGNAL(toggled(bool)), SLOT(magicModified(bool))); + top->addWidget(cb); + + top->addSpacing(2 * KDialog::spacingHint()); + + QHBoxLayout *hbox = new QHBoxLayout(top); + QVGroupBox *gb = new QVGroupBox(i18n("Mouse Bindings"), this); + hbox->addWidget(gb); + QGrid *grid = new QGrid(2, gb); + grid->setSpacing(KDialog::spacingHint()); + for (uint i=0; i< Settings::EnumButton::COUNT; i++) { + (void)new QLabel(i18n(MOUSE_BUTTON_LABELS[i]), grid); + QComboBox *cb = new QComboBox(false, grid, MOUSE_CONFIG_NAMES[i]); + for (uint k=0; k< (Settings::EnumMouseAction::COUNT-1); k++) + cb->insertItem(i18n(MOUSE_ACTION_LABELS[k])); + cb->setCurrentItem(i); + } + hbox->addStretch(1); + + top->addStretch(1); +} + +void GameConfig::magicModified(bool on) +{ + if ( !_magicDialogEnabled || !on ) return; + KMessageBox::information(this, i18n("When the \"magic\" reveal is on, you lose the ability to enter the highscores."), QString::null, "magic_reveal_warning"); +} + +//----------------------------------------------------------------------------- +static const char *COLOR_LABELS[Settings::EnumType::COUNT] = { + I18N_NOOP("Flag color:"), I18N_NOOP("Explosion color:"), + I18N_NOOP("Error color:") +}; + +static const char *COLOR_CONFIG_NAMES[Settings::EnumType::COUNT] = { + "kcfg_flagColor", "kcfg_explosionColor", "kcfg_errorColor" +}; + +static const char *N_COLOR_CONFIG_NAMES[KMines::NB_N_COLORS] = { + "kcfg_MineColor0", "kcfg_MineColor1", "kcfg_MineColor2", + "kcfg_MineColor3", "kcfg_MineColor4", "kcfg_MineColor5", + "kcfg_MineColor6", "kcfg_MineColor7" +}; + +AppearanceConfig::AppearanceConfig() + : QWidget(0, "appearance_config_widget") +{ + QVBoxLayout *top = new QVBoxLayout(this, KDialog::spacingHint()); + + QHBoxLayout *hbox = new QHBoxLayout(top); + QGrid *grid = new QGrid(2, this); + grid->setSpacing(KDialog::spacingHint()); + hbox->addWidget(grid); + for (uint i=0; isetFixedWidth(100); + } + for (uint i=0; isetFixedWidth(100); + } + hbox->addStretch(1); + + top->addStretch(1); +} + diff --git a/kmines/dialogs.h b/kmines/dialogs.h new file mode 100644 index 00000000..12dde5ac --- /dev/null +++ b/kmines/dialogs.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 1996-2003 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef DIALOGS_H +#define DIALOGS_H + +#include + +#include +#include + +#include "defines.h" +#include "settings.h" + +class KComboBox; +class KIntNumInput; + +//----------------------------------------------------------------------------- +class Smiley : public QPushButton, public KMines +{ + Q_OBJECT + public: + Smiley(QWidget *parent, const char *name = 0) + : QPushButton(QString::null, parent, name) {} + + public slots: + void setMood(Mood); + + private: + static const char **XPM_NAMES[NbMoods]; +}; + +//----------------------------------------------------------------------------- +class DigitalClock : public KGameLCDClock +{ + Q_OBJECT + public: + DigitalClock(QWidget *parent); + + void reset(bool customGame); + + bool cheating() const { return _cheating; } + uint nbActions() const { return _nbActions; } + KExtHighscore::Score score() const; + + public slots: + void start(); + void setCheating(); + void addAction() { _nbActions++; } + + private slots: + void timeoutClock(); + + private: + KExtHighscore::Score _first, _last; + uint _nbActions; + bool _cheating, _customGame; +}; + +//----------------------------------------------------------------------------- +class CustomConfig : public QWidget, public KMines +{ + Q_OBJECT + public: + CustomConfig(); + + static const uint maxWidth; + static const uint minWidth; + static const uint maxHeight; + static const uint minHeight; + + void init() { updateNbMines(); } + + private slots: + void typeChosen(int); + void updateNbMines(); + + private: + bool _block; + KIntNumInput *_width, *_height, *_mines; + KComboBox *_gameType; +}; + +//----------------------------------------------------------------------------- +class GameConfig : public QWidget, public KMines +{ + Q_OBJECT + public: + GameConfig(); + + static void saveLevel(Level::Type); + + void init() { _magicDialogEnabled = true; } + + private slots: + void magicModified(bool); + + private: + bool _magicDialogEnabled; + +}; + +class AppearanceConfig : public QWidget, public KMines +{ + Q_OBJECT + public: + AppearanceConfig(); +}; + +#endif diff --git a/kmines/field.cpp b/kmines/field.cpp new file mode 100644 index 00000000..1f3b3dfd --- /dev/null +++ b/kmines/field.cpp @@ -0,0 +1,462 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "field.h" +#include "field.moc" + +#include + +#include +#include +#include + +#include +#include + +#include "settings.h" +#include "solver/solver.h" +#include "dialogs.h" + + +using namespace KGrid2D; + +const Field::ActionData Field::ACTION_DATA[Nb_Actions] = { + { "Reveal", "reveal", I18N_NOOP("Case revealed") }, + { "AutoReveal", "autoreveal", I18N_NOOP("Case autorevealed") }, + { "SetFlag", "mark", I18N_NOOP("Flag set") }, + { "UnsetFlag", "unmark", I18N_NOOP("Flag unset") }, + { "SetUncertain", "set_uncertain", I18N_NOOP("Question mark set") }, + { "UnsetUncertain", "unset_uncertain", I18N_NOOP("Question mark unset") } +}; + +Field::Field(QWidget *parent) + : FieldFrame(parent), _state(Init), _solvingState(Regular), _level(Level::Easy) +{} + +void Field::readSettings() +{ + if ( inside(_cursor) ) { + QPainter p(this); + drawCase(p, _cursor); + } + if ( Settings::magicReveal() ) emit setCheating(); +} + +QSize Field::sizeHint() const +{ + return QSize(2*frameWidth() + _level.width()*Settings::caseSize(), + 2*frameWidth() + _level.height()*Settings::caseSize()); +} + +void Field::setLevel(const Level &level) +{ + _level = level; + reset(false); + adjustSize(); +} + +void Field::setReplayField(const QString &field) +{ + setState(Replaying); + initReplay(field); +} + +void Field::setState(GameState state) +{ + Q_ASSERT( state!=GameOver ); + emit gameStateChanged(state); + _state = state; +} + +void Field::reset(bool init) +{ + BaseField::reset(_level.width(), _level.height(), _level.nbMines()); + if ( init || _state==Init ) setState(Init); + else setState(Stopped); + if (Settings::magicReveal()) emit setCheating(); + _currentAction = Settings::EnumMouseAction::None; + _reveal = false; + _cursor.first = _level.width()/2; + _cursor.second = _level.height()/2; + _advisedCoord = Coord(-1, -1); + update(); +} + +void Field::paintEvent(QPaintEvent *e) +{ + QPainter painter(this); + drawFrame(&painter); + if ( _state==Paused ) return; + + Coord min = fromPoint(e->rect().topLeft()); + bound(min); + Coord max = fromPoint(e->rect().bottomRight()); + bound(max); + for (short i=min.first; i<=max.first; i++) + for (short j=min.second; j<=max.second; j++) + drawCase(painter, Coord(i,j)); +} + +void Field::changeCase(const Coord &p, CaseState newState) +{ + BaseField::changeCase(p, newState); + QPainter painter(this); + drawCase(painter, p); + if ( isActive() ) emit updateStatus( hasMine(p) ); +} + +QPoint Field::toPoint(const Coord &p) const +{ + QPoint qp; + qp.setX( p.first*Settings::caseSize() + frameWidth() ); + qp.setY( p.second*Settings::caseSize() + frameWidth() ); + return qp; +} + +Coord Field::fromPoint(const QPoint &qp) const +{ + double i = (double)(qp.x() - frameWidth()) / Settings::caseSize(); + double j = (double)(qp.y() - frameWidth()) / Settings::caseSize(); + return Coord((int)floor(i), (int)floor(j)); +} + +int Field::mapMouseButton(QMouseEvent *e) const +{ + switch (e->button()) { + case Qt::LeftButton: return Settings::mouseAction(Settings::EnumButton::left); + case Qt::MidButton: return Settings::mouseAction(Settings::EnumButton::mid); + case Qt::RightButton: return Settings::mouseAction(Settings::EnumButton::right); + default: return Settings::EnumMouseAction::ToggleFlag; + } +} + +void Field::revealActions(bool press) +{ + if ( _reveal==press ) return; // avoid flicker + _reveal = press; + + switch (_currentAction) { + case Reveal: + pressCase(_cursor, press); + break; + case AutoReveal: + pressClearFunction(_cursor, press); + break; + default: + break; + } +} + +void Field::mousePressEvent(QMouseEvent *e) +{ + if ( !isActive() || (_currentAction!=Settings::EnumMouseAction::None) ) return; + + emit setMood(Stressed); + _currentAction = mapMouseButton(e); + + Coord p = fromPoint(e->pos()); + if ( !inside(p) ) return; + placeCursor(p); + revealActions(true); +} + +void Field::mouseReleaseEvent(QMouseEvent *e) +{ + if ( !isActive() ) return; + + int tmp = _currentAction; + emit setMood(Normal); + revealActions(false); + int ma = mapMouseButton(e); + _currentAction = Settings::EnumMouseAction::None; + if ( ma!=tmp ) return; + + Coord p = fromPoint(e->pos()); + if ( !inside(p) ) return; + placeCursor(p); + + switch (ma) { + case Settings::EnumMouseAction::ToggleFlag: doMark(p); break; + case Settings::EnumMouseAction::ToggleUncertainFlag: doUmark(p); break; + case Settings::EnumMouseAction::Reveal: doReveal(p); break; + case Settings::EnumMouseAction::AutoReveal: doAutoReveal(p); break; + default: break; + } +} + +void Field::mouseMoveEvent(QMouseEvent *e) +{ + if ( !isActive() ) return; + + Coord p = fromPoint(e->pos()); + if ( p==_cursor ) return; // avoid flicker + + revealActions(false); + if ( !inside(p) ) return; + placeCursor(p); + revealActions(true); +} + +void Field::pressCase(const Coord &c, bool pressed) +{ + if ( state(c)==Covered ) { + QPainter painter(this); + drawCase(painter, c, pressed); + } +} + +void Field::pressClearFunction(const Coord &p, bool pressed) +{ + pressCase(p, pressed); + CoordList n = coveredNeighbours(p); + QPainter painter(this); + for (CoordList::const_iterator it=n.begin(); it!=n.end(); ++it) + drawCase(painter, *it, pressed); +} + +void Field::keyboardAutoReveal() +{ + _cursor_back = _cursor; + pressClearFunction(_cursor_back, true); + QTimer::singleShot(50, this, SLOT(keyboardAutoRevealSlot())); +} + +void Field::keyboardAutoRevealSlot() +{ + pressClearFunction(_cursor_back, false); + doAutoReveal(_cursor_back); +} + +void Field::doAutoReveal(const Coord &c) +{ + if ( !isActive() ) return; + if ( state(c)!=Uncovered ) return; + emit addAction(c, AutoReveal); + resetAdvised(); + doAction(AutoReveal, c, Settings::magicReveal()); +} + +void Field::pause() +{ + switch (_state) { + case Paused: setState(Playing); break; + case Playing: setState(Paused); break; + default: return; + } + update(); +} + +void Field::moveCursor(Neighbour n) +{ + Coord c = neighbour(_cursor, n); + if ( inside(c) ) placeCursor(c); +} + +void Field::moveToEdge(Neighbour n) +{ + Coord c = toEdge(_cursor, n); + if ( inside(c) ) placeCursor(c); +} + +bool Field::doReveal(const Coord &c, CoordList *autorevealed, + bool *caseUncovered) +{ + if ( !isActive() ) return true; + if ( state(c)!=Covered ) return true; + if ( firstReveal() ) setState(Playing); + CaseState state = + doAction(Reveal, c, Settings::magicReveal(), autorevealed, caseUncovered); + emit addAction(c, Reveal); + return ( state!=Error ); +} + +void Field::doMark(const Coord &c) +{ + if ( !isActive() ) return; + ActionType action; + CaseState oldState = state(c); + switch (oldState) { + case Covered: action = SetFlag; break; + case Marked: action = (Settings::uncertainMark() ? SetUncertain : UnsetFlag); break; + case Uncertain: action = UnsetUncertain; break; + default: return; + } + CaseState newState = doAction(action, c, Settings::magicReveal()); + addMarkAction(c, newState, oldState); +} + +void Field::doUmark(const Coord &c) +{ + if ( !isActive() ) return; + ActionType action; + CaseState oldState = state(c); + switch (oldState) { + case Covered: + case Marked: action = SetUncertain; break; + case Uncertain: action = UnsetUncertain; break; + default: return; + } + CaseState newState = doAction(action, c, Settings::magicReveal()); + addMarkAction(c, newState, oldState); +} + + +KMines::CaseState Field::doAction(ActionType type, const Coord &c, + bool complete, CoordList *autorevealed, + bool *caseUncovered) +{ + resetAdvised(); + CaseState state = Error; + if ( _solvingState==Solved ) complete = false; + + KNotifyClient::event(winId(), ACTION_DATA[type].event, + i18n(ACTION_DATA[type].eventMessage)); + switch (type) { + case Reveal: + if ( !reveal(c, autorevealed, caseUncovered) ) + emit gameStateChanged(GameOver); + else { + state = Uncovered; + if (complete) completeReveal(); + } + break; + case AutoReveal: + if ( !autoReveal(c, caseUncovered) ) + emit gameStateChanged(GameOver); + else { + state = Uncovered; + if (complete) completeReveal(); + } + break; + case SetFlag: + state = Marked; + if (complete) completeReveal(); + break; + case UnsetFlag: + case UnsetUncertain: + state = Covered; + break; + case SetUncertain: + state = Uncertain; + break; + case Nb_Actions: + Q_ASSERT(false); + break; + } + + if ( state!=Error ) changeCase(c, state); + return state; +} + +void Field::addMarkAction(const Coord &c, CaseState newS, CaseState oldS) +{ + switch (newS) { + case Marked: emit addAction(c, SetFlag); return; + case Uncertain: emit addAction(c, SetUncertain); return; + default: break; + } + switch (oldS) { + case Marked: emit addAction(c, UnsetFlag); return; + case Uncertain: emit addAction(c, UnsetUncertain); return; + default: break; + } +} + +void Field::placeCursor(const Coord &p) +{ + if ( !isActive() ) return; + + Q_ASSERT( inside(p) ); + Coord old = _cursor; + _cursor = p; + if ( Settings::keyboardGame() ) { + QPainter painter(this); + drawCase(painter, old); + drawCase(painter, _cursor); + } +} + +void Field::resetAdvised() +{ + if ( !inside(_advisedCoord) ) return; + QPainter p(this); + Coord tmp = _advisedCoord; + _advisedCoord = Coord(-1, -1); + drawCase(p, tmp); +} + +void Field::setAdvised(const Coord &c, double proba) +{ + resetAdvised(); + _solvingState = Advised; + _advisedCoord = c; + _advisedProba = proba; + if ( inside(c) ) { + QPainter p(this); + drawCase(p, c); + } +} + +void Field::drawCase(QPainter &painter, const Coord &c, bool pressed) const +{ + Q_ASSERT( inside(c) ); + + QString text; + uint nbMines = 0; + PixmapType type = NoPixmap; + + switch ( state(c) ) { + case Covered: + break; + case Marked: + type = FlagPixmap; + pressed = false; + break; + case Error: + type = ErrorPixmap; + pressed = true; + break; + case Uncertain: + text = '?'; + pressed = false; + break; + case Exploded: + type = ExplodedPixmap; + pressed = true; + break; + case Uncovered: + pressed = true; + if ( hasMine(c) ) type = MinePixmap; + else { + nbMines = nbMinesAround(c); + if (nbMines) text.setNum(nbMines); + } + } + + int i = -1; + if ( c==_advisedCoord ) { + if ( _advisedProba==1 ) i = 0; + else if ( _advisedProba>0.75 ) i = 1; + else if ( _advisedProba>0.5 ) i = 2; + else if ( _advisedProba>0.25 ) i = 3; + else i = 4; + } + + bool hasFocus = ( Settings::keyboardGame() && (c==_cursor) ); + drawBox(painter, toPoint(c), pressed, type, text, nbMines, i, hasFocus); +} diff --git a/kmines/field.h b/kmines/field.h new file mode 100644 index 00000000..32b959fe --- /dev/null +++ b/kmines/field.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef FIELD_H +#define FIELD_H + +#include "solver/bfield.h" +#include "frame.h" + + +//----------------------------------------------------------------------------- +class Field : public FieldFrame, public BaseField +{ + Q_OBJECT + public: + enum ActionType { Reveal = 0, AutoReveal, SetFlag, UnsetFlag, SetUncertain, + UnsetUncertain, Nb_Actions }; + struct ActionData { + const char *name, *event, *eventMessage; + }; + static const ActionData ACTION_DATA[Nb_Actions]; + + public: + Field(QWidget *parent); + + virtual QSize sizeHint() const; + + void setLevel(const Level &level); + void setReplayField(const QString &field); + const Level &level() const { return _level; } + void reset(bool init); + + GameState gameState() const { return _state; } + bool isActive() const { return _state!=Paused && _state!=GameOver; } + void pause(); + void setGameOver() { _state = GameOver; } + + void moveCursor(Neighbour); + void moveToEdge(Neighbour); + void doReveal() { doReveal(_cursor); } + void doMark() { doMark(_cursor); } + void doUmark() { doUmark(_cursor); } + void keyboardAutoReveal(); + CaseState doAction(ActionType type, const KGrid2D::Coord &c, + bool completeReveal, KGrid2D::CoordList *autorevealed = 0, + bool *caseUncovered = 0); + + void readSettings(); + + void setAdvised(const KGrid2D::Coord &c, double proba); + void setSolvingState(SolvingState state) { _solvingState = state; } + SolvingState solvingState() const { return _solvingState; } + + signals: + void updateStatus(bool); + void gameStateChanged(GameState); + void setMood(Mood); + void setCheating(); + void addAction(const KGrid2D::Coord &, Field::ActionType); + + protected: + void paintEvent(QPaintEvent *); + void mousePressEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + void mouseMoveEvent(QMouseEvent *); + + private slots: + void keyboardAutoRevealSlot(); + + private: + GameState _state; + bool _reveal; + SolvingState _solvingState; + KGrid2D::Coord _cursor, _cursor_back, _advisedCoord; + double _advisedProba; + int _currentAction; + Level _level; + + void pressCase(const KGrid2D::Coord &, bool); + void pressClearFunction(const KGrid2D::Coord &, bool); + void placeCursor(const KGrid2D::Coord &); + void revealActions(bool press); + + void doAutoReveal(const KGrid2D::Coord &); + bool doReveal(const KGrid2D::Coord &, KGrid2D::CoordList *autorevealed = 0, + bool *caseUncovered = 0); + void doMark(const KGrid2D::Coord &); + void doUmark(const KGrid2D::Coord &); + void changeCase(const KGrid2D::Coord &, CaseState newState); + void addMarkAction(const KGrid2D::Coord &, CaseState newS, CaseState oldS); + + QPoint toPoint(const KGrid2D::Coord &) const; + KGrid2D::Coord fromPoint(const QPoint &) const; + + void drawCase(QPainter &, const KGrid2D::Coord &, + bool forcePressed = false) const; + + int mapMouseButton(QMouseEvent *) const; + void resetAdvised(); + void setState(GameState); +}; + +#endif // FIELD_H diff --git a/kmines/frame.cpp b/kmines/frame.cpp new file mode 100644 index 00000000..a502b87d --- /dev/null +++ b/kmines/frame.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "frame.h" + +#include +#include +#include +#include + +#include "settings.h" + + +FieldFrame::FieldFrame(QWidget *parent) + : QFrame(parent, "field"), _button(0) +{ + setFrameStyle( QFrame::Box | QFrame::Raised ); + setLineWidth(2); + setMidLineWidth(2); +} + +void FieldFrame::adjustSize() +{ + setFixedSize(sizeHint()); + _button.resize(Settings::caseSize(), Settings::caseSize()); + + QBitmap mask; + for (uint i=0; i +#include +#include + +#include "defines.h" + +class QPainter; + +class FieldFrame : public QFrame, public KMines +{ + public: + FieldFrame(QWidget *parent); + + protected: + enum PixmapType { FlagPixmap = 0, MinePixmap, ExplodedPixmap, + ErrorPixmap, Nb_Pixmap_Types, + NoPixmap = Nb_Pixmap_Types }; + enum { Nb_Advised = 5 }; + + void drawBox(QPainter &, const QPoint &, bool pressed, + PixmapType, const QString &text, + uint nbMines, int advised, bool hasFocus) const; + virtual void adjustSize(); + + private: + QPushButton _button; + QPixmap _pixmaps[Nb_Pixmap_Types]; + QPixmap _advised[Nb_Advised]; + + void drawPixmap(QPixmap &, PixmapType, bool mask) const; + void drawAdvised(QPixmap &, uint i, bool mask) const; + void initPixmap(QPixmap &, bool mask) const; +}; + +#endif diff --git a/kmines/highscores.cpp b/kmines/highscores.cpp new file mode 100644 index 00000000..c0e40c10 --- /dev/null +++ b/kmines/highscores.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "highscores.h" + +#include +#include +#include +#include + +#include "version.h" +#include "defines.h" + + +namespace KExtHighscore +{ + +ExtManager::ExtManager() + : Manager(Level::NB_TYPES) +{ + setScoreType(MinuteTime); + setWWHighscores(KURL( HOMEPAGE ), SHORT_VERSION); + setShowStatistics(true); + const uint RANGE[16] = { 1, 3120, 3180, 3240, 3300, 3360, 3420, 3480, + 3510, 3540, 3550, 3560, 3570, 3580, 3590, 3600 }; + QMemArray s; + s.duplicate(RANGE, 16); + setScoreHistogram(s, ScoreBound); + + Item *item = new Item((uint)0, i18n("Clicks"), Qt::AlignRight); + addScoreItem("nb_actions", item); +} + +QString ExtManager::gameTypeLabel(uint gameType, LabelType type) const +{ + const Level::Data &data = Level::DATA[gameType]; + switch (type) { + case Icon: + case Standard: return data.label; + case I18N: return i18n(Level::LABELS[gameType]); + case WW: return data.wwLabel; + } + return QString::null; +} + +void ExtManager::convertLegacy(uint gameType) +{ + QString group; + switch ((Level::Type)gameType) { + case Level::Easy: group = "Easy level"; break; + case Level::Normal: group = "Normal level"; break; + case Level::Expert: group = "Expert level"; break; + case Level::NB_TYPES: Q_ASSERT(false); + } + + KConfigGroupSaver cg(kapp->config(), group); + QString name = cg.config()->readEntry("Name", QString::null); + if ( name.isNull() ) return; + if ( name.isEmpty() ) name = i18n("anonymous"); + uint minutes = cg.config()->readUnsignedNumEntry("Min", 0); + uint seconds = cg.config()->readUnsignedNumEntry("Sec", 0); + int score = 3600 - (minutes*60 + seconds); + if ( score<=0 ) return; + Score s(Won); + s.setScore(score); + s.setData("name", name); + submitLegacyScore(s); +} + +bool ExtManager::isStrictlyLess(const Score &s1, const Score &s2) const +{ + if ( s1.score()==s2.score() ) + // when time is same, favour more clicks (it means auto-reveal + // didn't help so much): + return s1.data("nb_actions").toUInt() +#include + +namespace KExtHighscore +{ + +class KDE_EXPORT ExtManager : public Manager +{ + public: + ExtManager(); + + private: + QString gameTypeLabel(uint gameTye, LabelType) const; + void convertLegacy(uint gameType); + bool isStrictlyLess(const Score &s1, const Score &s2) const; +}; + +} + +#endif diff --git a/kmines/kmines.kcfg b/kmines/kmines.kcfg new file mode 100644 index 00000000..382b6ccd --- /dev/null +++ b/kmines/kmines.kcfg @@ -0,0 +1,105 @@ + + + + + + + 4 + 100 + 20 + + + + 5 + 50 + 10 + + + + 5 + 50 + 10 + + + + 20 + + + + true + + + + false + + + + true + + + + false + + + + Easy + + + + + + + + + + + + + + + + + + + left + mid + right + + + + Reveal + AutoReveal + ToggleFlag + + + + + flag + explosion + error + + + + #ff0000 + + + + + #0000ff + #008800 + #888800 + #880088 + #ff0000 + #880000 + #000000 + #000000 + + + + true + + + diff --git a/kmines/kminesui.rc b/kmines/kminesui.rc new file mode 100644 index 00000000..cd1ef166 --- /dev/null +++ b/kmines/kminesui.rc @@ -0,0 +1,102 @@ + + + + + + &Move + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kmines/kzoommainwindow.cpp b/kmines/kzoommainwindow.cpp new file mode 100644 index 00000000..115d5175 --- /dev/null +++ b/kmines/kzoommainwindow.cpp @@ -0,0 +1,115 @@ +/* + This file is part of the KDE games library + Copyright (C) 2004 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kzoommainwindow.h" +#include "kzoommainwindow.moc" + +#include +#include +#include +#include + +KZoomMainWindow::KZoomMainWindow(uint min, uint max, uint step, const char *name) + : KMainWindow(0, name), _zoomStep(step), _minZoom(min), _maxZoom(max) +{ + installEventFilter(this); + + _zoomInAction = KStdAction::zoomIn(this, SLOT(zoomIn()), actionCollection()); + _zoomOutAction = + KStdAction::zoomOut(this, SLOT(zoomOut()), actionCollection()); + _menu = + KStdAction::showMenubar(this, SLOT(toggleMenubar()), actionCollection()); +} + +void KZoomMainWindow::init(const char *popupName) +{ + // zoom + setZoom(readZoomSetting()); + + // menubar + _menu->setChecked( menubarVisibleSetting() ); + toggleMenubar(); + + // context popup + if (popupName) { + QPopupMenu *popup = + static_cast(factory()->container(popupName, this)); + Q_ASSERT(popup); + if (popup) KContextMenuManager::insert(this, popup); + } +} + +void KZoomMainWindow::addWidget(QWidget *widget) +{ + widget->adjustSize(); + QWidget *tlw = widget->topLevelWidget(); + KZoomMainWindow *zm = + static_cast(tlw->qt_cast("KZoomMainWindow")); + Q_ASSERT(zm); + zm->_widgets.append(widget); + connect(widget, SIGNAL(destroyed()), zm, SLOT(widgetDestroyed())); +} + +void KZoomMainWindow::widgetDestroyed() +{ + _widgets.remove(static_cast(sender())); +} + +bool KZoomMainWindow::eventFilter(QObject *o, QEvent *e) +{ + if ( e->type()==QEvent::LayoutHint ) + setFixedSize(minimumSize()); // because K/QMainWindow + // does not manage fixed central widget + // with hidden menubar... + return KMainWindow::eventFilter(o, e); +} + +void KZoomMainWindow::setZoom(uint zoom) +{ + _zoom = zoom; + writeZoomSetting(_zoom); + QPtrListIterator it(_widgets); + for (; it.current(); ++it) + (*it)->adjustSize();; + _zoomOutAction->setEnabled( _zoom>_minZoom ); + _zoomInAction->setEnabled( _zoom<_maxZoom ); +} + +void KZoomMainWindow::zoomIn() +{ + setZoom(_zoom + _zoomStep); +} + +void KZoomMainWindow::zoomOut() +{ + Q_ASSERT( _zoom>=_zoomStep ); + setZoom(_zoom - _zoomStep); +} + +void KZoomMainWindow::toggleMenubar() +{ + if ( _menu->isChecked() ) menuBar()->show(); + else menuBar()->hide(); +} + +bool KZoomMainWindow::queryExit() +{ + writeMenubarVisibleSetting(_menu->isChecked()); + return KMainWindow::queryExit(); +} diff --git a/kmines/kzoommainwindow.h b/kmines/kzoommainwindow.h new file mode 100644 index 00000000..da8ec96c --- /dev/null +++ b/kmines/kzoommainwindow.h @@ -0,0 +1,126 @@ +/* + This file is part of the KDE games library + Copyright (C) 2004 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KZOOMMAINWINDOW_H +#define KZOOMMAINWINDOW_H + +#include + +class KToggleAction; + +/** + * KZoomMainWindow is a main window of fixed size. Its size can be + * modified with the "zoom in"/"zoom out" actions. + * + * It manages one or several widgets: their adjustSize() method is + * called whenever the zoom level is changed. + * The usual implementation for those widget is to redefine adjustSize() + * with code like: + * /code + * setFixedSize(newsize); + * /endcode + * + * This class also has a "show/hide menubar" action and allows the use + * of a context popup menu (useful to restore the menubar when hidden). + */ +class KZoomMainWindow : public KMainWindow +{ + Q_OBJECT +public: + /** Constructor. */ + KZoomMainWindow(uint minZoom, uint maxZoom, uint zoomStep, + const char *name = 0); + + /** Add a widget to be managed i.e. the adjustSize() method of the + * widget is called whenever the zoom is changed. + * This function assumes that the topLevelWidget() is the KZoomMainWindow. + */ + static void addWidget(QWidget *widget); + + uint zoom() const { return _zoom; } + +public slots: + void zoomIn(); + void zoomOut(); + void toggleMenubar(); + +protected: + /** You need to call this after the createGUI or setupGUI method + * is called. + * @param popupName is the name of the context popup menu as defined in + * the ui.rc file. + */ + void init(const char *popupName = 0); + + virtual void setZoom(uint zoom); + virtual bool eventFilter(QObject *o, QEvent *e); + virtual bool queryExit(); + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::setZoom(zoom); + * Settings::writeConfig(); + * /endcode + */ + virtual void writeZoomSetting(uint zoom) = 0; + + /** Youneed to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * return Settings::zoom(); + * /endcode + */ + virtual uint readZoomSetting() const = 0; + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::setMenubarVisible(visible); + * Settings::writeConfig(); + * /endcode + */ + virtual void writeMenubarVisibleSetting(bool visible) = 0; + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::menubarVisible(); + * /endcode + */ + virtual bool menubarVisibleSetting() const = 0; + +private slots: + void widgetDestroyed(); + +private: + uint _zoom, _zoomStep, _minZoom, _maxZoom; + QPtrList _widgets; + KAction *_zoomInAction, *_zoomOutAction; + KToggleAction *_menu; + + class KZoomMainWindowPrivate; + KZoomMainWindowPrivate *d; +}; + +#endif diff --git a/kmines/main.cpp b/kmines/main.cpp new file mode 100644 index 00000000..52620887 --- /dev/null +++ b/kmines/main.cpp @@ -0,0 +1,260 @@ +/* + * Copyright (c) 1996-2004 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "main.h" +#include "main.moc" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "status.h" +#include "highscores.h" +#include "version.h" +#include "dialogs.h" + +const MainWidget::KeyData MainWidget::KEY_DATA[NB_KEYS] = { +{I18N_NOOP("Move Up"), "keyboard_moveup", Key_Up, SLOT(moveUp())}, +{I18N_NOOP("Move Down"), "keyboard_movedown", Key_Down, SLOT(moveDown())}, +{I18N_NOOP("Move Right"), "keyboard_moveright", Key_Right, SLOT(moveRight())}, +{I18N_NOOP("Move Left"), "keyboard_moveleft", Key_Left, SLOT(moveLeft())}, +{I18N_NOOP("Move at Left Edge"), "keyboard_leftedge", Key_Home, SLOT(moveLeftEdge())}, +{I18N_NOOP("Move at Right Edge"), "keyboard_rightedge", Key_End, SLOT(moveRightEdge())}, +{I18N_NOOP("Move at Top Edge"), "keyboard_topedge", Key_PageUp, SLOT(moveTop())}, +{I18N_NOOP("Move at Bottom Edge"), "keyboard_bottomedge", Key_PageDown, SLOT(moveBottom())}, +{I18N_NOOP("Reveal Mine"), "keyboard_revealmine", Key_Space, SLOT(reveal())}, +{I18N_NOOP("Mark Mine"), "keyboard_markmine", Key_W, SLOT(mark())}, +{I18N_NOOP("Automatic Reveal"), "keyboard_autoreveal", Key_Return, SLOT(autoReveal())} +}; + + +MainWidget::MainWidget() + : KZoomMainWindow(4, 100, 1, "kmines") +{ + KNotifyClient::startDaemon(); + + _status = new Status(this); + connect(_status, SIGNAL(gameStateChangedSignal(KMines::GameState)), + SLOT(gameStateChanged(KMines::GameState))); + connect(_status, SIGNAL(pause()), SLOT(pause())); + + // Game & Popup + KStdGameAction::gameNew(_status, SLOT(restartGame()), actionCollection()); + _pause = KStdGameAction::pause(_status, SLOT(pauseGame()), + actionCollection()); + KStdGameAction::highscores(this, SLOT(showHighscores()), + actionCollection()); + KStdGameAction::quit(qApp, SLOT(quit()), actionCollection()); + + // keyboard + _keybCollection = new KActionCollection(this); + for (uint i=0; isetItems(list); + connect(_levels, SIGNAL(activated(int)), _status, SLOT(newGame(int))); + + // Adviser + _advise = + KStdGameAction::hint(_status, SLOT(advise()), actionCollection()); + _solve = KStdGameAction::solve(_status, SLOT(solve()), actionCollection()); + (void)new KAction(i18n("Solving Rate..."), 0, _status, SLOT(solveRate()), + actionCollection(), "solve_rate"); + + // Log + (void)new KAction(KGuiItem(i18n("View Log"), "viewmag"), 0, + _status, SLOT(viewLog()), + actionCollection(), "log_view"); + (void)new KAction(KGuiItem(i18n("Replay Log"), "player_play"), + 0, _status, SLOT(replayLog()), + actionCollection(), "log_replay"); + (void)new KAction(KGuiItem(i18n("Save Log..."), "filesave"), 0, + _status, SLOT(saveLog()), + actionCollection(), "log_save"); + (void)new KAction(KGuiItem(i18n("Load Log..."), "fileopen"), 0, + _status, SLOT(loadLog()), + actionCollection(), "log_load"); + + setupGUI( KMainWindow::Save | Create ); + readSettings(); + setCentralWidget(_status); + init("popup"); + addWidget(_status->field()); +} + +bool MainWidget::queryExit() +{ + _status->checkBlackMark(); + return KZoomMainWindow::queryExit(); +} + +void MainWidget::readSettings() +{ + settingsChanged(); + Level::Type type = (Level::Type) Settings::level(); + _levels->setCurrentItem(type); + _status->newGame(type); +} + +void MainWidget::showHighscores() +{ + KExtHighscore::show(this); +} + +void MainWidget::focusOutEvent(QFocusEvent *e) +{ + if ( Settings::pauseFocus() && e->reason()==QFocusEvent::ActiveWindow + && _status->isPlaying() ) pause(); + KMainWindow::focusOutEvent(e); +} + +void MainWidget::configureSettings() +{ + if ( KConfigDialog::showDialog("settings") ) return; + + KConfigDialog *dialog = new KConfigDialog(this, "settings", Settings::self()); + GameConfig *gc = new GameConfig; + dialog->addPage(gc, i18n("Game"), "package_system"); + dialog->addPage(new AppearanceConfig, i18n("Appearance"), "style"); + CustomConfig *cc = new CustomConfig; + dialog->addPage(cc, i18n("Custom Game"), "package_settings"); + connect(dialog, SIGNAL(settingsChanged()), SLOT(settingsChanged())); + dialog->show(); + cc->init(); + gc->init(); +} + +void MainWidget::configureHighscores() +{ + KExtHighscore::configure(this); +} + +void MainWidget::settingsChanged() +{ + bool enabled = Settings::keyboardGame(); + QValueList list = _keybCollection->actions(); + QValueList::Iterator it; + for (it = list.begin(); it!=list.end(); ++it) + (*it)->setEnabled(enabled); + _status->settingsChanged(); +} + +void MainWidget::configureKeys() +{ + KKeyDialog d(true, this); + d.insert(_keybCollection, i18n("Keyboard game")); + d.insert(actionCollection(), i18n("General")); + d.configure(); +} + +void MainWidget::configureNotifications() +{ + KNotifyDialog::configure(this); +} + +void MainWidget::gameStateChanged(KMines::GameState state) +{ + stateChanged(KMines::STATES[state]); + if ( state==Playing ) setFocus(); +} + +void MainWidget::pause() +{ + _pause->activate(); +} + +void MainWidget::writeZoomSetting(uint zoom) +{ + Settings::setCaseSize(zoom); + Settings::writeConfig(); +} + +uint MainWidget::readZoomSetting() const +{ + return Settings::caseSize(); +} + +void MainWidget::writeMenubarVisibleSetting(bool visible) +{ + Settings::setMenubarVisible(visible); + Settings::writeConfig(); +} + +bool MainWidget::menubarVisibleSetting() const +{ + return Settings::menubarVisible(); +} + +//---------------------------------------------------------------------------- +static const char *DESCRIPTION + = I18N_NOOP("KMines is a classic mine sweeper game"); + +int main(int argc, char **argv) +{ + KHighscore::init("kmines"); + + KAboutData aboutData("kmines", I18N_NOOP("KMines"), LONG_VERSION, + DESCRIPTION, KAboutData::License_GPL, + COPYLEFT, 0, HOMEPAGE); + aboutData.addAuthor("Nicolas Hadacek", 0, EMAIL); + aboutData.addCredit("Andreas Zehender", I18N_NOOP("Smiley pixmaps")); + aboutData.addCredit("Mikhail Kourinny", I18N_NOOP("Solver/Adviser")); + aboutData.addCredit("Thomas Capricelli", I18N_NOOP("Magic reveal mode")); + KCmdLineArgs::init(argc, argv, &aboutData); + + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + KExtHighscore::ExtManager manager; + + if ( a.isRestored() ) RESTORE(MainWidget) + else { + MainWidget *mw = new MainWidget; + mw->show(); + } + return a.exec(); +} diff --git a/kmines/main.h b/kmines/main.h new file mode 100644 index 00000000..21ec28b1 --- /dev/null +++ b/kmines/main.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef MAIN_H +#define MAIN_H + +#include "kzoommainwindow.h" + +#include "defines.h" + +class KAction; +class KToggleAction; +class KSelectAction; +class Status; + +class MainWidget : public KZoomMainWindow, public KMines +{ + Q_OBJECT + public: + MainWidget(); + + private slots: + void configureKeys(); + void configureSettings(); + void configureNotifications(); + void configureHighscores(); + void gameStateChanged(KMines::GameState); + void showHighscores(); + void settingsChanged(); + void pause(); + + protected: + virtual void focusOutEvent(QFocusEvent *); + virtual bool queryExit(); + + private: + Status *_status; + KToggleAction *_pause; + KSelectAction *_levels; + KAction *_advise, *_solve; + KActionCollection *_keybCollection; + + struct KeyData { + const char *label, *name; + Qt::Key keycode; + const char *slot; + }; + enum Key { NB_KEYS = 11 }; + static const KeyData KEY_DATA[NB_KEYS]; + + void readSettings(); + virtual void writeZoomSetting(uint zoom); + virtual uint readZoomSetting() const; + virtual void writeMenubarVisibleSetting(bool visible); + virtual bool menubarVisibleSetting() const; +}; + +#endif diff --git a/kmines/settings.kcfgc b/kmines/settings.kcfgc new file mode 100644 index 00000000..c863f41a --- /dev/null +++ b/kmines/settings.kcfgc @@ -0,0 +1,7 @@ +# Code generation options for kconfig_compiler +File=kmines.kcfg +IncludeFiles=defines.h +ClassName=Settings +Singleton=true +Mutators=level,MenubarVisible,CaseSize +CustomAdditions=true diff --git a/kmines/settings_addons.h b/kmines/settings_addons.h new file mode 100644 index 00000000..59be1ac1 --- /dev/null +++ b/kmines/settings_addons.h @@ -0,0 +1,5 @@ +public: + static Level customLevel() + { + return Level( self()->mCustomWidth, self()->mCustomHeight, self()->mCustomMines ); + } diff --git a/kmines/solver/Makefile.am b/kmines/solver/Makefile.am new file mode 100644 index 00000000..8a6696a7 --- /dev/null +++ b/kmines/solver/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = -I$(top_srcdir)/kmines/generic -I$(top_srcdir)/kmines -I$(top_srcdir)/libkdegames $(all_includes) + +noinst_LTLIBRARIES = libsolver.la +noinst_HEADERS = bfield.h solver.h headerP.h adviseFast.h adviseFull.h +libsolver_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) -no-undefined +libsolver_la_SOURCES = bfield.cpp solver.cpp advFastRules.cpp adviseFast.cpp \ + adviseFull.cpp +METASOURCES = solver.moc + +check_PROGRAMS = test testFast testSolve testRate +test_SOURCES = test.cpp +test_LDADD = ./libsolver.la $(LIB_KDECORE) +testFast_SOURCES = testFast.cpp +testFast_LDADD = ./libsolver.la $(LIB_KDECORE) +testSolve_SOURCES = testSolve.cpp +testSolve_LDADD = ./libsolver.la $(LIB_KDECORE) $(LIB_KDEUI) +testRate_SOURCES = testRate.cpp +testRate_LDADD = ./libsolver.la $(LIB_KDECORE) $(LIB_KDEUI) diff --git a/kmines/solver/advFastRules.cpp b/kmines/solver/advFastRules.cpp new file mode 100644 index 00000000..79c42bba --- /dev/null +++ b/kmines/solver/advFastRules.cpp @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include "adviseFast.h" + +using std::set; + +AdviseFast::RuleSet::RuleSet(FactSet *f) : + facts(f) +{ + FactSet::iterator i; + for(i=facts->begin(); i!=facts->end(); ++i) + addGeneral(i->first); +} + +AdviseFast::RuleSet::~RuleSet(){ +} + +void AdviseFast::RuleSet::addRule(Entry const &entry) +{ + _rules.insert(entry); +} + +bool AdviseFast::RuleSet::getSurePoint(Coord *sp) +{ + if(_surePoints.empty()){ + if(!apply()) return false; + } + + CoordSet::iterator i = _surePoints.begin(); + *sp = *i; + _surePoints.erase(i); + + return true; +} + +bool AdviseFast::RuleSet::reveal(Coord what) +{ + CoordSet affectedFacts; + if(!facts->reveal(what, &affectedFacts)) + // OOPS :( + return false; + + CoordSet::iterator i; + for( i = affectedFacts.begin(); + i != affectedFacts.end(); + ++i) + this->addGeneral(*i); + + return true; +} + +void AdviseFast::RuleSet::solve() +{ + Coord p; + while(getSurePoint(&p)) { + bool res = reveal(p); + assert(res); + Q_UNUSED(res); + } +} + +bool AdviseFast::RuleSet::apply() +{ + while(!_rules.empty()){ + set::iterator i = _rules.begin(); + std::auto_ptr r (this->newRule(*i)); + _rules.erase(i); + + if(r->apply(&this->_surePoints)) return true; + } + + return false; +} + +AdviseFast::Rule * +AdviseFast::RuleSet::newRule(Entry const &e){ + CoordSet::const_iterator i = e.second.begin(); + Coord p, p1; + switch(e.first){ + case EMPTY: + assert(e.second.size() == 1); + return new EmptyRule(*i, this); + + case FULL: + assert(e.second.size() == 1); + return new FullRule(*i, this); + + case INCLUDE: + assert(e.second.size() == 2); + p = *i; ++i; p1 = *i; + return new InclusionRule(p, p1, this); + + case INCLUDE1: + assert(e.second.size() == 2); + p = *i; ++i; p1 = *i; + return new InclusionRule(p1, p, this); + + case INTERSECT: + assert(e.second.size() == 2); + p = *i; ++i; p1 = *i; + return new IntersectionRule(p, p1, this); + + case INTERSECT1: + assert(e.second.size() == 2); + p = *i; ++i; p1 = *i; + return new IntersectionRule(p1, p, this); + + case GENERAL: + assert(e.second.size() == 1); + return new GeneralRule(*i, this); + + default: + assert(false); + } + + // Make compiler happy + return 0; +} + +void AdviseFast::RuleSet::removeRef(Coord p){ + set::iterator i, j; + + for( i = j = _rules.begin(); + i != _rules.end(); + i = j) + { + ++j; + if(i->second.count(p)) _rules.erase(i); + } +} + +void AdviseFast::RuleSet::addGeneral(Coord p){ + this->removeRef(p); + Entry e; + e.first = GENERAL; + e.second.insert(p); + this->addRule(e); +} + +#if defined(DEBUG) && DEBUG >= 2 +int AdviseFast::Rule::leaks = 0; +#endif + +AdviseFast::Rule::Rule(RuleSet *parent) : + _parent(parent), + _facts(parent->facts) +{ +#if defined(DEBUG) && DEBUG >= 2 + cout << "Rule::Rule, leaks = " << ++leaks << endl; +#endif +} + +AdviseFast::Rule::~Rule() +{ +#if defined(DEBUG) && DEBUG >= 2 + cout << "Rule::~Rule, leaks = " << --leaks << endl; +#endif +} + +AdviseFast::GeneralRule::GeneralRule( + Coord fact, + RuleSet *parent) : + Rule(parent), + _fact(fact) +{} + +bool AdviseFast::GeneralRule::apply(CoordSet *) +{ + +#if defined(DEBUG) && DEBUG >= 2 + operator <<( + cout << "Applying general rule ", + _fact) << endl; +#endif + + // Return if there's no more such fact + if(!_facts->count(_fact)) return false; + Fact const &f = (*_facts)[_fact]; + +#if defined(DEBUG) && DEBUG >= 2 + cout << f << endl; +#endif + + // Insert intersection rules first + // relatedFacts -- facts which have non-zero intersection + CoordSet relatedFacts; + { + CoordSet::const_iterator i; + for( i=f.pointSet.begin(); + i!=f.pointSet.end(); + ++i){ + + CoordSet const & ps = + *_facts->getContainingFacts(*i); + relatedFacts.insert( + ps.begin(), ps.end()); + } + } + relatedFacts.erase(_fact); // ;) + + CoordSet::iterator i; + for( i=relatedFacts.begin(); + i!=relatedFacts.end(); + ++i) + { + RuleSet::Entry e; + e.second.insert(_fact); + e.second.insert(*i); + + e.first = RuleSet::INTERSECT1; _parent->addRule(e); + e.first = RuleSet::INTERSECT; _parent->addRule(e); + e.first = RuleSet::INCLUDE1; _parent->addRule(e); + e.first = RuleSet::INCLUDE; _parent->addRule(e); + } + + // Now simple rules, so that they appear first in the list + RuleSet::Entry e; e.second.insert(_fact); + e.first = RuleSet::FULL; _parent->addRule(e); + e.first = RuleSet::EMPTY; _parent->addRule(e); + + // No point revealed, so... + return false; +} + +AdviseFast::EmptyRule::EmptyRule( + Coord fact, + RuleSet *parent) : + Rule(parent), + _fact(fact) +{} + +bool AdviseFast::EmptyRule::apply( + CoordSet *surePoints) +{ + +#if defined(DEBUG) && DEBUG >= 2 + operator <<( + cout << "Applying empty rule ", + _fact) << endl; +#endif + + if(!_facts->count(_fact)) return false; + Fact const &f = (*_facts)[_fact]; + +#if defined(DEBUG) && DEBUG >= 2 + cout << f << endl; +#endif + + // FactSet does not contain empty facts!! + assert(!f.pointSet.empty()); + + // If there are mines around, alas :( + if(f.mines) return false; + +#if defined(DEBUG) && DEBUG >= 2 + cout << "succeeded!" << endl; +#endif + + surePoints->insert( + f.pointSet.begin(), + f.pointSet.end()); + + _parent->removeRef(_fact); + + return true; +} + +AdviseFast::FullRule::FullRule( + Coord fact, + RuleSet *parent) : + Rule(parent), + _fact(fact) +{} + +bool AdviseFast::FullRule::apply( + CoordSet */*surePoints*/) +{ + +#if defined(DEBUG) && DEBUG >= 2 + operator <<( + cout << "Applying full rule ", + _fact) << endl; +#endif + + if(!_facts->count(_fact)) return false; + Fact f = (*_facts)[_fact]; + +#if defined(DEBUG) && DEBUG >= 2 + cout << f << endl; +#endif + + // FactSet does not contain empty facts!! + assert(!f.pointSet.empty()); + + // The point set is not full of mines... :( + if(f.mines != (int)f.pointSet.size()) return false; + +#if defined(DEBUG) && DEBUG >= 2 + cout << "succeeded!" << endl; +#endif + + CoordSet affectedFacts; + CoordSet::iterator i; + for( i=f.pointSet.begin(); + i!=f.pointSet.end(); + ++i) + _facts->mark(*i, &affectedFacts); + + for( i=affectedFacts.begin(); + i!=affectedFacts.end(); + ++i) + _parent->addGeneral(*i); + _parent->removeRef(_fact); + + // No mines revealed + return false; +} + +AdviseFast::InclusionRule::InclusionRule( + Coord bigger, Coord smaller, + RuleSet *parent) : + Rule(parent), + _bigger(bigger), _smaller(smaller) +{} + +bool AdviseFast::InclusionRule::apply( + CoordSet */*surePoints*/) +{ + +#if defined(DEBUG) && DEBUG >= 2 + cout << "Applying inclusion rule "; + operator <<(cout, _bigger) << ' '; + operator <<(cout, _smaller) << endl; +#endif + + if(!_facts->count(_bigger)) return false; + if(!_facts->count(_smaller)) return false; + + Fact b = (*_facts)[_bigger]; + Fact s = (*_facts)[_smaller]; + +#if defined(DEBUG) && DEBUG >= 2 + cout << b << endl << s << endl; +#endif + + assert(!s.pointSet.empty()); + + CoordSet diff; + set_difference( + s.pointSet.begin(), + s.pointSet.end(), + b.pointSet.begin(), + b.pointSet.end(), + inserter(diff, diff.begin())); + if(!diff.empty()) + // That is s is not included in b + return false; + +#if defined(DEBUG) && DEBUG >= 2 + cout << "succeeded!" << endl; +#endif + + diff.clear(); + set_difference( + b.pointSet.begin(), + b.pointSet.end(), + s.pointSet.begin(), + s.pointSet.end(), + inserter(diff, diff.begin())); + + if(diff.empty()){ + _facts->deleteFact(_bigger); + _parent->removeRef(_bigger); + } else { + b.pointSet = diff; + b.mines -= s.mines; + _facts->addFact(_bigger, b); + _parent->addGeneral(_bigger); + } + + // No points revealed + return false; +} + +AdviseFast::IntersectionRule::IntersectionRule( + Coord bigger, Coord smaller, + RuleSet *parent) : + Rule(parent), + _bigger(bigger), _smaller(smaller) +{} + +bool AdviseFast::IntersectionRule::apply( + CoordSet *surePoints) +{ + +#if defined(DEBUG) && DEBUG >= 2 + cout << "Applying intersection rule "; + operator <<(cout, _bigger) << ' '; + operator <<(cout, _smaller) << endl; +#endif + + if(!_facts->count(_bigger)) return false; + if(!_facts->count(_smaller)) return false; + + Fact b = (*_facts)[_bigger]; + Fact s = (*_facts)[_smaller]; + +#if defined(DEBUG) && DEBUG >= 2 + cout << b << endl << s << endl; +#endif + + CoordSet diff; + set_difference( + b.pointSet.begin(), + b.pointSet.end(), + s.pointSet.begin(), + s.pointSet.end(), + inserter(diff, diff.begin())); + + if((int)diff.size() != b.mines - s.mines) + // Oops :( + return false; + +#if defined(DEBUG) && DEBUG >= 2 + cout << "succeeded!" << endl; +#endif + + CoordSet cross, diffs; + set_difference( + s.pointSet.begin(), + s.pointSet.end(), + b.pointSet.begin(), + b.pointSet.end(), + inserter(diffs, diffs.begin())); + set_intersection( + s.pointSet.begin(), + s.pointSet.end(), + b.pointSet.begin(), + b.pointSet.end(), + inserter(cross, cross.begin())); + + b.pointSet = diff; + b.mines -= s.mines; + _facts->addFact(_bigger, b); + + s.pointSet = cross; + _facts->addFact(_smaller, s); + + { + _parent->removeRef(_bigger); + _parent->addGeneral(_smaller); + + RuleSet::Entry e; + e.first = RuleSet::FULL; + e.second.insert(_bigger); + _parent->addRule(e); + } + + if(diffs.empty()) return false; + + // Otherwise we have something to reveal!! + surePoints->insert(diffs.begin(), diffs.end()); + return true; +} diff --git a/kmines/solver/adviseFast.cpp b/kmines/solver/adviseFast.cpp new file mode 100644 index 00000000..f15d508e --- /dev/null +++ b/kmines/solver/adviseFast.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "adviseFast.h" + +#include + + +using namespace AdviseFast; + +std::ostream &AdviseFast::operator <<(std::ostream &s, Fact const &f) +{ + return s << f.pointSet << "= " << f.mines; +} + +AdviseFast::FactSet::FactSet(BaseField *field) : + _field(field) +{ + Fact globalFact; globalFact.mines = field->nbMines(); + + int i, j; + for(i=field->height()-1; i>=0; --i) + for(j=field->width()-1; j>=0; --j){ + Coord p(j, i); + // #### hasMine implies isCovered (and the solver should not + // know if there is a mine :) [NH] + if ( field->isCovered(p) /*|| field->hasMine(p)*/ ) + globalFact.pointSet.insert(p); + else { + Fact f; + this->retrieveFact(p, &f); + this->addFact(p, f); + } + } + + this->addFact(Coord(-1,-1), globalFact); +} + +void AdviseFast::FactSet::retrieveFact( + Coord which, + Fact *where) +{ + where->mines = (_field->isCovered(which) ? -1 + : (int)_field->nbMinesAround(which)); + CoordList tmp = _field->coveredNeighbours(which); + for (CoordList::const_iterator it = tmp.begin(); it!=tmp.end(); ++it) + where->pointSet.insert(*it); +} + +void AdviseFast::FactSet::addFact( + Coord const &point, + Fact const &fact) +{ + if(this->count(point)) this->deleteFact(point); + + Fact &f = ((*this)[point] = fact); + + + // Remove marked points + CoordSet marked; + set_intersection( + f.pointSet.begin(), + f.pointSet.end(), + _marked.begin(), + _marked.end(), + inserter(marked, marked.begin())); + + CoordSet::iterator i; + for(i=marked.begin(); i!=marked.end(); ++i) + f.pointSet.erase(*i); + f.mines -= marked.size(); + + // Don't insert empty fact + if(f.pointSet.empty()) { this->erase(point); return;} + + for(i=f.pointSet.begin(); i!=f.pointSet.end(); ++i) + _containingFacts[*i].insert(point); +} + +void AdviseFast::FactSet::deleteFact( + Coord const &point) +{ + if(!this->count(point)) return; + CoordSet::iterator i; + Fact &f = (*this)[point]; + for(i=f.pointSet.begin(); i!=f.pointSet.end(); ++i){ + _containingFacts[*i].erase(point); + if(_containingFacts[*i].empty()) + _containingFacts.erase(*i); + } + this->erase(point); +} + +bool AdviseFast::FactSet::reveal( + Coord point, + CoordSet *affectedFacts) +{ + // Tolerate :) + if( !_field->isCovered(point) ) return true; // :) + + CoordList tmp; + if(_field->doReveal(point, &tmp, 0) == false) + // Blew up :( + return false; + + CoordSet autorevealed; + for (CoordList::const_iterator it = tmp.begin(); it!=tmp.end(); ++it) + autorevealed.insert(*it); + autorevealed.insert(point); + affectedFacts->insert(autorevealed.begin(), autorevealed.end()); + + CoordSet::const_iterator i; + for(i=autorevealed.begin(); i!=autorevealed.end(); ++i) + { + // I still think that each poing will belong to + // at least one fact, but don't want to waste time + // proving it :) + if(_containingFacts.count(*i)){ + CoordSet const &affF = _containingFacts[*i]; + affectedFacts->insert( + affF.begin(), affF.end()); + for(CoordSet::const_iterator j=affF.begin(); + j!=affF.end(); + ++j) + { + (*this)[*j].pointSet.erase(*i); + if((*this)[*j].pointSet.empty()) + this->erase(*j); + } + _containingFacts.erase(*i); + } + + Fact f; retrieveFact(*i, &f); + this->addFact(*i, f); + } + + return true; +} + +void AdviseFast::FactSet::mark( + Coord point, + CoordSet *affectedFacts) +{ + if(_marked.count(point)) return; + _marked.insert(point); + + // I still think that each poing will belong to + // at least one fact, but don't want to waste time + // proving it :) + if(_containingFacts.count(point)){ + CoordSet const &affF = _containingFacts[point]; + affectedFacts->insert(affF.begin(), affF.end()); + for(CoordSet::const_iterator i=affF.begin(); i!=affF.end(); ++i){ + (*this)[*i].pointSet.erase(point); + (*this)[*i].mines--; + if((*this)[*i].pointSet.empty()) + this->erase(*i); + } + _containingFacts.erase(point); + } + + _field->doMark(point); +} + +CoordSet const *AdviseFast::FactSet::getContainingFacts( + Coord const &point) const +{ + if(_containingFacts.count(point)) + return &const_cast &>(_containingFacts) + [point]; + else return 0; +} + +std::ostream &AdviseFast::operator <<(std::ostream &s, FactSet const &fs) +{ + FactSet::const_iterator i; + for(i=fs.begin(); i!=fs.end(); ++i) + s << i->first << ": " << i->second << endl; + return s; +} + +bool AdviseFast::adviseFast( + Coord *, + FactSet *, + RuleSet *) +{ return false;} diff --git a/kmines/solver/adviseFast.h b/kmines/solver/adviseFast.h new file mode 100644 index 00000000..db3b1955 --- /dev/null +++ b/kmines/solver/adviseFast.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef adviseFast_h +#define adviseFast_h + +#include "headerP.h" + + +namespace AdviseFast { + + class GeneralRule : public Rule { + public: + GeneralRule(Coord fact, RuleSet *rules); + virtual bool apply(CoordSet *surePoints); + private: + Coord _fact; + }; + + class EmptyRule : public Rule { + public: + EmptyRule(Coord fact, RuleSet *rules); + virtual bool apply(CoordSet *surePoints); + private: + Coord _fact; + }; + + class FullRule : public Rule { + public: + FullRule(Coord fact, RuleSet *rules); + virtual bool apply(CoordSet *surePoints); + private: + Coord _fact; + }; + + class InclusionRule : public Rule { + public: + InclusionRule(Coord bigger, Coord smaller, + RuleSet *rules); + virtual bool apply(CoordSet *surePoints); + private: + Coord _bigger, _smaller; + }; + + class IntersectionRule : public Rule { + public: + IntersectionRule(Coord bigger, Coord smaller, + RuleSet *rules); + virtual bool apply(CoordSet *surePoints); + private: + Coord _bigger, _smaller; + }; +} + +#endif diff --git a/kmines/solver/adviseFull.cpp b/kmines/solver/adviseFull.cpp new file mode 100644 index 00000000..815ff02c --- /dev/null +++ b/kmines/solver/adviseFull.cpp @@ -0,0 +1,655 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "adviseFull.h" +#include +#include +#include + +using std::list; +using std::map; +using std::set; +using namespace AdviseFull; + +AdviseFull::EquationSet::EquationSet() : + _maxPointSet(0) +{} + +AdviseFull::EquationSet::EquationSet( + AdviseFast::FactSet const &facts) : + _maxPointSet(0) +{ + AdviseFast::FactSet::const_iterator i; + for(i=facts.begin(); i!=facts.end(); ++i, ++_maxPointSet){ + Equation e; + e.pointSets.insert(_maxPointSet); + e.mines = i->second.mines; + _equations.push_back(e); + + _pointSets[_maxPointSet] = i->second.pointSet; + } +} + +void AdviseFull::EquationSet::normalize() +{ + short i=0; + set empty; + for(i=0; i<_maxPointSet; ++i){ + if(!_pointSets.count(i)) continue; + if(_pointSets[i].empty()){ + this->substitute(i, empty); + continue; + } + for(short j=i+1;j<_maxPointSet; ++j){ + if(!_pointSets.count(j)) continue; + if(_pointSets[j].empty()){ + this->substitute(j, empty); + continue; + } + + CoordSet intersect; + set_intersection( + _pointSets[i].begin(), + _pointSets[i].end(), + _pointSets[j].begin(), + _pointSets[j].end(), + inserter(intersect, intersect.begin())); + if(intersect.empty()) continue; + + CoordSet _i, _j; + set_difference( + _pointSets[i].begin(), + _pointSets[i].end(), + _pointSets[j].begin(), + _pointSets[j].end(), + inserter(_i, _i.begin())); + set_difference( + _pointSets[j].begin(), + _pointSets[j].end(), + _pointSets[i].begin(), + _pointSets[i].end(), + inserter(_j, _j.begin())); + + set _ip, _jp; + _ip.insert(_maxPointSet); + _jp.insert(_maxPointSet); + _pointSets[_maxPointSet++] = intersect; + _ip.insert(_maxPointSet); + _pointSets[_maxPointSet++] = _i; + _jp.insert(_maxPointSet); + _pointSets[_maxPointSet++] = _j; + + this->substitute(i, _ip); + this->substitute(j, _jp); + break; + } + } +} + +void AdviseFull::EquationSet::separate( + list *result) const +{ + result->clear(); // :) + + list equations = _equations; + + while(!equations.empty()){ + // Add a new equation set to *results + result->push_back(EquationSet()); + EquationSet &workingSet = result->back(); + workingSet._maxPointSet = _maxPointSet; + + // Start iteration process + // recentlyDeceased is a set of pointSets added on the + // last iteration + workingSet._equations.push_back(equations.front()); + set recentlyDeceased = equations.front().pointSets; + equations.pop_front(); + + // The iteration process + while(!recentlyDeceased.empty()){ + + // Temporary set + set rd; + + list::iterator i = equations.begin(); + while(i != equations.end()){ + set intersect; + set_intersection( + i->pointSets.begin(), + i->pointSets.end(), + recentlyDeceased.begin(), + recentlyDeceased.end(), + inserter(intersect, intersect.begin())); + if(intersect.empty()){ + ++i; + } else { + set_difference( + i->pointSets.begin(), + i->pointSets.end(), + intersect.begin(), + intersect.end(), + inserter(rd, rd.begin())); + workingSet._equations.push_back(*i); + i = equations.erase(i); + } + } + + // Now switch recentlyDeceased + set::iterator j; + for( j = recentlyDeceased.begin(); + j != recentlyDeceased.end(); + ++j) + { + workingSet._pointSets[*j] = + (const_cast< + map &>( + _pointSets))[*j]; + } + + recentlyDeceased = rd; + } + } +} + +map const &AdviseFull::EquationSet::solve( + list *results) const +{ + +#ifdef DEBUG + printf("Entering EquationSet::solve\n"); + prettyprint(); +#endif + + EquationSet eqs = *this; + + // Get the most evident solutions + Solution only; + list &EQS = eqs._equations; + + bool success; + do { + + success = false; + list::iterator i = EQS.begin(); + + while(i!=EQS.end()){ +#if defined(DEBUG) && DEBUG >= 2 + printf("Taking look at the equation...\n"); +#endif + // Substitute known values + { set::iterator j; + set known; + for( j = i->pointSets.begin(); + j != i->pointSets.end(); + ++j) + { + if(only.count(*j)){ + i->mines -= only[*j]; + known.insert(*j); + } + } + + // STL bug ?? + for( j = known.begin(); + j != known.end(); + ++j) + i->pointSets.erase(*j); + } + // From now on the equation has no known values +#if defined(DEBUG) && DEBUG >= 2 + printf("Substituted known values.\n"); +#endif + if(i->mines < 0) + // Discrepancy + return _pointSets; + + + if(i->pointSets.empty()){ +#if defined(DEBUG) && DEBUG >= 2 + printf("Empty equation.\n"); +#endif + if(i->mines){ + // No points, non-zero mine count + // No solution + return _pointSets; + } else { + i = EQS.erase(i); + continue; + } + } + + if(i->mines == 0){ + set::iterator j; + for( + j=i->pointSets.begin(); + j!=i->pointSets.end(); + ++j) + only[*j] = 0; + + EQS.erase(i); + success = true; + break; + } + + if(i->pointSets.size() == 1){ + short j = *i->pointSets.begin(); + + if((int)eqs._pointSets[j].size() < i->mines) + // Discrepancy !! + return _pointSets; + + only[j] = i->mines; + + EQS.erase(i); + success = true; + break; + } + + ++i; + } + + } while(success); + + // If no equations left we have a unique solution + if(EQS.empty()){ +#ifdef DEBUG + printf("Got a single solution!\n"); +#endif + results->push_back(only); + return _pointSets; + } + + // Otherwise the first equation is not empty. + // Find the range for first element + short var = *EQS.begin()->pointSets.begin(); + std::pair range; + range.first = 0; + range.second = eqs._pointSets[var].size(); + + // A list of equations containing var + list::iterator> containers; + list::iterator i; + for( i = EQS.begin(); + i != EQS.end(); + ++i) + { + if(i->pointSets.count(var)){ + i->pointSets.erase(var); + containers.push_back(i); + + if(i->mines < range.second) + range.second = i->mines; + + // The total size of other point sets + // in the equation + short totalsize = 0; + set::iterator j; + for( j = i->pointSets.begin(); + j != i->pointSets.end(); + ++j) + totalsize += eqs._pointSets[*j].size(); + + if(range.first < i->mines - totalsize) + range.first = i->mines - totalsize; + } + } + // Found the range + + // Now set properly equation set for first recursion + list::iterator>::iterator super_iter; // ;) + short varvalue = range.first; + for( super_iter = containers.begin(); + super_iter != containers.end(); + ++super_iter) + (*super_iter)->mines -= varvalue; + + // Recursive calls here + while(varvalue <= range.second){ + only[var] = varvalue; + list tempResults; + eqs.solve(&tempResults); + + // Mix solutions with only and put them + // in *results + list::iterator j; + for( j=tempResults.begin(); + j!=tempResults.end(); + ++j) + { + j->insert(only.begin(), only.end()); + results->push_back(*j); + } + + // Prepare next recursive call + ++varvalue; + for( super_iter = containers.begin(); + super_iter != containers.end(); + ++super_iter) + --(*super_iter)->mines; + } + + return _pointSets; +} + +void AdviseFull::EquationSet::prettyprint() const +{ + +#if defined(DEBUG) + printf("Point Sets:\n"); + map::const_iterator i; + for(i=_pointSets.begin(); i!=_pointSets.end(); ++i){ + printf("%d:", i->first); + CoordSet::const_iterator j; + for(j=i->second.begin(); j!=i->second.end(); ++j) + printf("\t(%d,%d)\n", j->second, j->first); + } +#endif + + printf("Equations:\n"); + list::const_iterator l; + for(l=_equations.begin(); l!=_equations.end(); ++l){ + set::const_iterator j; + for(j=l->pointSets.begin(); j!=l->pointSets.end(); ++j) + printf("%d ", *j); + printf("= %d\n", l->mines); + } +} + +void AdviseFull::EquationSet::substitute( + short out, + set const &in) +{ + list::iterator i; + for( i = _equations.begin(); + i != _equations.end(); + ++i) + { + if(i->pointSets.count(out)){ + i->pointSets.erase(out); + i->pointSets.insert(in.begin(), in.end()); + } + } + + _pointSets.erase(out); +} + +bool AdviseFull::surePoints( + map const &m, + list const &l, + CoordSet *surePoints) +{ + // A set of candidates to be surePoints + list sp; + { + map::const_iterator i; + for(i=m.begin(); i!=m.end(); ++i) sp.push_back(i->first); + } + + // Scan solution list + list::const_iterator i; + for(i=l.begin(); i!=l.end(); ++i){ + list::iterator j = sp.begin(); + while(j != sp.end()){ + // Non-empty possibility + if((const_cast(*i))[*j]){ + j = sp.erase(j); + if(sp.empty()) // No candidates left + return false; + } else // Stay alive for now + ++j; + } + } + + // There are SOME sure points; + // Fill *surePoints + list::iterator isp; + map &mm = const_cast &>(m); + for(isp = sp.begin(); isp != sp.end(); ++isp) + surePoints->insert(mm[*isp].begin(), mm[*isp].end()); + + return true; +} + +float AdviseFull::variantNumberFraction( + map const &m, + EquationSet::Solution const ÷nd, + EquationSet::Solution const &divisor, + float fraction) +{ + short count_difference = 0; + float quotient = 1; + + EquationSet::Solution::const_iterator i; + for(i=divisor.begin(); i!=divisor.end(); ++i){ + int j = i->first; + assert(m.count(j)); + int size = (const_cast &>(m))[j].size(); + int dvd = (const_cast(dividend))[j]; + int dvr = (const_cast(divisor))[j]; + + count_difference += dvd - dvr; + + if(dvd < dvr){ + dvr = size - dvr; + dvd = size - dvd; + } + while(dvr < dvd) { + float num = size-dvr++; + quotient *= num/dvr; + } + } + + // Sorry, expensive call, but I'm lazy :(( + if(count_difference){ + assert(fraction != 0.); +#if defined(DEBUG) + float correction = pow( fraction/(1-fraction), count_difference ); + cout << "Got into correction, " << + count_difference << ' ' << correction << endl; +#endif + quotient *= pow( (1-fraction)/fraction , -count_difference ); + } + +#if defined(DEBUG) && DEBUG >= 2 + printf("variantNumberFraction: %.02f.\n", quotient); +#endif + + return quotient; +} + +void AdviseFull::getProbabilities( + map const &m, + list const &l, + ProbabilityMap *probabilities, + float fraction) +{ + assert(!l.empty()); + EquationSet::Solution const &front = l.front(); + + float probabilitiesSum = 0; + map probs; + { map::const_iterator i; + for(i=m.begin(); i!=m.end(); ++i) + probs[i->first] = 0; + } + + list::const_iterator i; + for(i=l.begin(); i!=l.end(); ++i){ + float frac = variantNumberFraction(m, *i, front, fraction); + EquationSet::Solution::const_iterator j; + for(j=i->begin(); j!=i->end(); ++j) + probs[j->first] += j->second*frac; + probabilitiesSum += frac; + } + + probabilities->clear(); + + map::iterator j; + for(j=probs.begin(); j!= probs.end(); ++j){ + CoordSet const &ps = const_cast &>(m)[j->first]; + j->second /= ps.size() * probabilitiesSum; + CoordSet::const_iterator k; + for(k=ps.begin(); k!=ps.end(); ++k) + probabilities->insert( + std::pair(j->second, *k)); + } + + // That's it :) +} + +void AdviseFull::adviseFull( + AdviseFast::FactSet *facts, + CoordSet *surePoints, + ProbabilityMap *probabilities) +{ + EquationSet eqs(*facts); + +#if defined(DEBUG) && DEBUG >= 2 + eqs.prettyprint(); +#endif + + eqs.normalize(); +#if defined(DEBUG) && DEBUG >= 2 + eqs.prettyprint(); +#endif + + list eqss; + eqs.separate(&eqss); +#ifdef DEBUG + {list::iterator i; + for(i=eqss.begin(); i!=eqss.end(); ++i) + i->prettyprint(); + } +#endif + + + // OK, uneffective, but simple :( + surePoints->clear(); + probabilities->clear(); + + // Get a fraction; + float fraction; + { BaseField const *f = facts->getField(); + fraction = ((float)f->nbMines()) / (f->width() * f->height()); + } + + /* From now on the first equation set on the list includes + * the equation corresponding to "total" fact. This is the + * first equation on the set. + * + * Give it a special treatment ;) */ + if(!eqss.empty()) do { + EquationSet prime = eqss.front(); + EquationSet::Equation total = prime._equations.front(); + prime._equations.pop_front(); + + list prime_sep; + prime.separate(&prime_sep); + + // Find a pool + list::iterator i = prime._equations.begin(); + while(!prime._equations.empty()){ + set::iterator j; + for( j = i->pointSets.begin(); + j != i->pointSets.end(); + ++j) + prime._pointSets.erase(*j); + i = prime._equations.erase(i); + } + + assert(prime._pointSets.size() <= 1); + if(prime._pointSets.size() == 0) break; + + short pool = prime._pointSets.begin()->first; + CoordSet const &p = prime._pointSets[pool]; +#ifdef DEBUG + cout << "Prime equation set:" << endl << + " separated into " << prime_sep.size() << endl << + " pool size is " << p.size() << endl; +#endif + // Euristic + // if( prime_sep.size () > 6 && p.size() >= prime_sep.size() * 10){ + if(p.size() < (prime_sep.size()+1) * 10) + // No special treatment!! + break; + + + // Actually, just substitute prime (!!!) + eqss.pop_front(); + eqss.insert(eqss.begin(), + prime_sep.begin(), + prime_sep.end()); + + prime._equations.clear(); + EquationSet::Equation o; + o.pointSets.insert(pool); + // #### is the convertion right ? (NH) + o.mines = (ushort)(fraction * p.size()); + // A precaution + if(o.mines == 0) o.mines = 1; // ;) + + prime._equations.push_front(o); + eqss.push_front(prime); + + +#ifdef DEBUG + cout << "Specially treated:" << endl; + { + list::iterator i; + for(i=eqss.begin(); i!=eqss.end(); ++i) + i->prettyprint(); + } +#endif + } while (false); + + list::const_iterator i; + for(i=eqss.begin(); i!=eqss.end(); ++i){ + CoordSet sp; ProbabilityMap pb; + + list solutions; + map const &m = i->solve(&solutions); +#ifdef DEBUG + printf("Got solutions.\n"); +#if defined(DEBUG) && DEBUG >= 2 + { list::iterator i; + for( i = solutions.begin(); + i != solutions.end(); + ++i) + { + EquationSet::Solution::iterator j; + for(j=i->begin(); j!=i->end(); ++j) + printf("%d:\t%d\n", + j->first, j->second); + printf("\n"); + } + } +#endif +#endif + + //bool sure = + AdviseFull::surePoints(m, solutions, &sp); + surePoints->insert(sp.begin(), sp.end()); + + getProbabilities(m, solutions, &pb, fraction); + probabilities->insert(pb.begin(), pb.end()); + } + + // That's it + return; +} diff --git a/kmines/solver/adviseFull.h b/kmines/solver/adviseFull.h new file mode 100644 index 00000000..2b0cc97b --- /dev/null +++ b/kmines/solver/adviseFull.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ADVISE_FULL_H +#define __ADVISE_FULL_H + +#include +#include + +#include "headerP.h" + + +namespace AdviseFull { + class EquationSet { + public: // Well, why is it necessary? + struct Equation { + std::set pointSets; + short mines; + }; + typedef std::map Solution; + + public: + EquationSet(); + EquationSet(AdviseFast::FactSet const &facts); + + std::list _equations; + std::map _pointSets; + + /** Make sure no _pointSets have + * non-empty intersection */ + void normalize(); + + /** Returns in *results a set of equation sets + * which can be solved separately. + * *this assumed normalized :) */ + void separate(std::list *results) const; + + /** Solves... returns _pointSets. + * It's nice to have *this separated :) */ + std::map const &solve( + std::list *results) const; + + void prettyprint() const; + + private: + /** One more than max(_pointSets[i].first) */ + short _maxPointSet; + + /** Substitutes a pointSet in all equations */ + void substitute( + short out, + std::set const &in); + }; + + bool surePoints( + std::map const &m, + std::list const &l, + CoordSet *surePoints); + + /** The fourth argument is a fraction of mines in the "pool" */ + void getProbabilities( + std::map const &m, + std::list const &l, + ProbabilityMap *probabilities, + float fraction = 0); + + /** Get the quotient of the number of variants of + * point distribution satisfying dividend and divisor + * solutions */ + /** The fourth argument is a fraction of mines in the "pool" */ + float variantNumberFraction( + std::map const &m, + EquationSet::Solution const ÷nd, + EquationSet::Solution const &divisor, + float fraction = 0); +} + +#endif diff --git a/kmines/solver/bfield.cpp b/kmines/solver/bfield.cpp new file mode 100644 index 00000000..d6c03643 --- /dev/null +++ b/kmines/solver/bfield.cpp @@ -0,0 +1,221 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "bfield.h" + + +using namespace KGrid2D; + +BaseField::BaseField(long seed) + : _nbUncovered(0), _nbMarked(0), _nbUncertain(0), _random(seed) +{} + +CoordList BaseField::coveredNeighbours(const Coord &p) const +{ + CoordList n; + CoordList tmp = neighbours(p); + for (CoordList::const_iterator it=tmp.begin(); it!=tmp.end(); ++it) + if ( state(*it)!=Uncovered ) n.append(*it); + return n; +} + +uint BaseField::nbMinesAround(const Coord &p) const +{ + uint nb = 0; + CoordList n = neighbours(p); + for (CoordList::const_iterator it=n.begin(); it!=n.end(); ++it) + if ( hasMine(*it) ) nb++; + return nb; +} + +void BaseField::reset(uint width, uint height, uint nbMines) +{ + _firstReveal = true; + _nbMarked = 0; + _nbUncertain = 0; + _nbUncovered = 0; + _nbMines = nbMines; + + Case tmp; + tmp.mine = false; + tmp.state = Covered; + resize(width, height); + fill(tmp); +} + +bool BaseField::checkField(uint w, uint h, uint nb, const QString &s) +{ + if ( s.length()!=w*h ) return false; + uint n = 0; + unsigned int strLength(s.length()); + for (uint i=0; i + +#include +#include + +#include "defines.h" + + +class BaseField : public KGrid2D::Square, public KMines +{ + public: + // seed for KRandomSequence (used by solver check programs) + BaseField(long seed = 0); + virtual ~BaseField() {} + + void reset(uint width, uint height, uint nbMines); + static bool checkField(uint width, uint height, uint nbMines, + const QString &field); + void initReplay(const QString &field); // string == "0100011011000101..." + +// -------------------------- +// interface used by the solver + uint nbMines() const { return _nbMines; } + bool isCovered(const KGrid2D::Coord &p) const + { return ( state(p)!=KMines::Uncovered ); } + uint nbMinesAround(const KGrid2D::Coord &) const; + KGrid2D::CoordList coveredNeighbours(const KGrid2D::Coord &p) const; + bool isSolved() const { return (size() - _nbUncovered)==_nbMines; } + + // return false if the case revealed contains a mine. + virtual bool doReveal(const KGrid2D::Coord &c, + KGrid2D::CoordList *autorevealed, bool *caseUncovered) + { return reveal(c, autorevealed, caseUncovered); } + virtual void doMark(const KGrid2D::Coord &); +// ------------------------- + + uint nbMarked() const { return _nbMarked; } + QCString string() const; + + void showAllMines(bool won); + + protected: + bool firstReveal() const { return _firstReveal; } + KMines::CaseState state(const KGrid2D::Coord &p) const + { return (*this)[p].state; } + bool hasMine(const KGrid2D::Coord &p) const { return (*this)[p].mine; } + virtual void changeCase(const KGrid2D::Coord &, KMines::CaseState); + bool reveal(const KGrid2D::Coord &c, + KGrid2D::CoordList *autorevealed, bool *caseUncovered); + bool autoReveal(const KGrid2D::Coord &, bool *caseUncovered); + void completeReveal(); + + private: + bool _firstReveal; + uint _nbUncovered, _nbMarked, _nbUncertain, _nbMines; + KRandomSequence _random; + + void uncover(const KGrid2D::Coord &, KGrid2D::CoordList *autoreveal); + void changeState(KMines::CaseState, int increment); +}; + +#endif diff --git a/kmines/solver/headerP.h b/kmines/solver/headerP.h new file mode 100644 index 00000000..984e3113 --- /dev/null +++ b/kmines/solver/headerP.h @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __HEADERP_H +#define __HEADERP_H + +//#define DEBUG 2 + +#include +#include +#include +#include +#include + +#include "bfield.h" + + +using namespace KGrid2D; +using std::cout; +using std::endl; + +typedef std::set > CoordSet; + +inline std::ostream &operator <<(std::ostream &s, const Coord &c) +{ + s << '(' << c.first << ',' << c.second << ')' << endl; + return s; +} + +inline std::ostream &operator <<(std::ostream &s, const CoordSet &set) +{ + for(CoordSet::const_iterator i=set.begin(); i!=set.end(); ++i) + s << *i; + return s; +} + +inline std::ostream &operator <<(std::ostream &s, const BaseField &f) +{ + for (uint j=0; j { + public: + FactSet(BaseField *); + BaseField const *getField() const { return _field;} + + /** Reveals a point on the field underlining + * Returns false on blowup !!! */ + bool reveal( + Coord what, + CoordSet *affectedFacts); + void mark( + Coord what, + CoordSet *affectedFacts); + CoordSet const *getContainingFacts( + Coord const &) + const; + /** May be used to substitute fact */ + void addFact(Coord const &, Fact const &); + void deleteFact(Coord const &); + void retrieveFact(Coord which, Fact *where); + + private: + BaseField *_field; + std::map _containingFacts; + CoordSet _marked; + }; + std::ostream &operator <<(std::ostream &, FactSet const &); + + /** A Rule abstraction that can be applied. + * Applying the rule results in either modifyling the + * RuleSet which it belongs to or FactSet it is based on + * or both ;) + */ + class RuleSet; + struct Rule { + Rule(RuleSet *parent); + virtual ~Rule(); + virtual bool apply(CoordSet *surePoints) = 0; + + RuleSet *_parent; + FactSet *_facts; +#if defined(DEBUG) +# if DEBUG >= 2 + private: + static int leaks; +# endif +#endif + }; + + /** A set of rules */ + class RuleSet { + public: + enum RuleType { + EMPTY, + FULL, + INCLUDE, + INCLUDE1, + INTERSECT, + INTERSECT1, + GENERAL}; + + typedef std::pair Entry; + + RuleSet(FactSet *); + ~RuleSet(); + void addRule(Entry const &); + + /** A factory method */ + Rule *newRule(Entry const &); + + /** Remove all references to a point from RuleSet */ + void removeRef(Coord); + + /** removeRef + add a General Rule */ + void addGeneral(Coord); + + /** Returns false on blowup */ + bool reveal(Coord p); + + /** Returns false on failure */ + bool getSurePoint(Coord *sp); + /** Works until is stuck :) */ + void solve(); + + FactSet *facts; + + private: + std::set _rules; + CoordSet _surePoints; + + /** Fills _surePoints. + * Returns false if nothing done. */ + bool apply(); + }; + + /** Returns true on success */ + bool adviseFast( + Coord *point, + FactSet *facts, + RuleSet *rules); + +} + + +namespace AdviseFull { + typedef std::multimap ProbabilityMap; + + /** If there are sure free cells, + * sets surePoints, otherwise sets probabilities */ + void adviseFull( + AdviseFast::FactSet *facts, + CoordSet *surePoints, + ProbabilityMap *probabilities); + +} + +#endif diff --git a/kmines/solver/solver.cpp b/kmines/solver/solver.cpp new file mode 100644 index 00000000..00807fca --- /dev/null +++ b/kmines/solver/solver.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) + * Copyright (c) 2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "solver.h" +#include "solver.moc" + +#include +#include + +#include +#include +#include +#include + +#include + +#include "headerP.h" + + +//----------------------------------------------------------------------------- +class SolverPrivate +{ + public: + SolverPrivate() : facts(0), rules(0) {} + ~SolverPrivate() { + delete facts; + delete rules; + } + + AdviseFast::FactSet *facts; + AdviseFast::RuleSet *rules; +#ifdef DEBUG + unsigned long t0, t; +#endif +}; + +Solver::Solver(QObject *parent) + : QObject(parent) +{ + d = new SolverPrivate; + +#ifdef DEBUG +#define PRINT_ELAPSED(purpose) \ + d->t = time(0); \ + cout << "Spent " << d->t - d->t0 << " seconds on " purpose << endl; \ + d->t0 = d->t; + +#endif +} + +Solver::~Solver() +{ + delete d; +} + +Coord Solver::advise(BaseField &field, float &probability) +{ + Coord point; + probability = 1; + delete d->facts; + d->facts = new AdviseFast::FactSet(&field); + delete d->rules; + d->rules = new AdviseFast::RuleSet(d->facts); + + if( AdviseFast::adviseFast(&point, d->facts, d->rules) ) return point; + + CoordSet surePoints; + AdviseFull::ProbabilityMap probabilities; + AdviseFull::adviseFull(d->facts, &surePoints, &probabilities); + + // return one of the sure point (random choice to limit the tropism) [NH] + if( !surePoints.empty() ) { + KRandomSequence r; + uint k = r.getLong(surePoints.size()); + CoordSet::iterator it = surePoints.begin(); + for (uint i=0; ifirst; + return probabilities.begin()->second; + } + + // Otherwise the Field is already solved :) + return Coord(-1,-1); +} + +void Solver::solve(BaseField &field, bool noGuess) +{ + _field = &field; + initSolve(false, noGuess); +} + +bool Solver::initSolve(bool oneStep, bool noGuess) +{ + _inOneStep = oneStep; + _noGuess = noGuess; + delete d->facts; + d->facts = new AdviseFast::FactSet(_field); + delete d->rules; + d->rules = new AdviseFast::RuleSet(d->facts); +#ifdef DEBUG + d->t0 = time(0); +#endif + return solveStep(); +} + +bool Solver::solveStep() +{ + if ( _field->isSolved() ) { + emit solvingDone(true); + return true; + } + + d->rules->solve(); + +#ifdef DEBUG + PRINT_ELAPSED("fast rules") +#endif + + if( _field->isSolved() ) { + emit solvingDone(true); + return true; + } + + CoordSet surePoints; + AdviseFull::ProbabilityMap probabilities; + AdviseFull::adviseFull(d->facts, &surePoints, &probabilities); + +#ifdef DEBUG + PRINT_ELAPSED("full rules") +#endif + + if(!surePoints.empty()){ + CoordSet::iterator i; + for(i=surePoints.begin(); i!=surePoints.end(); ++i) { + bool b = d->rules->reveal(*i); + assert(b); + } + } else if ( !_noGuess ) { +#ifdef DEBUG + cout << "Applying heuristics!" << endl; + cout << *_field << endl; +#endif + // Minimum probability logic + assert(!probabilities.empty()); +#ifdef DEBUG + AdviseFull::ProbabilityMap::iterator i=probabilities.begin(); + cout << "Probability is " << i->first << endl; +#endif + bool success = d->rules->reveal(probabilities.begin()->second); + if ( !success ) { + emit solvingDone(false); + return false; + } + } + + if (_inOneStep) return solveStep(); + else QTimer::singleShot(0, this, SLOT(solveStep())); + return false; +} + +bool Solver::solveOneStep(BaseField &field) +{ + _field = &field; + return initSolve(true, false); +} + + +//----------------------------------------------------------------------------- +SolvingRateDialog::SolvingRateDialog(const BaseField &field, QWidget *parent) + : KDialogBase(Plain, i18n("Compute Solving Rate"), Ok|Close, + Close, parent, "compute_solving_rate", true, true), + _refField(field) +{ + connect(&_solver, SIGNAL(solvingDone(bool)), SLOT(solvingDone(bool))); + + KGuiItem item = KStdGuiItem::ok(); + item.setText(i18n("Start")); + setButtonOK(item); + + QVBoxLayout *top = new QVBoxLayout(plainPage(), 0, spacingHint()); + QLabel *label = new QLabel(i18n("Width: %1").arg(field.width()), + plainPage()); + top->addWidget(label); + label = new QLabel(i18n("Height: %1").arg(field.height()), plainPage()); + top->addWidget(label); + label = new QLabel(i18n("Mines: %1 (%2%)").arg(field.nbMines()) + .arg( field.nbMines() * 100.0 / field.size()), + plainPage()); + top->addWidget(label); + + top->addSpacing(spacingHint()); + + _progress = new KProgress(NB_STEPS, plainPage()); + _progress->setTextEnabled(true); + _progress->setFormat("%v"); + top->addWidget(_progress); + + _label = new QLabel(i18n("Success rate:"), plainPage()); + top->addWidget(_label); +} + +void SolvingRateDialog::slotOk() +{ + enableButtonOK(false); + _i = 0; + _success = 0; + _progress->setValue(0); + QTimer::singleShot(0, this, SLOT(step())); +} + +void SolvingRateDialog::step() +{ + if ( _i==NB_STEPS ) { + enableButtonOK(true); + return; + } + _i++; + _field.reset(_refField.width(), _refField.height(), _refField.nbMines()); + _solver.solve(_field, false); +} + +void SolvingRateDialog::solvingDone(bool success) +{ + if (success) _success++; + _label->setText(i18n("Success rate: %1%") + .arg(_success * 100.0 / _i, 0, 'f', 3)); + _progress->advance(1); + QTimer::singleShot(0, this, SLOT(step())); +} diff --git a/kmines/solver/solver.h b/kmines/solver/solver.h new file mode 100644 index 00000000..f076874e --- /dev/null +++ b/kmines/solver/solver.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) + * Copyright (c) 2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __SOLVER_H +#define __SOLVER_H + +#include + +#include "bfield.h" + + +class QLabel; +class KProgress; +class SolverPrivate; + +class Solver : public QObject +{ + Q_OBJECT + public: + Solver(QObject *parent = 0); + ~Solver(); + + /** A method to advice a point placement */ + KGrid2D::Coord advise(BaseField &field, float &probability); + + /** Solve current mine field */ + void solve(BaseField &field, bool noGuess); + + /** Solve without signals/slot (for test programs) */ + bool solveOneStep(BaseField &field); + + signals: + void solvingDone(bool success); + + private slots: + bool solveStep(); + + private: + BaseField *_field; + bool _inOneStep, _noGuess; + SolverPrivate *d; + + bool initSolve(bool oneStep, bool noGuess); +}; + +class SolvingRateDialog : public KDialogBase +{ + Q_OBJECT + public: + SolvingRateDialog(const BaseField &field, QWidget *parent); + + private slots: + void step(); + void slotOk(); + void solvingDone(bool success); + + private: + const BaseField &_refField; + BaseField _field; + Solver _solver; + uint _i, _success; + QLabel *_label; + KProgress *_progress; + + static const uint NB_STEPS = 200; +}; + +#endif diff --git a/kmines/solver/test.cpp b/kmines/solver/test.cpp new file mode 100644 index 00000000..dd56d7a0 --- /dev/null +++ b/kmines/solver/test.cpp @@ -0,0 +1,45 @@ +/** A program to test advisory library */ + +#include "bfield.h" +#include "headerP.h" + +#define W 10 +#define H 10 + +int main(int argc, char *argv[]) +{ + long seed = (argc<2 ? time(0) : atoi(argv[1])); + cout << "seed = " << seed << endl; + + BaseField f(seed); + f.reset(W, H, 10); + + KRandomSequence random(seed); + Coord c(random.getLong(W), random.getLong(H)); + f.doReveal(c, 0, 0); + + CoordSet sp; + AdviseFull::ProbabilityMap pm; + + AdviseFast::FactSet facts(&f); + AdviseFull::adviseFull(&facts, &sp, &pm); + + float pic[H][W]; + + for(uint i=0; isecond.second][pmi->second.first] = pmi->first; + + QString s; + for(uint i=0;i +#include + +#include "bfield.h" +#include "solver.h" +#include "headerP.h" + +int main(int argc, char *argv[]) +{ + if ( argc!=4 ) + qFatal("Arguments: width height nbMines"); + + long seed = time(0); + cout << "seed = " << seed << endl; + + short W, H, M; + W = atoi(argv[1]); assert(W > 0); + H = atoi(argv[2]); assert(H > 0); + M = atoi(argv[3]); assert(M >= 0); // ;) + + BaseField field(seed); + field.reset(W, H, M); + + Solver solver; + if( !solver.solveOneStep(field) ) cout << "OOPS!!" << endl; + else cout << "Solved!" << endl; + + cout << field << endl; + + return 0; +} diff --git a/kmines/status.cpp b/kmines/status.cpp new file mode 100644 index 00000000..a3d5f061 --- /dev/null +++ b/kmines/status.cpp @@ -0,0 +1,478 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "status.h" +#include "status.moc" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "solver/solver.h" +#include "dialogs.h" +#include "version.h" + + +Status::Status(QWidget *parent) + : QWidget(parent, "status"), _oldLevel(Level::Easy) +{ + _timer = new QTimer(this); + connect(_timer, SIGNAL(timeout()), SLOT(replayStep())); + + _solver = new Solver(this); + connect(_solver, SIGNAL(solvingDone(bool)), SLOT(solvingDone(bool))); + +// top layout + QGridLayout *top = new QGridLayout(this, 2, 5, 10, 10); + top->setColStretch(1, 1); + top->setColStretch(3, 1); + +// status bar + // mines left LCD + left = new KGameLCD(5, this); + left->setFrameStyle(QFrame::Panel | QFrame::Sunken); + left->setDefaultBackgroundColor(black); + left->setDefaultColor(white); + QWhatsThis::add(left, i18n("Mines left.
" + "It turns red " + "when you have flagged more cases than " + "present mines.
")); + top->addWidget(left, 0, 0); + + // smiley + smiley = new Smiley(this); + connect(smiley, SIGNAL(clicked()), SLOT(smileyClicked())); + smiley->setFocusPolicy(QWidget::NoFocus); + QWhatsThis::add(smiley, i18n("Press to start a new game")); + top->addWidget(smiley, 0, 2); + + // digital clock LCD + dg = new DigitalClock(this); + QWhatsThis::add(dg, i18n("Time elapsed.
" + "It turns blue " + "if it is a highscore " + "and red " + "if it is the best time.
")); + top->addWidget(dg, 0, 4); + +// mines field + _fieldContainer = new QWidget(this); + QGridLayout *g = new QGridLayout(_fieldContainer, 1, 1); + _field = new Field(_fieldContainer); + _field->readSettings(); + g->addWidget(_field, 0, 0, AlignCenter); + connect( _field, SIGNAL(updateStatus(bool)), SLOT(updateStatus(bool)) ); + connect(_field, SIGNAL(gameStateChanged(GameState)), + SLOT(gameStateChangedSlot(GameState)) ); + connect(_field, SIGNAL(setMood(Mood)), smiley, SLOT(setMood(Mood))); + connect(_field, SIGNAL(setCheating()), dg, SLOT(setCheating())); + connect(_field,SIGNAL(addAction(const KGrid2D::Coord &, Field::ActionType)), + SLOT(addAction(const KGrid2D::Coord &, Field::ActionType))); + QWhatsThis::add(_field, i18n("Mines field.")); + +// resume button + _resumeContainer = new QWidget(this); + g = new QGridLayout(_resumeContainer, 1, 1); + QFont f = font(); + f.setBold(true); + QPushButton *pb + = new QPushButton(i18n("Press to Resume"), _resumeContainer); + pb->setFont(f); + connect(pb, SIGNAL(clicked()), SIGNAL(pause())); + g->addWidget(pb, 0, 0, AlignCenter); + + _stack = new QWidgetStack(this); + _stack->addWidget(_fieldContainer); + _stack->addWidget(_resumeContainer); + _stack->raiseWidget(_fieldContainer); + top->addMultiCellWidget(_stack, 1, 1, 0, 4); +} + +void Status::smileyClicked() +{ + if ( _field->gameState()==Paused ) emit pause(); + else restartGame(); +} + +void Status::newGame(int t) +{ + if ( _field->gameState()==Paused ) emit pause(); + Level::Type type = (Level::Type)t; + Settings::setLevel(type); + if ( type!=Level::Custom ) newGame( Level(type) ); + else newGame( Settings::customLevel() ); +} + +void Status::newGame(const Level &level) +{ + _timer->stop(); + if ( level.type()!=Level::Custom ) + KExtHighscore::setGameType(level.type()); + _field->setLevel(level); +} + +bool Status::checkBlackMark() +{ + bool bm = ( _field->gameState()==Playing ); + if (bm) KExtHighscore::submitScore(KExtHighscore::Lost, this); + return bm; +} + +void Status::restartGame() +{ + if ( _field->gameState()==Paused ) emit pause(); + else if ( _field->gameState()==Replaying ) { + _timer->stop(); + _field->setLevel(_oldLevel); + } else { + bool bm = checkBlackMark(); + _field->reset(bm); + } +} + +void Status::settingsChanged() +{ + _field->readSettings(); + + if ( Settings::level()!=Level::Custom ) return; + Level l = Settings::customLevel(); + if ( l==_field->level() ) return; + if ( _field->gameState()==Paused ) emit pause(); + newGame(l); +} + +void Status::updateStatus(bool mine) +{ + int r = _field->nbMines() - _field->nbMarked(); + QColor color = (r<0 && !_field->isSolved() ? red : white); + left->setColor(color); + left->display(r); + + if ( _field->isSolved() && !mine ) + gameStateChanged(GameOver, true); // ends only for wins +} + +void Status::setGameOver(bool won) +{ + if ( !won ) + KNotifyClient::event(winId(), "explosion", i18n("Explosion!")); + _field->showAllMines(won); + smiley->setMood(won ? Happy : Sad); + if ( _field->gameState()==Replaying ) return; + + _field->setGameOver(); + dg->stop(); + if ( _field->level().type()!=Level::Custom && !dg->cheating() ) { + if (won) KExtHighscore::submitScore(dg->score(), this); + else KExtHighscore::submitScore(KExtHighscore::Lost, this); + } + + KNotifyClient::event(winId(), won ? "won" : "lost", + won ? i18n("Game won!") : i18n("Game lost!")); + + // game log + _logRoot.setAttribute("count", dg->nbActions()); + + if ( Settings::magicReveal() ) + _logRoot.setAttribute("complete_reveal", "true"); + QString sa = "none"; + if ( _field->solvingState()==Solved ) sa = "solving"; + else if ( _field->solvingState()==Advised ) sa = "advising"; + _logRoot.setAttribute("solver", sa); + + QDomElement f = _log.createElement("Field"); + _logRoot.appendChild(f); + QDomText data = _log.createTextNode(_field->string()); + f.appendChild(data); +} + +void Status::setStopped() +{ + smiley->setMood(Normal); + updateStatus(false); + bool custom = ( _field->level().type()==Level::Custom ); + dg->reset(custom); + _field->setSolvingState(Regular); +} + +void Status::setPlaying() +{ + smiley->setMood(Normal); + dg->start(); + if ( _field->gameState()==Paused ) return; // do not restart game log... + + // game log + const Level &level = _field->level(); + _log = QDomDocument("kmineslog"); + _logRoot = _log.createElement("kmineslog"); + _logRoot.setAttribute("version", SHORT_VERSION); + QDateTime date = QDateTime::currentDateTime(); + _logRoot.setAttribute("date", date.toString(Qt::ISODate)); + _logRoot.setAttribute("width", level.width()); + _logRoot.setAttribute("height", level.height()); + _logRoot.setAttribute("mines", level.nbMines()); + _log.appendChild(_logRoot); + _logList = _log.createElement("ActionList"); + _logRoot.appendChild(_logList); +} + +void Status::gameStateChanged(GameState state, bool won) +{ + QWidget *w = _fieldContainer; + + switch (state) { + case Playing: + setPlaying(); + break; + case GameOver: + setGameOver(won); + break; + case Paused: + smiley->setMood(Sleeping); + dg->stop(); + w = _resumeContainer; + break; + case Stopped: + case Init: + setStopped(); + break; + case Replaying: + smiley->setMood(Normal); + break; + case NB_STATES: + Q_ASSERT(false); + break; + } + + _stack->raiseWidget(w); + emit gameStateChangedSignal(state); +} + +void Status::addAction(const KGrid2D::Coord &c, Field::ActionType type) +{ + QDomElement action = _log.createElement("Action"); + action.setAttribute("time", dg->pretty()); + action.setAttribute("column", c.first); + action.setAttribute("line", c.second); + action.setAttribute("type", Field::ACTION_DATA[type].name); + _logList.appendChild(action); + dg->addAction(); +} + +void Status::advise() +{ + int res = KMessageBox::warningContinueCancel(this, + i18n("When the solver gives " + "you advice, your score will not be added to the highscores."), + QString::null, QString::null, "advice_warning"); + if ( res==KMessageBox::Cancel ) return; + dg->setCheating(); + float probability; + KGrid2D::Coord c = _solver->advise(*_field, probability); + _field->setAdvised(c, probability); +} + +void Status::solve() +{ + dg->setCheating(); + _solver->solve(*_field, false); + _field->setSolvingState(Solved); +} + +void Status::solvingDone(bool success) +{ + if ( !success ) gameStateChanged(GameOver, false); +} + +void Status::solveRate() +{ + SolvingRateDialog sd(*_field, this); + sd.exec(); +} + +void Status::viewLog() +{ + KDialogBase d(this, "view_log", true, i18n("View Game Log"), + KDialogBase::Close, KDialogBase::Close); + QTextEdit *view = new QTextEdit(&d); + view->setReadOnly(true); + view->setTextFormat(PlainText); + view->setText(_log.toString()); + d.setMainWidget(view); + d.resize(500, 400); + d.exec(); +} + +void Status::saveLog() +{ + KURL url = KFileDialog::getSaveURL(QString::null, QString::null, this); + if ( url.isEmpty() ) return; + if ( KIO::NetAccess::exists(url, false, this) ) { + KGuiItem gi = KStdGuiItem::save(); + gi.setText(i18n("Overwrite")); + int res = KMessageBox::warningYesNo(this, + i18n("The file already exists. Overwrite?"), + i18n("File Exists"), gi, KStdGuiItem::cancel()); + if ( res==KMessageBox::No ) return; + } + KTempFile tmp; + (*tmp.textStream()) << _log.toString(); + tmp.close(); + KIO::NetAccess::upload(tmp.name(), url, this); + tmp.unlink(); +} + +void Status::loadLog() +{ + KURL url = KFileDialog::getOpenURL(QString::null, QString::null, this); + if ( url.isEmpty() ) return; + QString tmpFile; + bool success = false; + QDomDocument doc; + if( KIO::NetAccess::download(url, tmpFile, this) ) { + QFile file(tmpFile); + if ( file.open(IO_ReadOnly) ) { + int errorLine; + bool ok = doc.setContent(&file, 0, &errorLine); + if ( !ok ) { + KMessageBox::sorry(this, i18n("Cannot read XML file on line %1") + .arg(errorLine)); + return; + } + success = true; + } + KIO::NetAccess::removeTempFile(tmpFile); + + } + if ( !success ) { + KMessageBox::sorry(this, i18n("Cannot load file.")); + return; + } + + if ( !checkLog(doc) ) + KMessageBox::sorry(this, i18n("Log file not recognized.")); + else { + _log = doc; + _logRoot = doc.namedItem("kmineslog").toElement(); + emit gameStateChangedSignal(GameOver); + } +} + +bool Status::checkLog(const QDomDocument &doc) +{ + // check root element + if ( doc.doctype().name()!="kmineslog" ) return false; + QDomElement root = doc.namedItem("kmineslog").toElement(); + if ( root.isNull() ) return false; + bool ok; + uint w = root.attribute("width").toUInt(&ok); + if ( !ok || w>CustomConfig::maxWidth || wCustomConfig::maxHeight || hLevel::maxNbMines(w, h) ) return false; + + // check field + QDomElement field = root.namedItem("Field").toElement(); + if ( field.isNull() ) return false; + QString ftext = field.text(); + if ( !BaseField::checkField(w, h, nb, ftext) ) return false; + + // check action list + QDomElement list = root.namedItem("ActionList").toElement(); + if ( list.isNull() ) return false; + QDomNodeList actions = list.elementsByTagName("Action"); + if ( actions.count()==0 ) return false; + for (uint i=0; i=h ) return false; + uint j = a.attribute("column").toUInt(&ok); + if ( !ok || j>=w ) return false; + QString type = a.attribute("type"); + uint k = 0; + for (; klevel(); + newGame(level); + _field->setReplayField(f.toElement().text()); + QString s = _logRoot.attribute("complete_reveal"); + _completeReveal = ( s=="true" ); + + f = _logRoot.namedItem("ActionList"); + _actions = f.toElement().elementsByTagName("Action"); + _index = 0; + _timer->start(500); +} + +void Status::replayStep() +{ + if ( _index>=_actions.count() ) { + _timer->stop(); + _actions = QDomNodeList(); + return; + } + + _timer->changeInterval(200); + QDomElement a = _actions.item(_index).toElement(); + dg->setTime(a.attribute("time")); + uint i = a.attribute("column").toUInt(); + uint j = a.attribute("line").toUInt(); + QString type = a.attribute("type"); + for (uint k=0; kdoAction((Field::ActionType)k, + KGrid2D::Coord(i, j), _completeReveal); + break; + } + _index++; +} diff --git a/kmines/status.h b/kmines/status.h new file mode 100644 index 00000000..6fd06a76 --- /dev/null +++ b/kmines/status.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef STATUS_H +#define STATUS_H + +#include + +#include "field.h" + +class Smiley; +class KGameLCD; +class DigitalClock; +class Solver; +class QWidgetStack; +class QTimer; + +class Status : public QWidget, public KMines +{ + Q_OBJECT + public : + Status(QWidget *parent); + + const Level ¤tLevel() const { return _field->level(); } + bool isPlaying() const { return _field->gameState()==Playing; } + void settingsChanged(); + Field *field() { return _field; } + + bool checkBlackMark(); + + signals: + void pause(); + void gameStateChangedSignal(KMines::GameState); + + public slots: + void newGame(int type); + void restartGame(); + void updateStatus(bool); + void pauseGame() { _field->pause(); } + + void moveUp() { _field->moveCursor(KGrid2D::SquareBase::Up); } + void moveDown() { _field->moveCursor(KGrid2D::SquareBase::Down); } + void moveLeft() { _field->moveCursor(KGrid2D::SquareBase::Left); } + void moveRight() { _field->moveCursor(KGrid2D::SquareBase::Right); } + void moveLeftEdge() { _field->moveToEdge(KGrid2D::SquareBase::Left); } + void moveRightEdge() { _field->moveToEdge(KGrid2D::SquareBase::Right); } + void moveTop() { _field->moveToEdge(KGrid2D::SquareBase::Up); } + void moveBottom() { _field->moveToEdge(KGrid2D::SquareBase::Down); } + void reveal() { _field->doReveal(); } + void mark() { _field->doMark(); } + void autoReveal() { _field->keyboardAutoReveal(); } + + void advise(); + void solve(); + void solveRate(); + void addAction(const KGrid2D::Coord &, Field::ActionType type); + + void viewLog(); + void replayLog(); + void saveLog(); + void loadLog(); + + private slots: + void gameStateChangedSlot(GameState state) + { gameStateChanged(state, false); } + void smileyClicked(); + void solvingDone(bool success); + void replayStep(); + + private: + Field *_field; + QWidget *_fieldContainer, *_resumeContainer; + QWidgetStack *_stack; + + Smiley *smiley; + KGameLCD *left; + DigitalClock *dg; + Solver *_solver; + + QDomDocument _log; + QDomElement _logRoot, _logList; + QDomNodeList _actions; + uint _index; + bool _completeReveal; + Level _oldLevel; + QTimer *_timer; + + void setGameOver(bool won); + void setStopped(); + void setPlaying(); + void newGame(const Level &); + void gameStateChanged(GameState, bool won); + static bool checkLog(const QDomDocument &); +}; + +#endif // STATUS_H diff --git a/kmines/version.h b/kmines/version.h new file mode 100644 index 00000000..0e8f3568 --- /dev/null +++ b/kmines/version.h @@ -0,0 +1,5 @@ +#define SHORT_VERSION "2.1.10" +#define LONG_VERSION "2.1.10 (25 Aug 2005)" +#define COPYLEFT "(c) 1996-2005, Nicolas Hadacek\n(c) 2001, Mikhail Kourinny" +#define EMAIL "hadacek@kde.org" +#define HOMEPAGE "http://kmines.sourceforge.net/" diff --git a/knetwalk/AUTHORS b/knetwalk/AUTHORS new file mode 100644 index 00000000..c856c23b --- /dev/null +++ b/knetwalk/AUTHORS @@ -0,0 +1,6 @@ +Original author: +QNetwalk, Copyright (C) 2004, Andi Peredri + +Ported to kde by: +Thomas Nagy +Cell-locking implemented by Reinhold Kainhofer diff --git a/knetwalk/Makefile.am b/knetwalk/Makefile.am new file mode 100644 index 00000000..1bfdcf48 --- /dev/null +++ b/knetwalk/Makefile.am @@ -0,0 +1 @@ +SUBDIRS=src diff --git a/knetwalk/TODO b/knetwalk/TODO new file mode 100644 index 00000000..749f01f3 --- /dev/null +++ b/knetwalk/TODO @@ -0,0 +1,5 @@ +* Fiber lighting should be made more evident (lighted/unlighted contrast) + The contrast between on/off is not blatant +* Connect all your friends so you can talk on the phone together +* Make more obvious that even monitors or the server can change orientation +* Tiles size should be configurable (larger) - use kzoomwindow diff --git a/knetwalk/configure.in.in b/knetwalk/configure.in.in new file mode 100644 index 00000000..2c5f0ffd --- /dev/null +++ b/knetwalk/configure.in.in @@ -0,0 +1,2 @@ + +#MIN_CONFIG(3.3) diff --git a/knetwalk/src/Makefile.am b/knetwalk/src/Makefile.am new file mode 100644 index 00000000..630095f6 --- /dev/null +++ b/knetwalk/src/Makefile.am @@ -0,0 +1,71 @@ +SUBDIRS = pics sounds + +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) + + +bin_PROGRAMS = knetwalk +knetwalk_LDFLAGS = $(all_libraries) $(KDE_RPATH) +knetwalk_LDADD = $(LIB_KDEGAMES) $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_QT) +knetwalk_DEPENDENCIES = $(LIB_KDEGAMES_DEP) +knetwalk_SOURCES = cell.cpp highscores.cpp main.cpp mainwindow.cpp settings.kcfgc + +xdg_apps_DATA = knetwalk.desktop + +knetwalk_METASOURCES = AUTO +rcdir = $(kde_datadir)/knetwalk +rc_DATA = knetwalkui.rc + +appdatadir = $(kde_datadir)/knetwalk +appdata_DATA = eventsrc + +messages: rc.cpp + $(XGETTEXT) rc.cpp *.cpp -o $(podir)/knetwalk.pot + +# for system-wide highscore file +DESTBIN = $(DESTDIR)$(bindir)/$(bin_PROGRAMS) +DESTHIGHSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY) +DESTSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY)/$(bin_PROGRAMS).scores + +install-data-local: + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + echo "********************************************************" ;\ + echo "" ;\ + echo "This game is installed sgid \"games\" to use the" ;\ + echo "system-wide highscore file (in "$(HIGHSCORE_DIRECTORY)")." ;\ + echo "" ;\ + echo "If the system-wide highscore file does not exist, it is" ;\ + echo "created with the correct ownership and permissions. See the" ;\ + echo "INSTALL file in \"kdegames/libkdegames/highscore\" for details." ;\ + echo "" ;\ + echo "********************************************************" ;\ + fi + +install-exec-hook: + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + chown $(highscore_user):$(highscore_group) $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + mkdir -p $(DESTHIGHSCORES) && \ + chown $(highscore_user):$(highscore_group) $(DESTHIGHSCORES) \ + && chmod 750 $(DESTHIGHSCORES) \ + || echo "Error: Could not create the highscore directory with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + chown $(highscore_user):$(highscore_group) $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test ${setgid} = true; then \ + chmod 2755 $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + touch $(DESTSCORES) && chown $(highscore_user):$(highscore_group) $(DESTSCORES) \ + && chmod 0660 $(DESTSCORES) \ + || echo "Error: Could not create system-wide highscore file with correct permissions !!" ;\ + fi + diff --git a/knetwalk/src/SConscript b/knetwalk/src/SConscript new file mode 100644 index 00000000..3be117e9 --- /dev/null +++ b/knetwalk/src/SConscript @@ -0,0 +1,23 @@ +#! /usr/bin/env python +Import('env') +myenv=env.Copy() +knetwalk_sources = [ +'cell.cpp', 'main.cpp', 'mainwindow.cpp', 'settings.kcfgc', 'highscores.cpp' +] +myenv.KDEprogram( "knetwalk", knetwalk_sources) +myenv.KDEaddpaths_includes( ['./', '#./']) +myenv.KDEaddlibs( ['qt-mt', 'kio', 'kdecore', 'kdegames']) +myenv.KDEinstall('KDEDATA', 'knetwalk', 'knetwalkui.rc') +myenv.KDEinstall('KDEDATA', 'knetwalk', 'eventsrc') +myenv.KDEinstall('KDEXDG', '', 'knetwalk.desktop') +myenv.KDEinstall('KDEKCFG', '', 'knetwalk.kcfg') + +soundfiles=""" +sounds/click.wav +sounds/connect.wav +sounds/start.wav +sounds/turn.wav +sounds/win.wav +""".split() + +myenv.KDEinstall('KDEDATA', 'knetwalk/sounds', soundfiles) diff --git a/knetwalk/src/cell.cpp b/knetwalk/src/cell.cpp new file mode 100644 index 00000000..4d561cec --- /dev/null +++ b/knetwalk/src/cell.cpp @@ -0,0 +1,241 @@ +/*************************************************************************** + * Copyright (C) 2004, 2005 Andi Peredri * + * andi@ukr.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 * + * as published by the Free Software Foundation (see COPYING) * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + ***************************************************************************/ + +#include +#include + +#include +#include +#include + +#include "cell.h" + +Cell::PixmapMap Cell::connectedpixmap; +Cell::PixmapMap Cell::disconnectedpixmap; + +void Cell::initPixmaps() +{ + typedef QMap NamesMap; + NamesMap names; + names[L] = "0001"; + names[D] = "0010"; + names[D|L] = "0011"; + names[R] = "0100"; + names[R|L] = "0101"; + names[R|D] = "0110"; + names[R|D|L] = "0111"; + names[U] = "1000"; + names[U|L] = "1001"; + names[U|D] = "1010"; + names[U|D|L] = "1011"; + names[U|R] = "1100"; + names[U|R|L] = "1101"; + names[U|R|D] = "1110"; + + NamesMap::ConstIterator it; + for(it = names.constBegin(); it != names.constEnd(); ++it) + { + connectedpixmap[it.key()]=new QPixmap(KGlobal::iconLoader()->loadIcon( + locate("data","knetwalk/cable"+it.data()+".png"), KIcon::NoGroup, 32) ); + + QImage image = connectedpixmap[it.key()]->convertToImage(); + for(int y = 0; y < image.height(); y++) + { + QRgb* line = (QRgb*)image.scanLine(y); + for(int x = 0; x < image.width(); x++) + { + QRgb pix = line[x]; + if(qAlpha(pix) == 255) + { + int g = (255 + 4 * qGreen(pix)) / 5; + int b = (255 + 4 * qBlue(pix)) / 5; + int r = (255 + 4 * qRed(pix)) / 5; + line[x] = qRgb(r, g, b); + } + } + } + disconnectedpixmap[it.key()] = new QPixmap(image); + } +} + +Cell::Cell(QWidget* parent, int i) : QWidget(parent, 0, WNoAutoErase) +{ + angle = 0; + light = 0; + iindex = i; + ddirs = Free; + changed = true; + connected = false; + root = false; + locked = false; +} + +int Cell::index() const +{ + return iindex; +} + +Cell::Dirs Cell::dirs() const +{ + return ddirs; +} + +bool Cell::isConnected() const +{ + return connected; +} + +bool Cell::isRotated() const +{ + return angle; +} + +bool Cell::isLocked() const +{ + return locked; +} + +void Cell::setLocked( bool newlocked ) +{ + if ( locked == newlocked ) return; + locked = newlocked; + changed = true; + update(); +} + + +void Cell::setDirs(Dirs d) +{ + if(ddirs == d) return; + ddirs = d; + changed = true; + update(); +} + +void Cell::setConnected(bool b) +{ + if(connected == b) return; + connected = b; + changed = true; + update(); +} + +void Cell::setRoot(bool b) +{ + if(root == b) return; + root = b; + changed = true; + update(); +} + +void Cell::setLight(int l) +{ + light = l; + changed = true; + update(); +} + +void Cell::paintEvent(QPaintEvent*) +{ + if(changed) + { + changed = false; + if ( locked ) { + pixmap = KGlobal::iconLoader()->loadIcon(locate("data", "knetwalk/background_locked.png"), KIcon::NoGroup, 32); + } else { + pixmap = KGlobal::iconLoader()->loadIcon(locate("data", "knetwalk/background.png"), KIcon::NoGroup, 32); + } + + QPainter paint; + paint.begin(&pixmap); + + if(light) + { + paint.setPen(QPen(white, 5)); + paint.drawLine(0, width() - light, width(), 2 * width() - light); + } + + if((ddirs != Free) && (ddirs != None)) + { + double offset = 0; + if(angle) + { + offset = width() / 2; + paint.translate(offset, offset); + paint.rotate(angle); + } + + if(connected) + paint.drawPixmap(int(-offset), int(-offset), *connectedpixmap[ddirs]); + else paint.drawPixmap(int(-offset), int(-offset), *disconnectedpixmap[ddirs]); + paint.resetXForm(); + + QPixmap pix; + + if(root) + { + pix=KGlobal::iconLoader()->loadIcon(locate("data", "knetwalk/server.png"), KIcon::NoGroup, 32); + } + else if(ddirs == U || ddirs == L || ddirs == D || ddirs == R) + { + if(connected) + pix=KGlobal::iconLoader()->loadIcon(locate("data","knetwalk/computer2.png"),KIcon::NoGroup,32); + else + pix=KGlobal::iconLoader()->loadIcon(locate("data","knetwalk/computer1.png"),KIcon::NoGroup,32); + } + paint.drawPixmap(0, 0, pix); + } + paint.end(); + } + bitBlt(this, 0, 0, &pixmap); +} + +void Cell::mousePressEvent(QMouseEvent* e) +{ + if(e->button() == LeftButton) + emit lClicked(iindex); + else if(e->button() == RightButton) + emit rClicked(iindex); + else if(e->button() == MidButton) + emit mClicked(iindex); +} + +void Cell::rotate(int a) +{ + angle += a; + changed = true; + while(angle >= 45) + { + angle -= 90; + int newdirs = Free; + if(ddirs & U) newdirs |= R; + if(ddirs & R) newdirs |= D; + if(ddirs & D) newdirs |= L; + if(ddirs & L) newdirs |= U; + setDirs(Dirs(newdirs)); + } + while(angle < -45) + { + angle += 90; + int newdirs = Free; + if(ddirs & U) newdirs |= L; + if(ddirs & R) newdirs |= U; + if(ddirs & D) newdirs |= R; + if(ddirs & L) newdirs |= D; + setDirs(Dirs(newdirs)); + } + update(); +} + +#include "cell.moc" diff --git a/knetwalk/src/cell.h b/knetwalk/src/cell.h new file mode 100644 index 00000000..2dd5009c --- /dev/null +++ b/knetwalk/src/cell.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2004, 2005 Andi Peredri * + * andi@ukr.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 * + * as published by the Free Software Foundation (see COPYING) * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + ***************************************************************************/ + +#ifndef CELL_H +#define CELL_H + +#include +#include + +class Cell : public QWidget +{ + Q_OBJECT + public: + enum Dirs { Free = 0, U = 1, R = 2, D = 4, L = 8, None = 16 }; + Cell(QWidget* parent, int i); + int index() const; + void rotate(int a); + void setDirs(Dirs d); + void setRoot(bool b); + void setLight(int l); + void setConnected(bool b); + void setLocked( bool newlocked = true ); + bool isConnected() const; + bool isRotated() const; + bool isLocked() const; + Dirs dirs() const; + static void initPixmaps(); + signals: + void lClicked(int); + void rClicked(int); + void mClicked(int); + protected: + virtual void paintEvent(QPaintEvent*); + virtual void mousePressEvent(QMouseEvent*); + private: + typedef QMap PixmapMap; + static PixmapMap connectedpixmap; + static PixmapMap disconnectedpixmap; + int angle; + int light; + int iindex; + bool connected; + bool changed; + bool root; + bool locked; + Dirs ddirs; + QPixmap pixmap; +}; + +#endif diff --git a/knetwalk/src/defines.h b/knetwalk/src/defines.h new file mode 100644 index 00000000..ff1f122f --- /dev/null +++ b/knetwalk/src/defines.h @@ -0,0 +1,25 @@ +/*************************************************************************** + * Copyright (C) 2005 Thomas Nagy * + * tnagyemail-mail@yahoo.fr * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 * + * as published by the Free Software Foundation (see COPYING) * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + ***************************************************************************/ + +#ifndef _DEFINES_H +#define _DEFINES_H + +const char *levels[4] = { +I18N_NOOP("Novice"), +I18N_NOOP("Normal"), +I18N_NOOP("Expert"), +I18N_NOOP("Master") +}; + +#endif // _DEFINES_H diff --git a/knetwalk/src/eventsrc b/knetwalk/src/eventsrc new file mode 100644 index 00000000..fbbd28c3 --- /dev/null +++ b/knetwalk/src/eventsrc @@ -0,0 +1,258 @@ +[!Global!] +IconName=knetwalk +Comment=knetwalk +Comment[bn]=কে-নেটওয়াক +Comment[fi]=Knetwalk +Comment[hu]=KNetWalk +Comment[sv]=Knetwalk + +[clicksound] +Name=Click +Name[be]=ПÑтрычка +Name[bg]=Щракване +Name[bn]=কà§à¦²à¦¿à¦• +Name[br]=Klik +Name[bs]=Klikni +Name[ca]=Clic +Name[cs]=Kliknutí +Name[cy]=Clicio +Name[da]=Klik +Name[de]=Klick +Name[el]=Κλικ +Name[eo]=Kliki +Name[es]=Clic +Name[et]=Klõps +Name[eu]=Klikatu +Name[fa]=Ùشار +Name[fi]=Napsauta +Name[fr]=Clic +Name[ga]=Cliceáil +Name[he]=לחיצה +Name[hr]=Klik +Name[hu]=Kattintás +Name[is]=Smella +Name[it]=Clic +Name[ja]=クリック +Name[km]=ចុច +Name[ko]=í´ë¦­ +Name[lt]=Paspausti +Name[lv]=KlikÅ¡Ä·is +Name[mk]=Кликање +Name[nb]=Klikk +Name[nds]=Klick +Name[ne]=कà¥à¤²à¤¿à¤• गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Klik +Name[nn]=Klikk +Name[pa]=ਦਬਾਓ +Name[pl]=Dobierz +Name[pt]=Carregar +Name[pt_BR]=Clicar +Name[ru]=Щелчок +Name[se]=Coahkkal +Name[sk]=Kliknutie +Name[sl]=Klik +Name[sr]=Кликни +Name[sr@Latn]=Klikni +Name[sv]=Klick +Name[ta]=சொடà¯à®•à¯à®•à¯ +Name[tg]=Ðнгуштзанӣ +Name[tr]=Tık +Name[uk]=Клац +Name[zh_CN]=å•å‡» +Name[zh_TW]=é»žé¸ +default_sound=click.wav +default_presentation=0 + +[connectsound] +Name=Connect +Name[be]=ЗлучÑнне +Name[bg]=Връзка +Name[bn]=সংযোগ সà§à¦¥à¦¾à¦ªà¦¨ করো +Name[br]=Kevreañ +Name[bs]=Spoji se +Name[cs]=Propojení +Name[da]=Forbind +Name[de]=Verbinden +Name[el]=ΣÏνδεση +Name[eo]=Konekti +Name[et]=Ãœhendus +Name[eu]=Konektatu +Name[fa]=اتصال +Name[fi]=Yhdistä +Name[fr]=Connecter +Name[ga]=Nasc +Name[he]=התחבר +Name[hr]=Poveži +Name[hu]=Csatlakozás +Name[is]=Tengjast +Name[it]=Connetti +Name[km]=ážâ€‹áž—្ជាប់ +Name[ko]=ì—°ê²° +Name[lv]=PieslÄ“gties +Name[mk]=Поврзување +Name[nds]=Tokoppeln +Name[ne]=जडान गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Verbinden +Name[pa]=ਜà©à©œà©‹ +Name[pl]=PoÅ‚Ä…cz +Name[pt]=Ligar +Name[pt_BR]=Conectar +Name[ru]=Подключение +Name[sl]=Povezava +Name[sr]=Повежи Ñе +Name[sr@Latn]=Poveži se +Name[sv]=Anslut +Name[uk]=З'єднати +Name[wa]=Raloyî +Name[zh_TW]=é€£çµ +default_sound=connect.wav +default_presentation=0 + +[winsound] +Name=Game won +Name[ar]=ربحت اللعبة +Name[be]=Перамога +Name[bg]=Спечелихте +Name[bn]=খেলা জিতেছেন +Name[br]=Gounezet eo ar c'hoari +Name[bs]=Pobjeda +Name[ca]=Partida guanyada +Name[cs]=Vyhraná hra +Name[cy]=Gêm wedi ei ennill +Name[da]=Spillet vundet +Name[de]=Spiel gewonnen +Name[el]=Παιχνίδι κεÏδήθηκε +Name[eo]=Ludo venkita +Name[es]=Partida ganada +Name[et]=Mäng läbi, sina võitsid +Name[eu]=Jokoa irabazi da +Name[fa]=برد بازی +Name[fi]=Peli voitettu +Name[fr]=Partie gagnée +Name[gl]=Xogo gañado +Name[he]=ניצחת! +Name[hi]=खेल में जीत हà¥à¤ˆ +Name[hr]=Igra je dobivena +Name[hu]=GyÅ‘zelem +Name[is]=Leikur unninn +Name[it]=Partita vinta +Name[ja]=ゲームã«å‹ã¡ +Name[km]=ល្បែង​បាន​ឈ្នះ +Name[ko]=게임ì—ì„œ ì´ê¹€ +Name[lt]=Žaidimas laimÄ—tas +Name[lv]=SpÄ“le uzvarÄ“ta +Name[mk]=Играта е добиена +Name[nb]=Du vant +Name[nds]=Speel wunnen +Name[ne]=खेल जितà¥à¤¨à¥ भयो +Name[nl]=Spel gewonnen +Name[nn]=Du vann +Name[pa]=ਖੇਡ ਜਿੱਤੀ +Name[pl]=Gra wygrana +Name[pt]=Jogo ganho +Name[pt_BR]=Jogo ganho +Name[ro]=Joc cîştigat +Name[ru]=Победа +Name[se]=Don vuitet +Name[sk]=Vyhraná hra +Name[sl]=Igra je dobljena +Name[sr]=Игра је добијена +Name[sr@Latn]=Igra je dobijena +Name[sv]=Du vann spelet +Name[ta]=ஆடà¯à®Ÿà®®à¯ ஜெயிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Name[tg]=Дар бозӣ ғолиб омадед +Name[tr]=Oyun kazanıldı +Name[uk]=Гру виграно +Name[wa]=Djeu wangnî +Name[zh_CN]=æ‚¨èµ¢äº†æ¸¸æˆ +Name[zh_TW]=éŠæˆ²ç²å‹ +default_sound=win.wav +default_presentation=1 + +[startsound] +Name=Connect +Name[be]=ЗлучÑнне +Name[bg]=Връзка +Name[bn]=সংযোগ সà§à¦¥à¦¾à¦ªà¦¨ করো +Name[br]=Kevreañ +Name[bs]=Spoji se +Name[cs]=Propojení +Name[da]=Forbind +Name[de]=Verbinden +Name[el]=ΣÏνδεση +Name[eo]=Konekti +Name[et]=Ãœhendus +Name[eu]=Konektatu +Name[fa]=اتصال +Name[fi]=Yhdistä +Name[fr]=Connecter +Name[ga]=Nasc +Name[he]=התחבר +Name[hr]=Poveži +Name[hu]=Csatlakozás +Name[is]=Tengjast +Name[it]=Connetti +Name[km]=ážâ€‹áž—្ជាប់ +Name[ko]=ì—°ê²° +Name[lv]=PieslÄ“gties +Name[mk]=Поврзување +Name[nds]=Tokoppeln +Name[ne]=जडान गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Verbinden +Name[pa]=ਜà©à©œà©‹ +Name[pl]=PoÅ‚Ä…cz +Name[pt]=Ligar +Name[pt_BR]=Conectar +Name[ru]=Подключение +Name[sl]=Povezava +Name[sr]=Повежи Ñе +Name[sr@Latn]=Poveži se +Name[sv]=Anslut +Name[uk]=З'єднати +Name[wa]=Raloyî +Name[zh_TW]=é€£çµ +default_sound=start.wav +default_presentation=1 + +[turnsound] +Name=Turn +Name[be]=Ход +Name[bg]=Ход +Name[bn]=ঘোরো +Name[bs]=Okreni +Name[cs]=Tah +Name[de]=Runde +Name[el]=ΓÏÏος +Name[eo]=Turni +Name[et]=Käik +Name[eu]=Biratu +Name[fa]=چرخش +Name[fi]=Käännä +Name[fr]=Tourner +Name[he]=סיבוב +Name[hr]=Potez +Name[hu]=Lépés +Name[is]=Beygja +Name[it]=Turno +Name[km]=ážœáŸáž“ +Name[ko]=회전 +Name[lt]=Ä–jimas +Name[lv]=GÄjiens +Name[mk]=Вртење +Name[nds]=Törn +Name[ne]=घà¥à¤®à¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Draaien +Name[pa]=ਵਾਰੀ +Name[pl]=Tura +Name[pt]=Jogada +Name[pt_BR]=Virar +Name[ru]=Поворот +Name[sl]=Poteza +Name[sr]=Круг +Name[sr@Latn]=Krug +Name[sv]=Vänd +Name[uk]=Хід +Name[wa]=Toû +default_sound=turn.wav +default_presentation=0 + diff --git a/knetwalk/src/highscores.cpp b/knetwalk/src/highscores.cpp new file mode 100644 index 00000000..28742662 --- /dev/null +++ b/knetwalk/src/highscores.cpp @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2005 Thomas Nagy * + * tnagyemail-mail@yahoo.fr * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 * + * as published by the Free Software Foundation (see COPYING) * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + ***************************************************************************/ + +#include +#include +#include +#include + +#include "defines.h" +#include "highscores.h" +#include "settings.h" + +namespace KExtHighscore +{ + ExtManager::ExtManager() : Manager(4) + { + setScoreType(Normal); + /* + setWWHighscores(KURL( HOMEPAGE ), VERSION); + setShowStatistics(true); + */ + const uint RANGE[16] = { 0, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160 }; + QMemArray s; + s.duplicate(RANGE, 16); + setScoreHistogram(s, ScoreNotBound); + //Item *item = new Item((uint)0, i18n("Clicks"), Qt::AlignRight); + //addScoreItem("nb_actions", item); + } + + QString ExtManager::gameTypeLabel(uint gameType, LabelType /*type*/) const + { + /*const Level::Data &data = Level::DATA[gameType]; + switch (type) { + case Icon: + case Standard: return data.label; + case I18N: return i18n(level[gameType]); + case WW: return data.wwLabel; + } + return QString::null;*/ + return i18n(levels[gameType]); + } + + void ExtManager::convertLegacy(uint gameType) + { + QString group; + switch (gameType) + { + case Settings::EnumSkill::Novice: group = "Novice level"; break; + case Settings::EnumSkill::Normal: group = "Normal level"; break; + case Settings::EnumSkill::Expert: group = "Expert level"; break; + case Settings::EnumSkill::Master: group = "Master level"; break; + default: Q_ASSERT(false); + } + + KConfigGroupSaver cg(kapp->config(), group); + QString name = cg.config()->readEntry("Name", QString::null); + if ( name.isNull() ) return; + if ( name.isEmpty() ) name = i18n("anonymous"); + int score = cg.config()->readNumEntry("score", 0); + if ( score<=0 ) return; + Score s(Won); + s.setScore(score); + s.setData("name", name); + submitLegacyScore(s); + } + + bool ExtManager::isStrictlyLess(const Score &s1, const Score &s2) const + { + if ( s1.score()==s2.score() ) + // when time is same, favour more clicks (it means auto-reveal + // didn't help so much): + return true; //s1.data("nb_actions").toUInt() +#include + +namespace KExtHighscore +{ + + class KDE_EXPORT ExtManager : public Manager + { + public: + ExtManager(); + + private: + QString gameTypeLabel(uint gameTye, LabelType) const; + void convertLegacy(uint gameType); + bool isStrictlyLess(const Score &s1, const Score &s2) const; + }; +} + +#endif diff --git a/knetwalk/src/knetwalk.desktop b/knetwalk/src/knetwalk.desktop new file mode 100644 index 00000000..6db50658 --- /dev/null +++ b/knetwalk/src/knetwalk.desktop @@ -0,0 +1,14 @@ +[Desktop Entry] +Type=Application +Exec=knetwalk -caption "%c" %i %m +DocPath=knetwalk/index.html +Name=knetwalk +Name[bn]=কে-নেটওয়াক +Name[fi]=Knetwalk +Name[hu]=KNetWalk +Name[pa]=ਕੇ-ਨੈੱਟਵਾਕ +Name[sv]=Knetwalk +Terminal=false +Icon=knetwalk +X-KDE-StartupNotify=true +Categories=Qt;KDE;Game;StrategyGame; diff --git a/knetwalk/src/knetwalk.kcfg b/knetwalk/src/knetwalk.kcfg new file mode 100644 index 00000000..8469b0fb --- /dev/null +++ b/knetwalk/src/knetwalk.kcfg @@ -0,0 +1,23 @@ + + + + + + + + + + + Novice + + + + + + + + + diff --git a/knetwalk/src/knetwalkui.rc b/knetwalk/src/knetwalkui.rc new file mode 100644 index 00000000..fcb44181 --- /dev/null +++ b/knetwalk/src/knetwalkui.rc @@ -0,0 +1,9 @@ + + + + +Main Toolbar + + + + diff --git a/knetwalk/src/main.cpp b/knetwalk/src/main.cpp new file mode 100644 index 00000000..72aaa4f3 --- /dev/null +++ b/knetwalk/src/main.cpp @@ -0,0 +1,72 @@ +/*************************************************************************** + * Copyright (C) 2005, Thomas Nagy * + * tnagyemail-mail@yahoo@fr * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 * + * as published by the Free Software Foundation (see COPYING) * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "highscores.h" +#include "settings.h" +#include "mainwindow.h" + +static const char description[] = +I18N_NOOP("KNetWalk, a game for system administrators."); + +static const char version[] = "1.0"; + +static KCmdLineOptions options[] = +{ + { "Novice", I18N_NOOP( "Start in novice mode" ), 0 }, + { "Normal", I18N_NOOP( "Start in normal mode" ), 0 }, + { "Expert", I18N_NOOP( "Start in expert mode" ), 0 }, + { "Master", I18N_NOOP( "Start in master mode" ), 0 }, + KCmdLineLastOption +}; + +int main(int argc, char ** argv) +{ + KAboutData about("knetwalk", I18N_NOOP("knetwalk"), version, description, + KAboutData::License_GPL, I18N_NOOP("(C) 2004, 2005 Andi Peredri, ported to KDE by Thomas Nagy"), 0, + "tnagyemail-mail@yahoo.fr"); + about.addAuthor( "Andi Peredri", 0, "andi@ukr.net" ); + about.addAuthor( "Thomas Nagy", 0, "tnagy2^8@yahoo.fr" ); + + KCmdLineArgs::init(argc, argv, &about); + KCmdLineArgs::addCmdLineOptions(options); + + KApplication app; + + KGlobal::locale()->insertCatalogue("libkdegames"); + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + if (args->isSet("Novice")) Settings::setSkill(Settings::EnumSkill::Novice); + if (args->isSet("Normal")) Settings::setSkill(Settings::EnumSkill::Normal); + if (args->isSet("Expert")) Settings::setSkill(Settings::EnumSkill::Expert); + if (args->isSet("Master")) Settings::setSkill(Settings::EnumSkill::Master); + args->clear(); + + KHighscore::init("knetwalk"); + KExtHighscore::ExtManager manager; + + + MainWindow* wi = new MainWindow; + app.setMainWidget(wi); + wi->show(); + + return app.exec(); +} + diff --git a/knetwalk/src/mainwindow.cpp b/knetwalk/src/mainwindow.cpp new file mode 100644 index 00000000..15e615ed --- /dev/null +++ b/knetwalk/src/mainwindow.cpp @@ -0,0 +1,421 @@ +/*************************************************************************** + * Copyright (C) 2004, 2005 Andi Peredri * + * andi@ukr.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 * + * as published by the Free Software Foundation (see COPYING) * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "cell.h" +#include "mainwindow.h" + +static QMap contrdirs; + +MainWindow::MainWindow(QWidget *parent, const char* name, WFlags /*fl*/) : + KMainWindow(parent, name, WStyle_NoBorder) +{ + m_clickcount = 0; + + contrdirs[Cell::U] = Cell::D; + contrdirs[Cell::R] = Cell::L; + contrdirs[Cell::D] = Cell::U; + contrdirs[Cell::L] = Cell::R; + + KNotifyClient::startDaemon(); + + KStdGameAction::gameNew(this, SLOT(slotNewGame()), actionCollection()); + + KStdGameAction::highscores(this, SLOT(showHighscores()), actionCollection()); + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + KStdGameAction::configureHighscores(this, SLOT(configureHighscores()), actionCollection()); + + m_levels = KStdGameAction::chooseGameType(0, 0, actionCollection()); + QStringList lst; + lst += i18n("Novice"); + lst += i18n("Normal"); + lst += i18n("Expert"); + lst += i18n("Master"); + m_levels->setItems(lst); + + setFixedSize(minimumSizeHint()); + + statusBar()->insertItem("abcdefghijklmnopqrst: 0 ",1); + setAutoSaveSettings(); + createGUI(); + connect(m_levels, SIGNAL(activated(int)), this, SLOT(newGame(int))); + + + QWhatsThis::add(this, i18n("

Rules of the Game

" + "

You are the system administrator and your goal" + " is to connect each computer to the central server." + "

Click the right mouse button to turn the cable" + " in a clockwise direction, and the left mouse button" + " to turn it in a counter-clockwise direction." + "

Start the LAN with as few turns as possible!")); + + //const int cellsize = KGlobal::iconLoader()->loadIcon("knetwalk/background.png", KIcon::User, 32).width(); + const int cellsize = 32; + const int gridsize = cellsize * MasterBoardSize + 2; + + QGrid* grid = new QGrid(MasterBoardSize, this); + grid->setFrameStyle(QFrame::Panel | QFrame::Sunken); + grid->setFixedSize(gridsize, gridsize); + setCentralWidget(grid); + + Cell::initPixmaps(); + for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) + { + board[i] = new Cell(grid, i); + board[i]->setFixedSize(cellsize, cellsize); + connect(board[i], SIGNAL(lClicked(int)), SLOT(lClicked(int))); + connect(board[i], SIGNAL(rClicked(int)), SLOT(rClicked(int))); + connect(board[i], SIGNAL(mClicked(int)), SLOT(mClicked(int))); + } + srand(time(0)); + + slotNewGame(); +} + +void MainWindow::configureHighscores() +{ + KExtHighscore::configure(this); +} + +void MainWindow::showHighscores() +{ + KExtHighscore::show(this); +} + +void MainWindow::slotNewGame() +{ + newGame( Settings::skill() ); +} + +void MainWindow::newGame(int sk) +{ + if (sk==Settings::EnumSkill::Novice || sk==Settings::EnumSkill::Normal + || sk==Settings::EnumSkill::Expert || sk==Settings::EnumSkill::Master) + { + Settings::setSkill(sk); + } + + if(Settings::skill() == Settings::EnumSkill::Master) wrapped = true; + else wrapped = false; + + KExtHighscore::setGameType(Settings::skill()); + + Settings::writeConfig(); + + m_clickcount = 0; + QString clicks = i18n("Click: %1"); + statusBar()->changeItem(clicks.arg(QString::number(m_clickcount)),1); + + KNotifyClient::event(winId(), "startsound", i18n("New Game")); + for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) + { + board[i]->setDirs(Cell::None); + board[i]->setConnected(false); + board[i]->setRoot(false); + board[i]->setLocked(false); + } + + const int size = (Settings::skill() == Settings::EnumSkill::Novice) ? NoviceBoardSize : + (Settings::skill() == Settings::EnumSkill::Normal) ? NormalBoardSize : + (Settings::skill() == Settings::EnumSkill::Expert) ? ExpertBoardSize : MasterBoardSize; + + const int start = (MasterBoardSize - size) / 2; + const int rootrow = rand() % size; + const int rootcol = rand() % size; + + root = board[(start + rootrow) * MasterBoardSize + start + rootcol]; + root->setConnected(true); + root->setRoot(true); + + while(true) + { + for(int row = start; row < start + size; row++) + for(int col = start; col < start + size; col++) + board[row * MasterBoardSize + col]->setDirs(Cell::Free); + + CellList list; + list.append(root); + if(rand() % 2) addRandomDir(list); + + while(!list.isEmpty()) + { + if(rand() % 2) + { + addRandomDir(list); + if(rand() % 2) addRandomDir(list); + list.remove(list.begin()); + } + else + { + list.append(list.first()); + list.remove(list.begin()); + } + } + + int cells = 0; + for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) + { + Cell::Dirs d = board[i]->dirs(); + if((d != Cell::Free) && (d != Cell::None)) cells++; + } + if(cells >= MinimumNumCells) break; + } + + for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) + board[i]->rotate((rand() % 4) * 90); + updateConnections(); +} + +bool MainWindow::updateConnections() +{ + bool newconnection[MasterBoardSize * MasterBoardSize]; + for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) + newconnection[i] = false; + + CellList list; + if(!root->isRotated()) + { + newconnection[root->index()] = true; + list.append(root); + } + while(!list.isEmpty()) + { + Cell* cell = list.first(); + Cell* ucell = uCell(cell); + Cell* rcell = rCell(cell); + Cell* dcell = dCell(cell); + Cell* lcell = lCell(cell); + + if((cell->dirs() & Cell::U) && ucell && (ucell->dirs() & Cell::D) && + !newconnection[ucell->index()] && !ucell->isRotated()) + { + newconnection[ucell->index()] = true; + list.append(ucell); + } + if((cell->dirs() & Cell::R) && rcell && (rcell->dirs() & Cell::L) && + !newconnection[rcell->index()] && !rcell->isRotated()) + { + newconnection[rcell->index()] = true; + list.append(rcell); + } + if((cell->dirs() & Cell::D) && dcell && (dcell->dirs() & Cell::U) && + !newconnection[dcell->index()] && !dcell->isRotated()) + { + newconnection[dcell->index()] = true; + list.append(dcell); + } + if((cell->dirs() & Cell::L) && lcell && (lcell->dirs() & Cell::R) && + !newconnection[lcell->index()] && !lcell->isRotated()) + { + newconnection[lcell->index()] = true; + list.append(lcell); + } + list.remove(list.begin()); + } + + bool isnewconnection = false; + for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) + { + if(!board[i]->isConnected() && newconnection[i]) + isnewconnection = true; + board[i]->setConnected(newconnection[i]); + } + return isnewconnection; +} + +void MainWindow::addRandomDir(CellList& list) +{ + Cell* cell = list.first(); + Cell* ucell = uCell(cell); + Cell* rcell = rCell(cell); + Cell* dcell = dCell(cell); + Cell* lcell = lCell(cell); + + typedef QMap CellMap; + CellMap freecells; + + if(ucell && ucell->dirs() == Cell::Free) freecells[Cell::U] = ucell; + if(rcell && rcell->dirs() == Cell::Free) freecells[Cell::R] = rcell; + if(dcell && dcell->dirs() == Cell::Free) freecells[Cell::D] = dcell; + if(lcell && lcell->dirs() == Cell::Free) freecells[Cell::L] = lcell; + if(freecells.isEmpty()) return; + + CellMap::ConstIterator it = freecells.constBegin(); + for(int i = rand() % freecells.count(); i > 0; --i) ++it; + + cell->setDirs(Cell::Dirs(cell->dirs() | it.key())); + it.data()->setDirs(contrdirs[it.key()]); + list.append(it.data()); +} + +Cell* MainWindow::uCell(Cell* cell) const +{ + if(cell->index() >= MasterBoardSize) + return board[cell->index() - MasterBoardSize]; + else if(wrapped) + return board[MasterBoardSize * (MasterBoardSize - 1) + cell->index()]; + else return 0; +} + +Cell* MainWindow::dCell(Cell* cell) const +{ + if(cell->index() < MasterBoardSize * (MasterBoardSize - 1)) + return board[cell->index() + MasterBoardSize]; + else if(wrapped) + return board[cell->index() - MasterBoardSize * (MasterBoardSize - 1)]; + else return 0; +} + +Cell* MainWindow::lCell(Cell* cell) const +{ + if(cell->index() % MasterBoardSize > 0) + return board[cell->index() - 1]; + else if(wrapped) + return board[cell->index() - 1 + MasterBoardSize]; + else return 0; +} + +Cell* MainWindow::rCell(Cell* cell) const +{ + if(cell->index() % MasterBoardSize < MasterBoardSize - 1) + return board[cell->index() + 1]; + else if(wrapped) + return board[cell->index() + 1 - MasterBoardSize]; + else return 0; +} + +void MainWindow::lClicked(int index) +{ + rotate(index, true); +} + +void MainWindow::rClicked(int index) +{ + rotate(index, false); +} + +void MainWindow::mClicked(int index) +{ + board[index]->setLocked( !board[index]->isLocked() ); +} + +void MainWindow::rotate(int index, bool toleft) +{ + const Cell::Dirs d = board[index]->dirs(); + if((d == Cell::Free) || (d == Cell::None) || isGameOver() || board[index]->isLocked() ) + { + KNotifyClient::event(winId(), "clicksound"); + blink(index); + } + else + { + KNotifyClient::event(winId(), "turnsound"); + board[index]->rotate(toleft ? -6 : 6); + updateConnections(); + for(int i = 0; i < 14; i++) + { + kapp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput); + QTimer::singleShot(20, board[index], SLOT(update())); + kapp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput | QEventLoop::WaitForMore); + board[index]->rotate(toleft ? -6 : 6); + } + + if (updateConnections()) + KNotifyClient::event(winId(), "connectsound"); + + m_clickcount++; + QString clicks = i18n("Click: %1"); + statusBar()->changeItem(clicks.arg(QString::number(m_clickcount)),1); + + if (isGameOver()) + { + KNotifyClient::event(winId(), "winsound"); + blink(index); + + KExtHighscore::Score score(KExtHighscore::Won); + score.setScore(m_clickcount); + KExtHighscore::submitScore(score, this); + } + } +} + +void MainWindow::blink(int index) +{ + for(int i = 0; i < board[index]->width() * 2; i += 2) + { + kapp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput); + QTimer::singleShot(20, board[index], SLOT(update())); + kapp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput | + QEventLoop::WaitForMore); + board[index]->setLight(i); + } + board[index]->setLight(0); +} + +bool MainWindow::isGameOver() +{ + for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) + { + const Cell::Dirs d = board[i]->dirs(); + if((d != Cell::Free) && (d != Cell::None) && !board[i]->isConnected()) + return false; + } + return true; +} + +void MainWindow::closeEvent(QCloseEvent* event) +{ + event->accept(); +} + +void MainWindow::configureNotifications() +{ + KNotifyDialog::configure(this); +} + +#include "mainwindow.moc" diff --git a/knetwalk/src/mainwindow.h b/knetwalk/src/mainwindow.h new file mode 100644 index 00000000..c28b6ed6 --- /dev/null +++ b/knetwalk/src/mainwindow.h @@ -0,0 +1,96 @@ +/*************************************************************************** + * Copyright (C) 2004, 2005 Andi Peredri * + * andi@ukr.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 * + * as published by the Free Software Foundation (see COPYING) * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + ***************************************************************************/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include + +class Cell; +class QAction; +class QSound; +class QLCDNumber; +class KSelectAction; + +class MainWindow : public KMainWindow +{ + Q_OBJECT + public: + MainWindow(QWidget *parent=0, const char* name=0, WFlags fl=0); + protected: + virtual void closeEvent(QCloseEvent*); + private: + //enum Skill { Novice, Normal, Expert, Master }; + enum BoardSize + { + NoviceBoardSize = 5, + NormalBoardSize = 7, + ExpertBoardSize = 9, + MasterBoardSize = 9 + }; + enum + { + NumHighscores = 10, + MinimumNumCells = 20 + }; + typedef QValueList CellList; + public slots: + void slotNewGame(); + void newGame(int); + + void lClicked(int index); + void rClicked(int index); + void mClicked(int index); + + void showHighscores(); + void configureHighscores(); + void configureNotifications(); + + private: + Cell* uCell(Cell* cell) const; + Cell* dCell(Cell* cell) const; + Cell* lCell(Cell* cell) const; + Cell* rCell(Cell* cell) const; + bool isGameOver(); + bool startBrowser(const QString& url); + bool updateConnections(); + void blink(int index); + void rotate(int index, bool toleft); + void addRandomDir(CellList& list); + void dialog(const QString& caption, const QString& text); + private: + bool wrapped; + Cell* root; + Cell* board[MasterBoardSize * MasterBoardSize]; + + QSound* clicksound; + QSound* connectsound; + QSound* startsound; + QSound* turnsound; + QSound* winsound; + + QString username; + QString soundpath; + QAction* soundaction; + QStringList highscores; + QLCDNumber* lcd; + QPopupMenu* gamemenu; + QPopupMenu* skillmenu; + + int m_clickcount; + KSelectAction* m_levels; +}; + +#endif // MAINWINDOW_H diff --git a/knetwalk/src/pics/Makefile.am b/knetwalk/src/pics/Makefile.am new file mode 100644 index 00000000..2eb67856 --- /dev/null +++ b/knetwalk/src/pics/Makefile.am @@ -0,0 +1,9 @@ + +pics_DATA = background.png background_locked.png cable0001.png cable0010.png cable0011.png cable0100.png cable0101.png cable0110.png cable0111.png cable1000.png cable1001.png cable1010.png cable1011.png cable1100.png cable1101.png cable1110.png computer1.png computer2.png server.png + +picsdir = $(kde_datadir)/knetwalk/ + +KDE_ICON = knetwalk + +EXTRA_DIST = $(pics_DATA) + diff --git a/knetwalk/src/pics/SConscript b/knetwalk/src/pics/SConscript new file mode 100644 index 00000000..2a9b5e7c --- /dev/null +++ b/knetwalk/src/pics/SConscript @@ -0,0 +1,31 @@ +#! /usr/bin/env python +Import( '*' ) +myenv=env.Copy() + +files=[ +'background.png', +'cable0001.png', +'cable0010.png', +'cable0011.png', +'cable0100.png', +'cable0101.png', +'cable0110.png', +'cable0111.png', +'cable1000.png', +'cable1001.png', +'cable1010.png', +'cable1011.png', +'cable1100.png', +'cable1101.png', +'cable1110.png', +'computer1.png', +'computer2.png', +'highscores.png', +'homepage.png', +'newgame.png', +'quit.png', +'server.png', +] + +myenv.KDEinstall('KDEDATA', 'knetwalk', files) +myenv.KDEicon('knetwalk') diff --git a/knetwalk/src/pics/background.png b/knetwalk/src/pics/background.png new file mode 100644 index 00000000..d1484f54 Binary files /dev/null and b/knetwalk/src/pics/background.png differ diff --git a/knetwalk/src/pics/background_locked.png b/knetwalk/src/pics/background_locked.png new file mode 100644 index 00000000..323f9184 Binary files /dev/null and b/knetwalk/src/pics/background_locked.png differ diff --git a/knetwalk/src/pics/cable0001.png b/knetwalk/src/pics/cable0001.png new file mode 100644 index 00000000..c0d59875 Binary files /dev/null and b/knetwalk/src/pics/cable0001.png differ diff --git a/knetwalk/src/pics/cable0010.png b/knetwalk/src/pics/cable0010.png new file mode 100644 index 00000000..e8649078 Binary files /dev/null and b/knetwalk/src/pics/cable0010.png differ diff --git a/knetwalk/src/pics/cable0011.png b/knetwalk/src/pics/cable0011.png new file mode 100644 index 00000000..e4005407 Binary files /dev/null and b/knetwalk/src/pics/cable0011.png differ diff --git a/knetwalk/src/pics/cable0100.png b/knetwalk/src/pics/cable0100.png new file mode 100644 index 00000000..6a6d277d Binary files /dev/null and b/knetwalk/src/pics/cable0100.png differ diff --git a/knetwalk/src/pics/cable0101.png b/knetwalk/src/pics/cable0101.png new file mode 100644 index 00000000..a6494c60 Binary files /dev/null and b/knetwalk/src/pics/cable0101.png differ diff --git a/knetwalk/src/pics/cable0110.png b/knetwalk/src/pics/cable0110.png new file mode 100644 index 00000000..b5d97781 Binary files /dev/null and b/knetwalk/src/pics/cable0110.png differ diff --git a/knetwalk/src/pics/cable0111.png b/knetwalk/src/pics/cable0111.png new file mode 100644 index 00000000..f526acde Binary files /dev/null and b/knetwalk/src/pics/cable0111.png differ diff --git a/knetwalk/src/pics/cable1000.png b/knetwalk/src/pics/cable1000.png new file mode 100644 index 00000000..84815cf3 Binary files /dev/null and b/knetwalk/src/pics/cable1000.png differ diff --git a/knetwalk/src/pics/cable1001.png b/knetwalk/src/pics/cable1001.png new file mode 100644 index 00000000..6dff6282 Binary files /dev/null and b/knetwalk/src/pics/cable1001.png differ diff --git a/knetwalk/src/pics/cable1010.png b/knetwalk/src/pics/cable1010.png new file mode 100644 index 00000000..fd599ab4 Binary files /dev/null and b/knetwalk/src/pics/cable1010.png differ diff --git a/knetwalk/src/pics/cable1011.png b/knetwalk/src/pics/cable1011.png new file mode 100644 index 00000000..4b0e09b2 Binary files /dev/null and b/knetwalk/src/pics/cable1011.png differ diff --git a/knetwalk/src/pics/cable1100.png b/knetwalk/src/pics/cable1100.png new file mode 100644 index 00000000..1d827a88 Binary files /dev/null and b/knetwalk/src/pics/cable1100.png differ diff --git a/knetwalk/src/pics/cable1101.png b/knetwalk/src/pics/cable1101.png new file mode 100644 index 00000000..0d0a947a Binary files /dev/null and b/knetwalk/src/pics/cable1101.png differ diff --git a/knetwalk/src/pics/cable1110.png b/knetwalk/src/pics/cable1110.png new file mode 100644 index 00000000..eea56481 Binary files /dev/null and b/knetwalk/src/pics/cable1110.png differ diff --git a/knetwalk/src/pics/computer1.png b/knetwalk/src/pics/computer1.png new file mode 100644 index 00000000..0ae03fd9 Binary files /dev/null and b/knetwalk/src/pics/computer1.png differ diff --git a/knetwalk/src/pics/computer2.png b/knetwalk/src/pics/computer2.png new file mode 100644 index 00000000..f6bd3a3f Binary files /dev/null and b/knetwalk/src/pics/computer2.png differ diff --git a/knetwalk/src/pics/hi128-app-knetwalk.png b/knetwalk/src/pics/hi128-app-knetwalk.png new file mode 100644 index 00000000..288d300e Binary files /dev/null and b/knetwalk/src/pics/hi128-app-knetwalk.png differ diff --git a/knetwalk/src/pics/hi22-app-knetwalk.png b/knetwalk/src/pics/hi22-app-knetwalk.png new file mode 100644 index 00000000..67757f6a Binary files /dev/null and b/knetwalk/src/pics/hi22-app-knetwalk.png differ diff --git a/knetwalk/src/pics/hi32-app-knetwalk.png b/knetwalk/src/pics/hi32-app-knetwalk.png new file mode 100644 index 00000000..ed30860e Binary files /dev/null and b/knetwalk/src/pics/hi32-app-knetwalk.png differ diff --git a/knetwalk/src/pics/hi64-app-knetwalk.png b/knetwalk/src/pics/hi64-app-knetwalk.png new file mode 100644 index 00000000..ef33d807 Binary files /dev/null and b/knetwalk/src/pics/hi64-app-knetwalk.png differ diff --git a/knetwalk/src/pics/knetwalk.svgz b/knetwalk/src/pics/knetwalk.svgz new file mode 100644 index 00000000..deefcc4a Binary files /dev/null and b/knetwalk/src/pics/knetwalk.svgz differ diff --git a/knetwalk/src/pics/server.png b/knetwalk/src/pics/server.png new file mode 100644 index 00000000..92c27b34 Binary files /dev/null and b/knetwalk/src/pics/server.png differ diff --git a/knetwalk/src/settings.kcfgc b/knetwalk/src/settings.kcfgc new file mode 100644 index 00000000..6444924a --- /dev/null +++ b/knetwalk/src/settings.kcfgc @@ -0,0 +1,5 @@ +# Code generation options for kconfig_compiler +File=knetwalk.kcfg +ClassName=Settings +Singleton=true +Mutators=username,skill diff --git a/knetwalk/src/sounds/Makefile.am b/knetwalk/src/sounds/Makefile.am new file mode 100644 index 00000000..bb01e375 --- /dev/null +++ b/knetwalk/src/sounds/Makefile.am @@ -0,0 +1,4 @@ + +sounds_DATA = click.wav connect.wav start.wav turn.wav win.wav + +soundsdir = $(kde_datadir)/knetwalk/sounds diff --git a/knetwalk/src/sounds/click.wav b/knetwalk/src/sounds/click.wav new file mode 100644 index 00000000..4f621d59 Binary files /dev/null and b/knetwalk/src/sounds/click.wav differ diff --git a/knetwalk/src/sounds/connect.wav b/knetwalk/src/sounds/connect.wav new file mode 100644 index 00000000..26c46f84 Binary files /dev/null and b/knetwalk/src/sounds/connect.wav differ diff --git a/knetwalk/src/sounds/start.wav b/knetwalk/src/sounds/start.wav new file mode 100644 index 00000000..6e69bf43 Binary files /dev/null and b/knetwalk/src/sounds/start.wav differ diff --git a/knetwalk/src/sounds/turn.wav b/knetwalk/src/sounds/turn.wav new file mode 100644 index 00000000..3f909e76 Binary files /dev/null and b/knetwalk/src/sounds/turn.wav differ diff --git a/knetwalk/src/sounds/win.wav b/knetwalk/src/sounds/win.wav new file mode 100644 index 00000000..fc091fe5 Binary files /dev/null and b/knetwalk/src/sounds/win.wav differ diff --git a/kolf/AUTHORS b/kolf/AUTHORS new file mode 100644 index 00000000..4ab73b0f --- /dev/null +++ b/kolf/AUTHORS @@ -0,0 +1,3 @@ +Jason Katz-Brown + +Others listed in main.cpp. diff --git a/kolf/COPYING b/kolf/COPYING new file mode 100644 index 00000000..d3a8f707 --- /dev/null +++ b/kolf/COPYING @@ -0,0 +1,341 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/kolf/Makefile.am b/kolf/Makefile.am new file mode 100644 index 00000000..95449e11 --- /dev/null +++ b/kolf/Makefile.am @@ -0,0 +1,49 @@ +SUBDIRS = objects sounds courses pics graphics +INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) + +bin_PROGRAMS = +lib_LTLIBRARIES = libkolf.la +kdeinit_LTLIBRARIES = kolf.la + +libkolf_la_SOURCES = \ + kolf.cpp game.cpp canvasitem.cpp ball.cpp newgame.cpp config.cpp \ + scoreboard.cpp editor.cpp pluginloader.cpp object.cpp vector.cpp \ + printdialogpage.cpp kcomboboxdialog.cpp kvolumecontrol.cpp \ + floater.cpp slope.cpp + +libkolf_la_LDFLAGS = $(all_libraries) -no-undefined -version-info 3:0:2 +libkolf_la_LIBADD = $(LIB_KDEGAMES) $(LIB_KDEPRINT) $(LIB_KIO) -lartskde +libkolf_la_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + + +# the installed header files +kolfinclude_HEADERS = \ + ball.h game.h statedb.h config.h canvasitem.h object.h rtti.h vector.h \ + floater.h slope.h + +kolfincludedir=$(includedir)/kolf + + +# kdeinited lib +kolf_la_SOURCES = main.cpp +kolf_la_LDFLAGS = $(KDE_RPATH) $(all_libraries) -module -avoid-version +kolf_la_LIBADD = libkolf.la + + +EXTRA_DIST = kolf.desktop + +appdir = $(kde_datadir)/kolf +app_DATA = kolfui.rc intro tutorial.kolf tutorial.kolfgame + +xdg_apps_DATA = kolf.desktop + +applicationmimedir = $(kde_mimedir)/application +applicationmime_DATA = x-kourse.desktop x-kolf.desktop + +magicdir = $(kde_confdir)/magic +magic_DATA = kolf.magic + +METASOURCES = AUTO + +messages: rc.cpp game.h floater.h slope.h + $(XGETTEXT) *.cpp canvasitem.h game.h -o $(podir)/kolf.pot diff --git a/kolf/Makefile.am.fast b/kolf/Makefile.am.fast new file mode 100644 index 00000000..2d39a25e --- /dev/null +++ b/kolf/Makefile.am.fast @@ -0,0 +1,32 @@ +SUBDIRS=objects sounds courses pics graphics +bin_PROGRAMS=kolf +INCLUDES=-I$(top_srcdir)/libkdegames $(all_includes) + +kolf_SOURCES=main.cpp kolf.cpp game.cpp canvasitem.cpp ball.cpp newgame.cpp config.cpp scoreboard.cpp editor.cpp pluginloader.cpp object.cpp vector.cpp printdialogpage.cpp kcomboboxdialog.cpp kvolumecontrol.cpp floater.cpp slope.cpp +kolf_LDFLAGS=$(all_libraries) $(KDE_RPATH) -export-dynamic +kolf_LDADD=$(top_builddir)/libkdegames/libkdegames.la $(LIB_KDEPRINT) $(LIB_KIO) -lartskde + +EXTRA_DIST=kolf.desktop + +appdir=$(kde_datadir)/kolf +app_DATA=kolfui.rc intro tutorial.kolf tutorial.kolfgame + +appsdir=$(kde_appsdir)/Games/Arcade +apps_DATA=kolf.desktop + +applicationmimedir=$(kde_mimedir)/application +applicationmime_DATA=x-kourse.desktop x-kolf.desktop + +magicdir=$(kde_confdir)/magic +magic_DATA=kolf.magic + +kolfinclude_HEADERS=ball.h game.h statedb.h config.h canvasitem.h object.h rtti.h vector.h floater.h slope.h +kolfincludedir=$(includedir)/kolf + +METASOURCES=AUTO + +messages: rc.cpp game.h floater.h slope.h + $(XGETTEXT) *.cpp -o $(podir)/kolf.pot + +install-data-local: + rm -rf $(DESTDIR)$(kde_appsdir)/Games/kolf.desktop diff --git a/kolf/PLUGINS.docbook b/kolf/PLUGINS.docbook new file mode 100644 index 00000000..4da65ddb --- /dev/null +++ b/kolf/PLUGINS.docbook @@ -0,0 +1 @@ +This will include plugin writing instructions later. diff --git a/kolf/README.pool b/kolf/README.pool new file mode 100644 index 00000000..a571b1dd --- /dev/null +++ b/kolf/README.pool @@ -0,0 +1,7 @@ +Heya, + +I made a kolf pool game as an example of kolf plugin use. kolf/obects/poolball/ contains the PoolBall class, which is installed as a plugin. + +So, compile PoolBall, install it, and open pool.kolf. Kolf courses can specify any object name, and it is up to the user to install these objects that the course needs. Kolf will warn when an object is not found in the hole being loaded. + +Jason diff --git a/kolf/TODO b/kolf/TODO new file mode 100644 index 00000000..4a584e49 --- /dev/null +++ b/kolf/TODO @@ -0,0 +1 @@ +Nothing diff --git a/kolf/ball.cpp b/kolf/ball.cpp new file mode 100644 index 00000000..8adaa8cb --- /dev/null +++ b/kolf/ball.cpp @@ -0,0 +1,466 @@ +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "rtti.h" +#include "vector.h" +#include "canvasitem.h" +#include "game.h" +#include "ball.h" + +Ball::Ball(QCanvas *canvas) + : QCanvasEllipse(canvas) +{ + m_doDetect = true; + m_collisionLock = false; + setBeginningOfHole(false); + setBlowUp(false); + setPen(black); + resetSize(); + collisionId = 0; + m_addStroke = false; + m_placeOnGround = false; + m_forceStillGoing = false; + frictionMultiplier = 1.0; + QFont font(kapp->font()); + //font.setPixelSize(10); + label = new QCanvasText("", font, canvas); + label->setColor(white); + label->setVisible(false); + + // this sets z + setState(Stopped); + label->setZ(z() - .1); +} + +void Ball::aboutToDie() +{ + delete label; +} + +void Ball::setState(BallState newState) +{ + state = newState; + if (state == Stopped) + setZ(1000); + else + setBeginningOfHole(false); +} + +void Ball::advance(int phase) +{ + // not used anymore + // can be used to make ball wobble + if (phase == 1 && m_blowUp) + { + if (blowUpCount >= 50) + { + // i should make this a config option + //setAddStroke(addStroke() + 1); + setBlowUp(false); + resetSize(); + return; + } + + const double diff = 8; + double randnum = kapp->random(); + const double width = 6 + randnum * (diff / RAND_MAX); + randnum = kapp->random(); + const double height = 6 + randnum * (diff / RAND_MAX); + setSize(width, height); + blowUpCount++; + } +} + +void Ball::friction() +{ + if (state == Stopped || state == Holed || !isVisible()) { setVelocity(0, 0); return; } + const double subtractAmount = .027 * frictionMultiplier; + if (m_vector.magnitude() <= subtractAmount) + { + state = Stopped; + setVelocity(0, 0); + game->timeout(); + return; + } + m_vector.setMagnitude(m_vector.magnitude() - subtractAmount); + setVector(m_vector); + + frictionMultiplier = 1.0; +} + +void Ball::setVelocity(double vx, double vy) +{ + QCanvasEllipse::setVelocity(vx, vy); + + if (vx == 0 && vy == 0) + { + m_vector.setDirection(0); + m_vector.setMagnitude(0); + return; + } + + double ballAngle = atan2(-vy, vx); + + m_vector.setDirection(ballAngle); + m_vector.setMagnitude(sqrt(pow(vx, 2) + pow(vy, 2))); +} + +void Ball::setVector(const Vector &newVector) +{ + m_vector = newVector; + + if (newVector.magnitude() == 0) + { + setVelocity(0, 0); + return; + } + + QCanvasEllipse::setVelocity(cos(newVector.direction()) * newVector.magnitude(), -sin(newVector.direction()) * newVector.magnitude()); +} + +void Ball::moveBy(double dx, double dy) +{ + double oldx; + double oldy; + oldx = x(); + oldy = y(); + QCanvasEllipse::moveBy(dx, dy); + + if (game && !game->isPaused()) + collisionDetect(oldx, oldy); + + if ((dx || dy) && game && game->curBall() == this) + game->ballMoved(); + + label->move(x() + width(), y() + height()); +} + +void Ball::doAdvance() +{ + QCanvasEllipse::advance(1); +} + +namespace Lines +{ + // provides a point made of doubles + + struct Line + { + Point p1, p2; + }; + + int ccw(const Point &p0, const Point &p1, const Point &p2) + { + double dx1, dx2, dy1, dy2; + dx1 = p1.x - p0.x; dy1 = p1.y - p0.y; + dx2 = p2.x - p0.x; dy2 = p2.y - p0.y; + if (dx1*dy2 > dy1*dx2) return +1; + if (dx1*dy2 < dy1*dx2) return -1; + if ((dx1*dx2 < 0) || (dy1*dy2 < 0)) return -1; + if ((dx1*dx1+dy1*dy1) < (dx2*dx2+dy2*dy2)) + return +1; + return 0; + } + + int intersects(const Line &l1, const Line &l2) + { + // Charles says, TODO: Account for vertical lines + // Jason says, in my testing vertical lines work + return ((ccw(l1.p1, l1.p2, l2.p1) + *ccw(l1.p1, l1.p2, l2.p2)) <= 0) + && ((ccw(l2.p1, l2.p2, l1.p1) + *ccw(l2.p1, l2.p2, l1.p2)) <= 0); + } + + + bool intersects( + double xa1, double ya1, double xb1, double yb1, + double xa2, double ya2, double xb2, double yb2 + ) + { + Line l1, l2; + l1.p1.x = xa1; + l1.p1.y = ya1; + l1.p2.x = xb1; + l1.p2.y = yb1; + + l2.p1.x = xa2; + l2.p1.y = ya2; + l2.p2.x = xb2; + l2.p2.y = yb2; + + return intersects(l1, l2); + } +} + +void Ball::collisionDetect(double oldx, double oldy) +{ + if (!isVisible() || state == Holed || !m_doDetect) + return; + + if (collisionId >= INT_MAX - 1) + collisionId = 0; + else + collisionId++; + + //kdDebug(12007) << "------" << endl; + //kdDebug(12007) << "Ball::collisionDetect id " << collisionId << endl; + + // every other time... + // do friction + if (collisionId % 2 && !(xVelocity() == 0 && yVelocity() == 0)) + friction(); + + const double minSpeed = .06; + + QCanvasItemList m_list = collisions(true); + + // please don't ask why QCanvas doesn't actually sort its list; + // it just doesn't. + m_list.sort(); + + this->m_list = m_list; + + for (QCanvasItemList::Iterator it = m_list.begin(); it != m_list.end(); ++it) + { + QCanvasItem *item = *it; + + if (item->rtti() == Rtti_NoCollision || item->rtti() == Rtti_Putter) + continue; + + if (item->rtti() == rtti() && !m_collisionLock) + { + // it's one of our own kind, a ball + Ball *oball = dynamic_cast(item); + if (!oball || oball->collisionLock()) + continue; + oball->setCollisionLock(true); + + if ((oball->x() - x() != 0 && oball->y() - y() != 0) && state == Rolling && oball->curState() != Holed) + { + m_collisionLock = true; + // move this ball to where it was barely touching + double ballAngle = m_vector.direction(); + while (collisions(true).contains(item) > 0) + move(x() - cos(ballAngle) / 2.0, y() + sin(ballAngle) / 2.0); + + // make a 2 pixel separation + move(x() - 2 * cos(ballAngle), y() + 2 * sin(ballAngle)); + + Vector bvector = oball->curVector(); + m_vector -= bvector; + + Vector unit1 = Vector(QPoint(x(), y()), QPoint(oball->x(), oball->y())); + unit1 = unit1.unit(); + + Vector unit2 = m_vector.unit(); + + double cos = unit1 * unit2; + + unit1 *= m_vector.magnitude() * cos; + m_vector -= unit1; + m_vector += bvector; + + bvector += unit1; + + oball->setVector(bvector); + setVector(m_vector); + + oball->setState(Rolling); + setState(Rolling); + + oball->doAdvance(); + } + + continue; + } + else if (item->rtti() == Rtti_WallPoint) + { + //kdDebug(12007) << "collided with WallPoint\n"; + // iterate through the rst + QPtrList points; + for (QCanvasItemList::Iterator pit = it; pit != m_list.end(); ++pit) + { + if ((*pit)->rtti() == Rtti_WallPoint) + { + WallPoint *point = (WallPoint *)(*pit); + if (point) + points.prepend(point); + } + } + + // ok now we have a list of wall points we are on + + WallPoint *iterpoint = 0; + WallPoint *finalPoint = 0; + + // this wont be least when we're done hopefully + double leastAngleDifference = 9999; + + for (iterpoint = points.first(); iterpoint; iterpoint = points.next()) + { + //kdDebug(12007) << "-----\n"; + const Wall *parentWall = iterpoint->parentWall(); + const QPoint qp(iterpoint->x() + parentWall->x(), iterpoint->y() + parentWall->y()); + const Point p(qp.x(), qp.y()); + const QPoint qother = QPoint(parentWall->startPoint() == qp? parentWall->endPoint() : parentWall->startPoint()) + QPoint(parentWall->x(), parentWall->y()); + const Point other(qother.x(), qother.y()); + + // vector of wall + Vector v = Vector(p, other); + + // difference between our path and the wall path + double ourDir = m_vector.direction(); + + double wallDir = M_PI - v.direction(); + + //kdDebug(12007) << "ourDir: " << rad2deg(ourDir) << endl; + //kdDebug(12007) << "wallDir: " << rad2deg(wallDir) << endl; + + const double angleDifference = fabs(M_PI - fabs(ourDir - wallDir)); + //kdDebug(12007) << "computed angleDifference: " << rad2deg(angleDifference) << endl; + + // only if this one is the least of all + if (angleDifference < leastAngleDifference) + { + leastAngleDifference = angleDifference; + finalPoint = iterpoint; + //kdDebug(12007) << "it's the one\n"; + } + } + + // this'll never happen + if (!finalPoint) + continue; + + // collide with our chosen point + finalPoint->collision(this, collisionId); + + // don't worry about colliding with walls + // wall points are ok alone + goto end; + } + + if (!isVisible() || state == Holed) + return; + + CanvasItem *citem = dynamic_cast(item); + if (citem) + { + if (!citem->terrainCollisions()) + { + // read: if (not do terrain collisions) + if (!citem->collision(this, collisionId)) + { + // if (skip smart wall test) + if (citem->vStrut() || item->rtti() == Rtti_Wall) + goto end; + else + goto wallCheck; + } + } + break; + } + } + + for (QCanvasItemList::Iterator it = m_list.begin(); it != m_list.end(); ++it) + { + CanvasItem *citem = dynamic_cast(*it); + if (citem && citem->terrainCollisions()) + { + // slopes return false + // as only one should be processed + // however that might not always be true + + // read: if (not do terrain collisions) + if (!citem->collision(this, collisionId)) + { + break; + } + } + } + +// Charles's smart wall check: + + wallCheck: + + { // check if I went through a wall + QCanvasItemList items; + if (game) + items = game->canvas()->allItems(); + for (QCanvasItemList::Iterator i = items.begin(); i != items.end(); ++i) + { + if ((*i)->rtti() != Rtti_Wall) + continue; + + QCanvasItem *item = (*i); + Wall *wall = dynamic_cast(item); + if (!wall || !wall->isVisible()) + continue; + + if (Lines::intersects( + wall->startPoint().x() + wall->x(), wall->startPoint().y() + wall->y(), + wall->endPoint().x() + wall->x(), wall->endPoint().y() + wall->y(), + + oldx, oldy, x(), y() + )) + { + //kdDebug(12007) << "smart wall collision\n"; + wall->collision(this, collisionId); + break; + } + + + } + } + + end: + + if (m_vector.magnitude() < minSpeed && m_vector.magnitude()) + { + setVelocity(0, 0); + setState(Stopped); + } +} + +BallState Ball::currentState() +{ + return state; +} + +void Ball::showInfo() +{ + label->setVisible(isVisible()); +} + +void Ball::hideInfo() +{ + label->setVisible(false); +} + +void Ball::setName(const QString &name) +{ + label->setText(name); +} + +void Ball::setCanvas(QCanvas *c) +{ + QCanvasEllipse::setCanvas(c); + label->setCanvas(c); +} + +void Ball::setVisible(bool yes) +{ + QCanvasEllipse::setVisible(yes); + + label->setVisible(yes && game && game->isInfoShowing()); +} + diff --git a/kolf/ball.h b/kolf/ball.h new file mode 100644 index 00000000..098d82ef --- /dev/null +++ b/kolf/ball.h @@ -0,0 +1,114 @@ +#ifndef KOLF_BALL_H +#define KOLF_BALL_H + +#include +#include + +#include + +#include "vector.h" +#include "rtti.h" + +enum BallState { Rolling = 0, Stopped, Holed }; + +class Ball : public QCanvasEllipse, public CanvasItem +{ +public: + Ball(QCanvas *canvas); + virtual void aboutToDie(); + + BallState currentState(); + + virtual void resetSize() { setSize(7, 7); } + virtual void advance(int phase); + virtual void doAdvance(); + virtual void moveBy(double dx, double dy); + virtual void setVelocity(double vx, double vy); + + virtual bool deleteable() const { return false; } + + virtual bool canBeMovedByOthers() const { return true; } + + BallState curState() const { return state; } + void setState(BallState newState); + + QColor color() const { return m_color; } + void setColor(QColor color) { m_color = color; setBrush(color); } + + void setMoved(bool yes) { m_moved = yes; } + bool moved() const { return m_moved; } + void setBlowUp(bool yes) { m_blowUp = yes; blowUpCount = 0; } + bool blowUp() const { return m_blowUp; } + + void setFrictionMultiplier(double news) { frictionMultiplier = news; }; + void friction(); + void collisionDetect(double oldx, double oldy); + + virtual int rtti() const { return Rtti_Ball; }; + + int addStroke() const { return m_addStroke; } + bool placeOnGround(Vector &v) { v = oldVector; return m_placeOnGround; } + void setAddStroke(int newStrokes) { m_addStroke = newStrokes; } + void setPlaceOnGround(bool placeOnGround) { m_placeOnGround = placeOnGround; oldVector = m_vector; } + + bool beginningOfHole() const { return m_beginningOfHole; } + void setBeginningOfHole(bool yes) { m_beginningOfHole = yes; } + + bool forceStillGoing() const { return m_forceStillGoing; } + void setForceStillGoing(bool yes) { m_forceStillGoing = yes; } + + Vector curVector() const { return m_vector; } + void setVector(const Vector &newVector); + + bool collisionLock() const { return m_collisionLock; } + void setCollisionLock(bool yes) { m_collisionLock = yes; } + virtual void fastAdvanceDone() { setCollisionLock(false); } + + void setDoDetect(bool yes) { m_doDetect = yes; } + bool doDetect() const { return m_doDetect; } + + virtual void showInfo(); + virtual void hideInfo(); + virtual void setName(const QString &); + virtual void setCanvas(QCanvas *c); + virtual void setVisible(bool yes); + +private: + BallState state; + QColor m_color; + long int collisionId; + double frictionMultiplier; + + bool m_blowUp; + int blowUpCount; + int m_addStroke; + bool m_placeOnGround; + double m_oldvx; + double m_oldvy; + + bool m_moved; + bool m_beginningOfHole; + bool m_forceStillGoing; + + Vector m_vector; + Vector oldVector; + bool m_collisionLock; + + bool m_doDetect; + QCanvasItemList m_list; + + QCanvasText *label; +}; + + +inline int rad2deg(double theDouble) +{ + return (int)((360L / (2L * M_PI)) * theDouble); +} + +inline double deg2rad(double theDouble) +{ + return (((2L * M_PI) / 360L) * theDouble); +} + +#endif diff --git a/kolf/canvasitem.cpp b/kolf/canvasitem.cpp new file mode 100644 index 00000000..5e39257c --- /dev/null +++ b/kolf/canvasitem.cpp @@ -0,0 +1,48 @@ +#include + +#include + +#include "game.h" +#include "canvasitem.h" + +QCanvasRectangle *CanvasItem::onVStrut() +{ + QCanvasItem *qthis = dynamic_cast(this); + if (!qthis) + return 0; + QCanvasItemList l = qthis->collisions(true); + l.sort(); + bool aboveVStrut = false; + CanvasItem *item = 0; + QCanvasItem *qitem = 0; + for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) + { + item = dynamic_cast(*it); + if (item) + { + qitem = *it; + if (item->vStrut()) + { + //kdDebug(12007) << "above vstrut\n"; + aboveVStrut = true; + break; + } + } + } + + QCanvasRectangle *ritem = dynamic_cast(qitem); + + return aboveVStrut && ritem? ritem : 0; +} + +void CanvasItem::save(KConfig *cfg) +{ + cfg->writeEntry("dummykey", true); +} + +void CanvasItem::playSound(QString file, double vol) +{ + if (game) + game->playSound(file, vol); +} + diff --git a/kolf/canvasitem.h b/kolf/canvasitem.h new file mode 100644 index 00000000..e7a37cb6 --- /dev/null +++ b/kolf/canvasitem.h @@ -0,0 +1,182 @@ +#ifndef KOLF_CANVASITEM_H +#define KOLF_CANVASITEM_H + +#include + +#include "config.h" + +class Ball; +class KConfig; +class StateDB; +class KolfGame; + +class CanvasItem +{ +public: + CanvasItem() { game = 0; } + virtual ~CanvasItem() {} + /** + * load your settings from the KConfig, which represents a course. + */ + virtual void load(KConfig *) {} + /** + * load a point if you wish. Rarely necessary. + */ + virtual void loadState(StateDB * /*db*/) {} + /** + * returns a bool that is true if your item needs to load after other items + */ + virtual bool loadLast() const { return false; } + /** + * called if the item is made by user while editing, with the item that was selected on the hole; + */ + virtual void selectedItem(QCanvasItem * /*item*/) {} + /** + * called after the item is moved the very first time by the game + */ + virtual void firstMove(int /*x*/, int /*y*/) {} + /** + * save your settings. + */ + virtual void save(KConfig *cfg); + /** + * save a point if you wish. Rarely necessary. + */ + virtual void saveState(StateDB * /*db*/) {} + /** + * called for information when shot started + */ + virtual void shotStarted() {} + /** + * called right before any items are saved. + */ + virtual void aboutToSave() {} + /** + * called right after all items are saved. + */ + virtual void savingDone() {} + /** + * called when the edit mode has been changed. + */ + virtual void editModeChanged(bool /*editing*/) {} + /** + * the item should delete any other objects it's created. + * DO NOT DO THIS KIND OF STUFF IN THE DESTRUCTOR! + */ + virtual void aboutToDie() {} + /** + * returns the object to get rid of when the delete button is pressed on this item. Some sub-objects will return something other than this. + */ + virtual CanvasItem *itemToDelete() { return this; } + /** + * called when user presses delete key while editing. This is very rarely reimplemented, and generally shouldn't be. + */ + virtual void aboutToDelete() {} + /** + * returns whether this item should be able to be deleted by user while editing. + */ + virtual bool deleteable() const { return true; } + /** + * returns whether this item should get doAdvance called -- it is called in sync with ball advancing (which is twice as fast as the advance() calling rate) + */ + virtual bool fastAdvance() const { return false; } + /** + * called when all items have had their chance at a doAdvance + */ + virtual void fastAdvanceDone() {} + /** + * called if fastAdvance is enabled + */ + virtual void doAdvance() {} + /** + * if all items of this type of item (based on rtti()) that are "colliding" (ie, in the same spot) with ball should get collision() called. + */ + virtual bool terrainCollisions() const { return false; } + /** + * returns whether or not this item lifts items on top of it. + */ + virtual bool vStrut() const { return false; } + /** + * show extra item info + */ + virtual void showInfo() {}; + /** + * hide extra item info + */ + virtual void hideInfo() {}; + /** + * update your Z value (this is called by various things when perhaps the value should change) if this is called by a vStrut, it will pass 'this'. + */ + virtual void updateZ(QCanvasRectangle * /*vStrut*/ = 0) {}; + /** + * clean up for prettyness + */ + virtual void clean() {}; + /** + * scale factor changed (game->scaleFactor(), the world matrix is game->worldMatrix()) + * NOTE: not used in Kolf 1.1, which comes with KDE 3.1. + */ + virtual void scaleChanged() {}; + /** + * returns whether this item can be moved by others (if you want to move an item, you should honor this!) + */ + virtual bool canBeMovedByOthers() const { return false; } + /** + * returns a Config that can be used to configure this item by the user. + * The default implementation returns one that says 'No configuration options'. + */ + virtual Config *config(QWidget *parent) { return new DefaultConfig(parent); } + /** + * returns other items that should be moveable (besides this one of course). + */ + virtual QPtrList moveableItems() const { return QPtrList(); } + /** + * returns whether this can be moved by the user while editing. + */ + virtual bool moveable() const { return true; } + + void setId(int newId) { id = newId; } + int curId() const { return id; } + + /** + * call to play sound (ie, playSound("wall") plays kdedir/share/apps/kolf/sounds/wall.wav). + * optionally, specify vol to be between 0-1, for no sound to full volume, respectively. + */ + void playSound(QString file, double vol = 1); + + /** + * called on ball's collision. Return if terrain collisions should be processed. + */ + virtual bool collision(Ball * /*ball*/, long int /*id*/) { return true; } + + /** + * reimplement if you want extra items to have access to the game object. + * playSound() relies on having this. + */ + virtual void setGame(KolfGame *game) { this->game = game; } + + /** + * returns whether this is a corner resizer + */ + virtual bool cornerResize() const { return false; } + + QString name() const { return m_name; } + void setName(const QString &newname) { m_name = newname; } + +protected: + /** + * pointer to main KolfGame + */ + KolfGame *game; + + /** + * returns the highest vertical strut the item is on + */ + QCanvasRectangle *onVStrut(); + +private: + QString m_name; + int id; +}; + +#endif diff --git a/kolf/config.cpp b/kolf/config.cpp new file mode 100644 index 00000000..7652cf40 --- /dev/null +++ b/kolf/config.cpp @@ -0,0 +1,48 @@ +#include +#include + +#include +#include + +#include "config.h" + +Config::Config(QWidget *parent, const char *name) + : QFrame(parent, name) +{ + startedUp = false; +} + +void Config::ctorDone() +{ + startedUp = true; +} + +int Config::spacingHint() +{ + return KDialog::spacingHint() / 2; +} + +int Config::marginHint() +{ + return KDialog::marginHint(); +} + +void Config::changed() +{ + if (startedUp) + emit modified(); +} + +MessageConfig::MessageConfig(QString text, QWidget *parent, const char *name) + : Config(parent, name) +{ + QVBoxLayout *layout = new QVBoxLayout(this, marginHint(), spacingHint()); + layout->addWidget(new QLabel(text, this)); +} + +DefaultConfig::DefaultConfig(QWidget *parent, const char *name) + : MessageConfig(i18n("No configuration options"), parent, name) +{ +} + +#include "config.moc" diff --git a/kolf/config.h b/kolf/config.h new file mode 100644 index 00000000..07c68938 --- /dev/null +++ b/kolf/config.h @@ -0,0 +1,42 @@ +#ifndef KOLF_CONFIG_H +#define KOLF_CONFIG_H + +#include + +class Config : public QFrame +{ + Q_OBJECT + +public: + Config(QWidget *parent, const char *name = 0); + void ctorDone(); + +signals: + void modified(); + +protected: + int spacingHint(); + int marginHint(); + bool startedUp; + void changed(); +}; + +// this is easy to use to show a message +class MessageConfig : public Config +{ + Q_OBJECT + +public: + MessageConfig(QString text, QWidget *parent, const char *name = 0); +}; + +// internal +class DefaultConfig : public MessageConfig +{ + Q_OBJECT + +public: + DefaultConfig(QWidget *parent, const char *name = 0); +}; + +#endif diff --git a/kolf/configure.in.in b/kolf/configure.in.in new file mode 100644 index 00000000..9754545e --- /dev/null +++ b/kolf/configure.in.in @@ -0,0 +1,3 @@ +if test "x$build_arts" = "xno"; then + DO_NOT_COMPILE="$DO_NOT_COMPILE kolf" +fi diff --git a/kolf/courses.list b/kolf/courses.list new file mode 100644 index 00000000..021d608c --- /dev/null +++ b/kolf/courses.list @@ -0,0 +1,10 @@ +tutorial.kolf +pool.kolf +courses/Classic.kolf +courses/Easy.kolf +courses/Hard.kolf +courses/Medium.kolf +courses/Practice +courses/ReallyEasy +courses/Impossible +courses/USApro diff --git a/kolf/courses/ADDING_COURSES b/kolf/courses/ADDING_COURSES new file mode 100644 index 00000000..2b86c6b2 --- /dev/null +++ b/kolf/courses/ADDING_COURSES @@ -0,0 +1,8 @@ +Hi! + +To add a course to the default distribution, first email me at jason@katzbrown.com. + +If I say 'sure!', cvs add it here (with or without file extension, preferably without), add it to the Makefile.am, and add it to kolf/courses.list. + +Thanks! +Jason diff --git a/kolf/courses/Classic.kolf b/kolf/courses/Classic.kolf new file mode 100644 index 00000000..199d937a --- /dev/null +++ b/kolf/courses/Classic.kolf @@ -0,0 +1,1986 @@ +[0-course@-50,-50] +Name=Classic +Name[af]=Klasieke +Name[bg]=КлаÑичеÑко +Name[bn]=কà§à¦²à¦¾à¦¸à¦¿à¦• +Name[br]=Da gustum +Name[bs]=KlasiÄni +Name[ca]=Clàsic +Name[cs]=Klasický +Name[da]=Klassisk +Name[de]=Klassisch +Name[el]=Κλασσικό +Name[es]=Clásico +Name[et]=Klassika +Name[fi]=Klassinen +Name[fr]=Classique +Name[gl]=Clásico +Name[he]=קלסי +Name[hi]=कà¥à¤²à¤¾à¤¸à¤¿à¤• +Name[hr]=KlasiÄno +Name[hu]=Klasszikus +Name[is]=Klassísk +Name[it]=Classico +Name[ja]=クラッシック +Name[mk]=КлаÑичен +Name[nb]=Klassisk +Name[nl]=Klassiek +Name[nn]=Klassisk +Name[pl]=Klasyczny +Name[pt]=Clássico +Name[pt_BR]=Clássico +Name[ro]=Clasic +Name[ru]=КлаÑÑика +Name[sk]=Klasické +Name[sl]=KlasiÄna +Name[sr]=КлаÑичан +Name[sr@Latn]=KlasiÄan +Name[sv]=Klassisk +Name[ta]=சிறநà¯à®¤ +Name[tg]=КлаÑÑика +Name[tr]=Klasik +Name[uk]=КлаÑичний +Name[ven]=Maimbo +Name[wa]=Classike +Name[xh]=Yeyakudala +Name[xx]=xxClassicxx +Name[zh_CN]=ç»å…¸ +Name[zh_TW]=å¤å…¸ +Name[zu]=Okuhle kakhulu +author=Niklas Knutsson +name=Classic + +[1-ball@200,360] +dummykey=true + +[1-cup@197,56|3] +dummykey=true + +[1-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[1-sand@-49,288|16] +changeEnabled=false +changeEvery=50 +height=482 +width=332 + +[1-slope@160,19|13] +grade=4 +gradient=Elliptic +height=76 +reversed=false +stuckOnGround=false +width=76 + +[1-wall@0,0|10] +endPoint=285,66 +startPoint=247,9 + +[1-wall@0,0|11] +endPoint=285,66 +startPoint=238,128 + +[1-wall@0,0|13] +endPoint=213,190 +startPoint=185,190 + +[1-wall@0,0|26] +endPoint=238,387 +startPoint=156,387 + +[1-wall@0,0|27] +endPoint=247,9 +startPoint=149,9 + +[1-wall@0,0|4] +endPoint=156,128 +startPoint=156,387 + +[1-wall@0,0|5] +endPoint=238,127 +startPoint=238,387 + +[1-wall@0,0|6] +endPoint=180,128 +startPoint=155,129 + +[1-wall@0,0|7] +endPoint=238,128 +startPoint=212,127 + +[1-wall@0,0|8] +endPoint=111,67 +startPoint=156,128 + +[1-wall@0,0|9] +endPoint=149,9 +startPoint=111,67 + +[10-ball@327,329] +dummykey=true + +[10-cup@59,60|16] +dummykey=true + +[10-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[10-slope@10,11|15] +grade=4 +gradient=Elliptic +height=54 +reversed=false +stuckOnGround=false +width=54 + +[10-slope@105,66|9] +grade=3 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-slope@116,118|13] +grade=8 +gradient=Elliptic +height=59 +reversed=false +stuckOnGround=false +width=59 + +[10-slope@132,178|12] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-slope@16,63|17] +grade=5 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-slope@17,247|26] +grade=4 +gradient=Elliptic +height=138 +reversed=false +stuckOnGround=false +width=138 + +[10-slope@172,124|11] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-slope@176,232|5] +grade=4 +gradient=Elliptic +height=60 +reversed=false +stuckOnGround=false +width=60 + +[10-slope@178,174|8] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-slope@211,174|14] +grade=3 +gradient=Elliptic +height=66 +reversed=false +stuckOnGround=false +width=66 + +[10-slope@242,6|25] +grade=4 +gradient=Elliptic +height=144 +reversed=false +stuckOnGround=false +width=144 + +[10-slope@248,224|6] +grade=4 +gradient=Elliptic +height=63 +reversed=false +stuckOnGround=false +width=63 + +[10-slope@59,63|10] +grade=7 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-slope@66,25|18] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-slope@72,106|8] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-wall@0,0|19] +endPoint=242,256 +startPoint=242,329 + +[10-wall@0,0|20] +endPoint=179,90 +startPoint=126,116 + +[10-wall@0,0|21] +endPoint=80,104 +startPoint=50,135 + +[10-wall@0,0|22] +endPoint=390,301 +startPoint=299,387 + +[10-wall@0,0|23] +endPoint=10,11 +startPoint=10,95 + +[10-wall@0,0|24] +endPoint=100,11 +startPoint=10,11 + +[10-wall@0,0|3] +endPoint=10,95 +startPoint=299,387 + +[10-wall@0,0|5] +endPoint=390,301 +startPoint=100,11 + +[11-ball@200,356] +dummykey=true + +[11-cup@204,194|8] +dummykey=true + +[11-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[11-slope@117,42|10] +grade=2 +gradient=Vertical +height=64 +reversed=false +stuckOnGround=false +width=169 + +[11-slope@153,144|7] +grade=5 +gradient=Elliptic +height=103 +reversed=false +stuckOnGround=false +width=103 + +[11-slope@283,42|14] +grade=2 +gradient=Opposite Diagonal +height=64 +reversed=false +stuckOnGround=false +width=73 + +[11-slope@284,106|11] +grade=2 +gradient=Horizontal +height=132 +reversed=true +stuckOnGround=false +width=70 + +[11-slope@284,235|13] +grade=2 +gradient=Diagonal +height=35 +reversed=true +stuckOnGround=false +width=73 + +[11-slope@286,279|21] +grade=4 +gradient=Elliptic +height=102 +reversed=false +stuckOnGround=false +width=102 + +[11-slope@40,235|15] +grade=2 +gradient=Opposite Diagonal +height=39 +reversed=true +stuckOnGround=false +width=77 + +[11-slope@40,42|12] +grade=2 +gradient=Diagonal +height=64 +reversed=false +stuckOnGround=false +width=78 + +[11-slope@42,106|9] +grade=2 +gradient=Horizontal +height=131 +reversed=false +stuckOnGround=false +width=75 + +[11-slope@9,278|20] +grade=4 +gradient=Elliptic +height=106 +reversed=false +stuckOnGround=false +width=106 + +[11-wall@0,0|16] +endPoint=115,276 +startPoint=116,388 + +[11-wall@0,0|17] +endPoint=287,271 +startPoint=287,388 + +[11-wall@0,0|18] +endPoint=115,276 +startPoint=11,276 + +[11-wall@0,0|19] +endPoint=388,271 +startPoint=287,271 + +[11-wall@0,0|3] +endPoint=141,171 +startPoint=141,222 + +[11-wall@0,0|4] +endPoint=224,259 +startPoint=183,259 + +[11-wall@0,0|5] +endPoint=265,169 +startPoint=265,221 + +[11-wall@0,0|6] +endPoint=228,132 +startPoint=180,132 + +[12-ball@191,359] +dummykey=true + +[12-cup@190,61|4] +dummykey=true + +[12-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[12-sand@-19,297|16] +changeEnabled=false +changeEvery=50 +height=394 +width=212 + +[12-sand@27,396|17] +changeEnabled=false +changeEvery=50 +height=192 +width=186 + +[12-slope@141,119|13] +grade=3 +gradient=Vertical +height=26 +reversed=false +stuckOnGround=false +width=92 + +[12-wall@0,0|14] +endPoint=230,9 +startPoint=146,9 + +[12-wall@0,0|15] +endPoint=234,387 +startPoint=143,387 + +[12-wall@0,0|3] +endPoint=142,117 +startPoint=143,387 + +[12-wall@0,0|5] +endPoint=234,118 +startPoint=234,387 + +[12-wall@0,0|6] +endPoint=111,63 +startPoint=142,117 + +[12-wall@0,0|7] +endPoint=146,9 +startPoint=111,63 + +[12-wall@0,0|8] +endPoint=266,61 +startPoint=234,118 + +[12-wall@0,0|9] +endPoint=230,9 +startPoint=266,61 + +[12-windmill@142,146|12] +botWallVisible=false +bottom=true +height=47 +leftWallVisible=true +rightWallVisible=true +speed=6 +topWallVisible=false +width=92 + +[12-windmill@142,192|11] +botWallVisible=false +bottom=true +height=65 +leftWallVisible=true +rightWallVisible=true +speed=5 +topWallVisible=false +width=92 + +[12-windmill@142,257|10] +botWallVisible=false +bottom=true +height=49 +leftWallVisible=true +rightWallVisible=true +speed=3 +topWallVisible=false +width=92 + +[13-ball@42,350] +dummykey=true + +[13-blackhole@302,95|4] +exit=133,78 +exitDeg=180 +maxspeed=5 +minspeed=2 + +[13-cup@33,77|9] +dummykey=true + +[13-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[13-puddle@209,99|13] +changeEnabled=false +changeEvery=50 +height=46 +width=54 + +[13-puddle@212,77|12] +changeEnabled=false +changeEvery=50 +height=46 +width=50 + +[13-puddle@214,120|14] +changeEnabled=false +changeEvery=50 +height=38 +width=48 + +[13-puddle@221,57|11] +changeEnabled=false +changeEvery=50 +height=48 +width=48 + +[13-puddle@226,143|15] +changeEnabled=false +changeEvery=50 +height=46 +width=52 + +[13-puddle@236,38|10] +changeEnabled=false +changeEvery=50 +height=36 +width=46 + +[13-slope@237,31|3] +grade=8 +gradient=Elliptic +height=130 +reversed=true +stuckOnGround=false +width=130 + +[13-wall@0,0|16] +endPoint=368,23 +startPoint=237,24 + +[13-wall@0,0|17] +endPoint=368,23 +startPoint=367,187 + +[13-wall@0,0|18] +endPoint=367,187 +startPoint=16,391 + +[13-wall@0,0|19] +endPoint=237,24 +startPoint=16,348 + +[13-wall@0,0|20] +endPoint=16,391 +startPoint=16,348 + +[13-wall@0,0|21] +endPoint=172,201 +startPoint=142,216 + +[13-wall@0,0|22] +endPoint=163,268 +startPoint=134,250 + +[13-wall@0,0|23] +endPoint=242,187 +startPoint=223,221 + +[13-wall@0,0|24] +endPoint=206,280 +startPoint=199,247 + +[13-wall@0,0|25] +endPoint=120,277 +startPoint=90,292 + +[13-wall@0,0|5] +endPoint=137,19 +startPoint=10,17 + +[13-wall@0,0|6] +endPoint=138,142 +startPoint=137,19 + +[13-wall@0,0|7] +endPoint=138,142 +startPoint=10,143 + +[13-wall@0,0|8] +endPoint=10,17 +startPoint=10,143 + +[14-ball@265,361] +dummykey=true + +[14-cup@76,93|15] +dummykey=true + +[14-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[14-slope@105,206|18] +grade=4 +gradient=Opposite Diagonal +height=181 +reversed=true +stuckOnGround=false +width=248 + +[14-slope@108,13|20] +grade=4 +gradient=Diagonal +height=194 +reversed=false +stuckOnGround=false +width=245 + +[14-slope@351,14|21] +grade=4 +gradient=Opposite Diagonal +height=194 +reversed=false +stuckOnGround=false +width=50 + +[14-slope@351,206|22] +grade=4 +gradient=Diagonal +height=181 +reversed=true +stuckOnGround=false +width=49 + +[14-wall@0,0|10] +endPoint=50,37 +startPoint=20,68 + +[14-wall@0,0|11] +endPoint=20,68 +startPoint=20,109 + +[14-wall@0,0|12] +endPoint=20,109 +startPoint=49,152 + +[14-wall@0,0|13] +endPoint=100,151 +startPoint=49,152 + +[14-wall@0,0|14] +endPoint=131,135 +startPoint=100,151 + +[14-wall@0,0|16] +endPoint=246,207 +startPoint=213,206 + +[14-wall@0,0|17] +endPoint=313,206 +startPoint=271,207 + +[14-wall@0,0|20] +endPoint=145,110 +startPoint=131,135 + +[14-wall@0,0|3] +endPoint=213,207 +startPoint=212,387 + +[14-wall@0,0|4] +endPoint=312,206 +startPoint=313,387 + +[14-wall@0,0|5] +endPoint=313,387 +startPoint=212,387 + +[14-wall@0,0|6] +endPoint=131,135 +startPoint=213,206 + +[14-wall@0,0|7] +endPoint=138,69 +startPoint=313,206 + +[14-wall@0,0|8] +endPoint=138,69 +startPoint=108,37 + +[14-wall@0,0|9] +endPoint=108,37 +startPoint=50,37 + +[15-ball@82,336] +dummykey=true + +[15-cup@283,95|7] +dummykey=true + +[15-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[15-slope@-1,0|16] +grade=4 +gradient=Diagonal +height=319 +reversed=true +stuckOnGround=false +width=281 + +[15-slope@1,319|19] +grade=4 +gradient=Opposite Diagonal +height=80 +reversed=false +stuckOnGround=false +width=102 + +[15-slope@119,75|17] +grade=4 +gradient=Diagonal +height=322 +reversed=false +stuckOnGround=false +width=279 + +[15-slope@251,-1|18] +grade=4 +gradient=Opposite Diagonal +height=91 +reversed=true +stuckOnGround=false +width=150 + +[15-wall@0,0|10] +endPoint=98,277 +startPoint=98,218 + +[15-wall@0,0|11] +endPoint=190,301 +startPoint=132,301 + +[15-wall@0,0|12] +endPoint=283,197 +startPoint=222,197 + +[15-wall@0,0|13] +endPoint=193,167 +startPoint=193,106 + +[15-wall@0,0|14] +endPoint=159,240 +startPoint=158,196 + +[15-wall@0,0|15] +endPoint=202,240 +startPoint=159,240 + +[15-wall@0,0|3] +endPoint=10,315 +startPoint=269,18 + +[15-wall@0,0|4] +endPoint=377,88 +startPoint=113,394 + +[15-wall@0,0|5] +endPoint=377,88 +startPoint=269,18 + +[15-wall@0,0|6] +endPoint=113,394 +startPoint=10,315 + +[15-wall@0,0|8] +endPoint=250,96 +startPoint=250,133 + +[15-wall@0,0|9] +endPoint=290,134 +startPoint=250,133 + +[16-ball@132,358] +dummykey=true + +[16-blackhole@109,200|12] +exit=110,111 +exitDeg=90 +maxspeed=5 +minspeed=3 + +[16-cup@318,52|13] +dummykey=true + +[16-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[16-sand@233,399|19] +changeEnabled=false +changeEvery=50 +height=150 +width=86 + +[16-sand@307,259|17] +changeEnabled=false +changeEvery=50 +height=116 +width=26 + +[16-sand@387,206|18] +changeEnabled=false +changeEvery=50 +height=146 +width=100 + +[16-sand@402,371|16] +changeEnabled=false +changeEvery=50 +height=404 +width=358 + +[16-slope@177,19|14] +grade=4 +gradient=Opposite Diagonal +height=101 +reversed=true +stuckOnGround=false +width=175 + +[16-slope@55,115|11] +grade=5 +gradient=Vertical +height=224 +reversed=true +stuckOnGround=false +width=107 + +[16-wall@0,0|10] +endPoint=160,117 +startPoint=56,116 + +[16-wall@0,0|16] +endPoint=241,94 +startPoint=186,59 + +[16-wall@0,0|3] +endPoint=56,78 +startPoint=57,381 + +[16-wall@0,0|4] +endPoint=160,379 +startPoint=57,381 + +[16-wall@0,0|5] +endPoint=160,117 +startPoint=160,379 + +[16-wall@0,0|6] +endPoint=114,19 +startPoint=56,78 + +[16-wall@0,0|7] +endPoint=354,19 +startPoint=114,19 + +[16-wall@0,0|8] +endPoint=354,116 +startPoint=160,117 + +[16-wall@0,0|9] +endPoint=354,19 +startPoint=354,116 + +[16-windmill@288,278|20] +botWallVisible=true +bottom=false +height=96 +leftWallVisible=true +rightWallVisible=true +speed=5 +topWallVisible=false +width=86 + +[17-ball@91,316] +dummykey=true + +[17-bumper@152,251|14] +dummykey=true + +[17-bumper@175,181|12] +dummykey=true + +[17-bumper@214,222|13] +dummykey=true + +[17-bumper@245,156|15] +dummykey=true + +[17-bumper@333,114|16] +dummykey=true + +[17-cup@321,312|10] +dummykey=true + +[17-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[17-slope@-3,-2|17] +grade=2 +gradient=Diagonal +height=323 +reversed=true +stuckOnGround=false +width=291 + +[17-slope@288,190|18] +grade=2 +gradient=Vertical +height=156 +reversed=false +stuckOnGround=false +width=68 + +[17-wall@0,0|11] +endPoint=310,66 +startPoint=243,66 + +[17-wall@0,0|3] +endPoint=271,32 +startPoint=32,299 + +[17-wall@0,0|4] +endPoint=289,179 +startPoint=107,375 + +[17-wall@0,0|5] +endPoint=355,103 +startPoint=271,32 + +[17-wall@0,0|6] +endPoint=107,375 +startPoint=32,299 + +[17-wall@0,0|7] +endPoint=289,347 +startPoint=289,179 + +[17-wall@0,0|8] +endPoint=355,103 +startPoint=355,347 + +[17-wall@0,0|9] +endPoint=355,347 +startPoint=289,347 + +[18-ball@173,353] +dummykey=true + +[18-bridge@172,92|28] +botWallVisible=false +height=47 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=30 + +[18-cup@188,44|7] +dummykey=true + +[18-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[18-puddle@215,126|17] +changeEnabled=false +changeEvery=50 +height=44 +width=322 + +[18-puddle@31,169|14] +changeEnabled=false +changeEvery=50 +height=44 +width=80 + +[18-puddle@341,133|18] +changeEnabled=false +changeEvery=50 +height=56 +width=176 + +[18-puddle@41,155|15] +changeEnabled=false +changeEvery=50 +height=52 +width=70 + +[18-puddle@5,178|27] +changeEnabled=false +changeEvery=50 +height=48 +width=122 + +[18-puddle@53,146|16] +changeEnabled=false +changeEvery=50 +height=36 +width=76 + +[18-puddle@74,135|18] +changeEnabled=false +changeEvery=50 +height=30 +width=90 + +[18-slope@122,156|16] +grade=2 +gradient=Vertical +height=222 +reversed=true +stuckOnGround=false +width=134 + +[18-slope@172,118|9] +grade=3 +gradient=Vertical +height=30 +reversed=true +stuckOnGround=false +width=30 + +[18-slope@172,89|10] +grade=3 +gradient=Vertical +height=30 +reversed=false +stuckOnGround=false +width=30 + +[18-wall@0,0|3] +endPoint=122,16 +startPoint=122,377 + +[18-wall@0,0|4] +endPoint=258,16 +startPoint=257,377 + +[18-wall@0,0|5] +endPoint=257,377 +startPoint=122,377 + +[18-wall@0,0|6] +endPoint=258,16 +startPoint=122,16 + +[2-ball@191,371] +dummykey=true + +[2-bumper@165,269|19] +dummykey=true + +[2-cup@362,65|14] +dummykey=true + +[2-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[2-puddle@-43,17|23] +changeEnabled=false +changeEvery=50 +height=380 +width=288 + +[2-sand@-28,-1|22] +changeEnabled=false +changeEvery=50 +height=502 +width=336 + +[2-sand@223,221|18] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[2-slope@139,151|19] +grade=3 +gradient=Vertical +height=193 +reversed=true +stuckOnGround=true +width=113 + +[2-slope@180,116|20] +grade=6 +gradient=Diagonal +height=33 +reversed=false +stuckOnGround=false +width=15 + +[2-slope@193,117|9] +grade=6 +gradient=Vertical +height=33 +reversed=false +stuckOnGround=false +width=42 + +[2-slope@235,116|21] +grade=6 +gradient=Opposite Diagonal +height=34 +reversed=false +stuckOnGround=false +width=16 + +[2-wall@0,0|10] +endPoint=244,38 +startPoint=140,151 + +[2-wall@0,0|11] +endPoint=296,91 +startPoint=251,153 + +[2-wall@0,0|12] +endPoint=389,37 +startPoint=244,38 + +[2-wall@0,0|13] +endPoint=389,91 +startPoint=297,92 + +[2-wall@0,0|16] +endPoint=274,69 +startPoint=296,91 + +[2-wall@0,0|17] +endPoint=314,60 +startPoint=294,41 + +[2-wall@0,0|20] +endPoint=250,388 +startPoint=139,388 + +[2-wall@0,0|21] +endPoint=389,37 +startPoint=389,91 + +[2-wall@0,0|3] +endPoint=139,149 +startPoint=139,388 + +[2-wall@0,0|4] +endPoint=251,153 +startPoint=250,388 + +[2-wall@0,0|7] +endPoint=191,149 +startPoint=140,151 + +[2-wall@0,0|8] +endPoint=251,153 +startPoint=230,153 + +[3-ball@197,369] +dummykey=true + +[3-blackhole@200,179|6] +exit=197,147 +exitDeg=90 +maxspeed=2 +minspeed=1 + +[3-cup@364,39|13] +dummykey=true + +[3-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[3-slope@-2,-1|18] +grade=4 +gradient=Diagonal +height=318 +reversed=true +stuckOnGround=false +width=150 + +[3-slope@143,152|5] +grade=5 +gradient=Vertical +height=197 +reversed=true +stuckOnGround=true +width=106 + +[3-slope@176,108|10] +grade=7 +gradient=Diagonal +height=42 +reversed=false +stuckOnGround=false +width=39 + +[3-slope@257,83|19] +grade=4 +gradient=Diagonal +height=319 +reversed=false +stuckOnGround=false +width=143 + +[3-wall@0,0|11] +endPoint=178,9 +startPoint=178,150 + +[3-wall@0,0|12] +endPoint=215,71 +startPoint=214,151 + +[3-wall@0,0|14] +endPoint=385,72 +startPoint=215,71 + +[3-wall@0,0|16] +endPoint=250,388 +startPoint=144,388 + +[3-wall@0,0|17] +endPoint=393,10 +startPoint=178,9 + +[3-wall@0,0|18] +endPoint=296,38 +startPoint=236,38 + +[3-wall@0,0|20] +endPoint=393,10 +startPoint=385,72 + +[3-wall@0,0|3] +endPoint=145,151 +startPoint=144,388 + +[3-wall@0,0|4] +endPoint=250,151 +startPoint=250,388 + +[3-wall@0,0|7] +endPoint=250,151 +startPoint=145,151 + +[3-wall@0,0|8] +endPoint=198,224 +startPoint=171,201 + +[3-wall@0,0|9] +endPoint=226,201 +startPoint=198,224 + +[4-ball@181,367] +dummykey=true + +[4-cup@360,284|14] +dummykey=true + +[4-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[4-puddle@-2,45|24] +changeEnabled=false +changeEvery=50 +height=314 +width=236 + +[4-puddle@67,-7|25] +changeEnabled=false +changeEvery=50 +height=194 +width=348 + +[4-slope@328,206|23] +grade=3 +gradient=Diagonal +height=180 +reversed=true +stuckOnGround=false +width=63 + +[4-slope@330,200|15] +grade=3 +gradient=Diagonal +height=186 +reversed=false +stuckOnGround=false +width=60 + +[4-wall@0,0|10] +endPoint=321,83 +startPoint=206,82 + +[4-wall@0,0|11] +endPoint=389,150 +startPoint=322,82 + +[4-wall@0,0|12] +endPoint=272,145 +startPoint=329,202 + +[4-wall@0,0|13] +endPoint=329,203 +startPoint=329,386 + +[4-wall@0,0|16] +endPoint=236,386 +startPoint=124,387 + +[4-wall@0,0|17] +endPoint=389,150 +startPoint=388,386 + +[4-wall@0,0|18] +endPoint=388,386 +startPoint=329,386 + +[4-wall@0,0|19] +endPoint=321,83 +startPoint=310,96 + +[4-wall@0,0|20] +endPoint=283,129 +startPoint=271,144 + +[4-wall@0,0|21] +endPoint=343,189 +startPoint=329,202 + +[4-wall@0,0|22] +endPoint=389,150 +startPoint=373,162 + +[4-wall@0,0|3] +endPoint=124,205 +startPoint=124,387 + +[4-wall@0,0|4] +endPoint=236,201 +startPoint=236,386 + +[4-wall@0,0|6] +endPoint=144,186 +startPoint=124,205 + +[4-wall@0,0|7] +endPoint=219,186 +startPoint=236,201 + +[4-wall@0,0|8] +endPoint=206,82 +startPoint=144,144 + +[4-wall@0,0|9] +endPoint=272,145 +startPoint=219,144 + +[4-windmill@144,144|5] +botWallVisible=false +bottom=true +height=42 +leftWallVisible=true +rightWallVisible=true +speed=6 +topWallVisible=false +width=75 + +[5-ball@194,372] +dummykey=true + +[5-bumper@246,44|15] +dummykey=true + +[5-cup@347,44|9] +dummykey=true + +[5-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[5-slope@-1,-1|16] +grade=4 +gradient=Horizontal +height=398 +reversed=false +stuckOnGround=false +width=140 + +[5-slope@139,126|10] +grade=5 +gradient=Vertical +height=225 +reversed=true +stuckOnGround=false +width=111 + +[5-slope@139,96|10] +grade=6 +gradient=Vertical +height=32 +reversed=false +stuckOnGround=false +width=112 + +[5-slope@252,98|17] +grade=4 +gradient=Horizontal +height=296 +reversed=true +stuckOnGround=false +width=148 + +[5-slope@260,84|18] +grade=4 +gradient=Horizontal +height=14 +reversed=true +stuckOnGround=false +width=171 + +[5-slope@319,18|11] +grade=6 +gradient=Elliptic +height=57 +reversed=false +stuckOnGround=false +width=57 + +[5-slope@387,0|19] +grade=4 +gradient=Horizontal +height=84 +reversed=true +stuckOnGround=false +width=17 + +[5-wall@0,0|12] +endPoint=250,388 +startPoint=140,388 + +[5-wall@0,0|13] +endPoint=388,7 +startPoint=227,8 + +[5-wall@0,0|14] +endPoint=388,7 +startPoint=387,80 + +[5-wall@0,0|3] +endPoint=140,95 +startPoint=140,388 + +[5-wall@0,0|4] +endPoint=250,95 +startPoint=250,388 + +[5-wall@0,0|6] +endPoint=227,8 +startPoint=140,95 + +[5-wall@0,0|7] +endPoint=266,80 +startPoint=250,95 + +[5-wall@0,0|8] +endPoint=387,80 +startPoint=266,80 + +[6-ball@212,370] +dummykey=true + +[6-bridge@193,151|8] +botWallVisible=false +height=47 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=35 + +[6-cup@210,61|17] +dummykey=true + +[6-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[6-puddle@204,177|4] +changeEnabled=false +changeEvery=50 +height=38 +width=126 + +[6-puddle@238,175|5] +changeEnabled=false +changeEvery=50 +height=34 +width=92 + +[6-sand@419,109|18] +changeEnabled=false +changeEvery=50 +height=150 +width=126 + +[6-sand@436,9|17] +changeEnabled=false +changeEvery=50 +height=276 +width=228 + +[6-slope@142,8|20] +grade=3 +gradient=Vertical +height=27 +reversed=true +stuckOnGround=false +width=140 + +[6-slope@193,143|9] +grade=3 +gradient=Vertical +height=26 +reversed=false +stuckOnGround=false +width=35 + +[6-slope@193,168|8] +grade=3 +gradient=Vertical +height=30 +reversed=true +stuckOnGround=false +width=35 + +[6-wall@0,0|12] +endPoint=140,8 +startPoint=141,387 + +[6-wall@0,0|13] +endPoint=283,8 +startPoint=284,387 + +[6-wall@0,0|14] +endPoint=211,269 +startPoint=211,280 + +[6-wall@0,0|15] +endPoint=181,252 +startPoint=181,289 + +[6-wall@0,0|16] +endPoint=240,252 +startPoint=240,288 + +[6-wall@0,0|18] +endPoint=283,8 +startPoint=140,8 + +[6-wall@0,0|19] +endPoint=284,387 +startPoint=141,387 + +[7-ball@343,355] +dummykey=true + +[7-cup@235,349|12] +dummykey=true + +[7-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[7-puddle@170,137|7] +changeEnabled=false +changeEvery=50 +height=38 +width=120 + +[7-puddle@204,128|23] +changeEnabled=false +changeEvery=50 +height=48 +width=56 + +[7-puddle@257,247|8] +changeEnabled=false +changeEvery=50 +height=70 +width=40 + +[7-slope@10,149|19] +grade=2 +gradient=Vertical +height=27 +reversed=false +stuckOnGround=false +width=88 + +[7-slope@113,22|22] +grade=4 +gradient=Elliptic +height=68 +reversed=false +stuckOnGround=false +width=68 + +[7-slope@12,175|20] +grade=2 +gradient=Vertical +height=27 +reversed=true +stuckOnGround=false +width=86 + +[7-slope@123,169|17] +grade=8 +gradient=Elliptic +height=104 +reversed=false +stuckOnGround=false +width=104 + +[7-slope@128,306|15] +grade=2 +gradient=Horizontal +height=82 +reversed=false +stuckOnGround=false +width=24 + +[7-slope@148,306|16] +grade=2 +gradient=Horizontal +height=81 +reversed=true +stuckOnGround=false +width=23 + +[7-slope@205,13|21] +grade=4 +gradient=Elliptic +height=68 +reversed=true +stuckOnGround=false +width=68 + +[7-slope@291,217|18] +grade=2 +gradient=Vertical +height=171 +reversed=false +stuckOnGround=false +width=96 + +[7-wall@0,0|10] +endPoint=75,9 +startPoint=10,74 + +[7-wall@0,0|11] +endPoint=77,388 +startPoint=11,322 + +[7-wall@0,0|13] +endPoint=291,334 +startPoint=266,308 + +[7-wall@0,0|14] +endPoint=292,362 +startPoint=265,388 + +[7-wall@0,0|3] +endPoint=293,91 +startPoint=292,389 + +[7-wall@0,0|4] +endPoint=293,91 +startPoint=97,91 + +[7-wall@0,0|5] +endPoint=97,91 +startPoint=97,308 + +[7-wall@0,0|6] +endPoint=292,308 +startPoint=97,308 + +[7-wall@0,0|9] +endPoint=389,77 +startPoint=323,10 + +[8-ball@197,360] +dummykey=true + +[8-cup@197,41|5] +dummykey=true + +[8-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[8-puddle@320,230|19] +changeEnabled=false +changeEvery=50 +height=62 +width=82 + +[8-puddle@324,228|22] +changeEnabled=false +changeEvery=50 +height=90 +width=88 + +[8-puddle@340,216|21] +changeEnabled=false +changeEvery=50 +height=54 +width=68 + +[8-puddle@340,243|20] +changeEnabled=false +changeEvery=50 +height=88 +width=122 + +[8-slope@138,205|14] +grade=3 +gradient=Opposite Diagonal +height=46 +reversed=false +stuckOnGround=false +width=52 + +[8-slope@140,139|16] +grade=3 +gradient=Diagonal +height=40 +reversed=true +stuckOnGround=false +width=54 + +[8-slope@19,32|18] +grade=4 +gradient=Elliptic +height=96 +reversed=false +stuckOnGround=false +width=96 + +[8-slope@196,256|15] +grade=3 +gradient=Diagonal +height=53 +reversed=false +stuckOnGround=false +width=56 + +[8-slope@210,85|17] +grade=3 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[8-wall@0,0|10] +endPoint=252,87 +startPoint=216,87 + +[8-wall@0,0|11] +endPoint=252,388 +startPoint=139,389 + +[8-wall@0,0|12] +endPoint=252,10 +startPoint=139,9 + +[8-wall@0,0|13] +endPoint=190,141 +startPoint=140,141 + +[8-wall@0,0|3] +endPoint=139,9 +startPoint=139,389 + +[8-wall@0,0|4] +endPoint=252,10 +startPoint=252,388 + +[8-wall@0,0|6] +endPoint=251,310 +startPoint=197,310 + +[8-wall@0,0|7] +endPoint=190,251 +startPoint=141,251 + +[8-wall@0,0|8] +endPoint=251,192 +startPoint=199,192 + +[8-wall@0,0|9] +endPoint=176,87 +startPoint=140,87 + +[9-ball@275,369] +dummykey=true + +[9-cup@183,50|6] +dummykey=true + +[9-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[9-puddle@280,79|19] +changeEnabled=false +changeEvery=50 +height=54 +width=68 + +[9-sand@-11,294|21] +changeEnabled=false +changeEvery=50 +height=174 +width=152 + +[9-sand@-16,398|20] +changeEnabled=false +changeEvery=50 +height=254 +width=362 + +[9-slope@141,105|15] +grade=3 +gradient=Vertical +height=24 +reversed=true +stuckOnGround=false +width=84 + +[9-slope@141,79|14] +grade=3 +gradient=Vertical +height=26 +reversed=false +stuckOnGround=false +width=84 + +[9-slope@224,204|12] +grade=4 +gradient=Vertical +height=31 +reversed=false +stuckOnGround=false +width=86 + +[9-slope@224,298|10] +grade=3 +gradient=Vertical +height=22 +reversed=false +stuckOnGround=false +width=87 + +[9-slope@226,235|13] +grade=4 +gradient=Vertical +height=25 +reversed=true +stuckOnGround=false +width=84 + +[9-slope@226,320|11] +grade=3 +gradient=Vertical +height=21 +reversed=true +stuckOnGround=false +width=85 + +[9-wall@0,0|16] +endPoint=310,387 +startPoint=225,388 + +[9-wall@0,0|17] +endPoint=225,9 +startPoint=142,9 + +[9-wall@0,0|18] +endPoint=225,160 +startPoint=225,169 + +[9-wall@0,0|3] +endPoint=226,205 +startPoint=225,388 + +[9-wall@0,0|4] +endPoint=226,205 +startPoint=142,203 + +[9-wall@0,0|5] +endPoint=142,9 +startPoint=142,203 + +[9-wall@0,0|7] +endPoint=311,128 +startPoint=310,387 + +[9-wall@0,0|8] +endPoint=311,128 +startPoint=225,128 + +[9-wall@0,0|9] +endPoint=225,9 +startPoint=225,128 diff --git a/kolf/courses/Easy.kolf b/kolf/courses/Easy.kolf new file mode 100644 index 00000000..97f7d292 --- /dev/null +++ b/kolf/courses/Easy.kolf @@ -0,0 +1,1737 @@ +[0-course@-50,-50] +Name=Easy Course +Name[af]=Maklike Natuurlik +Name[bg]=ЛеÑно +Name[bn]=সহজ খেলা +Name[bs]=Lagani teren +Name[ca]=Camp fàcil +Name[cs]=Jednoduchý +Name[da]=Nem bane +Name[de]=Leicht +Name[el]=ΕÏκολη πίστα +Name[es]=Campo sencillo +Name[et]=Lihtne väljak +Name[fi]=Helppo kenttä +Name[fr]=Parcours facile +Name[ga]=Cúrsa Éasca +Name[gl]=Campo sinxelo +Name[he]=מסלול קל +Name[hi]=आसान कोरà¥à¤¸ +Name[hr]=Lagani teren +Name[hu]=Könnyű pálya +Name[is]=Auðveld leið +Name[it]=Percorso facile +Name[ja]=ç°¡å˜ãªã‚³ãƒ¼ã‚¹ +Name[mk]=ЛеÑен терен +Name[nb]=Enkel bane +Name[nl]=Eenvoudig parcours +Name[nn]=Enkel bane +Name[nso]=Course ye Bonolo +Name[pl]=Åatwy tor +Name[pt]=Percurso Fácil +Name[pt_BR]=Curso Fácil +Name[ro]=Cale uÅŸoară +Name[ru]=Ð›Ñ‘Ð³ÐºÐ°Ñ Ð¿Ð»Ð¾Ñ‰Ð°Ð´ÐºÐ° +Name[sk]=Jednoduché ihrisko +Name[sl]=Lahko igriÅ¡Äe +Name[sr]=Лак терен +Name[sr@Latn]=Lak teren +Name[sv]=Enkel bana +Name[ta]=சà¯à®²à®ªà®®à®¾à®© மாரà¯à®•à¯à®•à®®à¯ +Name[tg]=Майдони Сабук +Name[tr]=Kolay Pist +Name[uk]=Легкий +Name[ven]=Thero i Leluwaho +Name[xh]=Indlela Elula +Name[xx]=xxEasy Coursexx +Name[zh_CN]=è½»æ¾çš„路线 +Name[zh_TW]=簡單的路線 +Name[zu]=Indlela elula +author=Jason Katz-Brown +name=Easy Course + +[1-ball@267,126] +dummykey=true + +[1-cup@313,325|22] +dummykey=true + +[1-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[1-puddle@69,72|14] +changeEnabled=false +changeEvery=50 +height=92 +width=112 + +[1-puddle@84,103|19] +changeEnabled=false +changeEvery=50 +height=144 +width=96 + +[1-sand@-6,255|19] +changeEnabled=false +changeEvery=50 +height=256 +width=112 + +[1-sand@-6,368|18] +changeEnabled=false +changeEvery=50 +height=238 +width=190 + +[1-sand@33,295|0] +changeEnabled=false +changeEvery=50 +height=116 +width=54 + +[1-sand@37,261|1] +changeEnabled=false +changeEvery=50 +height=84 +width=34 + +[1-sand@40,303|2] +changeEnabled=false +changeEvery=50 +height=88 +width=54 + +[1-slope@211,282|21] +grade=3 +gradient=Horizontal +height=83 +reversed=false +stuckOnGround=false +width=61 + +[1-wall@0,0|10] +endPoint=347,282 +startPoint=348,364 + +[1-wall@0,0|11] +endPoint=347,282 +startPoint=211,281 + +[1-wall@0,0|12] +endPoint=195,257 +startPoint=211,281 + +[1-wall@0,0|13] +endPoint=281,208 +startPoint=195,257 + +[1-wall@0,0|3] +endPoint=343,148 +startPoint=281,208 + +[1-wall@0,0|4] +endPoint=327,85 +startPoint=343,148 + +[1-wall@0,0|5] +endPoint=327,85 +startPoint=273,56 + +[1-wall@0,0|6] +endPoint=273,56 +startPoint=122,182 + +[1-wall@0,0|7] +endPoint=122,182 +startPoint=112,349 + +[1-wall@0,0|8] +endPoint=138,368 +startPoint=112,349 + +[1-wall@0,0|9] +endPoint=348,364 +startPoint=138,368 + +[10-ball@107,335] +dummykey=true + +[10-blackhole@128,259|15] +exit=227,337 +exitDeg=220 +maxspeed=2 +minspeed=1 + +[10-cup@322,331|3] +dummykey=true + +[10-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[10-puddle@262,-7|45] +changeEnabled=false +changeEvery=50 +height=90 +width=166 + +[10-puddle@50,-21|44] +changeEnabled=false +changeEvery=50 +height=224 +width=466 + +[10-slope@109,151|41] +grade=4 +gradient=Horizontal +height=71 +reversed=true +stuckOnGround=false +width=54 + +[10-slope@150,349|45] +grade=8 +gradient=Horizontal +height=41 +reversed=false +stuckOnGround=false +width=44 + +[10-slope@162,329|46] +grade=7 +gradient=Horizontal +height=21 +reversed=false +stuckOnGround=false +width=33 + +[10-slope@173,121|42] +grade=6 +gradient=Elliptic +height=165 +reversed=false +stuckOnGround=false +width=165 + +[10-slope@21,275|47] +grade=7 +gradient=Vertical +height=65 +reversed=false +stuckOnGround=false +width=49 + +[10-wall@0,0|10] +endPoint=139,218 +startPoint=107,225 + +[10-wall@0,0|11] +endPoint=107,225 +startPoint=80,256 + +[10-wall@0,0|12] +endPoint=80,256 +startPoint=69,302 + +[10-wall@0,0|13] +endPoint=69,302 +startPoint=75,340 + +[10-wall@0,0|14] +endPoint=91,360 +startPoint=75,340 + +[10-wall@0,0|16] +endPoint=372,358 +startPoint=335,379 + +[10-wall@0,0|17] +endPoint=335,379 +startPoint=288,374 + +[10-wall@0,0|18] +endPoint=288,374 +startPoint=248,353 + +[10-wall@0,0|19] +endPoint=246,351 +startPoint=219,321 + +[10-wall@0,0|20] +endPoint=219,321 +startPoint=194,325 + +[10-wall@0,0|22] +endPoint=194,325 +startPoint=174,314 + +[10-wall@0,0|23] +endPoint=372,358 +startPoint=386,320 + +[10-wall@0,0|24] +endPoint=386,320 +startPoint=387,260 + +[10-wall@0,0|25] +endPoint=372,197 +startPoint=355,158 + +[10-wall@0,0|26] +endPoint=355,158 +startPoint=289,111 + +[10-wall@0,0|27] +endPoint=289,111 +startPoint=232,111 + +[10-wall@0,0|28] +endPoint=232,111 +startPoint=187,128 + +[10-wall@0,0|29] +endPoint=187,128 +startPoint=158,152 + +[10-wall@0,0|30] +endPoint=158,152 +startPoint=111,153 + +[10-wall@0,0|33] +endPoint=111,153 +startPoint=34,222 + +[10-wall@0,0|35] +endPoint=34,222 +startPoint=21,286 + +[10-wall@0,0|36] +endPoint=21,286 +startPoint=21,353 + +[10-wall@0,0|37] +endPoint=21,353 +startPoint=55,393 + +[10-wall@0,0|38] +endPoint=55,393 +startPoint=201,394 + +[10-wall@0,0|39] +endPoint=201,394 +startPoint=240,374 + +[10-wall@0,0|4] +endPoint=128,367 +startPoint=91,360 + +[10-wall@0,0|40] +endPoint=240,374 +startPoint=248,353 + +[10-wall@0,0|45] +endPoint=372,197 +startPoint=387,260 + +[10-wall@0,0|5] +endPoint=128,367 +startPoint=159,343 + +[10-wall@0,0|6] +endPoint=159,343 +startPoint=176,311 + +[10-wall@0,0|7] +endPoint=174,314 +startPoint=180,266 + +[10-wall@0,0|8] +endPoint=180,266 +startPoint=164,230 + +[10-wall@0,0|9] +endPoint=164,230 +startPoint=139,218 + +[11-ball@63,369] +dummykey=true + +[11-blackhole@116,216|10] +exit=275,307 +exitDeg=140 +maxspeed=4 +minspeed=2 + +[11-blackhole@124,260|6] +exit=23,381 +exitDeg=50 +maxspeed=6 +minspeed=5 + +[11-blackhole@128,126|21] +exit=103,387 +exitDeg=90 +maxspeed=2 +minspeed=1 + +[11-blackhole@139,184|7] +exit=347,109 +exitDeg=130 +maxspeed=2 +minspeed=1 + +[11-blackhole@163,234|11] +exit=24,25 +exitDeg=309 +maxspeed=5 +minspeed=3 + +[11-blackhole@164,136|4] +exit=15,147 +exitDeg=349 +maxspeed=7 +minspeed=6 + +[11-blackhole@187,159|12] +exit=281,22 +exitDeg=329 +maxspeed=2 +minspeed=1 + +[11-blackhole@194,207|5] +exit=308,183 +exitDeg=170 +maxspeed=10 +minspeed=3 + +[11-blackhole@59,187|9] +exit=101,20 +exitDeg=289 +maxspeed=5 +minspeed=3 + +[11-blackhole@82,233|8] +exit=301,235 +exitDeg=160 +maxspeed=8 +minspeed=4 + +[11-blackhole@86,154|13] +exit=198,384 +exitDeg=110 +maxspeed=5 +minspeed=3 + +[11-cup@307,85|3] +dummykey=true + +[11-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[11-sand@383,371|16] +changeEnabled=false +changeEvery=50 +height=214 +width=252 + +[11-sand@388,295|15] +changeEnabled=false +changeEvery=50 +height=178 +width=146 + +[11-slope@-15,24|17] +grade=2 +gradient=Elliptic +height=316 +reversed=true +stuckOnGround=false +width=316 + +[11-slope@226,1|17] +grade=4 +gradient=Elliptic +height=162 +reversed=true +stuckOnGround=false +width=162 + +[11-wall@0,0|18] +endPoint=242,70 +startPoint=185,27 + +[11-wall@0,0|19] +endPoint=242,70 +startPoint=288,157 + +[11-wall@0,0|20] +endPoint=185,27 +startPoint=72,11 + +[11-wall@0,0|22] +endPoint=81,346 +startPoint=84,388 + +[12-ball@247,85] +dummykey=true + +[12-cup@79,101|3] +dummykey=true + +[12-floater@245,248|11] +botWallVisible=false +endPoint=245,248 +height=66 +leftWallVisible=true +rightWallVisible=true +speed=2 +startPoint=241,177 +topWallVisible=false +width=47 + +[12-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=true +maxstrokes=10 +par=3 + +[12-slope@205,213|21] +grade=4 +gradient=Horizontal +height=170 +reversed=false +stuckOnGround=true +width=85 + +[12-wall@0,0|10] +endPoint=361,153 +startPoint=339,75 + +[12-wall@0,0|13] +endPoint=38,326 +startPoint=36,166 + +[12-wall@0,0|14] +endPoint=88,382 +startPoint=296,383 + +[12-wall@0,0|15] +endPoint=346,333 +startPoint=313,214 + +[12-wall@0,0|17] +endPoint=38,326 +startPoint=88,382 + +[12-wall@0,0|20] +endPoint=346,333 +startPoint=296,383 + +[12-wall@0,0|4] +endPoint=339,75 +startPoint=246,29 + +[12-wall@0,0|5] +endPoint=246,29 +startPoint=137,64 + +[12-wall@0,0|7] +endPoint=137,166 +startPoint=215,213 + +[12-wall@0,0|8] +endPoint=215,213 +startPoint=314,213 + +[12-wall@0,0|9] +endPoint=313,214 +startPoint=361,153 + +[12-windmill@36,64|12] +botWallVisible=false +bottom=true +height=102 +leftWallVisible=true +rightWallVisible=true +speed=5 +topWallVisible=true +width=101 + +[13-ball@356,340] +dummykey=true + +[13-cup@213,189|3] +dummykey=true + +[13-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[13-puddle@166,190|8] +changeEnabled=false +changeEvery=50 +height=86 +width=22 + +[13-puddle@234,150|9] +changeEnabled=false +changeEvery=50 +height=26 +width=140 + +[13-puddle@276,228|7] +changeEnabled=false +changeEvery=50 +height=28 +width=230 + +[13-sand@137,322|14] +changeEnabled=false +changeEvery=50 +height=96 +width=12 + +[13-sand@359,72|11] +changeEnabled=false +changeEvery=50 +height=76 +width=14 + +[13-sand@362,118|12] +changeEnabled=false +changeEvery=50 +height=72 +width=10 + +[13-sign@219,247|14] +Comment=Hit slowly... +Comment[af]=Getref stadig... +Comment[ar]=إضرب ببطء... +Comment[bg]=Леки и технични удари... +Comment[bn]=ধীরে আঘাত করà§à¦¨... +Comment[bs]=Polako pucaj... +Comment[ca]=Picar lentament... +Comment[cs]=Trefujte pomalu... +Comment[da]=SlÃ¥ langsomt... +Comment[de]=Langsam... +Comment[el]=Ρίξε αÏγά... +Comment[es]=Golpear lentamente... +Comment[et]=Löö aeglaselt... +Comment[fi]=Lyö hitaasti... +Comment[fr]=Taper doucement... +Comment[gl]=Golpear a modo +Comment[he]=חבוט ל×ט... +Comment[hi]=धीरे से मारें... +Comment[hr]=Udarite polako... +Comment[hu]=Lassan megütni... +Comment[is]=Sláðu varlega... +Comment[it]=Colpisci piano... +Comment[ja]=ゆã£ãり打ã¨ã†... +Comment[lv]=Sit lÄ“nÄm... +Comment[mk]=Удрете пополека... +Comment[nb]=SlÃ¥ forsiktig ... +Comment[nl]=Zachtjes slaan... +Comment[nn]=SlÃ¥ forsiktig ... +Comment[nso]=Betha kago nanya... +Comment[pl]=Powolne uderzenie... +Comment[pt]=Bata devagarinho... +Comment[pt_BR]=Arremessar lentamente... +Comment[ro]=LoveÅŸte încet... +Comment[ru]=Бейте не Ñпеша... +Comment[sk]=Jemné údery... +Comment[sl]=Udarite poÄasi ... +Comment[sr]=Ударите полако... +Comment[sr@Latn]=Udarite polako... +Comment[sv]=SlÃ¥ lÃ¥ngsamt... +Comment[ta]=மெதà¯à®µà®¾à®• அடிகà¯à®•à®µà¯à®®à¯... +Comment[tg]=ОҳиÑта занед... +Comment[tr]=YavaÅŸ basın... +Comment[uk]=Бийте без поÑпіху... +Comment[ven]=Irwani nga u ongolowa... +Comment[xh]=Betha ngokucothayo... +Comment[xx]=xxHit slowly...xx +Comment[zh_CN]=缓慢击打... +Comment[zh_TW]=緩慢地敲擊... +Comment[zu]=Shaya kancane... +botWallVisible=true +height=55 +leftWallVisible=true +rightWallVisible=false +topWallVisible=true +width=180 + +[13-slope@12,11|8] +grade=2 +gradient=Horizontal +height=124 +reversed=true +stuckOnGround=false +width=373 + +[13-slope@12,133|7] +grade=2 +gradient=Vertical +height=254 +reversed=false +stuckOnGround=false +width=139 + +[13-slope@139,266|6] +grade=2 +gradient=Horizontal +height=121 +reversed=false +stuckOnGround=false +width=248 + +[13-wall@0,0|10] +endPoint=385,132 +startPoint=365,12 + +[14-ball@94,323] +dummykey=true + +[14-bridge@261,66|13] +botWallVisible=false +height=91 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=99 + +[14-bumper@135,356|12] +dummykey=true + +[14-cup@136,355|13] +dummykey=true + +[14-cup@147,145|3] +dummykey=true + +[14-cup@293,288|8] +dummykey=true + +[14-cup@313,113|11] +dummykey=true + +[14-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[14-puddle@292,288|7] +changeEnabled=false +changeEvery=50 +height=102 +width=102 + +[14-sand@145,142|5] +changeEnabled=false +changeEvery=50 +height=130 +width=132 + +[14-slope@219,215|6] +grade=6 +gradient=Elliptic +height=145 +reversed=false +stuckOnGround=false +width=145 + +[14-slope@233,34|9] +grade=3 +gradient=Elliptic +height=160 +reversed=false +stuckOnGround=false +width=160 + +[14-slope@58,54|4] +grade=7 +gradient=Elliptic +height=177 +reversed=false +stuckOnGround=false +width=177 + +[15-ball@32,360] +dummykey=true + +[15-cup@357,168|3] +dummykey=true + +[15-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=4 + +[15-slope@10,9|6] +grade=4 +gradient=Vertical +height=332 +reversed=true +stuckOnGround=false +width=63 + +[15-slope@34,12|4] +grade=4 +gradient=Diagonal +height=376 +reversed=false +stuckOnGround=false +width=355 + +[15-slope@71,10|5] +grade=4 +gradient=Diagonal +height=332 +reversed=true +stuckOnGround=false +width=313 + +[16-ball@299,105] +dummykey=true + +[16-blackhole@308,282|23] +exit=358,136 +exitDeg=160 +maxspeed=3 +minspeed=2 + +[16-blackhole@37,334|24] +exit=227,218 +exitDeg=239 +maxspeed=1 +minspeed=0 + +[16-cup@106,333|3] +dummykey=true + +[16-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[16-slope@183,211|22] +grade=4 +gradient=Elliptic +height=108 +reversed=true +stuckOnGround=false +width=108 + +[16-slope@48,70|20] +grade=6 +gradient=Elliptic +height=100 +reversed=false +stuckOnGround=false +width=100 + +[16-wall@0,0|10] +endPoint=371,115 +startPoint=323,59 + +[16-wall@0,0|11] +endPoint=323,59 +startPoint=64,58 + +[16-wall@0,0|12] +endPoint=64,58 +startPoint=27,141 + +[16-wall@0,0|13] +endPoint=81,374 +startPoint=61,341 + +[16-wall@0,0|14] +endPoint=27,292 +startPoint=68,281 + +[16-wall@0,0|15] +endPoint=68,281 +startPoint=170,269 + +[16-wall@0,0|16] +endPoint=170,269 +startPoint=173,249 + +[16-wall@0,0|17] +endPoint=173,249 +startPoint=155,236 + +[16-wall@0,0|18] +endPoint=155,236 +startPoint=49,225 + +[16-wall@0,0|19] +endPoint=49,225 +startPoint=27,141 + +[16-wall@0,0|25] +endPoint=61,341 +startPoint=49,366 + +[16-wall@0,0|26] +endPoint=27,368 +startPoint=13,351 + +[16-wall@0,0|27] +endPoint=13,351 +startPoint=12,312 + +[16-wall@0,0|28] +endPoint=12,312 +startPoint=27,292 + +[16-wall@0,0|29] +endPoint=27,368 +startPoint=49,366 + +[16-wall@0,0|30] +endPoint=346,277 +startPoint=335,244 + +[16-wall@0,0|4] +endPoint=319,316 +startPoint=81,374 + +[16-wall@0,0|5] +endPoint=319,316 +startPoint=346,277 + +[16-wall@0,0|6] +endPoint=335,244 +startPoint=156,185 + +[16-wall@0,0|7] +endPoint=156,185 +startPoint=160,161 + +[16-wall@0,0|8] +endPoint=160,161 +startPoint=357,159 + +[16-wall@0,0|9] +endPoint=357,159 +startPoint=371,115 + +[17-ball@37,125] +dummykey=true + +[17-cup@356,45|3] +dummykey=true + +[17-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[17-wall@0,0|10] +endPoint=69,156 +startPoint=136,161 + +[17-wall@0,0|11] +endPoint=136,161 +startPoint=133,359 + +[17-wall@0,0|12] +endPoint=133,359 +startPoint=117,338 + +[17-wall@0,0|13] +endPoint=134,210 +startPoint=71,208 + +[17-wall@0,0|14] +endPoint=71,247 +startPoint=138,248 + +[17-wall@0,0|15] +endPoint=243,191 +startPoint=195,152 + +[17-wall@0,0|16] +endPoint=278,190 +startPoint=326,152 + +[17-wall@0,0|17] +endPoint=312,189 +startPoint=180,199 + +[17-wall@0,0|18] +endPoint=312,189 +startPoint=355,196 + +[17-wall@0,0|19] +endPoint=322,242 +startPoint=175,248 + +[17-wall@0,0|20] +endPoint=322,242 +startPoint=348,249 + +[17-wall@0,0|21] +endPoint=264,147 +startPoint=256,208 + +[17-wall@0,0|22] +endPoint=256,208 +startPoint=226,257 + +[17-wall@0,0|23] +endPoint=226,257 +startPoint=175,295 + +[17-wall@0,0|24] +endPoint=303,260 +startPoint=271,198 + +[17-wall@0,0|25] +endPoint=265,365 +startPoint=265,266 + +[17-wall@0,0|26] +endPoint=303,260 +startPoint=355,291 + +[17-wall@0,0|27] +endPoint=265,365 +startPoint=251,351 + +[17-wall@0,0|28] +endPoint=224,287 +startPoint=245,306 + +[17-wall@0,0|29] +endPoint=242,322 +startPoint=209,343 + +[17-wall@0,0|31] +endPoint=320,352 +startPoint=271,320 + +[17-wall@0,0|4] +endPoint=277,76 +startPoint=66,83 + +[17-wall@0,0|5] +endPoint=277,76 +startPoint=323,87 + +[17-wall@0,0|6] +endPoint=121,41 +startPoint=131,118 + +[17-wall@0,0|7] +endPoint=272,44 +startPoint=248,117 + +[17-wall@0,0|8] +endPoint=68,316 +startPoint=69,156 + +[17-wall@0,0|9] +endPoint=68,316 +startPoint=47,358 + +[17-wall@3,0|30] +endPoint=316,280 +startPoint=279,302 + +[18-ball@246,335] +dummykey=true + +[18-cup@109,107|3] +dummykey=true + +[18-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[18-puddle@348,-8|18] +changeEnabled=false +changeEvery=50 +height=100 +width=272 + +[18-puddle@410,-2|17] +changeEnabled=false +changeEvery=50 +height=280 +width=172 + +[18-slope@164,26|16] +grade=4 +gradient=Horizontal +height=107 +reversed=false +stuckOnGround=false +width=219 + +[18-wall@0,0|10] +endPoint=367,151 +startPoint=315,47 + +[18-wall@0,0|11] +endPoint=315,47 +startPoint=209,26 + +[18-wall@0,0|12] +endPoint=209,26 +startPoint=66,47 + +[18-wall@0,0|13] +endPoint=66,47 +startPoint=41,105 + +[18-wall@0,0|14] +endPoint=41,105 +startPoint=89,175 + +[18-wall@0,0|15] +endPoint=89,175 +startPoint=168,175 + +[18-wall@0,0|19] +endPoint=140,314 +startPoint=169,361 + +[18-wall@0,0|20] +endPoint=140,314 +startPoint=167,256 + +[18-wall@0,0|6] +endPoint=299,377 +startPoint=169,361 + +[18-wall@0,0|7] +endPoint=299,377 +startPoint=362,323 + +[18-wall@0,0|8] +endPoint=362,323 +startPoint=387,252 + +[18-wall@0,0|9] +endPoint=387,252 +startPoint=367,151 + +[18-windmill@167,133|4] +botWallVisible=false +bottom=true +height=123 +leftWallVisible=true +rightWallVisible=true +speed=3 +topWallVisible=false +width=180 + +[18-windmill@167,171|5] +botWallVisible=false +bottom=true +height=85 +leftWallVisible=false +rightWallVisible=false +speed=5 +topWallVisible=false +width=179 + +[2-ball@60,82] +dummykey=true + +[2-cup@343,362|3] +dummykey=true + +[2-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[2-puddle@379,-8|10] +changeEnabled=false +changeEvery=50 +height=172 +width=206 + +[2-puddle@397,65|13] +changeEnabled=false +changeEvery=50 +height=96 +width=112 + +[2-slope@10,232|16] +grade=4 +gradient=Vertical +height=158 +reversed=false +stuckOnGround=false +width=215 + +[2-slope@224,156|9] +grade=3 +gradient=Vertical +height=76 +reversed=true +stuckOnGround=false +width=167 + +[2-wall@0,0|4] +endPoint=130,12 +startPoint=130,210 + +[2-wall@0,0|5] +endPoint=225,129 +startPoint=225,388 + +[3-ball@210,356] +dummykey=true + +[3-bumper@54,246|9] +dummykey=true + +[3-bumper@98,299|8] +dummykey=true + +[3-cup@341,351|12] +dummykey=true + +[3-cup@62,354|11] +dummykey=true + +[3-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[3-slope@184,11|7] +grade=4 +gradient=Vertical +height=177 +reversed=true +stuckOnGround=false +width=21 + +[3-slope@205,11|6] +grade=4 +gradient=Diagonal +height=177 +reversed=true +stuckOnGround=false +width=162 + +[3-slope@32,10|5] +grade=4 +gradient=Opposite Diagonal +height=179 +reversed=true +stuckOnGround=false +width=154 + +[3-wall@0,0|3] +endPoint=262,212 +startPoint=263,389 + +[3-wall@0,0|4] +endPoint=159,213 +startPoint=159,386 + +[3-windmill@263,288|10] +botWallVisible=false +bottom=false +height=101 +leftWallVisible=true +rightWallVisible=false +speed=2 +topWallVisible=false +width=125 + +[4-ball@91,344] +dummykey=true + +[4-cup@291,91|18] +dummykey=true + +[4-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[4-puddle@115,111|7] +changeEnabled=false +changeEvery=50 +height=70 +width=86 + +[4-puddle@125,133|3] +changeEnabled=false +changeEvery=50 +height=62 +width=66 + +[4-puddle@136,121|5] +changeEnabled=false +changeEvery=50 +height=60 +width=72 + +[4-puddle@277,291|15] +changeEnabled=false +changeEvery=50 +height=76 +width=90 + +[4-puddle@296,322|11] +changeEnabled=false +changeEvery=50 +height=128 +width=98 + +[4-slope@255,136|20] +grade=4 +gradient=Elliptic +height=123 +reversed=false +stuckOnGround=false +width=123 + +[4-slope@28,155|19] +grade=3 +gradient=Elliptic +height=145 +reversed=false +stuckOnGround=false +width=145 + +[5-ball@197,252] +dummykey=true + +[5-cup@85,77|33] +dummykey=true + +[5-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[5-puddle@150,258|42] +changeEnabled=false +changeEvery=50 +height=134 +width=62 + +[5-puddle@198,207|11] +changeEnabled=false +changeEvery=50 +height=52 +width=130 + +[5-puddle@248,182|10] +changeEnabled=false +changeEvery=50 +height=76 +width=42 + +[5-puddle@278,158|11] +changeEnabled=false +changeEvery=50 +height=44 +width=84 + +[5-slope@14,195|36] +grade=3 +gradient=Elliptic +height=106 +reversed=true +stuckOnGround=false +width=106 + +[5-slope@160,169|15] +grade=4 +gradient=Diagonal +height=220 +reversed=false +stuckOnGround=false +width=227 + +[5-slope@181,12|35] +grade=3 +gradient=Elliptic +height=128 +reversed=false +stuckOnGround=false +width=128 + +[5-wall@0,0|9] +endPoint=127,390 +startPoint=13,314 + +[6-ball@351,186] +dummykey=true + +[6-blackhole@306,352|12] +exit=16,145 +exitDeg=80 +maxspeed=2 +minspeed=2 + +[6-blackhole@367,38|11] +exit=19,10 +exitDeg=279 +maxspeed=1 +minspeed=1 + +[6-bridge@281,64|6] +botWallVisible=true +height=155 +leftWallVisible=false +rightWallVisible=true +topWallVisible=true +width=116 + +[6-bridge@5,2|7] +botWallVisible=true +height=149 +leftWallVisible=true +rightWallVisible=false +topWallVisible=true +width=96 + +[6-cup@30,67|9] +dummykey=true + +[6-floater@78,34|8] +botWallVisible=true +endPoint=78,34 +height=78 +leftWallVisible=false +rightWallVisible=false +speed=2 +startPoint=207,104 +topWallVisible=true +width=128 + +[6-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=true +maxstrokes=10 +par=3 + +[6-puddle@198,123|6] +changeEnabled=false +changeEvery=50 +height=394 +width=316 + +[6-slope@109,429|8] +grade=4 +gradient=Elliptic +height=77 +reversed=true +stuckOnGround=false +width=77 + +[6-slope@99,35|18] +grade=3 +gradient=Elliptic +height=75 +reversed=true +stuckOnGround=false +width=75 + +[6-wall@0,0|10] +endPoint=381,9 +startPoint=392,65 + +[6-wall@0,0|13] +endPoint=381,218 +startPoint=386,329 + +[6-wall@0,0|14] +endPoint=386,329 +startPoint=299,391 + +[6-wall@0,0|15] +endPoint=299,391 +startPoint=39,345 + +[6-wall@0,0|16] +endPoint=39,345 +startPoint=14,204 + +[6-wall@0,0|17] +endPoint=14,204 +startPoint=24,152 + +[6-wall@0,0|9] +endPoint=381,9 +startPoint=317,3 + +[7-ball@65,363] +dummykey=true + +[7-cup@309,83|3] +dummykey=true + +[7-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[7-puddle@337,401|4] +changeEnabled=false +changeEvery=50 +height=504 +width=412 + +[7-slope@-2,-3|5] +grade=2 +gradient=Diagonal +height=353 +reversed=true +stuckOnGround=false +width=394 + +[7-slope@247,27|10] +grade=2 +gradient=Elliptic +height=123 +reversed=true +stuckOnGround=false +width=123 + +[7-wall@0,0|6] +endPoint=370,157 +startPoint=379,91 + +[7-wall@0,0|7] +endPoint=379,91 +startPoint=370,42 + +[7-wall@0,0|8] +endPoint=370,42 +startPoint=321,15 + +[7-wall@0,0|9] +endPoint=321,15 +startPoint=110,16 + +[8-ball@324,331] +dummykey=true + +[8-cup@171,249|32] +dummykey=true + +[8-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[8-slope@212,51|33] +grade=2 +gradient=Elliptic +height=96 +reversed=false +stuckOnGround=false +width=96 + +[8-slope@84,231|34] +grade=4 +gradient=Elliptic +height=51 +reversed=false +stuckOnGround=false +width=51 + +[8-wall@0,0|10] +endPoint=104,210 +startPoint=84,204 + +[8-wall@0,0|11] +endPoint=84,204 +startPoint=80,183 + +[8-wall@0,0|12] +endPoint=80,183 +startPoint=105,150 + +[8-wall@0,0|13] +endPoint=105,150 +startPoint=159,137 + +[8-wall@0,0|14] +endPoint=159,137 +startPoint=222,143 + +[8-wall@0,0|15] +endPoint=222,143 +startPoint=275,180 + +[8-wall@0,0|16] +endPoint=275,180 +startPoint=305,263 + +[8-wall@0,0|17] +endPoint=305,263 +startPoint=276,308 + +[8-wall@0,0|18] +endPoint=276,308 +startPoint=275,337 + +[8-wall@0,0|19] +endPoint=275,337 +startPoint=285,369 + +[8-wall@0,0|20] +endPoint=285,369 +startPoint=310,382 + +[8-wall@0,0|21] +endPoint=310,382 +startPoint=346,377 + +[8-wall@0,0|22] +endPoint=346,377 +startPoint=377,350 + +[8-wall@0,0|23] +endPoint=377,350 +startPoint=382,302 + +[8-wall@0,0|24] +endPoint=382,302 +startPoint=382,136 + +[8-wall@0,0|25] +endPoint=382,136 +startPoint=338,61 + +[8-wall@0,0|26] +endPoint=338,61 +startPoint=243,30 + +[8-wall@0,0|27] +endPoint=243,30 +startPoint=99,43 + +[8-wall@0,0|28] +endPoint=99,43 +startPoint=35,104 + +[8-wall@0,0|29] +endPoint=35,104 +startPoint=18,230 + +[8-wall@0,0|3] +endPoint=147,351 +startPoint=83,329 + +[8-wall@0,0|30] +endPoint=18,230 +startPoint=32,285 + +[8-wall@0,0|31] +endPoint=32,285 +startPoint=83,329 + +[8-wall@0,0|4] +endPoint=147,351 +startPoint=230,330 + +[8-wall@0,0|5] +endPoint=230,330 +startPoint=271,265 + +[8-wall@0,0|6] +endPoint=271,265 +startPoint=247,193 + +[8-wall@0,0|7] +endPoint=247,193 +startPoint=202,157 + +[8-wall@0,0|8] +endPoint=202,157 +startPoint=138,165 + +[8-wall@0,0|9] +endPoint=138,165 +startPoint=104,210 + +[9-ball@342,333] +dummykey=true + +[9-blackhole@73,307|3] +exit=385,46 +exitDeg=180 +maxspeed=6 +minspeed=1 + +[9-cup@314,199|9] +dummykey=true + +[9-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[9-sand@155,305|6] +changeEnabled=false +changeEvery=50 +height=70 +width=48 + +[9-sand@165,309|7] +changeEnabled=false +changeEvery=50 +height=40 +width=34 + +[9-sand@174,305|8] +changeEnabled=false +changeEvery=50 +height=28 +width=44 + +[9-sand@179,290|5] +changeEnabled=false +changeEvery=50 +height=52 +width=92 + +[9-wall@0,0|10] +endPoint=386,143 +startPoint=140,120 + +[9-wall@0,0|11] +endPoint=106,12 +startPoint=10,50 + +[9-wall@0,0|4] +endPoint=388,253 +startPoint=12,223 diff --git a/kolf/courses/Hard.kolf b/kolf/courses/Hard.kolf new file mode 100644 index 00000000..47ba23bf --- /dev/null +++ b/kolf/courses/Hard.kolf @@ -0,0 +1,4403 @@ +[0-course@-50,-50] +Name=Hard Course +Name[af]=Hard Natuurlik +Name[bg]=Трудно +Name[bn]=কঠিন খেলা +Name[bs]=Težak teren +Name[ca]=Camp difícil +Name[cs]=Těžký +Name[da]=Svær bane +Name[de]=Schwer +Name[el]=ΔÏσκολη πίστα +Name[es]=Campo difícil +Name[et]=Raske väljak +Name[fi]=Vaikea kenttä +Name[fr]=Parcours difficile +Name[ga]=Cúrsa Crua +Name[gl]=Campo difícil +Name[he]=מסלול קשה +Name[hi]=कठिन कोरà¥à¤¸ +Name[hu]=Nehéz pálya +Name[is]=Erfið braut +Name[it]=Percorso difficile +Name[ja]=難ã—ã„コース +Name[mk]=Тежок терен +Name[nb]=Vanskelig bane +Name[nl]=Moeilijk parcours +Name[nn]=Vanskeleg bane +Name[nso]=Course ye Bothata +Name[pl]=Trudny tor +Name[pt]=Percurso Difícil +Name[pt_BR]=Curso Difícil +Name[ro]=Cale grea +Name[ru]=Ð¡Ð»Ð¾Ð¶Ð½Ð°Ñ Ð¿Ð»Ð¾Ñ‰Ð°Ð´ÐºÐ° +Name[sk]=Ťažké ihrisko +Name[sl]=Težko igriÅ¡Äe +Name[sr]=Тежак терен +Name[sr@Latn]=Težak teren +Name[sv]=SvÃ¥r bana +Name[ta]=கடினமான மாரà¯à®•à¯à®•à®®à¯ +Name[tg]=Майдони Душвор +Name[tr]=Zor Pist +Name[uk]=Складна майданчик +Name[ven]=Thero i Kondaho +Name[xh]=Indlela enzima +Name[xx]=xxHard Coursexx +Name[zh_CN]=困难的路线 +Name[zh_TW]=困難的路線 +Name[zu]=Indlela elukhuni +author=Jason Katz-Brown + +[1-ball@84,73] +dummykey=true + +[1-blackhole@83,203|10] +exit=111,214 +exitDeg=330 +maxspeed=5 +minspeed=3 + +[1-cup@40,198|5] +dummykey=true + +[1-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[1-puddle@-24,361|16] +changeEnabled=false +changeEvery=50 +height=124 +width=168 + +[1-puddle@1,336|17] +changeEnabled=false +changeEvery=50 +height=60 +width=110 + +[1-puddle@34,377|22] +changeEnabled=false +changeEvery=50 +height=54 +width=90 + +[1-puddle@42,351|18] +changeEnabled=false +changeEvery=50 +height=56 +width=40 + +[1-puddle@51,352|19] +changeEnabled=false +changeEvery=50 +height=32 +width=36 + +[1-puddle@54,363|20] +changeEnabled=false +changeEvery=50 +height=40 +width=46 + +[1-puddle@62,373|21] +changeEnabled=false +changeEvery=50 +height=52 +width=36 + +[1-sand@169,250|21] +changeEnabled=false +changeEvery=50 +height=60 +width=46 + +[1-sand@174,240|23] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[1-slope@10,135|15] +grade=3 +gradient=Vertical +height=40 +reversed=true +stuckOnGround=false +width=119 + +[1-slope@10,229|6] +grade=3 +gradient=Vertical +height=60 +reversed=false +stuckOnGround=false +width=83 + +[1-slope@221,145|17] +grade=4 +gradient=Elliptic +height=77 +reversed=false +stuckOnGround=false +width=77 + +[1-slope@56,177|18] +grade=4 +gradient=Elliptic +height=52 +reversed=true +stuckOnGround=false +width=52 + +[1-slope@65,78|24] +grade=2 +gradient=Diagonal +height=309 +reversed=false +stuckOnGround=false +width=323 + +[1-slope@8,14|14] +grade=2 +gradient=Opposite Diagonal +height=105 +reversed=true +stuckOnGround=false +width=383 + +[1-wall@0,0|17] +endPoint=129,176 +startPoint=129,137 + +[1-wall@0,0|24] +endPoint=94,287 +startPoint=93,234 + +[1-wall@0,0|3] +endPoint=93,234 +startPoint=129,176 + +[1-wall@0,0|4] +endPoint=129,137 +startPoint=14,137 + +[10-ball@308,365] +dummykey=true + +[10-cup@66,343|3] +dummykey=true + +[10-floater@171,129|54] +botWallVisible=false +endPoint=171,129 +height=53 +leftWallVisible=true +rightWallVisible=false +speed=3 +startPoint=25,212 +topWallVisible=true +width=76 + +[10-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=true +maxstrokes=10 +par=3 + +[10-puddle@111,207|40] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[10-puddle@115,231|25] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[10-puddle@136,250|28] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[10-puddle@139,193|58] +changeEnabled=false +changeEvery=50 +height=162 +width=122 + +[10-puddle@163,123|12] +changeEnabled=false +changeEvery=50 +height=40 +width=104 + +[10-puddle@164,260|36] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[10-puddle@188,144|52] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[10-puddle@190,268|34] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[10-puddle@196,261|35] +changeEnabled=false +changeEvery=50 +height=64 +width=60 + +[10-puddle@199,237|37] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[10-puddle@200,157|53] +changeEnabled=false +changeEvery=50 +height=94 +width=72 + +[10-puddle@200,200|54] +changeEnabled=false +changeEvery=50 +height=134 +width=72 + +[10-puddle@206,276|39] +changeEnabled=false +changeEvery=50 +height=48 +width=36 + +[10-slope@112,365|52] +grade=2 +gradient=Vertical +height=19 +reversed=true +stuckOnGround=false +width=98 + +[10-slope@12,271|46] +grade=2 +gradient=Diagonal +height=45 +reversed=false +stuckOnGround=true +width=20 + +[10-slope@12,315|53] +grade=2 +gradient=Horizontal +height=52 +reversed=false +stuckOnGround=true +width=21 + +[10-slope@134,308|48] +grade=2 +gradient=Vertical +height=23 +reversed=false +stuckOnGround=true +width=74 + +[10-slope@149,30|31] +grade=3 +gradient=Horizontal +height=85 +reversed=true +stuckOnGround=true +width=52 + +[10-slope@18,32|60] +grade=3 +gradient=Diagonal +height=59 +reversed=true +stuckOnGround=false +width=138 + +[10-slope@199,133|59] +grade=4 +gradient=Horizontal +height=69 +reversed=true +stuckOnGround=true +width=52 + +[10-slope@203,87|60] +grade=4 +gradient=Opposite Diagonal +height=58 +reversed=false +stuckOnGround=true +width=48 + +[10-slope@207,306|49] +grade=2 +gradient=Opposite Diagonal +height=28 +reversed=false +stuckOnGround=false +width=29 + +[10-slope@209,331|50] +grade=2 +gradient=Horizontal +height=32 +reversed=true +stuckOnGround=false +width=28 + +[10-slope@209,363|51] +grade=2 +gradient=Diagonal +height=19 +reversed=true +stuckOnGround=false +width=29 + +[10-slope@211,199|61] +grade=4 +gradient=Diagonal +height=40 +reversed=true +stuckOnGround=true +width=40 + +[10-slope@250,140|60] +grade=1 +gradient=Horizontal +height=243 +reversed=false +stuckOnGround=false +width=136 + +[10-slope@264,102|61] +grade=1 +gradient=Diagonal +height=39 +reversed=false +stuckOnGround=true +width=124 + +[10-slope@32,275|44] +grade=2 +gradient=Vertical +height=41 +reversed=false +stuckOnGround=true +width=59 + +[10-slope@89,276|45] +grade=2 +gradient=Opposite Diagonal +height=55 +reversed=false +stuckOnGround=true +width=75 + +[10-slope@9,365|47] +grade=2 +gradient=Opposite Diagonal +height=23 +reversed=true +stuckOnGround=false +width=104 + +[10-wall@0,0|10] +endPoint=77,31 +startPoint=29,60 + +[10-wall@0,0|11] +endPoint=29,60 +startPoint=11,187 + +[10-wall@0,0|4] +endPoint=229,252 +startPoint=288,385 + +[10-wall@0,0|5] +endPoint=232,206 +startPoint=229,252 + +[10-wall@0,0|57] +endPoint=338,159 +startPoint=302,131 + +[10-wall@0,0|6] +endPoint=389,148 +startPoint=355,92 + +[10-wall@0,0|62] +endPoint=6,316 +startPoint=11,187 + +[10-wall@0,0|63] +endPoint=8,374 +startPoint=6,316 + +[10-wall@0,0|64] +endPoint=35,387 +startPoint=8,374 + +[10-wall@0,0|65] +endPoint=70,392 +startPoint=35,387 + +[10-wall@0,0|66] +endPoint=246,397 +startPoint=70,392 + +[10-wall@0,0|67] +endPoint=275,355 +startPoint=246,397 + +[10-wall@0,0|68] +endPoint=387,384 +startPoint=288,385 + +[10-wall@0,0|69] +endPoint=389,148 +startPoint=387,384 + +[10-wall@0,0|7] +endPoint=355,92 +startPoint=274,56 + +[10-wall@0,0|8] +endPoint=274,56 +startPoint=179,31 + +[10-wall@0,0|9] +endPoint=179,31 +startPoint=77,31 + +[11-ball@27,26] +dummykey=true + +[11-blackhole@-23,90|4] +exit=-26,293 +exitDeg=0 +maxspeed=2 +minspeed=1 + +[11-blackhole@293,44|16] +exit=132,260 +exitDeg=0 +maxspeed=3 +minspeed=2 + +[11-cup@221,409|13] +dummykey=true + +[11-cup@223,332|15] +dummykey=true + +[11-floater@127,241|13] +botWallVisible=false +endPoint=127,241 +height=36 +leftWallVisible=true +rightWallVisible=true +speed=5 +startPoint=13,235 +topWallVisible=true +width=260 + +[11-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=4 + +[11-puddle@142,397|19] +changeEnabled=false +changeEvery=50 +height=94 +width=74 + +[11-puddle@149,362|3] +changeEnabled=false +changeEvery=50 +height=30 +width=56 + +[11-puddle@159,387|9] +changeEnabled=false +changeEvery=50 +height=72 +width=58 + +[11-puddle@170,398|18] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[11-puddle@173,382|20] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[11-puddle@192,390|10] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[11-puddle@210,393|44] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[11-puddle@231,394|12] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[11-puddle@258,388|11] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[11-puddle@272,380|45] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[11-puddle@284,376|8] +changeEnabled=false +changeEvery=50 +height=46 +width=46 + +[11-puddle@290,401|7] +changeEnabled=false +changeEvery=50 +height=88 +width=60 + +[11-puddle@293,-12|40] +changeEnabled=false +changeEvery=50 +height=50 +width=82 + +[11-puddle@332,7|39] +changeEnabled=false +changeEvery=50 +height=32 +width=82 + +[11-puddle@335,10|38] +changeEnabled=false +changeEvery=50 +height=34 +width=50 + +[11-puddle@349,12|37] +changeEnabled=false +changeEvery=50 +height=50 +width=56 + +[11-puddle@353,33|34] +changeEnabled=false +changeEvery=50 +height=34 +width=36 + +[11-puddle@356,12|33] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[11-puddle@361,32|39] +changeEnabled=false +changeEvery=50 +height=58 +width=48 + +[11-puddle@388,45|36] +changeEnabled=false +changeEvery=50 +height=48 +width=88 + +[11-puddle@391,10|35] +changeEnabled=false +changeEvery=50 +height=74 +width=102 + +[11-sand@111,152|26] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[11-sand@113,140|23] +changeEnabled=false +changeEvery=50 +height=80 +width=80 + +[11-sand@120,160|25] +changeEnabled=false +changeEvery=50 +height=56 +width=56 + +[11-sand@122,120|24] +changeEnabled=false +changeEvery=50 +height=40 +width=70 + +[11-sand@133,177|46] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[11-sand@141,155|27] +changeEnabled=false +changeEvery=50 +height=96 +width=56 + +[11-slope@11,37|22] +grade=2 +gradient=Opposite Diagonal +height=166 +reversed=false +stuckOnGround=false +width=178 + +[11-slope@13,217|41] +grade=4 +gradient=Vertical +height=22 +reversed=false +stuckOnGround=true +width=376 + +[11-slope@168,280|40] +grade=4 +gradient=Elliptic +height=107 +reversed=true +stuckOnGround=false +width=107 + +[11-slope@343,53|44] +grade=3 +gradient=Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[11-slope@61,13|30] +grade=2 +gradient=Vertical +height=79 +reversed=true +stuckOnGround=true +width=286 + +[11-slope@7,271|42] +grade=4 +gradient=Vertical +height=29 +reversed=true +stuckOnGround=true +width=382 + +[11-slope@73,128|28] +grade=2 +gradient=Vertical +height=77 +reversed=false +stuckOnGround=true +width=314 + +[11-slope@9,11|43] +grade=6 +gradient=Diagonal +height=56 +reversed=true +stuckOnGround=false +width=52 + +[11-slope@9,8|32] +grade=2 +gradient=Diagonal +height=140 +reversed=true +stuckOnGround=false +width=116 + +[11-wall@0,0|14] +endPoint=392,207 +startPoint=12,207 + +[11-wall@0,0|17] +endPoint=253,280 +startPoint=10,278 + +[11-wall@0,0|42] +endPoint=199,63 +startPoint=209,113 + +[12-ball@58,341] +dummykey=true + +[12-blackhole@244,110|62] +exit=166,91 +exitDeg=280 +maxspeed=3 +minspeed=3 + +[12-blackhole@359,23|65] +exit=111,236 +exitDeg=20 +maxspeed=5 +minspeed=3 + +[12-blackhole@46,112|41] +exit=144,33 +exitDeg=350 +maxspeed=5 +minspeed=3 + +[12-cup@177,164|16] +dummykey=true + +[12-cup@212,294|38] +dummykey=true + +[12-cup@229,270|40] +dummykey=true + +[12-cup@230,315|39] +dummykey=true + +[12-cup@256,319|37] +dummykey=true + +[12-floater@329,91|50] +botWallVisible=false +endPoint=329,91 +height=46 +leftWallVisible=false +rightWallVisible=false +speed=5 +startPoint=277,35 +topWallVisible=false +width=52 + +[12-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=3 + +[12-puddle@101,37|66] +changeEnabled=false +changeEvery=50 +height=96 +width=76 + +[12-puddle@104,63|28] +changeEnabled=false +changeEvery=50 +height=40 +width=54 + +[12-puddle@114,15|25] +changeEnabled=false +changeEvery=50 +height=98 +width=60 + +[12-puddle@62,43|24] +changeEnabled=false +changeEvery=50 +height=44 +width=54 + +[12-puddle@68,-60|62] +changeEnabled=false +changeEvery=50 +height=244 +width=150 + +[12-puddle@85,65|26] +changeEnabled=false +changeEvery=50 +height=34 +width=42 + +[12-puddle@91,43|21] +changeEnabled=false +changeEvery=50 +height=72 +width=100 + +[12-puddle@95,72|27] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[12-slope@238,231|53] +grade=3 +gradient=Vertical +height=11 +reversed=false +stuckOnGround=false +width=58 + +[12-slope@238,242|36] +grade=3 +gradient=Vertical +height=41 +reversed=true +stuckOnGround=false +width=51 + +[12-slope@262,236|49] +grade=6 +gradient=Diagonal +height=151 +reversed=false +stuckOnGround=false +width=126 + +[12-slope@7,74|67] +grade=4 +gradient=Elliptic +height=78 +reversed=true +stuckOnGround=false +width=78 + +[12-slope@86,130|17] +grade=4 +gradient=Diagonal +height=69 +reversed=true +stuckOnGround=false +width=63 + +[12-slope@92,94|52] +grade=4 +gradient=Horizontal +height=38 +reversed=true +stuckOnGround=false +width=56 + +[12-wall@0,0|10] +endPoint=21,360 +startPoint=35,379 + +[12-wall@0,0|12] +endPoint=274,328 +startPoint=288,292 + +[12-wall@0,0|13] +endPoint=81,161 +startPoint=96,93 + +[12-wall@0,0|14] +endPoint=81,161 +startPoint=136,332 + +[12-wall@0,0|15] +endPoint=20,305 +startPoint=13,332 + +[12-wall@0,0|20] +endPoint=282,74 +startPoint=96,93 + +[12-wall@0,0|30] +endPoint=252,339 +startPoint=218,337 + +[12-wall@0,0|31] +endPoint=218,337 +startPoint=196,320 + +[12-wall@0,0|32] +endPoint=274,328 +startPoint=252,339 + +[12-wall@0,0|33] +endPoint=196,320 +startPoint=189,292 + +[12-wall@0,0|34] +endPoint=196,264 +startPoint=189,292 + +[12-wall@0,0|35] +endPoint=236,251 +startPoint=196,264 + +[12-wall@0,0|4] +endPoint=13,332 +startPoint=21,360 + +[12-wall@0,0|42] +endPoint=316,78 +startPoint=282,74 + +[12-wall@0,0|43] +endPoint=337,57 +startPoint=318,79 + +[12-wall@0,0|44] +endPoint=339,30 +startPoint=337,57 + +[12-wall@0,0|45] +endPoint=333,14 +startPoint=339,30 + +[12-wall@0,0|46] +endPoint=316,78 +startPoint=310,111 + +[12-wall@0,0|47] +endPoint=365,51 +startPoint=337,57 + +[12-wall@0,0|48] +endPoint=38,285 +startPoint=20,305 + +[12-wall@0,0|49] +endPoint=387,54 +startPoint=365,51 + +[12-wall@0,0|5] +endPoint=71,383 +startPoint=35,379 + +[12-wall@0,0|51] +endPoint=307,131 +startPoint=284,137 + +[12-wall@0,0|54] +endPoint=136,332 +startPoint=133,354 + +[12-wall@0,0|55] +endPoint=56,280 +startPoint=38,285 + +[12-wall@0,0|56] +endPoint=284,137 +startPoint=264,134 + +[12-wall@0,0|57] +endPoint=264,134 +startPoint=241,139 + +[12-wall@0,0|58] +endPoint=241,139 +startPoint=221,129 + +[12-wall@0,0|59] +endPoint=221,129 +startPoint=212,115 + +[12-wall@0,0|6] +endPoint=104,374 +startPoint=71,383 + +[12-wall@0,0|60] +endPoint=212,115 +startPoint=194,107 + +[12-wall@0,0|61] +endPoint=194,107 +startPoint=186,86 + +[12-wall@0,0|63] +endPoint=147,372 +startPoint=134,357 + +[12-wall@0,0|64] +endPoint=186,388 +startPoint=147,372 + +[12-wall@0,0|68] +endPoint=236,251 +startPoint=238,230 + +[12-wall@0,0|7] +endPoint=288,292 +startPoint=310,111 + +[12-wall@0,0|8] +endPoint=133,354 +startPoint=104,374 + +[12-wall@0,0|9] +endPoint=265,189 +startPoint=154,189 + +[13-ball@38,51] +dummykey=true + +[13-cup@360,359|20] +dummykey=true + +[13-floater@68,306|7] +botWallVisible=true +endPoint=68,306 +height=11 +leftWallVisible=false +rightWallVisible=false +speed=9 +startPoint=133,75 +topWallVisible=false +width=50 + +[13-floater@83,372|16] +botWallVisible=false +endPoint=83,372 +height=47 +leftWallVisible=true +rightWallVisible=false +speed=4 +startPoint=344,371 +topWallVisible=true +width=14 + +[13-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=3 + +[13-sign@257,44|26] +Comment=Let the floaters push you! +Comment[bg]=Обръщане на грешките във Ваша полза! +Comment[bn]=ফà§à¦²à§‹à¦Ÿà¦¾à¦°à¦—à§à¦²à§‹ আপনাকে ধাকà§à¦•à¦¾ দিক! +Comment[bs]=Dopustite plutaÄima da vas guraju! +Comment[ca]=Permetre que els flotadors t'empenyin! +Comment[da]=Lad svæverne skubbe dig! +Comment[de]=Lassen Sie sich von den Flößen weiterschubsen. +Comment[es]=¡Dejar que los flotadores le empujen! +Comment[et]=Lase hõljukitel ennast lükata ! +Comment[fi]=Anna kelluvien painaa sinua! +Comment[fr]=Laissez les flotteurs vous pousser ! +Comment[gl]=Deixe que os flotadores o empuxen! +Comment[he]=תן ×œ×ž×©×˜×—×™× ×”×¦×¤×™× ×œ×“×—×•×£ ×ותך! +Comment[hi]=तैरने वाले आपको धकेल सकते हैं! +Comment[hu]=Csak a legjobbaknak! +Comment[is]=Láttu flotin ýta þér! +Comment[it]=Lasciati trasportare dai floater! +Comment[ja]=フローターã§ãƒœãƒ¼ãƒ«ã‚’プッシュã•ã›ã‚ˆã†! +Comment[mk]=Дозволете им на лебдачите да ве турнат! +Comment[nb]=La plattformene dytte deg! +Comment[nl]=Laat u door de drijvers duwen. +Comment[nn]=Lat plattformane dytta deg! +Comment[nso]=Dumelela di-floater digo tshofe! +Comment[pl]=Niech ta piÅ‚eczka leci! +Comment[pt]=Deixe os flutuadores empurrá-lo! +Comment[pt_BR]=Deixe as bóias levarem você! +Comment[ru]=ПуÑÑ‚ÑŒ Ð²Ð°Ñ Ð½ÐµÑут ползуны! +Comment[sk]=Nechajte sa unášaÅ¥! +Comment[sl]=Naj vas plovci porinejo! +Comment[sr]=Ðека Ð²Ð°Ñ Ð¿Ð»ÑƒÑ‚Ð°Ñ‡Ð¸ гурају! +Comment[sr@Latn]=Neka vas plutaÄi guraju! +Comment[sv]=LÃ¥t flottarna knuffa dig! +Comment[ta]=மிதவைகள௠உஙà¯à®•à®³à¯ˆà®¤à®³à¯à®³à®Ÿà¯à®Ÿà¯à®®à¯ ! +Comment[tg]=Иҷозат диҳед, ки хазандаҳо шуморо тела диҳанд! +Comment[tr]=Yüzücülerin size vurmasına izin verin! +Comment[uk]=Ðехай плавуни Ð²Ð°Ñ Ð¿Ñ–Ð´ÑˆÑ‚Ð¾Ð²Ñ…Ð½ÑƒÑ‚ÑŒ! +Comment[ven]=Irani zwipapamalisukumedzainwi! +Comment[xh]=Vumela ezidadayo tyhala wena! +Comment[xx]=xxLet the floaters push you!xx +Comment[zh_CN]=让浮å°æ‰˜èµ·æ‚¨ï¼ +Comment[zh_TW]=è®“æµ®æ¨™æŽ¨å‹•æ‚¨ï¼ +Comment[zu]=Yekela okuntantayo kukududule! +botWallVisible=true +height=196 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=111 + +[13-slope@10,10|3] +grade=6 +gradient=Vertical +height=137 +reversed=true +stuckOnGround=true +width=227 + +[13-slope@10,378|0] +grade=3 +gradient=Vertical +height=9 +reversed=false +stuckOnGround=true +width=209 + +[13-slope@11,363|1] +grade=3 +gradient=Vertical +height=17 +reversed=false +stuckOnGround=true +width=59 + +[13-slope@111,102|4] +grade=3 +gradient=Vertical +height=10 +reversed=false +stuckOnGround=true +width=27 + +[13-slope@12,147|16] +grade=5 +gradient=Vertical +height=131 +reversed=true +stuckOnGround=true +width=227 + +[13-slope@136,102|6] +grade=4 +gradient=Vertical +height=6 +reversed=false +stuckOnGround=true +width=17 + +[13-slope@183,277|25] +grade=5 +gradient=Vertical +height=111 +reversed=true +stuckOnGround=true +width=206 + +[13-slope@218,378|2] +grade=3 +gradient=Vertical +height=10 +reversed=false +stuckOnGround=true +width=105 + +[13-slope@332,330|19] +grade=6 +gradient=Elliptic +height=59 +reversed=true +stuckOnGround=false +width=59 + +[13-slope@69,307|8] +grade=8 +gradient=Vertical +height=8 +reversed=false +stuckOnGround=false +width=46 + +[13-slope@82,373|13] +grade=8 +gradient=Horizontal +height=18 +reversed=true +stuckOnGround=false +width=16 + +[13-slope@9,277|17] +grade=5 +gradient=Vertical +height=100 +reversed=true +stuckOnGround=true +width=175 + +[13-wall@0,0|10] +endPoint=124,297 +startPoint=65,372 + +[13-wall@0,0|11] +endPoint=13,364 +startPoint=36,377 + +[13-wall@0,0|12] +endPoint=238,12 +startPoint=236,276 + +[13-wall@0,0|14] +endPoint=113,114 +startPoint=96,12 + +[13-wall@0,0|16] +endPoint=322,389 +startPoint=331,343 + +[13-wall@0,0|17] +endPoint=331,343 +startPoint=353,326 + +[13-wall@0,0|18] +endPoint=353,326 +startPoint=390,326 + +[13-wall@0,0|21] +endPoint=388,276 +startPoint=236,276 + +[13-wall@0,0|23] +endPoint=159,103 +startPoint=173,108 + +[13-wall@0,0|24] +endPoint=113,114 +startPoint=159,103 + +[13-wall@0,0|5] +endPoint=173,108 +startPoint=124,297 + +[13-wall@0,0|9] +endPoint=65,372 +startPoint=36,377 + +[14-ball@360,259] +dummykey=true + +[14-blackhole@87,369|7] +exit=366,49 +exitDeg=50 +maxspeed=2 +minspeed=1 + +[14-cup@315,106|10] +dummykey=true + +[14-cup@87,276|6] +dummykey=true + +[14-floater@289,36|9] +botWallVisible=true +endPoint=289,36 +height=97 +leftWallVisible=true +rightWallVisible=true +speed=2 +startPoint=250,24 +topWallVisible=true +width=100 + +[14-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=2 + +[14-slope@10,362|8] +grade=4 +gradient=Horizontal +height=14 +reversed=true +stuckOnGround=false +width=83 + +[14-slope@11,10|4] +grade=2 +gradient=Vertical +height=338 +reversed=true +stuckOnGround=true +width=163 + +[14-slope@11,352|11] +grade=4 +gradient=Opposite Diagonal +height=11 +reversed=false +stuckOnGround=false +width=81 + +[14-slope@11,374|12] +grade=4 +gradient=Diagonal +height=12 +reversed=true +stuckOnGround=false +width=83 + +[14-slope@174,10|12] +grade=2 +gradient=Diagonal +height=339 +reversed=true +stuckOnGround=true +width=63 + +[14-slope@289,50|11] +grade=2 +gradient=Vertical +height=83 +reversed=false +stuckOnGround=false +width=101 + +[14-slope@290,78|18] +grade=4 +gradient=Elliptic +height=53 +reversed=true +stuckOnGround=false +width=53 + +[14-wall@0,0|13] +endPoint=203,205 +startPoint=181,353 + +[14-wall@0,0|14] +endPoint=170,362 +startPoint=115,361 + +[14-wall@0,0|15] +endPoint=105,365 +startPoint=115,361 + +[14-wall@0,0|16] +endPoint=98,387 +startPoint=105,365 + +[14-wall@0,0|17] +endPoint=181,353 +startPoint=170,362 + +[15-ball@20,380] +dummykey=true + +[15-blackhole@172,240|6] +exit=316,255 +exitDeg=42 +maxspeed=1 +minspeed=0 + +[15-blackhole@228,90|22] +exit=345,328 +exitDeg=10 +maxspeed=5 +minspeed=3 + +[15-blackhole@242,55|24] +exit=24,53 +exitDeg=10 +maxspeed=5 +minspeed=3 + +[15-blackhole@330,369|8] +exit=353,363 +exitDeg=10 +maxspeed=5 +minspeed=3 + +[15-blackhole@375,215|46] +exit=16,128 +exitDeg=0 +maxspeed=2 +minspeed=1 + +[15-blackhole@61,244|7] +exit=21,70 +exitDeg=10 +maxspeed=5 +minspeed=3 + +[15-bridge@206,351|38] +botWallVisible=false +height=29 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=51 + +[15-bridge@233,337|36] +botWallVisible=false +height=28 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=53 + +[15-bridge@52,20|13] +botWallVisible=true +height=79 +leftWallVisible=false +rightWallVisible=true +topWallVisible=true +width=153 + +[15-cup@182,78|14] +dummykey=true + +[15-cup@373,269|27] +dummykey=true + +[15-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[15-puddle@247,365|35] +changeEnabled=false +changeEvery=50 +height=88 +width=58 + +[15-puddle@252,383|37] +changeEnabled=false +changeEvery=50 +height=92 +width=68 + +[15-slope@10,311|45] +grade=6 +gradient=Opposite Diagonal +height=76 +reversed=false +stuckOnGround=false +width=84 + +[15-slope@107,329|31] +grade=7 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@116,285|30] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@117,178|38] +grade=4 +gradient=Elliptic +height=116 +reversed=true +stuckOnGround=false +width=116 + +[15-slope@14,120|37] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@14,190|43] +grade=4 +gradient=Elliptic +height=98 +reversed=true +stuckOnGround=false +width=98 + +[15-slope@147,135|35] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@149,339|45] +grade=6 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@168,296|34] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@218,265|44] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@221,182|29] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@240,238|41] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@26,293|36] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@283,9|33] +grade=4 +gradient=Opposite Diagonal +height=96 +reversed=true +stuckOnGround=false +width=105 + +[15-slope@339,275|26] +grade=4 +gradient=Opposite Diagonal +height=36 +reversed=false +stuckOnGround=false +width=39 + +[15-slope@361,304|25] +grade=4 +gradient=Vertical +height=79 +reversed=false +stuckOnGround=false +width=23 + +[15-slope@58,141|42] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@61,22|17] +grade=4 +gradient=Horizontal +height=75 +reversed=false +stuckOnGround=false +width=144 + +[15-slope@67,343|40] +grade=5 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@8,363|46] +grade=6 +gradient=Opposite Diagonal +height=25 +reversed=false +stuckOnGround=false +width=25 + +[15-slope@95,163|32] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-wall@0,0|10] +endPoint=257,172 +startPoint=295,241 + +[15-wall@0,0|11] +endPoint=256,173 +startPoint=201,137 + +[15-wall@0,0|12] +endPoint=201,137 +startPoint=12,110 + +[15-wall@0,0|18] +endPoint=295,241 +startPoint=331,283 + +[15-wall@0,0|19] +endPoint=363,242 +startPoint=330,281 + +[15-wall@0,0|20] +endPoint=382,237 +startPoint=363,242 + +[15-wall@0,0|23] +endPoint=273,135 +startPoint=257,172 + +[15-wall@0,0|28] +endPoint=382,237 +startPoint=390,249 + +[15-wall@0,0|9] +endPoint=331,283 +startPoint=351,387 + +[15-windmill@273,10|21] +botWallVisible=false +bottom=true +height=125 +leftWallVisible=false +rightWallVisible=false +speed=7 +topWallVisible=false +width=115 + +[16-ball@39,319] +dummykey=true + +[16-blackhole@354,290|51] +exit=319,39 +exitDeg=180 +maxspeed=4 +minspeed=3 + +[16-cup@-48,-27|3] +dummykey=true + +[16-cup@-48,-43|70] +dummykey=true + +[16-cup@-50,-40|71] +dummykey=true + +[16-cup@-51,-42|68] +dummykey=true + +[16-cup@-52,-41|69] +dummykey=true + +[16-cup@-57,-40|72] +dummykey=true + +[16-cup@87,83|73] +dummykey=true + +[16-floater@122,335|39] +botWallVisible=true +endPoint=122,335 +height=42 +leftWallVisible=false +rightWallVisible=false +speed=5 +startPoint=67,242 +topWallVisible=true +width=165 + +[16-floater@182,24|63] +botWallVisible=true +endPoint=182,24 +height=34 +leftWallVisible=false +rightWallVisible=true +speed=8 +startPoint=147,80 +topWallVisible=true +width=141 + +[16-floater@64,69|64] +botWallVisible=false +endPoint=64,69 +height=66 +leftWallVisible=true +rightWallVisible=false +speed=5 +startPoint=45,34 +topWallVisible=true +width=73 + +[16-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=3 + +[16-puddle@110,211|60] +changeEnabled=false +changeEvery=50 +height=60 +width=56 + +[16-puddle@115,229|44] +changeEnabled=false +changeEvery=50 +height=70 +width=88 + +[16-puddle@128,239|45] +changeEnabled=false +changeEvery=50 +height=62 +width=98 + +[16-puddle@14,174|39] +changeEnabled=false +changeEvery=50 +height=64 +width=162 + +[16-puddle@148,390|55] +changeEnabled=false +changeEvery=50 +height=94 +width=58 + +[16-puddle@152,246|46] +changeEnabled=false +changeEvery=50 +height=60 +width=114 + +[16-puddle@153,373|54] +changeEnabled=false +changeEvery=50 +height=86 +width=66 + +[16-puddle@160,355|53] +changeEnabled=false +changeEvery=50 +height=96 +width=74 + +[16-puddle@170,257|47] +changeEnabled=false +changeEvery=50 +height=70 +width=94 + +[16-puddle@172,334|52] +changeEnabled=false +changeEvery=50 +height=94 +width=88 + +[16-puddle@173,367|56] +changeEnabled=false +changeEvery=50 +height=82 +width=58 + +[16-puddle@177,309|51] +changeEnabled=false +changeEvery=50 +height=72 +width=80 + +[16-puddle@178,263|48] +changeEnabled=false +changeEvery=50 +height=72 +width=82 + +[16-puddle@178,271|49] +changeEnabled=false +changeEvery=50 +height=94 +width=82 + +[16-puddle@183,284|50] +changeEnabled=false +changeEvery=50 +height=122 +width=82 + +[16-puddle@189,358|57] +changeEnabled=false +changeEvery=50 +height=54 +width=36 + +[16-puddle@206,312|58] +changeEnabled=false +changeEvery=50 +height=70 +width=32 + +[16-puddle@65,186|40] +changeEnabled=false +changeEvery=50 +height=68 +width=100 + +[16-puddle@76,216|59] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[16-puddle@77,193|41] +changeEnabled=false +changeEvery=50 +height=74 +width=98 + +[16-puddle@89,201|42] +changeEnabled=false +changeEvery=50 +height=76 +width=80 + +[16-puddle@97,216|43] +changeEnabled=false +changeEvery=50 +height=72 +width=70 + +[16-sand@192,162|51] +changeEnabled=false +changeEvery=50 +height=58 +width=78 + +[16-sand@200,168|55] +changeEnabled=false +changeEvery=50 +height=62 +width=62 + +[16-sand@205,172|56] +changeEnabled=false +changeEvery=50 +height=62 +width=52 + +[16-sand@211,175|57] +changeEnabled=false +changeEvery=50 +height=60 +width=56 + +[16-sand@220,192|58] +changeEnabled=false +changeEvery=50 +height=32 +width=56 + +[16-sand@225,182|59] +changeEnabled=false +changeEvery=50 +height=54 +width=34 + +[16-sand@235,186|51] +changeEnabled=false +changeEvery=50 +height=22 +width=20 + +[16-sand@338,126|45] +changeEnabled=false +changeEvery=50 +height=62 +width=38 + +[16-sand@348,108|43] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[16-sand@363,121|41] +changeEnabled=false +changeEvery=50 +height=82 +width=78 + +[16-sand@366,95|50] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[16-sand@374,88|49] +changeEnabled=false +changeEvery=50 +height=40 +width=38 + +[16-sand@379,142|46] +changeEnabled=false +changeEvery=50 +height=64 +width=108 + +[16-sand@396,83|48] +changeEnabled=false +changeEvery=50 +height=72 +width=58 + +[16-slope@293,269|52] +grade=4 +gradient=Diagonal +height=118 +reversed=false +stuckOnGround=true +width=93 + +[16-wall@0,0|65] +endPoint=345,264 +startPoint=251,389 + +[16-wall@0,0|67] +endPoint=389,271 +startPoint=345,264 + +[17-ball@170,349] +dummykey=true + +[17-blackhole@233,359|24] +exit=288,38 +exitDeg=50 +maxspeed=3 +minspeed=1 + +[17-bumper@248,22|51] +dummykey=true + +[17-bumper@263,33|50] +dummykey=true + +[17-bumper@278,45|52] +dummykey=true + +[17-bumper@289,59|41] +dummykey=true + +[17-bumper@297,75|49] +dummykey=true + +[17-bumper@301,93|44] +dummykey=true + +[17-bumper@302,111|45] +dummykey=true + +[17-bumper@309,128|42] +dummykey=true + +[17-bumper@318,144|43] +dummykey=true + +[17-bumper@335,151|48] +dummykey=true + +[17-bumper@355,154|43] +dummykey=true + +[17-bumper@375,154|42] +dummykey=true + +[17-cup@353,116|36] +dummykey=true + +[17-cup@365,358|15] +dummykey=true + +[17-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[17-puddle@243,204|45] +changeEnabled=false +changeEvery=50 +height=50 +width=52 + +[17-puddle@253,194|44] +changeEnabled=false +changeEvery=50 +height=54 +width=66 + +[17-puddle@254,183|46] +changeEnabled=false +changeEvery=50 +height=64 +width=58 + +[17-puddle@262,213|47] +changeEnabled=false +changeEvery=50 +height=56 +width=88 + +[17-sand@-19,263|16] +changeEnabled=false +changeEvery=50 +height=138 +width=94 + +[17-sand@1,214|52] +changeEnabled=false +changeEvery=50 +height=82 +width=64 + +[17-sand@102,219|56] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[17-sand@109,197|12] +changeEnabled=false +changeEvery=50 +height=44 +width=58 + +[17-sand@115,216|4] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[17-sand@126,200|5] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[17-sand@136,201|29] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[17-sand@155,202|30] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[17-sand@160,189|32] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[17-sand@166,172|17] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[17-sand@167,162|3] +changeEnabled=false +changeEvery=50 +height=48 +width=44 + +[17-sand@178,156|54] +changeEnabled=false +changeEvery=50 +height=36 +width=52 + +[17-sand@187,166|55] +changeEnabled=false +changeEvery=50 +height=48 +width=60 + +[17-sand@193,175|31] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[17-sand@5,238|8] +changeEnabled=false +changeEvery=50 +height=98 +width=64 + +[17-sand@85,225|34] +changeEnabled=false +changeEvery=50 +height=60 +width=34 + +[17-sand@91,232|9] +changeEnabled=false +changeEvery=50 +height=28 +width=26 + +[17-sand@92,208|6] +changeEnabled=false +changeEvery=50 +height=46 +width=38 + +[17-slope@103,219|40] +grade=2 +gradient=Elliptic +height=108 +reversed=false +stuckOnGround=false +width=108 + +[17-slope@11,8|42] +grade=4 +gradient=Diagonal +height=160 +reversed=true +stuckOnGround=false +width=124 + +[17-slope@204,241|25] +grade=2 +gradient=Opposite Diagonal +height=268 +reversed=true +stuckOnGround=false +width=181 + +[17-slope@205,331|41] +grade=4 +gradient=Elliptic +height=57 +reversed=true +stuckOnGround=false +width=57 + +[17-slope@210,240|38] +grade=2 +gradient=Vertical +height=103 +reversed=true +stuckOnGround=false +width=63 + +[17-slope@238,332|40] +grade=2 +gradient=Horizontal +height=52 +reversed=false +stuckOnGround=false +width=66 + +[17-slope@78,12|39] +grade=3 +gradient=Vertical +height=68 +reversed=true +stuckOnGround=false +width=308 + +[17-slope@9,269|41] +grade=4 +gradient=Elliptic +height=109 +reversed=false +stuckOnGround=false +width=109 + +[17-wall@0,0|18] +endPoint=206,196 +startPoint=203,387 + +[17-wall@0,0|19] +endPoint=46,59 +startPoint=11,123 + +[17-wall@0,0|23] +endPoint=89,12 +startPoint=46,59 + +[17-windmill@322,91|53] +botWallVisible=false +bottom=false +height=43 +leftWallVisible=false +rightWallVisible=false +speed=5 +topWallVisible=false +width=67 + +[18-ball@174,371] +dummykey=true + +[18-cup@340,103|3] +dummykey=true + +[18-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[18-slope@308,71|19] +grade=4 +gradient=Elliptic +height=65 +reversed=true +stuckOnGround=false +width=65 + +[18-wall@0,0|10] +endPoint=388,174 +startPoint=250,151 + +[18-wall@0,0|11] +endPoint=324,257 +startPoint=320,270 + +[18-wall@0,0|12] +endPoint=342,336 +startPoint=339,348 + +[18-wall@0,0|13] +endPoint=228,98 +startPoint=239,90 + +[18-wall@0,0|14] +endPoint=15,367 +startPoint=12,143 + +[18-wall@0,0|15] +endPoint=219,390 +startPoint=36,391 + +[18-wall@0,0|16] +endPoint=36,391 +startPoint=15,367 + +[18-wall@0,0|17] +endPoint=388,12 +startPoint=275,11 + +[18-wall@0,0|18] +endPoint=388,12 +startPoint=390,212 + +[18-wall@0,0|4] +endPoint=238,238 +startPoint=75,312 + +[18-wall@0,0|5] +endPoint=228,98 +startPoint=12,143 + +[18-wall@0,0|6] +endPoint=275,11 +startPoint=239,90 + +[18-wall@0,0|7] +endPoint=390,212 +startPoint=324,257 + +[18-wall@0,0|8] +endPoint=320,270 +startPoint=342,336 + +[18-wall@0,0|9] +endPoint=339,348 +startPoint=219,390 + +[2-ball@112,41] +dummykey=true + +[2-blackhole@142,61|20] +exit=261,80 +exitDeg=305 +maxspeed=4 +minspeed=3 + +[2-blackhole@178,367|17] +exit=17,31 +exitDeg=349 +maxspeed=2 +minspeed=2 + +[2-blackhole@28,148|15] +exit=238,26 +exitDeg=0 +maxspeed=5 +minspeed=1 + +[2-blackhole@374,89|7] +exit=308,84 +exitDeg=244 +maxspeed=3 +minspeed=2 + +[2-cup@23,188|21] +dummykey=true + +[2-cup@375,52|6] +dummykey=true + +[2-floater@43,240|24] +botWallVisible=false +endPoint=43,240 +height=64 +leftWallVisible=false +rightWallVisible=true +speed=5 +startPoint=33,173 +topWallVisible=true +width=81 + +[2-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=2 + +[2-puddle@10,241|50] +changeEnabled=false +changeEvery=50 +height=42 +width=142 + +[2-puddle@48,248|29] +changeEnabled=false +changeEvery=50 +height=28 +width=66 + +[2-puddle@57,233|51] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[2-puddle@60,244|31] +changeEnabled=false +changeEvery=50 +height=32 +width=58 + +[2-puddle@68,232|52] +changeEnabled=false +changeEvery=50 +height=50 +width=40 + +[2-puddle@72,241|32] +changeEnabled=false +changeEvery=50 +height=30 +width=44 + +[2-puddle@75,225|34] +changeEnabled=false +changeEvery=50 +height=54 +width=38 + +[2-puddle@75,235|33] +changeEnabled=false +changeEvery=50 +height=36 +width=38 + +[2-sand@147,241|43] +changeEnabled=false +changeEvery=50 +height=54 +width=48 + +[2-sand@160,261|45] +changeEnabled=false +changeEvery=50 +height=22 +width=52 + +[2-sand@169,242|44] +changeEnabled=false +changeEvery=50 +height=52 +width=54 + +[2-slope@11,97|51] +grade=4 +gradient=Diagonal +height=21 +reversed=true +stuckOnGround=false +width=43 + +[2-slope@12,198|36] +grade=3 +gradient=Vertical +height=44 +reversed=false +stuckOnGround=true +width=35 + +[2-slope@12,75|50] +grade=4 +gradient=Horizontal +height=22 +reversed=true +stuckOnGround=false +width=42 + +[2-slope@13,35|49] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=false +stuckOnGround=false +width=42 + +[2-slope@190,89|58] +grade=3 +gradient=Diagonal +height=30 +reversed=false +stuckOnGround=false +width=47 + +[2-slope@192,119|57] +grade=3 +gradient=Opposite Diagonal +height=127 +reversed=true +stuckOnGround=true +width=43 + +[2-slope@210,22|58] +grade=2 +gradient=Opposite Diagonal +height=39 +reversed=true +stuckOnGround=false +width=20 + +[2-slope@224,11|46] +grade=2 +gradient=Vertical +height=48 +reversed=true +stuckOnGround=false +width=92 + +[2-slope@232,243|48] +grade=3 +gradient=Diagonal +height=29 +reversed=true +stuckOnGround=true +width=135 + +[2-slope@233,119|22] +grade=3 +gradient=Vertical +height=126 +reversed=true +stuckOnGround=true +width=153 + +[2-slope@234,243|23] +grade=3 +gradient=Horizontal +height=144 +reversed=false +stuckOnGround=true +width=154 + +[2-slope@235,88|59] +grade=3 +gradient=Vertical +height=31 +reversed=false +stuckOnGround=false +width=153 + +[2-slope@314,9|47] +grade=2 +gradient=Diagonal +height=49 +reversed=true +stuckOnGround=false +width=19 + +[2-slope@334,13|16] +grade=4 +gradient=Opposite Diagonal +height=31 +reversed=true +stuckOnGround=false +width=52 + +[2-slope@43,112|18] +grade=8 +gradient=Opposite Diagonal +height=84 +reversed=false +stuckOnGround=true +width=68 + +[2-slope@7,174|59] +grade=4 +gradient=Elliptic +height=33 +reversed=true +stuckOnGround=true +width=33 + +[2-wall@0,0|10] +endPoint=86,192 +startPoint=54,196 + +[2-wall@0,0|11] +endPoint=33,173 +startPoint=54,196 + +[2-wall@0,0|12] +endPoint=11,167 +startPoint=33,173 + +[2-wall@0,0|13] +endPoint=48,144 +startPoint=33,173 + +[2-wall@0,0|14] +endPoint=28,113 +startPoint=50,133 + +[2-wall@0,0|3] +endPoint=233,12 +startPoint=104,130 + +[2-wall@0,0|37] +endPoint=116,82 +startPoint=141,96 + +[2-wall@0,0|38] +endPoint=104,85 +startPoint=89,115 + +[2-wall@0,0|4] +endPoint=275,63 +startPoint=86,192 + +[2-wall@0,0|49] +endPoint=116,82 +startPoint=104,85 + +[2-wall@0,0|5] +endPoint=387,117 +startPoint=275,63 + +[2-wall@0,0|52] +endPoint=104,130 +startPoint=89,115 + +[2-wall@0,0|54] +endPoint=131,11 +startPoint=170,36 + +[2-wall@0,0|55] +endPoint=172,66 +startPoint=171,36 + +[2-wall@0,0|56] +endPoint=198,44 +startPoint=170,36 + +[2-wall@0,0|60] +endPoint=48,144 +startPoint=50,133 + +[3-ball@346,322] +dummykey=true + +[3-blackhole@192,132|23] +exit=47,35 +exitDeg=310 +maxspeed=3.75 +minspeed=1.78 + +[3-blackhole@208,286|7] +exit=147,367 +exitDeg=180 +maxspeed=6.63 +minspeed=2.33 + +[3-cup@341,43|16] +dummykey=true + +[3-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[3-puddle@260,299|41] +changeEnabled=false +changeEvery=50 +height=22 +width=22 + +[3-puddle@261,283|40] +changeEnabled=false +changeEvery=50 +height=20 +width=26 + +[3-puddle@262,312|10] +changeEnabled=false +changeEvery=50 +height=36 +width=54 + +[3-puddle@267,272|8] +changeEnabled=false +changeEvery=50 +height=36 +width=52 + +[3-puddle@276,290|9] +changeEnabled=false +changeEvery=50 +height=68 +width=50 + +[3-sand@269,142|45] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[3-sand@270,146|30] +changeEnabled=false +changeEvery=50 +height=32 +width=46 + +[3-sand@272,135|44] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[3-sand@277,127|43] +changeEnabled=false +changeEvery=50 +height=28 +width=34 + +[3-sand@282,122|42] +changeEnabled=false +changeEvery=50 +height=22 +width=32 + +[3-sand@284,133|29] +changeEnabled=false +changeEvery=50 +height=46 +width=32 + +[3-slope@11,10|25] +grade=3 +gradient=Diagonal +height=326 +reversed=true +stuckOnGround=false +width=128 + +[3-slope@112,381|41] +grade=8 +gradient=Opposite Diagonal +height=15 +reversed=true +stuckOnGround=false +width=35 + +[3-slope@113,334|40] +grade=8 +gradient=Diagonal +height=18 +reversed=false +stuckOnGround=false +width=35 + +[3-slope@113,351|14] +grade=8 +gradient=Horizontal +height=30 +reversed=false +stuckOnGround=false +width=30 + +[3-slope@150,302|11] +grade=6 +gradient=Opposite Diagonal +height=86 +reversed=false +stuckOnGround=false +width=81 + +[3-slope@170,248|46] +grade=4 +gradient=Elliptic +height=81 +reversed=true +stuckOnGround=false +width=81 + +[3-slope@174,112|47] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=false +width=40 + +[3-slope@281,117|41] +grade=2 +gradient=Diagonal +height=56 +reversed=false +stuckOnGround=false +width=53 + +[3-slope@282,172|40] +grade=2 +gradient=Opposite Diagonal +height=53 +reversed=true +stuckOnGround=false +width=53 + +[3-slope@304,10|41] +grade=4 +gradient=Opposite Diagonal +height=48 +reversed=true +stuckOnGround=false +width=84 + +[3-slope@334,119|24] +grade=2 +gradient=Opposite Diagonal +height=54 +reversed=false +stuckOnGround=false +width=52 + +[3-slope@334,173|33] +grade=2 +gradient=Diagonal +height=54 +reversed=true +stuckOnGround=false +width=48 + +[3-slope@99,36|39] +grade=3 +gradient=Elliptic +height=190 +reversed=true +stuckOnGround=false +width=190 + +[3-wall@0,0|13] +endPoint=90,390 +startPoint=14,350 + +[3-wall@0,0|19] +endPoint=301,42 +startPoint=323,62 + +[3-wall@0,0|20] +endPoint=274,39 +startPoint=261,46 + +[3-wall@0,0|22] +endPoint=255,24 +startPoint=242,41 + +[3-wall@0,0|28] +endPoint=100,175 +startPoint=13,132 + +[3-wall@0,0|29] +endPoint=78,206 +startPoint=14,175 + +[3-wall@0,0|3] +endPoint=390,233 +startPoint=224,228 + +[3-wall@0,0|30] +endPoint=150,198 +startPoint=224,228 + +[3-wall@0,0|31] +endPoint=192,250 +startPoint=128,227 + +[3-wall@0,0|34] +endPoint=255,24 +startPoint=250,13 + +[3-wall@0,0|35] +endPoint=192,252 +startPoint=192,250 + +[3-wall@0,0|36] +endPoint=192,250 +startPoint=189,264 + +[3-wall@0,0|38] +endPoint=261,46 +startPoint=242,41 + +[3-wall@0,0|42] +endPoint=292,10 +startPoint=301,42 + +[3-wall@0,0|43] +endPoint=301,42 +startPoint=274,39 + +[3-wall@0,0|5] +endPoint=189,264 +startPoint=151,310 + +[3-wall@0,0|6] +endPoint=151,390 +startPoint=151,310 + +[3-windmill@323,62|31] +botWallVisible=false +bottom=true +height=60 +leftWallVisible=true +rightWallVisible=false +speed=2 +topWallVisible=false +width=61 + +[4-ball@356,151] +dummykey=true + +[4-blackhole@160,36|45] +exit=156,218 +exitDeg=350 +maxspeed=2 +minspeed=1 + +[4-blackhole@360,332|116] +exit=122,234 +exitDeg=290 +maxspeed=5 +minspeed=3 + +[4-bridge@184,22|136] +botWallVisible=true +height=25 +leftWallVisible=false +rightWallVisible=false +topWallVisible=true +width=81 + +[4-cup@105,116|3] +dummykey=true + +[4-cup@187,330|136] +dummykey=true + +[4-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=4 + +[4-puddle@113,399|146] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[4-puddle@136,397|147] +changeEnabled=false +changeEvery=50 +height=48 +width=74 + +[4-puddle@141,184|76] +changeEnabled=false +changeEvery=50 +height=66 +width=100 + +[4-puddle@151,162|116] +changeEnabled=false +changeEvery=50 +height=54 +width=84 + +[4-puddle@159,395|60] +changeEnabled=false +changeEvery=50 +height=60 +width=108 + +[4-puddle@178,382|53] +changeEnabled=false +changeEvery=50 +height=34 +width=84 + +[4-puddle@188,158|117] +changeEnabled=false +changeEvery=50 +height=100 +width=106 + +[4-puddle@197,136|81] +changeEnabled=false +changeEvery=50 +height=154 +width=92 + +[4-puddle@207,397|144] +changeEnabled=false +changeEvery=50 +height=48 +width=40 + +[4-puddle@211,189|75] +changeEnabled=false +changeEvery=50 +height=40 +width=232 + +[4-puddle@214,67|139] +changeEnabled=false +changeEvery=50 +height=24 +width=66 + +[4-puddle@217,78|124] +changeEnabled=false +changeEvery=50 +height=42 +width=50 + +[4-puddle@218,2|143] +changeEnabled=false +changeEvery=50 +height=74 +width=38 + +[4-puddle@226,139|118] +changeEnabled=false +changeEvery=50 +height=82 +width=60 + +[4-puddle@226,149|120] +changeEnabled=false +changeEvery=50 +height=92 +width=116 + +[4-puddle@233,255|55] +changeEnabled=false +changeEvery=50 +height=30 +width=64 + +[4-puddle@236,65|123] +changeEnabled=false +changeEvery=50 +height=28 +width=66 + +[4-puddle@240,313|128] +changeEnabled=false +changeEvery=50 +height=26 +width=36 + +[4-puddle@241,3|140] +changeEnabled=false +changeEvery=50 +height=90 +width=42 + +[4-puddle@244,26|139] +changeEnabled=false +changeEvery=50 +height=82 +width=32 + +[4-puddle@244,315|59] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[4-puddle@247,257|56] +changeEnabled=false +changeEvery=50 +height=32 +width=54 + +[4-puddle@251,320|52] +changeEnabled=false +changeEvery=50 +height=24 +width=32 + +[4-puddle@266,258|58] +changeEnabled=false +changeEvery=50 +height=24 +width=24 + +[4-puddle@267,157|119] +changeEnabled=false +changeEvery=50 +height=42 +width=46 + +[4-puddle@273,261|51] +changeEnabled=false +changeEvery=50 +height=26 +width=24 + +[4-puddle@275,323|130] +changeEnabled=false +changeEvery=50 +height=22 +width=70 + +[4-puddle@279,176|121] +changeEnabled=false +changeEvery=50 +height=62 +width=98 + +[4-puddle@281,264|50] +changeEnabled=false +changeEvery=50 +height=26 +width=22 + +[4-puddle@287,267|54] +changeEnabled=false +changeEvery=50 +height=24 +width=22 + +[4-puddle@293,189|122] +changeEnabled=false +changeEvery=50 +height=42 +width=66 + +[4-puddle@295,273|57] +changeEnabled=false +changeEvery=50 +height=28 +width=26 + +[4-puddle@296,320|131] +changeEnabled=false +changeEvery=50 +height=22 +width=30 + +[4-puddle@302,312|127] +changeEnabled=false +changeEvery=50 +height=30 +width=24 + +[4-puddle@305,278|125] +changeEnabled=false +changeEvery=50 +height=24 +width=14 + +[4-puddle@307,291|128] +changeEnabled=false +changeEvery=50 +height=40 +width=24 + +[4-puddle@310,302|126] +changeEnabled=false +changeEvery=50 +height=32 +width=18 + +[4-puddle@311,185|99] +changeEnabled=false +changeEvery=50 +height=44 +width=52 + +[4-puddle@321,198|96] +changeEnabled=false +changeEvery=50 +height=32 +width=48 + +[4-puddle@333,195|123] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[4-puddle@354,202|154] +changeEnabled=false +changeEvery=50 +height=40 +width=62 + +[4-puddle@376,208|115] +changeEnabled=false +changeEvery=50 +height=40 +width=46 + +[4-puddle@397,211|114] +changeEnabled=false +changeEvery=50 +height=62 +width=54 + +[4-slope@12,188|117] +grade=4 +gradient=Vertical +height=28 +reversed=false +stuckOnGround=false +width=71 + +[4-slope@12,213|118] +grade=4 +gradient=Vertical +height=24 +reversed=true +stuckOnGround=false +width=82 + +[4-slope@146,60|125] +grade=4 +gradient=Diagonal +height=83 +reversed=true +stuckOnGround=false +width=48 + +[4-slope@169,225|124] +grade=1 +gradient=Vertical +height=32 +reversed=false +stuckOnGround=false +width=128 + +[4-slope@205,22|162] +grade=4 +gradient=Horizontal +height=25 +reversed=false +stuckOnGround=false +width=22 + +[4-slope@215,-1|137] +grade=3 +gradient=Opposite Diagonal +height=209 +reversed=true +stuckOnGround=false +width=181 + +[4-slope@261,193|138] +grade=3 +gradient=Opposite Diagonal +height=23 +reversed=true +stuckOnGround=false +width=12 + +[4-slope@273,196|125] +grade=3 +gradient=Vertical +height=21 +reversed=true +stuckOnGround=false +width=43 + +[4-slope@296,258|130] +grade=4 +gradient=Horizontal +height=47 +reversed=true +stuckOnGround=false +width=25 + +[4-slope@297,225|137] +grade=1 +gradient=Opposite Diagonal +height=35 +reversed=false +stuckOnGround=false +width=24 + +[4-slope@328,302|145] +grade=2 +gradient=Elliptic +height=64 +reversed=true +stuckOnGround=false +width=64 + +[4-slope@40,280|111] +grade=4 +gradient=Diagonal +height=32 +reversed=false +stuckOnGround=false +width=32 + +[4-slope@41,309|113] +grade=4 +gradient=Opposite Diagonal +height=30 +reversed=true +stuckOnGround=false +width=30 + +[4-slope@70,281|112] +grade=4 +gradient=Opposite Diagonal +height=30 +reversed=false +stuckOnGround=false +width=30 + +[4-slope@70,308|114] +grade=4 +gradient=Diagonal +height=30 +reversed=true +stuckOnGround=false +width=30 + +[4-slope@99,9|130] +grade=3 +gradient=Horizontal +height=43 +reversed=false +stuckOnGround=false +width=37 + +[4-wall@0,0|10] +endPoint=95,237 +startPoint=141,225 + +[4-wall@0,0|101] +endPoint=249,280 +startPoint=220,278 + +[4-wall@0,0|103] +endPoint=267,277 +startPoint=249,280 + +[4-wall@0,0|104] +endPoint=288,293 +startPoint=267,277 + +[4-wall@0,0|105] +endPoint=242,292 +startPoint=216,291 + +[4-wall@0,0|106] +endPoint=267,299 +startPoint=242,292 + +[4-wall@0,0|107] +endPoint=285,303 +startPoint=273,307 + +[4-wall@0,0|108] +endPoint=288,293 +startPoint=285,303 + +[4-wall@0,0|11] +endPoint=97,154 +startPoint=76,195 + +[4-wall@0,0|111] +endPoint=216,291 +startPoint=189,299 + +[4-wall@0,0|115] +endPoint=138,131 +startPoint=120,137 + +[4-wall@0,0|119] +endPoint=389,274 +startPoint=342,227 + +[4-wall@0,0|12] +endPoint=136,101 +startPoint=146,113 + +[4-wall@0,0|13] +endPoint=146,113 +startPoint=138,131 + +[4-wall@0,0|131] +endPoint=28,49 +startPoint=34,27 + +[4-wall@0,0|132] +endPoint=267,299 +startPoint=273,307 + +[4-wall@0,0|133] +endPoint=61,25 +startPoint=34,27 + +[4-wall@0,0|134] +endPoint=80,40 +startPoint=61,25 + +[4-wall@0,0|138] +endPoint=243,98 +startPoint=245,83 + +[4-wall@0,0|139] +endPoint=255,44 +startPoint=277,62 + +[4-wall@0,0|14] +endPoint=77,75 +startPoint=93,90 + +[4-wall@0,0|140] +endPoint=277,62 +startPoint=271,76 + +[4-wall@0,0|141] +endPoint=314,12 +startPoint=96,8 + +[4-wall@0,0|142] +endPoint=13,336 +startPoint=9,263 + +[4-wall@0,0|143] +endPoint=395,355 +startPoint=350,388 + +[4-wall@0,0|144] +endPoint=350,388 +startPoint=230,394 + +[4-wall@0,0|145] +endPoint=389,274 +startPoint=395,355 + +[4-wall@0,0|15] +endPoint=74,58 +startPoint=77,75 + +[4-wall@0,0|152] +endPoint=383,162 +startPoint=378,176 + +[4-wall@0,0|153] +endPoint=345,178 +startPoint=378,176 + +[4-wall@0,0|162] +endPoint=153,355 +startPoint=121,366 + +[4-wall@0,0|17] +endPoint=12,189 +startPoint=28,49 + +[4-wall@0,0|18] +endPoint=9,263 +startPoint=12,189 + +[4-wall@0,0|19] +endPoint=39,353 +startPoint=13,336 + +[4-wall@0,0|20] +endPoint=69,367 +startPoint=39,353 + +[4-wall@0,0|21] +endPoint=190,299 +startPoint=164,241 + +[4-wall@0,0|22] +endPoint=379,147 +startPoint=383,162 + +[4-wall@0,0|23] +endPoint=345,178 +startPoint=322,148 + +[4-wall@0,0|24] +endPoint=275,113 +startPoint=243,98 + +[4-wall@0,0|25] +endPoint=291,137 +startPoint=275,113 + +[4-wall@0,0|26] +endPoint=182,55 +startPoint=158,60 + +[4-wall@0,0|27] +endPoint=80,40 +startPoint=79,24 + +[4-wall@0,0|28] +endPoint=208,51 +startPoint=182,55 + +[4-wall@0,0|29] +endPoint=132,51 +startPoint=103,51 + +[4-wall@0,0|30] +endPoint=230,49 +startPoint=208,51 + +[4-wall@0,0|31] +endPoint=245,83 +startPoint=271,76 + +[4-wall@0,0|32] +endPoint=103,51 +startPoint=80,40 + +[4-wall@0,0|33] +endPoint=322,148 +startPoint=291,137 + +[4-wall@0,0|34] +endPoint=96,8 +startPoint=79,24 + +[4-wall@0,0|38] +endPoint=379,147 +startPoint=385,136 + +[4-wall@0,0|39] +endPoint=385,136 +startPoint=383,113 + +[4-wall@0,0|4] +endPoint=121,366 +startPoint=69,367 + +[4-wall@0,0|40] +endPoint=383,113 +startPoint=379,83 + +[4-wall@0,0|41] +endPoint=379,83 +startPoint=385,60 + +[4-wall@0,0|42] +endPoint=385,60 +startPoint=383,43 + +[4-wall@0,0|43] +endPoint=383,43 +startPoint=365,22 + +[4-wall@0,0|44] +endPoint=365,22 +startPoint=314,12 + +[4-wall@0,0|5] +endPoint=189,299 +startPoint=153,355 + +[4-wall@0,0|7] +endPoint=141,225 +startPoint=220,278 + +[4-wall@0,0|8] +endPoint=76,195 +startPoint=95,237 + +[4-wall@0,0|9] +endPoint=120,137 +startPoint=97,154 + +[4-wall@0,0|94] +endPoint=136,101 +startPoint=93,90 + +[4-wall@0,0|95] +endPoint=158,60 +startPoint=132,51 + +[5-ball@364,360] +dummykey=true + +[5-cup@32,41|3] +dummykey=true + +[5-floater@288,52|16] +botWallVisible=false +endPoint=288,52 +height=40 +leftWallVisible=false +rightWallVisible=false +speed=7 +startPoint=101,37 +topWallVisible=false +width=80 + +[5-floater@294,95|10] +botWallVisible=false +endPoint=294,95 +height=40 +leftWallVisible=false +rightWallVisible=false +speed=18 +startPoint=88,293 +topWallVisible=false +width=80 + +[5-floater@33,319|15] +botWallVisible=false +endPoint=33,319 +height=40 +leftWallVisible=false +rightWallVisible=false +speed=5 +startPoint=64,200 +topWallVisible=false +width=80 + +[5-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=2 + +[5-sand@137,111|16] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[5-sand@137,118|17] +changeEnabled=false +changeEvery=50 +height=48 +width=48 + +[5-sand@145,132|19] +changeEnabled=false +changeEvery=50 +height=20 +width=26 + +[5-sand@152,120|18] +changeEnabled=false +changeEvery=50 +height=44 +width=28 + +[5-sand@152,258|13] +changeEnabled=false +changeEvery=50 +height=64 +width=62 + +[5-sand@155,265|15] +changeEnabled=false +changeEvery=50 +height=60 +width=56 + +[5-sand@158,279|19] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[5-sand@159,244|11] +changeEnabled=false +changeEvery=50 +height=26 +width=44 + +[5-sand@162,269|20] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[5-sand@165,257|14] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[5-slope@12,11|17] +grade=2 +gradient=Diagonal +height=162 +reversed=true +stuckOnGround=true +width=77 + +[5-slope@271,268|16] +grade=4 +gradient=Diagonal +height=120 +reversed=false +stuckOnGround=true +width=118 + +[5-wall@0,0|4] +endPoint=345,237 +startPoint=122,68 + +[5-wall@0,0|5] +endPoint=148,359 +startPoint=68,99 + +[6-ball@36,37] +dummykey=true + +[6-blackhole@331,316|12] +exit=199,62 +exitDeg=0 +maxspeed=6 +minspeed=2 + +[6-blackhole@374,374|17] +exit=17,32 +exitDeg=0 +maxspeed=1 +minspeed=1 + +[6-bridge@166,277|10] +botWallVisible=true +height=74 +leftWallVisible=false +rightWallVisible=true +topWallVisible=true +width=219 + +[6-bridge@223,134|16] +botWallVisible=true +height=27 +leftWallVisible=true +rightWallVisible=false +topWallVisible=true +width=29 + +[6-bridge@310,72|15] +botWallVisible=true +height=32 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=28 + +[6-cup@307,154|14] +dummykey=true + +[6-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[6-slope@10,187|5] +grade=2 +gradient=Diagonal +height=87 +reversed=true +stuckOnGround=false +width=67 + +[6-slope@11,11|8] +grade=2 +gradient=Diagonal +height=130 +reversed=false +stuckOnGround=false +width=64 + +[6-slope@11,140|3] +grade=2 +gradient=Opposite Diagonal +height=51 +reversed=false +stuckOnGround=false +width=64 + +[6-slope@11,148|11] +grade=3 +gradient=Opposite Diagonal +height=241 +reversed=false +stuckOnGround=false +width=181 + +[6-slope@12,12|6] +grade=2 +gradient=Diagonal +height=127 +reversed=true +stuckOnGround=false +width=62 + +[6-slope@12,140|7] +grade=2 +gradient=Opposite Diagonal +height=48 +reversed=true +stuckOnGround=false +width=63 + +[6-slope@231,275|18] +grade=4 +gradient=Horizontal +height=77 +reversed=true +stuckOnGround=false +width=23 + +[6-wall@0,0|18] +endPoint=76,231 +startPoint=76,13 + +[6-windmill@194,42|19] +botWallVisible=false +bottom=true +height=149 +leftWallVisible=true +rightWallVisible=true +speed=6 +topWallVisible=true +width=166 + +[7-ball@211,293] +dummykey=true + +[7-blackhole@236,39|38] +exit=83,55 +exitDeg=40 +maxspeed=2 +minspeed=1 + +[7-blackhole@248,83|20] +exit=82,30 +exitDeg=30 +maxspeed=2 +minspeed=1 + +[7-cup@340,62|4] +dummykey=true + +[7-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[7-puddle@-30,384|21] +changeEnabled=false +changeEvery=50 +height=288 +width=384 + +[7-puddle@123,354|35] +changeEnabled=false +changeEvery=50 +height=136 +width=96 + +[7-puddle@147,390|14] +changeEnabled=false +changeEvery=50 +height=170 +width=64 + +[7-puddle@15,318|13] +changeEnabled=false +changeEvery=50 +height=116 +width=278 + +[7-puddle@271,143|22] +changeEnabled=false +changeEvery=50 +height=30 +width=62 + +[7-puddle@294,150|21] +changeEnabled=false +changeEvery=50 +height=32 +width=80 + +[7-puddle@341,161|15] +changeEnabled=false +changeEvery=50 +height=38 +width=86 + +[7-sand@132,70|14] +changeEnabled=false +changeEvery=50 +height=46 +width=72 + +[7-sand@185,132|19] +changeEnabled=false +changeEvery=50 +height=38 +width=60 + +[7-sand@311,116|18] +changeEnabled=false +changeEvery=50 +height=40 +width=80 + +[7-sand@329,119|17] +changeEnabled=false +changeEvery=50 +height=42 +width=68 + +[7-sand@349,123|16] +changeEnabled=false +changeEvery=50 +height=52 +width=60 + +[7-sand@364,134|24] +changeEnabled=false +changeEvery=50 +height=52 +width=30 + +[7-slope@11,10|5] +grade=4 +gradient=Vertical +height=209 +reversed=true +stuckOnGround=false +width=378 + +[7-slope@11,238|17] +grade=4 +gradient=Vertical +height=46 +reversed=false +stuckOnGround=false +width=118 + +[7-slope@127,237|16] +grade=4 +gradient=Opposite Diagonal +height=41 +reversed=false +stuckOnGround=false +width=63 + +[7-slope@129,278|15] +grade=4 +gradient=Horizontal +height=121 +reversed=true +stuckOnGround=false +width=62 + +[7-slope@226,238|41] +grade=4 +gradient=Diagonal +height=149 +reversed=false +stuckOnGround=false +width=163 + +[7-wall@0,0|23] +endPoint=284,123 +startPoint=240,117 + +[8-ball@40,30] +dummykey=true + +[8-bridge@151,203|21] +botWallVisible=true +height=26 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=66 + +[8-cup@198,167|10] +dummykey=true + +[8-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=6 + +[8-puddle@170,236|22] +changeEnabled=false +changeEvery=50 +height=36 +width=38 + +[8-puddle@186,239|41] +changeEnabled=false +changeEvery=50 +height=32 +width=30 + +[8-puddle@207,240|43] +changeEnabled=false +changeEvery=50 +height=32 +width=30 + +[8-puddle@268,292|17] +changeEnabled=false +changeEvery=50 +height=44 +width=48 + +[8-puddle@268,307|42] +changeEnabled=false +changeEvery=50 +height=34 +width=90 + +[8-puddle@279,280|19] +changeEnabled=false +changeEvery=50 +height=42 +width=40 + +[8-puddle@296,280|13] +changeEnabled=false +changeEvery=50 +height=82 +width=50 + +[8-slope@133,253|37] +grade=4 +gradient=Opposite Diagonal +height=15 +reversed=true +stuckOnGround=false +width=16 + +[8-slope@134,206|38] +grade=4 +gradient=Horizontal +height=47 +reversed=false +stuckOnGround=false +width=15 + +[8-slope@14,287|20] +grade=8 +gradient=Opposite Diagonal +height=101 +reversed=false +stuckOnGround=false +width=109 + +[8-slope@146,253|24] +grade=4 +gradient=Vertical +height=13 +reversed=true +stuckOnGround=false +width=78 + +[8-slope@150,129|27] +grade=3 +gradient=Horizontal +height=69 +reversed=false +stuckOnGround=false +width=28 + +[8-slope@221,204|39] +grade=4 +gradient=Horizontal +height=49 +reversed=true +stuckOnGround=false +width=18 + +[8-slope@223,253|36] +grade=4 +gradient=Diagonal +height=13 +reversed=true +stuckOnGround=false +width=15 + +[8-slope@233,91|35] +grade=3 +gradient=Vertical +height=66 +reversed=true +stuckOnGround=false +width=27 + +[8-slope@256,156|28] +grade=3 +gradient=Opposite Diagonal +height=20 +reversed=true +stuckOnGround=false +width=59 + +[8-slope@257,105|25] +grade=3 +gradient=Vertical +height=49 +reversed=true +stuckOnGround=false +width=27 + +[8-slope@284,122|26] +grade=3 +gradient=Vertical +height=34 +reversed=true +stuckOnGround=false +width=31 + +[8-slope@291,10|12] +grade=8 +gradient=Opposite Diagonal +height=98 +reversed=true +stuckOnGround=false +width=97 + +[8-wall@0,0|11] +endPoint=11,161 +startPoint=30,90 + +[8-wall@0,0|29] +endPoint=318,125 +startPoint=238,83 + +[8-wall@0,0|3] +endPoint=238,83 +startPoint=61,70 + +[8-wall@0,0|30] +endPoint=300,226 +startPoint=320,150 + +[8-wall@0,0|31] +endPoint=90,126 +startPoint=70,145 + +[8-wall@0,0|32] +endPoint=61,70 +startPoint=30,90 + +[8-wall@0,0|33] +endPoint=227,129 +startPoint=241,143 + +[8-wall@0,0|34] +endPoint=241,181 +startPoint=225,196 + +[8-wall@0,0|4] +endPoint=300,226 +startPoint=185,332 + +[8-wall@0,0|40] +endPoint=318,125 +startPoint=320,150 + +[8-wall@0,0|41] +endPoint=185,332 +startPoint=171,331 + +[8-wall@0,0|5] +endPoint=171,331 +startPoint=75,228 + +[8-wall@0,0|6] +endPoint=70,145 +startPoint=75,228 + +[8-wall@0,0|7] +endPoint=90,126 +startPoint=227,129 + +[8-wall@0,0|8] +endPoint=241,143 +startPoint=241,181 + +[8-wall@0,0|9] +endPoint=225,196 +startPoint=141,196 + +[9-ball@43,342] +dummykey=true + +[9-blackhole@189,342|22] +exit=44,144 +exitDeg=0 +maxspeed=3 +minspeed=1 + +[9-cup@195,165|20] +dummykey=true + +[9-cup@274,316|10] +dummykey=true + +[9-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[9-puddle@260,356|30] +changeEnabled=false +changeEvery=50 +height=34 +width=72 + +[9-puddle@284,362|3] +changeEnabled=false +changeEvery=50 +height=28 +width=60 + +[9-puddle@295,281|6] +changeEnabled=false +changeEvery=50 +height=36 +width=48 + +[9-puddle@298,286|31] +changeEnabled=false +changeEvery=50 +height=38 +width=38 + +[9-puddle@303,367|4] +changeEnabled=false +changeEvery=50 +height=28 +width=62 + +[9-puddle@308,294|7] +changeEnabled=false +changeEvery=50 +height=38 +width=46 + +[9-puddle@314,301|32] +changeEnabled=false +changeEvery=50 +height=38 +width=44 + +[9-puddle@316,364|5] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[9-puddle@321,307|8] +changeEnabled=false +changeEvery=50 +height=42 +width=40 + +[9-puddle@328,355|29] +changeEnabled=false +changeEvery=50 +height=36 +width=34 + +[9-puddle@329,320|28] +changeEnabled=false +changeEvery=50 +height=42 +width=38 + +[9-puddle@333,336|9] +changeEnabled=false +changeEvery=50 +height=62 +width=36 + +[9-sand@87,234|26] +changeEnabled=false +changeEvery=50 +height=36 +width=62 + +[9-slope@11,11|19] +grade=2 +gradient=Diagonal +height=513 +reversed=true +stuckOnGround=false +width=499 + +[9-slope@38,337|28] +grade=5 +gradient=Diagonal +height=10 +reversed=true +stuckOnGround=false +width=8 + +[9-slope@93,148|27] +grade=4 +gradient=Elliptic +height=72 +reversed=false +stuckOnGround=false +width=72 + +[9-wall@0,0|11] +endPoint=88,205 +startPoint=68,172 + +[9-wall@0,0|12] +endPoint=222,186 +startPoint=166,193 + +[9-wall@0,0|13] +endPoint=265,251 +startPoint=217,229 + +[9-wall@0,0|14] +endPoint=295,205 +startPoint=228,133 + +[9-wall@0,0|15] +endPoint=178,146 +startPoint=107,128 + +[9-wall@0,0|16] +endPoint=164,219 +startPoint=133,239 + +[9-wall@0,0|17] +endPoint=209,325 +startPoint=167,326 + +[9-wall@0,0|18] +endPoint=166,351 +startPoint=123,311 + +[9-wall@0,0|21] +endPoint=180,278 +startPoint=184,242 + +[9-wall@0,0|23] +endPoint=54,276 +startPoint=35,245 + +[9-wall@0,0|28] +endPoint=93,345 +startPoint=61,369 diff --git a/kolf/courses/Impossible b/kolf/courses/Impossible new file mode 100644 index 00000000..40e72e4e --- /dev/null +++ b/kolf/courses/Impossible @@ -0,0 +1,3573 @@ +[0-course@-50,-50] +Name=Impossible Course +Name[af]=Onmoontlik Natuurlik +Name[bg]=Ðевъзможно +Name[bn]=অসমà§à¦­à¦¬ কঠিন খেলা +Name[bs]=Nemoguć teren +Name[ca]=Cursa impossible +Name[cs]=Nesplnitelný +Name[da]=Umulig bane +Name[de]=Unmöglich +Name[es]=Campo imposible +Name[et]=Võimatu väljak +Name[fi]=Mahdoton kenttä +Name[fr]=Parcours impossible +Name[gl]=Campo imposible +Name[he]=מסלול בלתי ×פשרי +Name[hi]=असंभव कोरà¥à¤¸ +Name[hr]=Nemogući smjer +Name[hu]=Különlegesen nehéz pálya +Name[is]=Ómöguleg leið +Name[it]=Percorso impossibile +Name[ja]=ä¸å¯èƒ½ãªã‚³ãƒ¼ã‚¹ +Name[mk]=Ðевозможен терен +Name[nb]=Umulig bane +Name[nl]=Onmogelijk parcours +Name[nn]=Umogleg bane +Name[nso]=Course yeo esa Kgonegego +Name[pl]=Niemożliwy do wykonania +Name[pt]=Curso Impossível +Name[pt_BR]=Curso Impossível +Name[ro]=Cale imposibilă +Name[ru]=МиÑÑÐ¸Ñ Ð½ÐµÐ²Ñ‹Ð¿Ð¾Ð»Ð½Ð¸Ð¼Ð° +Name[sk]=Nezvládnuteľné ihrisko +Name[sl]=NemogoÄe igriÅ¡Äe +Name[sr]=Ðемогућ терен +Name[sr@Latn]=Nemoguć teren +Name[sv]=Omöjlig bana +Name[ta]=சாதà¯à®¤à®¿à®¯à®®à®¿à®²à¯à®²à®¾à®¤ மாரà¯à®•à¯à®•à®®à¯ +Name[tg]=Майдони Имконнопазир +Name[tr]=Ä°mkansız Pist +Name[uk]=ÐайÑкладніший майданчик +Name[ven]=Thero i sa koneiho +Name[xh]=Indlela Engenzekiyo +Name[xx]=xxImpossible Coursexx +Name[zh_CN]=无法完æˆçš„路线 +Name[zh_TW]=ä¸å¯èƒ½çš„路線 +Name[zu]=Indlela engenzeki +author=Jason Katz-Brown + +[1-ball@31,330] +dummykey=true + +[1-cup@328,318|16] +dummykey=true + +[1-floater@227,64|8] +botWallVisible=false +endPoint=227,64 +height=85 +leftWallVisible=false +rightWallVisible=false +speed=5 +startPoint=105,76 +topWallVisible=true +width=93 + +[1-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=true +maxstrokes=10 +par=2 + +[1-puddle@-5,99|20] +changeEnabled=false +changeEvery=50 +height=210 +width=60 + +[1-puddle@108,17|39] +changeEnabled=false +changeEvery=50 +height=32 +width=36 + +[1-puddle@11,15|22] +changeEnabled=false +changeEvery=50 +height=30 +width=32 + +[1-puddle@117,33|45] +changeEnabled=false +changeEvery=50 +height=32 +width=46 + +[1-puddle@124,27|25] +changeEnabled=false +changeEvery=50 +height=36 +width=78 + +[1-puddle@127,31|42] +changeEnabled=false +changeEvery=50 +height=36 +width=76 + +[1-puddle@131,37|43] +changeEnabled=false +changeEvery=50 +height=38 +width=58 + +[1-puddle@131,46|46] +changeEnabled=false +changeEvery=50 +height=26 +width=38 + +[1-puddle@139,46|44] +changeEnabled=false +changeEvery=50 +height=40 +width=46 + +[1-puddle@145,42|24] +changeEnabled=false +changeEvery=50 +height=52 +width=54 + +[1-puddle@163,41|40] +changeEnabled=false +changeEvery=50 +height=26 +width=30 + +[1-puddle@172,45|41] +changeEnabled=false +changeEvery=50 +height=26 +width=32 + +[1-puddle@177,56|6] +changeEnabled=false +changeEvery=50 +height=46 +width=104 + +[1-puddle@188,207|1] +changeEnabled=false +changeEvery=50 +height=68 +width=28 + +[1-puddle@19,9|5] +changeEnabled=false +changeEvery=50 +height=28 +width=34 + +[1-puddle@2,22|7] +changeEnabled=false +changeEvery=50 +height=38 +width=34 + +[1-puddle@216,158|4] +changeEnabled=false +changeEvery=50 +height=232 +width=82 + +[1-puddle@216,256|3] +changeEnabled=false +changeEvery=50 +height=184 +width=98 + +[1-puddle@22,8|0] +changeEnabled=false +changeEvery=50 +height=24 +width=44 + +[1-puddle@247,201|2] +changeEnabled=false +changeEvery=50 +height=84 +width=26 + +[1-puddle@40,-3|21] +changeEnabled=false +changeEvery=50 +height=42 +width=124 + +[1-puddle@7,24|0] +changeEnabled=false +changeEvery=50 +height=38 +width=28 + +[1-puddle@88,5|0] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[1-puddle@98,9|26] +changeEnabled=false +changeEvery=50 +height=50 +width=46 + +[1-sand@224,10|27] +changeEnabled=false +changeEvery=50 +height=46 +width=124 + +[1-sand@247,19|31] +changeEnabled=false +changeEvery=50 +height=30 +width=68 + +[1-sand@277,19|30] +changeEnabled=false +changeEvery=50 +height=40 +width=94 + +[1-sand@292,10|33] +changeEnabled=false +changeEvery=50 +height=16 +width=58 + +[1-sand@304,26|32] +changeEnabled=false +changeEvery=50 +height=32 +width=66 + +[1-sand@314,26|29] +changeEnabled=false +changeEvery=50 +height=40 +width=54 + +[1-sand@349,114|37] +changeEnabled=false +changeEvery=50 +height=62 +width=22 + +[1-sand@358,92|36] +changeEnabled=false +changeEvery=50 +height=80 +width=58 + +[1-sand@367,123|35] +changeEnabled=false +changeEvery=50 +height=96 +width=54 + +[1-sand@368,133|34] +changeEnabled=false +changeEvery=50 +height=82 +width=56 + +[1-sand@369,150|15] +changeEnabled=false +changeEvery=50 +height=86 +width=58 + +[1-sand@383,101|38] +changeEnabled=false +changeEvery=50 +height=38 +width=14 + +[1-slope@-42,10|22] +grade=2 +gradient=Horizontal +height=387 +reversed=true +stuckOnGround=false +width=135 + +[1-slope@135,310|11] +grade=4 +gradient=Opposite Diagonal +height=76 +reversed=true +stuckOnGround=true +width=61 + +[1-slope@136,82|10] +grade=4 +gradient=Horizontal +height=229 +reversed=false +stuckOnGround=true +width=60 + +[1-slope@192,301|12] +grade=4 +gradient=Vertical +height=90 +reversed=true +stuckOnGround=true +width=66 + +[1-slope@211,6|28] +grade=4 +gradient=Opposite Diagonal +height=82 +reversed=false +stuckOnGround=true +width=75 + +[1-slope@244,89|14] +grade=4 +gradient=Horizontal +height=213 +reversed=true +stuckOnGround=true +width=47 + +[1-slope@257,301|13] +grade=4 +gradient=Diagonal +height=88 +reversed=true +stuckOnGround=true +width=35 + +[1-slope@286,276|29] +grade=4 +gradient=Elliptic +height=82 +reversed=true +stuckOnGround=false +width=82 + +[1-slope@29,-3|9] +grade=4 +gradient=Opposite Diagonal +height=149 +reversed=true +stuckOnGround=true +width=173 + +[1-wall@0,0|17] +endPoint=358,358 +startPoint=298,362 + +[1-wall@0,0|18] +endPoint=358,358 +startPoint=380,331 + +[1-wall@0,0|19] +endPoint=380,331 +startPoint=383,206 + +[1-wall@0,0|23] +endPoint=280,345 +startPoint=298,362 + +[10-ball@351,365] +dummykey=true + +[10-blackhole@255,206|4] +exit=15,39 +exitDeg=0 +maxspeed=1 +minspeed=1 + +[10-cup@44,38|3] +dummykey=true + +[10-floater@220,184|2] +botWallVisible=true +endPoint=220,184 +height=43 +leftWallVisible=true +rightWallVisible=true +speed=7 +startPoint=69,209 +topWallVisible=true +width=71 + +[10-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=2 + +[10-puddle@194,199|0] +changeEnabled=false +changeEvery=50 +height=60 +width=72 + +[10-puddle@201,188|0] +changeEnabled=false +changeEvery=50 +height=56 +width=66 + +[10-puddle@201,213|0] +changeEnabled=false +changeEvery=50 +height=32 +width=56 + +[10-puddle@214,197|0] +changeEnabled=false +changeEvery=50 +height=60 +width=48 + +[10-slope@35,30|0] +grade=6 +gradient=Elliptic +height=338 +reversed=false +stuckOnGround=true +width=338 + +[10-wall@0,0|0] +endPoint=387,198 +startPoint=307,198 + +[10-wall@0,0|1] +endPoint=73,196 +startPoint=12,196 + +[11-ball@182,47] +dummykey=true + +[11-bridge@158,36|17] +botWallVisible=false +height=27 +leftWallVisible=false +rightWallVisible=false +topWallVisible=true +width=43 + +[11-cup@97,297|0] +dummykey=true + +[11-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=1 + +[11-sand@204,336|18] +changeEnabled=false +changeEvery=50 +height=30 +width=34 + +[11-sand@213,327|21] +changeEnabled=false +changeEvery=50 +height=18 +width=22 + +[11-sand@213,331|19] +changeEnabled=false +changeEvery=50 +height=24 +width=26 + +[11-sand@222,324|20] +changeEnabled=false +changeEvery=50 +height=26 +width=30 + +[11-slope@129,150|2] +grade=4 +gradient=Opposite Diagonal +height=91 +reversed=true +stuckOnGround=false +width=99 + +[11-slope@130,148|1] +grade=4 +gradient=Opposite Diagonal +height=93 +reversed=false +stuckOnGround=false +width=98 + +[11-slope@130,242|15] +grade=4 +gradient=Diagonal +height=75 +reversed=true +stuckOnGround=false +width=97 + +[11-slope@130,63|3] +grade=4 +gradient=Diagonal +height=88 +reversed=false +stuckOnGround=false +width=99 + +[11-slope@130,64|4] +grade=4 +gradient=Diagonal +height=87 +reversed=true +stuckOnGround=false +width=97 + +[11-slope@73,241|22] +grade=5 +gradient=Opposite Diagonal +height=73 +reversed=true +stuckOnGround=false +width=58 + +[11-wall@0,0|10] +endPoint=118,365 +startPoint=69,296 + +[11-wall@0,0|11] +endPoint=69,296 +startPoint=81,244 + +[11-wall@0,0|12] +endPoint=81,244 +startPoint=129,242 + +[11-wall@0,0|13] +endPoint=129,242 +startPoint=130,63 + +[11-wall@0,0|14] +endPoint=130,63 +startPoint=158,36 + +[11-wall@0,0|16] +endPoint=201,36 +startPoint=231,64 + +[11-wall@0,0|5] +endPoint=229,242 +startPoint=231,64 + +[11-wall@0,0|6] +endPoint=229,242 +startPoint=261,319 + +[11-wall@0,0|7] +endPoint=261,319 +startPoint=252,353 + +[11-wall@0,0|8] +endPoint=252,353 +startPoint=201,376 + +[11-wall@0,0|9] +endPoint=201,376 +startPoint=118,365 + +[12-ball@196,183] +dummykey=true + +[12-cup@107,101|2] +dummykey=true + +[12-cup@107,271|1] +dummykey=true + +[12-cup@285,103|0] +dummykey=true + +[12-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[12-slope@177,164|7] +grade=6 +gradient=Elliptic +height=37 +reversed=false +stuckOnGround=true +width=37 + +[12-slope@197,54|4] +grade=8 +gradient=Opposite Diagonal +height=128 +reversed=false +stuckOnGround=true +width=124 + +[12-slope@198,182|6] +grade=8 +gradient=Diagonal +height=123 +reversed=true +stuckOnGround=true +width=122 + +[12-slope@288,-2|15] +grade=3 +gradient=Elliptic +height=103 +reversed=false +stuckOnGround=false +width=103 + +[12-slope@347,68|8] +grade=2 +gradient=Elliptic +height=79 +reversed=false +stuckOnGround=false +width=79 + +[12-slope@68,183|5] +grade=8 +gradient=Opposite Diagonal +height=123 +reversed=true +stuckOnGround=true +width=130 + +[12-slope@69,52|3] +grade=8 +gradient=Diagonal +height=129 +reversed=false +stuckOnGround=true +width=128 + +[12-windmill@234,300|9] +botWallVisible=true +bottom=false +height=83 +leftWallVisible=true +rightWallVisible=true +speed=5 +topWallVisible=false +width=121 + +[13-ball@72,345] +dummykey=true + +[13-cup@296,86|0] +dummykey=true + +[13-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[13-puddle@338,3|16] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[13-puddle@357,-16|15] +changeEnabled=false +changeEvery=50 +height=62 +width=132 + +[13-puddle@358,21|13] +changeEnabled=false +changeEvery=50 +height=50 +width=46 + +[13-puddle@367,103|2] +changeEnabled=false +changeEvery=50 +height=64 +width=38 + +[13-puddle@367,55|12] +changeEnabled=false +changeEvery=50 +height=64 +width=38 + +[13-puddle@369,0|7] +changeEnabled=false +changeEvery=50 +height=80 +width=94 + +[13-puddle@370,145|4] +changeEnabled=false +changeEvery=50 +height=120 +width=78 + +[13-puddle@376,81|5] +changeEnabled=false +changeEvery=50 +height=76 +width=50 + +[13-puddle@380,31|6] +changeEnabled=false +changeEvery=50 +height=82 +width=80 + +[13-puddle@394,99|14] +changeEnabled=false +changeEvery=50 +height=68 +width=46 + +[13-puddle@428,179|8] +changeEnabled=false +changeEvery=50 +height=88 +width=164 + +[13-sand@103,64|10] +changeEnabled=false +changeEvery=50 +height=94 +width=130 + +[13-sand@106,102|26] +changeEnabled=false +changeEvery=50 +height=86 +width=48 + +[13-sand@106,172|20] +changeEnabled=false +changeEvery=50 +height=62 +width=36 + +[13-sand@107,91|25] +changeEnabled=false +changeEvery=50 +height=50 +width=64 + +[13-sand@120,149|21] +changeEnabled=false +changeEvery=50 +height=74 +width=14 + +[13-sand@35,210|18] +changeEnabled=false +changeEvery=50 +height=82 +width=46 + +[13-sand@43,201|27] +changeEnabled=false +changeEvery=50 +height=40 +width=48 + +[13-sand@60,148|23] +changeEnabled=false +changeEvery=50 +height=36 +width=30 + +[13-sand@66,96|9] +changeEnabled=false +changeEvery=50 +height=134 +width=72 + +[13-sand@67,181|11] +changeEnabled=false +changeEvery=50 +height=38 +width=92 + +[13-sand@72,194|19] +changeEnabled=false +changeEvery=50 +height=40 +width=90 + +[13-sand@73,171|24] +changeEnabled=false +changeEvery=50 +height=28 +width=66 + +[13-sand@76,164|22] +changeEnabled=false +changeEvery=50 +height=70 +width=56 + +[13-sand@96,126|17] +changeEnabled=false +changeEvery=50 +height=138 +width=66 + +[13-sign@142,193|1] +Comment=Reprieve +Comment[bg]=ОтмÑна +Comment[bn]=সাময়িকভাবে সà§à¦¥à¦—িত +Comment[bs]=Odgodi +Comment[ca]=Respir +Comment[da]=Red dig selv +Comment[de]=Aufschub +Comment[es]=Respiro +Comment[et]=Kergendus +Comment[fi]=Armonaika +Comment[fr]=Sursis +Comment[gl]=Respiro +Comment[he]=הקלה +Comment[hi]=विराम +Comment[hr]=Odgodi +Comment[hu]=Mázli +Comment[is]=Milda +Comment[it]=Pausa +Comment[ja]=カンタンãªä¸€çž¬ +Comment[mk]=Одложување +Comment[nb]=NÃ¥de +Comment[nl]=Gratie +Comment[nn]=NÃ¥de +Comment[pl]=Odroczenie +Comment[pt]=Tortura +Comment[pt_BR]=Suspensão +Comment[ru]=ОтÑрочка +Comment[sk]=Omilostnenie +Comment[sl]=Odložitev +Comment[sr]=Предах +Comment[sr@Latn]=Predah +Comment[sv]=Lättnad +Comment[ta]=ஒதà¯à®¤à®¿ வை +Comment[tg]=Таъхир +Comment[tr]=Ceza Ertele +Comment[uk]=Ð’Ñ–Ð´ÐºÐ»Ð°Ð´Ð°Ð½Ð½Ñ +Comment[ven]=U vhofhololwa +Comment[xh]=Urhoxiso lwethutyana +Comment[xx]=xxReprievexx +Comment[zh_CN]=暂时缓解 +Comment[zh_TW]=æš«ç·© +Comment[zu]=Phumuza +botWallVisible=false +height=47 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=121 + +[13-slope@212,12|3] +grade=4 +gradient=Elliptic +height=117 +reversed=false +stuckOnGround=false +width=117 + +[14-ball@63,341] +dummykey=true + +[14-blackhole@137,332|12] +exit=22,383 +exitDeg=40 +maxspeed=5 +minspeed=3 + +[14-blackhole@317,208|10] +exit=17,377 +exitDeg=40 +maxspeed=5 +minspeed=3 + +[14-bumper@179,215|4] +dummykey=true + +[14-bumper@290,116|6] +dummykey=true + +[14-cup@351,37|0] +dummykey=true + +[14-floater@113,315|3] +botWallVisible=true +endPoint=113,315 +height=39 +leftWallVisible=true +rightWallVisible=true +speed=7 +startPoint=29,195 +topWallVisible=true +width=49 + +[14-floater@281,189|5] +botWallVisible=true +endPoint=281,189 +height=39 +leftWallVisible=true +rightWallVisible=true +speed=19 +startPoint=112,79 +topWallVisible=true +width=74 + +[14-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=true +maxstrokes=10 +par=3 + +[14-puddle@334,315|9] +changeEnabled=false +changeEvery=50 +height=60 +width=96 + +[14-puddle@372,404|8] +changeEnabled=false +changeEvery=50 +height=242 +width=332 + +[14-puddle@391,360|7] +changeEnabled=false +changeEvery=50 +height=218 +width=226 + +[14-slope@-2,-2|2] +grade=4 +gradient=Diagonal +height=357 +reversed=true +stuckOnGround=true +width=360 + +[14-slope@36,37|1] +grade=4 +gradient=Diagonal +height=362 +reversed=false +stuckOnGround=true +width=364 + +[14-windmill@283,12|11] +botWallVisible=false +bottom=true +height=71 +leftWallVisible=true +rightWallVisible=true +speed=5 +topWallVisible=true +width=92 + +[15-ball@202,250] +dummykey=true + +[15-blackhole@122,62|42] +exit=107,219 +exitDeg=180 +maxspeed=5 +minspeed=3 + +[15-bridge@129,134|62] +botWallVisible=false +height=20 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=74 + +[15-bridge@129,135|63] +botWallVisible=false +height=37 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=16 + +[15-bridge@161,134|59] +botWallVisible=false +height=46 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=87 + +[15-bridge@180,152|61] +botWallVisible=false +height=42 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=34 + +[15-bridge@202,120|60] +botWallVisible=false +height=45 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=32 + +[15-bridge@222,152|64] +botWallVisible=false +height=28 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=47 + +[15-cup@185,340|0] +dummykey=true + +[15-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[15-slope@102,285|56] +grade=5 +gradient=Opposite Diagonal +height=25 +reversed=true +stuckOnGround=false +width=45 + +[15-slope@105,251|47] +grade=6 +gradient=Horizontal +height=16 +reversed=false +stuckOnGround=false +width=27 + +[15-slope@108,313|57] +grade=4 +gradient=Opposite Diagonal +height=55 +reversed=false +stuckOnGround=false +width=39 + +[15-slope@132,215|58] +grade=6 +gradient=Vertical +height=20 +reversed=true +stuckOnGround=false +width=34 + +[15-slope@145,308|53] +grade=4 +gradient=Horizontal +height=61 +reversed=true +stuckOnGround=false +width=27 + +[15-slope@167,212|46] +grade=4 +gradient=Horizontal +height=77 +reversed=true +stuckOnGround=false +width=118 + +[15-slope@172,308|54] +grade=4 +gradient=Vertical +height=25 +reversed=true +stuckOnGround=false +width=28 + +[15-slope@174,345|55] +grade=4 +gradient=Vertical +height=22 +reversed=false +stuckOnGround=false +width=27 + +[15-slope@304,150|45] +grade=4 +gradient=Horizontal +height=105 +reversed=false +stuckOnGround=false +width=59 + +[15-slope@65,251|52] +grade=4 +gradient=Opposite Diagonal +height=60 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@81,207|51] +grade=5 +gradient=Horizontal +height=24 +reversed=false +stuckOnGround=false +width=30 + +[15-slope@90,51|65] +grade=4 +gradient=Diagonal +height=51 +reversed=true +stuckOnGround=false +width=25 + +[15-wall@0,0|10] +endPoint=216,102 +startPoint=191,102 + +[15-wall@0,0|11] +endPoint=191,102 +startPoint=191,87 + +[15-wall@0,0|12] +endPoint=191,87 +startPoint=153,87 + +[15-wall@0,0|13] +endPoint=153,87 +startPoint=153,111 + +[15-wall@0,0|14] +endPoint=153,111 +startPoint=115,111 + +[15-wall@0,0|15] +endPoint=115,111 +startPoint=115,78 + +[15-wall@0,0|16] +endPoint=115,78 +startPoint=134,76 + +[15-wall@0,0|17] +endPoint=134,76 +startPoint=134,49 + +[15-wall@0,0|18] +endPoint=134,49 +startPoint=87,49 + +[15-wall@0,0|19] +endPoint=87,49 +startPoint=87,184 + +[15-wall@0,0|2] +endPoint=305,151 +startPoint=363,151 + +[15-wall@0,0|20] +endPoint=87,184 +startPoint=130,184 + +[15-wall@0,0|21] +endPoint=130,184 +startPoint=130,239 + +[15-wall@0,0|22] +endPoint=123,252 +startPoint=86,252 + +[15-wall@0,0|23] +endPoint=86,252 +startPoint=86,232 + +[15-wall@0,0|24] +endPoint=86,232 +startPoint=111,232 + +[15-wall@0,0|25] +endPoint=111,232 +startPoint=111,205 + +[15-wall@0,0|26] +endPoint=111,205 +startPoint=64,205 + +[15-wall@0,0|27] +endPoint=64,205 +startPoint=64,314 + +[15-wall@0,0|28] +endPoint=64,314 +startPoint=106,314 + +[15-wall@0,0|29] +endPoint=106,314 +startPoint=106,369 + +[15-wall@0,0|3] +endPoint=305,151 +startPoint=305,115 + +[15-wall@0,0|30] +endPoint=106,369 +startPoint=199,369 + +[15-wall@0,0|31] +endPoint=199,369 +startPoint=199,307 + +[15-wall@0,0|32] +endPoint=199,307 +startPoint=146,307 + +[15-wall@0,0|33] +endPoint=146,307 +startPoint=146,285 + +[15-wall@0,0|34] +endPoint=146,285 +startPoint=105,285 + +[15-wall@0,0|35] +endPoint=105,285 +startPoint=105,268 + +[15-wall@0,0|36] +endPoint=105,268 +startPoint=166,268 + +[15-wall@0,0|37] +endPoint=166,268 +startPoint=166,291 + +[15-wall@0,0|38] +endPoint=166,291 +startPoint=305,291 + +[15-wall@0,0|39] +endPoint=305,291 +startPoint=305,256 + +[15-wall@0,0|4] +endPoint=305,115 +startPoint=270,115 + +[15-wall@0,0|40] +endPoint=305,256 +startPoint=363,256 + +[15-wall@0,0|41] +endPoint=363,256 +startPoint=363,151 + +[15-wall@0,0|43] +endPoint=166,268 +startPoint=166,212 + +[15-wall@0,0|44] +endPoint=166,212 +startPoint=285,211 + +[15-wall@0,0|49] +endPoint=165,236 +startPoint=133,270 + +[15-wall@0,0|5] +endPoint=270,115 +startPoint=270,70 + +[15-wall@0,0|50] +endPoint=123,252 +startPoint=130,239 + +[15-wall@0,0|6] +endPoint=270,70 +startPoint=238,70 + +[15-wall@0,0|7] +endPoint=238,70 +startPoint=238,89 + +[15-wall@0,0|8] +endPoint=238,89 +startPoint=216,89 + +[15-wall@0,0|9] +endPoint=216,89 +startPoint=216,102 + +[15-windmill@133,212|48] +botWallVisible=false +bottom=false +height=11 +leftWallVisible=false +rightWallVisible=false +speed=2 +topWallVisible=false +width=33 + +[16-ball@56,49] +dummykey=true + +[16-cup@212,321|1] +dummykey=true + +[16-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[16-puddle@266,315|25] +changeEnabled=false +changeEvery=50 +height=18 +width=10 + +[16-puddle@274,301|22] +changeEnabled=false +changeEvery=50 +height=22 +width=22 + +[16-puddle@276,314|28] +changeEnabled=false +changeEvery=50 +height=8 +width=12 + +[16-puddle@280,328|26] +changeEnabled=false +changeEvery=50 +height=12 +width=20 + +[16-puddle@286,315|24] +changeEnabled=false +changeEvery=50 +height=14 +width=10 + +[16-puddle@290,309|29] +changeEnabled=false +changeEvery=50 +height=16 +width=14 + +[16-puddle@292,327|31] +changeEnabled=false +changeEvery=50 +height=12 +width=18 + +[16-puddle@296,300|23] +changeEnabled=false +changeEvery=50 +height=16 +width=10 + +[16-puddle@300,321|27] +changeEnabled=false +changeEvery=50 +height=16 +width=14 + +[16-puddle@306,308|21] +changeEnabled=false +changeEvery=50 +height=16 +width=10 + +[16-puddle@329,382|8] +changeEnabled=false +changeEvery=50 +height=26 +width=36 + +[16-puddle@336,400|4] +changeEnabled=false +changeEvery=50 +height=52 +width=390 + +[16-puddle@351,359|7] +changeEnabled=false +changeEvery=50 +height=46 +width=38 + +[16-puddle@355,386|6] +changeEnabled=false +changeEvery=50 +height=74 +width=72 + +[16-puddle@409,335|5] +changeEnabled=false +changeEvery=50 +height=336 +width=150 + +[16-puddle@422,231|9] +changeEnabled=false +changeEvery=50 +height=228 +width=114 + +[16-puddle@58,420|0] +changeEnabled=false +changeEvery=50 +height=96 +width=550 + +[16-sand@306,84|19] +changeEnabled=false +changeEvery=50 +height=28 +width=52 + +[16-sand@312,-7|16] +changeEnabled=false +changeEvery=50 +height=102 +width=84 + +[16-sand@322,78|18] +changeEnabled=false +changeEvery=50 +height=38 +width=44 + +[16-sand@332,65|17] +changeEnabled=false +changeEvery=50 +height=58 +width=54 + +[16-sand@342,76|20] +changeEnabled=false +changeEvery=50 +height=32 +width=76 + +[16-sand@387,22|15] +changeEnabled=false +changeEvery=50 +height=142 +width=196 + +[16-slope@-2,-3|14] +grade=7 +gradient=Diagonal +height=260 +reversed=true +stuckOnGround=false +width=267 + +[16-slope@2,360|3] +grade=4 +gradient=Vertical +height=30 +reversed=false +stuckOnGround=false +width=293 + +[16-slope@202,43|13] +grade=2 +gradient=Elliptic +height=140 +reversed=true +stuckOnGround=false +width=140 + +[16-slope@271,313|10] +grade=4 +gradient=Diagonal +height=66 +reversed=false +stuckOnGround=false +width=74 + +[16-slope@319,91|12] +grade=4 +gradient=Diagonal +height=139 +reversed=false +stuckOnGround=false +width=81 + +[16-slope@320,230|11] +grade=4 +gradient=Horizontal +height=108 +reversed=false +stuckOnGround=false +width=44 + +[16-slope@69,154|2] +grade=3 +gradient=Elliptic +height=154 +reversed=false +stuckOnGround=false +width=154 + +[17-ball@55,55] +dummykey=true + +[17-blackhole@269,363|4] +exit=198,384 +exitDeg=90 +maxspeed=3 +minspeed=2 + +[17-cup@146,232|21] +dummykey=true + +[17-cup@88,233|1] +dummykey=true + +[17-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[17-sand@333,344|15] +changeEnabled=false +changeEvery=50 +height=40 +width=54 + +[17-sand@340,343|16] +changeEnabled=false +changeEvery=50 +height=28 +width=56 + +[17-sand@351,351|14] +changeEnabled=false +changeEvery=50 +height=38 +width=52 + +[17-sand@359,354|13] +changeEnabled=false +changeEvery=50 +height=44 +width=50 + +[17-sign@23,152|20] +Comment=Luck +Comment[ar]=حظ +Comment[bg]=КъÑмет +Comment[bn]=ভাগà§à¦¯ +Comment[br]=Chañs +Comment[bs]=Sreća +Comment[ca]=Sort +Comment[cs]=Å tÄ›stí +Comment[da]=Held +Comment[de]=Glück +Comment[el]=ΤÏχη +Comment[es]=Suerte +Comment[et]=Õnn +Comment[fi]=Onni +Comment[fr]=Chance +Comment[gl]=Sorte +Comment[he]=מזל +Comment[hi]=भागà¥à¤¯ +Comment[hr]=Sreća +Comment[hu]=Szerencse +Comment[is]=Lukka +Comment[it]=Fortunato +Comment[ja]=é‹ +Comment[lv]=Veiksme +Comment[mk]=Среќа +Comment[nb]=Flaks +Comment[nl]=Geluk +Comment[nn]=Flaks +Comment[nso]=Mahlatse +Comment[pl]=Szczęście +Comment[pt]=Sorte +Comment[pt_BR]=Sorte +Comment[ro]=Noroc +Comment[ru]=Удача +Comment[sk]=Šťastie +Comment[sl]=SreÄa +Comment[sr]=Срећа +Comment[sr@Latn]=Sreća +Comment[sv]=Tur +Comment[ta]=அதிரà¯à®·à¯à®Ÿà®®à¯ +Comment[tg]=Барор +Comment[tr]=Åžans +Comment[uk]=удача +Comment[ven]=Mashudu +Comment[wa]=Tchance +Comment[xh]=Ithamsanqa +Comment[xx]=xxLuckxx +Comment[zh_CN]=å¹¸è¿ +Comment[zh_TW]=å¹¸é‹ +Comment[zu]=Inhlanhla +botWallVisible=false +height=51 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=105 + +[17-slope@113,18|9] +grade=2 +gradient=Elliptic +height=183 +reversed=false +stuckOnGround=false +width=183 + +[17-slope@237,229|5] +grade=5 +gradient=Vertical +height=109 +reversed=false +stuckOnGround=false +width=153 + +[17-slope@289,85|19] +grade=2 +gradient=Elliptic +height=146 +reversed=false +stuckOnGround=false +width=146 + +[17-slope@43,331|11] +grade=8 +gradient=Horizontal +height=58 +reversed=true +stuckOnGround=false +width=27 + +[17-slope@71,246|10] +grade=2 +gradient=Vertical +height=43 +reversed=false +stuckOnGround=false +width=91 + +[17-slope@77,297|18] +grade=3 +gradient=Diagonal +height=89 +reversed=false +stuckOnGround=false +width=84 + +[17-wall@0,0|0] +endPoint=235,141 +startPoint=12,141 + +[17-wall@0,0|12] +endPoint=70,330 +startPoint=44,330 + +[17-wall@0,0|17] +endPoint=43,389 +startPoint=13,332 + +[17-wall@0,0|2] +endPoint=373,113 +startPoint=311,33 + +[17-wall@0,0|22] +endPoint=131,289 +startPoint=119,298 + +[17-wall@0,0|23] +endPoint=119,298 +startPoint=109,288 + +[17-wall@0,0|3] +endPoint=235,141 +startPoint=235,387 + +[17-wall@0,0|6] +endPoint=162,217 +startPoint=70,217 + +[17-wall@0,0|7] +endPoint=162,217 +startPoint=162,388 + +[17-wall@0,0|8] +endPoint=70,217 +startPoint=70,330 + +[18-ball@134,349] +dummykey=true + +[18-blackhole@362,297|7] +exit=207,44 +exitDeg=0 +maxspeed=2 +minspeed=1 + +[18-cup@309,150|0] +dummykey=true + +[18-floater@55,53|1] +botWallVisible=false +endPoint=55,53 +height=119 +leftWallVisible=false +rightWallVisible=false +speed=8 +startPoint=42,235 +topWallVisible=false +width=28 + +[18-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=true +maxstrokes=10 +par=2 + +[18-puddle@104,229|32] +changeEnabled=false +changeEvery=50 +height=32 +width=174 + +[18-puddle@238,220|2] +changeEnabled=false +changeEvery=50 +height=52 +width=404 + +[18-puddle@279,199|5] +changeEnabled=false +changeEvery=50 +height=26 +width=100 + +[18-puddle@307,201|4] +changeEnabled=false +changeEvery=50 +height=26 +width=244 + +[18-puddle@33,222|33] +changeEnabled=false +changeEvery=50 +height=28 +width=42 + +[18-puddle@50,218|34] +changeEnabled=false +changeEvery=50 +height=22 +width=52 + +[18-puddle@605,204|3] +changeEnabled=false +changeEvery=50 +height=90 +width=750 + +[18-sand@-35,168|17] +changeEnabled=false +changeEvery=50 +height=40 +width=152 + +[18-sand@17,105|16] +changeEnabled=false +changeEvery=50 +height=152 +width=106 + +[18-sand@36,11|15] +changeEnabled=false +changeEvery=50 +height=272 +width=86 + +[18-sand@43,152|19] +changeEnabled=false +changeEvery=50 +height=50 +width=104 + +[18-sand@51,106|20] +changeEnabled=false +changeEvery=50 +height=60 +width=54 + +[18-sand@58,167|26] +changeEnabled=false +changeEvery=50 +height=26 +width=48 + +[18-sand@63,83|23] +changeEnabled=false +changeEvery=50 +height=36 +width=24 + +[18-sand@65,164|25] +changeEnabled=false +changeEvery=50 +height=38 +width=50 + +[18-sand@69,119|22] +changeEnabled=false +changeEvery=50 +height=44 +width=34 + +[18-sand@69,139|21] +changeEnabled=false +changeEvery=50 +height=70 +width=60 + +[18-sand@69,149|24] +changeEnabled=false +changeEvery=50 +height=82 +width=56 + +[18-slope@145,-5|8] +grade=7 +gradient=Horizontal +height=409 +reversed=false +stuckOnGround=true +width=129 + +[18-slope@269,244|28] +grade=4 +gradient=Opposite Diagonal +height=185 +reversed=true +stuckOnGround=false +width=132 + +[18-slope@30,233|18] +grade=4 +gradient=Vertical +height=34 +reversed=true +stuckOnGround=true +width=98 + +[18-slope@31,323|12] +grade=6 +gradient=Vertical +height=43 +reversed=false +stuckOnGround=true +width=52 + +[18-slope@32,243|14] +grade=5 +gradient=Horizontal +height=125 +reversed=true +stuckOnGround=true +width=20 + +[18-slope@50,241|10] +grade=5 +gradient=Horizontal +height=163 +reversed=false +stuckOnGround=true +width=76 + +[18-wall@0,0|11] +endPoint=84,367 +startPoint=35,369 + +[18-wall@0,0|13] +endPoint=84,328 +startPoint=84,367 + +[18-wall@0,0|27] +endPoint=385,123 +startPoint=385,166 + +[18-wall@0,0|29] +endPoint=422,377 +startPoint=226,377 + +[18-wall@0,0|30] +endPoint=91,380 +startPoint=84,328 + +[18-wall@0,0|31] +endPoint=91,380 +startPoint=35,369 + +[18-wall@0,0|9] +endPoint=31,244 +startPoint=31,400 + +[18-windmill@201,-2|6] +botWallVisible=false +bottom=true +height=121 +leftWallVisible=true +rightWallVisible=false +speed=5 +topWallVisible=false +width=201 + +[2-ball@62,329] +dummykey=true + +[2-cup@71,229|3] +dummykey=true + +[2-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[2-puddle@285,-3|2] +changeEnabled=false +changeEvery=50 +height=42 +width=20 + +[2-puddle@315,13|12] +changeEnabled=false +changeEvery=50 +height=32 +width=70 + +[2-puddle@331,-5|1] +changeEnabled=false +changeEvery=50 +height=68 +width=76 + +[2-puddle@364,0|9] +changeEnabled=false +changeEvery=50 +height=58 +width=132 + +[2-sand@15,163|19] +changeEnabled=false +changeEvery=50 +height=86 +width=66 + +[2-sand@31,76|16] +changeEnabled=false +changeEvery=50 +height=58 +width=42 + +[2-sand@76,33|17] +changeEnabled=false +changeEvery=50 +height=36 +width=52 + +[2-slope@1,124|14] +grade=4 +gradient=Vertical +height=146 +reversed=false +stuckOnGround=false +width=179 + +[2-slope@111,100|4] +grade=3 +gradient=Diagonal +height=301 +reversed=false +stuckOnGround=false +width=291 + +[2-slope@181,130|0] +grade=4 +gradient=Vertical +height=32 +reversed=true +stuckOnGround=false +width=119 + +[2-slope@255,9|19] +grade=4 +gradient=Opposite Diagonal +height=34 +reversed=true +stuckOnGround=false +width=36 + +[2-slope@256,-12|20] +grade=4 +gradient=Horizontal +height=25 +reversed=false +stuckOnGround=false +width=30 + +[2-slope@290,10|18] +grade=4 +gradient=Vertical +height=34 +reversed=true +stuckOnGround=false +width=111 + +[2-wall@0,0|10] +endPoint=298,169 +startPoint=278,188 + +[2-wall@0,0|11] +endPoint=181,200 +startPoint=191,188 + +[2-wall@0,0|15] +endPoint=179,178 +startPoint=191,186 + +[2-wall@0,0|18] +endPoint=278,20 +startPoint=115,20 + +[2-wall@0,0|5] +endPoint=180,270 +startPoint=3,272 + +[2-wall@0,0|6] +endPoint=179,127 +startPoint=180,270 + +[2-wall@0,0|7] +endPoint=191,188 +startPoint=278,188 + +[2-wall@0,0|8] +endPoint=298,169 +startPoint=298,103 + +[3-ball@55,341] +dummykey=true + +[3-blackhole@85,70|9] +exit=166,79 +exitDeg=269 +maxspeed=4 +minspeed=1 + +[3-cup@252,186|3] +dummykey=true + +[3-floater@87,154|13] +botWallVisible=true +endPoint=87,154 +height=14 +leftWallVisible=true +rightWallVisible=true +speed=8 +startPoint=29,78 +topWallVisible=true +width=15 + +[3-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=true +maxstrokes=10 +par=2 + +[3-sand@42,34|11] +changeEnabled=false +changeEvery=50 +height=50 +width=40 + +[3-sand@50,28|14] +changeEnabled=false +changeEvery=50 +height=26 +width=32 + +[3-sand@59,20|12] +changeEnabled=false +changeEvery=50 +height=28 +width=64 + +[3-sand@79,26|0] +changeEnabled=false +changeEvery=50 +height=28 +width=26 + +[3-slope@0,166|10] +grade=3 +gradient=Vertical +height=45 +reversed=false +stuckOnGround=false +width=134 + +[3-slope@136,74|4] +grade=4 +gradient=Horizontal +height=234 +reversed=true +stuckOnGround=true +width=205 + +[3-wall@0,0|6] +endPoint=338,308 +startPoint=132,307 + +[3-wall@0,0|7] +endPoint=132,307 +startPoint=136,75 + +[3-wall@0,0|8] +endPoint=343,77 +startPoint=338,308 + +[3-wall@0,0|9] +endPoint=343,77 +startPoint=136,75 + +[4-ball@125,336] +dummykey=true + +[4-cup@321,206|3] +dummykey=true + +[4-floater@286,190|4] +botWallVisible=true +endPoint=286,190 +height=40 +leftWallVisible=true +rightWallVisible=true +speed=19 +startPoint=78,89 +topWallVisible=false +width=80 + +[4-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=true +maxstrokes=10 +par=2 + +[4-puddle@-22,215|6] +changeEnabled=false +changeEvery=50 +height=228 +width=204 + +[4-slope@-1,280|9] +grade=4 +gradient=Diagonal +height=89 +reversed=true +stuckOnGround=false +width=102 + +[4-slope@-4,77|7] +grade=4 +gradient=Opposite Diagonal +height=74 +reversed=false +stuckOnGround=false +width=102 + +[4-slope@-81,6|5] +grade=4 +gradient=Opposite Diagonal +height=295 +reversed=true +stuckOnGround=true +width=482 + +[4-slope@37,150|8] +grade=4 +gradient=Horizontal +height=131 +reversed=true +stuckOnGround=false +width=60 + +[5-ball@106,356] +dummykey=true + +[5-blackhole@167,37|21] +exit=212,97 +exitDeg=339 +maxspeed=2 +minspeed=1 + +[5-bridge@156,148|17] +botWallVisible=true +height=23 +leftWallVisible=false +rightWallVisible=false +topWallVisible=true +width=46 + +[5-cup@237,229|3] +dummykey=true + +[5-cup@249,34|33] +dummykey=true + +[5-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[5-puddle@213,14|39] +changeEnabled=false +changeEvery=50 +height=44 +width=34 + +[5-puddle@218,15|15] +changeEnabled=false +changeEvery=50 +height=32 +width=30 + +[5-puddle@223,13|13] +changeEnabled=false +changeEvery=50 +height=26 +width=32 + +[5-puddle@248,1|37] +changeEnabled=false +changeEvery=50 +height=42 +width=76 + +[5-puddle@262,57|38] +changeEnabled=false +changeEvery=50 +height=22 +width=48 + +[5-puddle@271,59|30] +changeEnabled=false +changeEvery=50 +height=22 +width=38 + +[5-puddle@279,22|41] +changeEnabled=true +changeEvery=28 +height=50 +width=26 + +[5-sand@-20,411|36] +changeEnabled=false +changeEvery=50 +height=172 +width=274 + +[5-sand@-28,349|37] +changeEnabled=false +changeEvery=50 +height=312 +width=146 + +[5-sand@23,345|42] +changeEnabled=false +changeEvery=50 +height=46 +width=54 + +[5-sand@40,354|43] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[5-slope@156,152|19] +grade=4 +gradient=Horizontal +height=15 +reversed=true +stuckOnGround=false +width=48 + +[5-slope@203,195|34] +grade=2 +gradient=Vertical +height=23 +reversed=true +stuckOnGround=true +width=70 + +[5-slope@204,239|35] +grade=2 +gradient=Vertical +height=24 +reversed=false +stuckOnGround=true +width=68 + +[5-slope@284,-2|32] +grade=3 +gradient=Opposite Diagonal +height=140 +reversed=true +stuckOnGround=false +width=114 + +[5-slope@294,186|27] +grade=2 +gradient=Vertical +height=74 +reversed=true +stuckOnGround=true +width=36 + +[5-slope@295,301|31] +grade=8 +gradient=Diagonal +height=99 +reversed=false +stuckOnGround=false +width=106 + +[5-slope@56,225|19] +grade=4 +gradient=Vertical +height=58 +reversed=true +stuckOnGround=true +width=99 + +[5-slope@57,39|16] +grade=6 +gradient=Vertical +height=33 +reversed=false +stuckOnGround=true +width=97 + +[5-slope@58,112|17] +grade=4 +gradient=Vertical +height=40 +reversed=true +stuckOnGround=true +width=99 + +[5-slope@59,152|18] +grade=2 +gradient=Horizontal +height=10 +reversed=true +stuckOnGround=false +width=95 + +[5-slope@59,163|18] +grade=5 +gradient=Vertical +height=41 +reversed=false +stuckOnGround=true +width=95 + +[5-slope@61,23|20] +grade=2 +gradient=Horizontal +height=16 +reversed=true +stuckOnGround=true +width=118 + +[5-wall@0,0|10] +endPoint=330,96 +startPoint=330,362 + +[5-wall@0,0|11] +endPoint=330,362 +startPoint=201,316 + +[5-wall@0,0|12] +endPoint=201,316 +startPoint=202,170 + +[5-wall@0,0|14] +endPoint=156,170 +startPoint=154,318 + +[5-wall@0,0|22] +endPoint=293,175 +startPoint=202,148 + +[5-wall@0,0|23] +endPoint=293,175 +startPoint=294,286 + +[5-wall@0,0|24] +endPoint=294,286 +startPoint=264,301 + +[5-wall@0,0|25] +endPoint=270,263 +startPoint=270,188 + +[5-wall@0,0|26] +endPoint=270,188 +startPoint=202,170 + +[5-wall@0,0|28] +endPoint=157,54 +startPoint=185,53 + +[5-wall@0,0|29] +endPoint=185,53 +startPoint=182,21 + +[5-wall@0,0|4] +endPoint=57,21 +startPoint=57,321 + +[5-wall@0,0|5] +endPoint=57,21 +startPoint=182,21 + +[5-wall@0,0|6] +endPoint=157,54 +startPoint=156,148 + +[5-wall@0,0|8] +endPoint=202,148 +startPoint=200,56 + +[5-wall@0,0|9] +endPoint=200,56 +startPoint=330,96 + +[5-windmill@332,149|40] +botWallVisible=false +bottom=false +height=71 +leftWallVisible=false +rightWallVisible=false +speed=9 +topWallVisible=false +width=75 + +[6-ball@82,356] +dummykey=true + +[6-cup@258,84|3] +dummykey=true + +[6-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[6-puddle@200,8|6] +changeEnabled=false +changeEvery=50 +height=166 +width=88 + +[6-puddle@221,61|25] +changeEnabled=false +changeEvery=50 +height=24 +width=28 + +[6-puddle@229,53|2] +changeEnabled=false +changeEvery=50 +height=28 +width=30 + +[6-puddle@240,58|24] +changeEnabled=false +changeEvery=50 +height=14 +width=46 + +[6-puddle@256,23|8] +changeEnabled=false +changeEvery=50 +height=80 +width=150 + +[6-puddle@274,57|1] +changeEnabled=false +changeEvery=50 +height=16 +width=50 + +[6-puddle@283,55|0] +changeEnabled=false +changeEvery=50 +height=26 +width=32 + +[6-puddle@307,10|7] +changeEnabled=false +changeEvery=50 +height=166 +width=74 + +[6-sand@102,98|26] +changeEnabled=false +changeEvery=50 +height=46 +width=38 + +[6-sand@331,215|9] +changeEnabled=false +changeEvery=50 +height=62 +width=74 + +[6-sand@333,148|11] +changeEnabled=false +changeEvery=50 +height=36 +width=46 + +[6-sand@333,158|34] +changeEnabled=false +changeEvery=50 +height=20 +width=22 + +[6-sand@335,184|32] +changeEnabled=false +changeEvery=50 +height=24 +width=26 + +[6-sand@338,141|27] +changeEnabled=false +changeEvery=50 +height=38 +width=38 + +[6-sand@342,207|28] +changeEnabled=false +changeEvery=50 +height=66 +width=78 + +[6-sand@343,133|10] +changeEnabled=false +changeEvery=50 +height=54 +width=38 + +[6-sand@344,161|33] +changeEnabled=false +changeEvery=50 +height=32 +width=34 + +[6-sand@348,198|29] +changeEnabled=false +changeEvery=50 +height=54 +width=66 + +[6-sand@350,148|35] +changeEnabled=false +changeEvery=50 +height=28 +width=18 + +[6-sand@350,168|36] +changeEnabled=false +changeEvery=50 +height=32 +width=30 + +[6-sand@350,179|31] +changeEnabled=false +changeEvery=50 +height=42 +width=46 + +[6-sand@361,188|30] +changeEnabled=false +changeEvery=50 +height=52 +width=42 + +[6-sand@59,94|23] +changeEnabled=false +changeEvery=50 +height=94 +width=64 + +[6-sand@73,100|22] +changeEnabled=false +changeEvery=50 +height=94 +width=90 + +[6-sand@77,84|21] +changeEnabled=false +changeEvery=50 +height=84 +width=102 + +[6-slope@-1,115|4] +grade=3 +gradient=Vertical +height=74 +reversed=false +stuckOnGround=false +width=404 + +[6-slope@-2,215|5] +grade=3 +gradient=Vertical +height=81 +reversed=true +stuckOnGround=false +width=407 + +[6-slope@140,61|16] +grade=4 +gradient=Opposite Diagonal +height=43 +reversed=true +stuckOnGround=false +width=43 + +[6-slope@142,-1|17] +grade=4 +gradient=Horizontal +height=64 +reversed=false +stuckOnGround=false +width=36 + +[6-slope@182,79|14] +grade=4 +gradient=Vertical +height=26 +reversed=true +stuckOnGround=false +width=32 + +[6-slope@208,61|15] +grade=4 +gradient=Diagonal +height=46 +reversed=true +stuckOnGround=false +width=49 + +[6-slope@242,48|12] +grade=4 +gradient=Vertical +height=27 +reversed=true +stuckOnGround=false +width=29 + +[6-slope@270,64|13] +grade=4 +gradient=Opposite Diagonal +height=44 +reversed=true +stuckOnGround=false +width=30 + +[6-slope@299,59|18] +grade=4 +gradient=Vertical +height=48 +reversed=true +stuckOnGround=false +width=25 + +[6-slope@322,57|19] +grade=4 +gradient=Diagonal +height=50 +reversed=true +stuckOnGround=false +width=37 + +[6-slope@324,-2|20] +grade=4 +gradient=Horizontal +height=59 +reversed=true +stuckOnGround=false +width=35 + +[7-ball@53,349] +dummykey=true + +[7-blackhole@111,125|20] +exit=31,3 +exitDeg=270 +maxspeed=5 +minspeed=3 + +[7-blackhole@146,160|19] +exit=197,312 +exitDeg=90 +maxspeed=3 +minspeed=2 + +[7-blackhole@99,169|21] +exit=3,359 +exitDeg=0 +maxspeed=4 +minspeed=2 + +[7-cup@118,151|3] +dummykey=true + +[7-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[7-sand@306,70|11] +changeEnabled=false +changeEvery=50 +height=30 +width=36 + +[7-sand@307,45|10] +changeEnabled=false +changeEvery=50 +height=72 +width=78 + +[7-sand@322,78|9] +changeEnabled=false +changeEvery=50 +height=102 +width=50 + +[7-sand@330,103|8] +changeEnabled=false +changeEvery=50 +height=70 +width=74 + +[7-sand@331,56|13] +changeEnabled=false +changeEvery=50 +height=26 +width=28 + +[7-sand@339,84|12] +changeEnabled=false +changeEvery=50 +height=32 +width=30 + +[7-sand@40,238|16] +changeEnabled=false +changeEvery=50 +height=70 +width=56 + +[7-sand@49,222|18] +changeEnabled=false +changeEvery=50 +height=26 +width=38 + +[7-sand@53,257|19] +changeEnabled=false +changeEvery=50 +height=46 +width=64 + +[7-sand@56,243|15] +changeEnabled=false +changeEvery=50 +height=60 +width=54 + +[7-sand@62,239|17] +changeEnabled=false +changeEvery=50 +height=42 +width=52 + +[7-sand@69,254|14] +changeEnabled=false +changeEvery=50 +height=62 +width=70 + +[7-slope@159,316|23] +grade=4 +gradient=Horizontal +height=85 +reversed=true +stuckOnGround=true +width=46 + +[7-slope@242,228|7] +grade=4 +gradient=Diagonal +height=173 +reversed=false +stuckOnGround=true +width=158 + +[7-slope@43,12|18] +grade=2 +gradient=Elliptic +height=214 +reversed=false +stuckOnGround=true +width=214 + +[7-slope@86,317|22] +grade=4 +gradient=Horizontal +height=84 +reversed=false +stuckOnGround=true +width=48 + +[7-wall@0,0|6] +endPoint=248,316 +startPoint=-3,316 + +[8-ball@272,147] +dummykey=true + +[8-blackhole@296,38|9] +exit=254,371 +exitDeg=139 +maxspeed=5 +minspeed=3 + +[8-cup@296,64|3] +dummykey=true + +[8-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[8-puddle@345,350|12] +changeEnabled=false +changeEvery=50 +height=32 +width=52 + +[8-puddle@354,401|11] +changeEnabled=false +changeEvery=50 +height=124 +width=168 + +[8-puddle@400,8|30] +changeEnabled=false +changeEvery=50 +height=40 +width=34 + +[8-puddle@402,-8|30] +changeEnabled=false +changeEvery=50 +height=50 +width=56 + +[8-puddle@405,106|30] +changeEnabled=false +changeEvery=50 +height=78 +width=32 + +[8-puddle@405,135|30] +changeEnabled=false +changeEvery=50 +height=48 +width=20 + +[8-puddle@405,86|30] +changeEnabled=false +changeEvery=50 +height=64 +width=36 + +[8-puddle@411,49|30] +changeEnabled=false +changeEvery=50 +height=122 +width=62 + +[8-puddle@423,170|30] +changeEnabled=false +changeEvery=50 +height=154 +width=58 + +[8-puddle@447,406|10] +changeEnabled=false +changeEvery=50 +height=318 +width=268 + +[8-sand@144,184|15] +changeEnabled=false +changeEvery=50 +height=42 +width=32 + +[8-sand@145,176|14] +changeEnabled=false +changeEvery=50 +height=36 +width=30 + +[8-sand@75,139|35] +changeEnabled=false +changeEvery=50 +height=34 +width=34 + +[8-sand@77,135|13] +changeEnabled=false +changeEvery=50 +height=28 +width=38 + +[8-sand@81,138|28] +changeEnabled=false +changeEvery=50 +height=32 +width=30 + +[8-slope@188,16|7] +grade=2 +gradient=Horizontal +height=73 +reversed=true +stuckOnGround=false +width=81 + +[8-slope@214,112|23] +grade=4 +gradient=Elliptic +height=171 +reversed=false +stuckOnGround=false +width=171 + +[8-slope@253,11|22] +grade=3 +gradient=Elliptic +height=80 +reversed=false +stuckOnGround=false +width=80 + +[8-slope@317,14|8] +grade=3 +gradient=Horizontal +height=76 +reversed=true +stuckOnGround=false +width=53 + +[8-slope@41,253|41] +grade=4 +gradient=Opposite Diagonal +height=132 +reversed=false +stuckOnGround=false +width=145 + +[8-slope@45,89|34] +grade=3 +gradient=Vertical +height=142 +reversed=true +stuckOnGround=false +width=132 + +[8-wall@0,0|10] +endPoint=379,154 +startPoint=364,135 + +[8-wall@0,0|11] +endPoint=364,135 +startPoint=346,123 + +[8-wall@0,0|12] +endPoint=346,123 +startPoint=324,111 + +[8-wall@0,0|13] +endPoint=324,111 +startPoint=292,109 + +[8-wall@0,0|14] +endPoint=292,109 +startPoint=262,116 + +[8-wall@0,0|15] +endPoint=262,116 +startPoint=243,128 + +[8-wall@0,0|16] +endPoint=243,128 +startPoint=225,144 + +[8-wall@0,0|17] +endPoint=225,144 +startPoint=218,158 + +[8-wall@0,0|18] +endPoint=218,158 +startPoint=212,180 + +[8-wall@0,0|19] +endPoint=212,180 +startPoint=211,202 + +[8-wall@0,0|2] +endPoint=41,388 +startPoint=246,386 + +[8-wall@0,0|20] +endPoint=211,202 +startPoint=218,229 + +[8-wall@0,0|21] +endPoint=218,229 +startPoint=231,250 + +[8-wall@0,0|22] +endPoint=231,250 +startPoint=246,264 + +[8-wall@0,0|24] +endPoint=384,218 +startPoint=378,238 + +[8-wall@0,0|25] +endPoint=378,238 +startPoint=368,256 + +[8-wall@0,0|26] +endPoint=368,256 +startPoint=351,268 + +[8-wall@0,0|27] +endPoint=256,274 +startPoint=246,264 + +[8-wall@0,0|29] +endPoint=369,90 +startPoint=177,89 + +[8-wall@0,0|3] +endPoint=247,289 +startPoint=224,294 + +[8-wall@0,0|31] +endPoint=369,14 +startPoint=41,14 + +[8-wall@0,0|32] +endPoint=41,14 +startPoint=41,388 + +[8-wall@0,0|33] +endPoint=246,386 +startPoint=351,268 + +[8-wall@0,0|34] +endPoint=175,256 +startPoint=177,89 + +[8-wall@0,0|4] +endPoint=224,294 +startPoint=203,287 + +[8-wall@0,0|45] +endPoint=247,289 +startPoint=256,274 + +[8-wall@0,0|5] +endPoint=203,287 +startPoint=186,275 + +[8-wall@0,0|6] +endPoint=186,275 +startPoint=175,256 + +[8-wall@0,0|7] +endPoint=384,218 +startPoint=387,196 + +[8-wall@0,0|8] +endPoint=387,196 +startPoint=384,176 + +[8-wall@0,0|9] +endPoint=384,176 +startPoint=379,154 + +[9-ball@25,24] +dummykey=true + +[9-blackhole@118,60|0] +exit=269,90 +exitDeg=270 +maxspeed=5 +minspeed=5 + +[9-blackhole@149,72|7] +exit=364,381 +exitDeg=120 +maxspeed=8 +minspeed=6 + +[9-blackhole@273,274|18] +exit=116,237 +exitDeg=20 +maxspeed=5 +minspeed=3 + +[9-blackhole@311,345|4] +exit=196,54 +exitDeg=169 +maxspeed=3 +minspeed=3 + +[9-blackhole@68,112|12] +exit=373,150 +exitDeg=269 +maxspeed=5 +minspeed=4 + +[9-blackhole@81,56|8] +exit=385,373 +exitDeg=160 +maxspeed=8 +minspeed=6 + +[9-bridge@125,329|0] +botWallVisible=true +height=38 +leftWallVisible=false +rightWallVisible=false +topWallVisible=true +width=195 + +[9-bridge@191,260|9] +botWallVisible=false +height=36 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=32 + +[9-bridge@319,329|0] +botWallVisible=false +height=36 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=30 + +[9-bridge@323,138|0] +botWallVisible=false +height=192 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=27 + +[9-bumper@129,201|8] +dummykey=true + +[9-bumper@152,151|4] +dummykey=true + +[9-bumper@374,137|14] +dummykey=true + +[9-cup@146,175|17] +dummykey=true + +[9-cup@156,193|18] +dummykey=true + +[9-cup@168,176|3] +dummykey=true + +[9-cup@207,272|9] +dummykey=true + +[9-floater@54,43|21] +botWallVisible=false +endPoint=54,43 +height=1 +leftWallVisible=false +rightWallVisible=false +speed=4 +startPoint=42,69 +topWallVisible=true +width=19 + +[9-floater@80,296|9] +botWallVisible=false +endPoint=80,296 +height=28 +leftWallVisible=false +rightWallVisible=true +speed=2 +startPoint=191,299 +topWallVisible=false +width=30 + +[9-floater@82,99|0] +botWallVisible=false +endPoint=82,99 +height=1 +leftWallVisible=false +rightWallVisible=false +speed=9 +startPoint=49,98 +topWallVisible=true +width=10 + +[9-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=2 + +[9-puddle@179,312|33] +changeEnabled=false +changeEvery=50 +height=70 +width=22 + +[9-puddle@193,296|32] +changeEnabled=false +changeEvery=50 +height=82 +width=60 + +[9-puddle@217,292|0] +changeEnabled=false +changeEvery=50 +height=94 +width=114 + +[9-puddle@256,317|34] +changeEnabled=false +changeEvery=50 +height=78 +width=38 + +[9-puddle@257,325|35] +changeEnabled=false +changeEvery=50 +height=42 +width=42 + +[9-puddle@265,297|37] +changeEnabled=false +changeEvery=50 +height=58 +width=22 + +[9-puddle@266,333|36] +changeEnabled=false +changeEvery=50 +height=28 +width=32 + +[9-puddle@65,133|30] +changeEnabled=false +changeEvery=50 +height=30 +width=22 + +[9-puddle@73,140|29] +changeEnabled=false +changeEvery=50 +height=36 +width=32 + +[9-puddle@75,129|13] +changeEnabled=false +changeEvery=50 +height=30 +width=48 + +[9-puddle@79,142|31] +changeEnabled=false +changeEvery=50 +height=22 +width=26 + +[9-puddle@84,133|28] +changeEnabled=false +changeEvery=50 +height=32 +width=32 + +[9-sand@100,369|27] +changeEnabled=false +changeEvery=50 +height=26 +width=52 + +[9-sand@108,343|24] +changeEnabled=false +changeEvery=50 +height=60 +width=96 + +[9-sand@130,350|7] +changeEnabled=false +changeEvery=50 +height=62 +width=90 + +[9-sand@83,363|26] +changeEnabled=false +changeEvery=50 +height=56 +width=62 + +[9-sand@91,353|25] +changeEnabled=false +changeEvery=50 +height=66 +width=66 + +[9-sign@146,329|23] +Comment=Chaos +Comment[ar]=Ùوضى +Comment[bg]=Ð¥Ð°Ð¾Ñ +Comment[bn]=গোলমাল +Comment[bs]=Haos +Comment[ca]=Caos +Comment[cy]=Anhrefn +Comment[da]=Kaos +Comment[el]=Χάος +Comment[es]=Caos +Comment[et]=Kaos +Comment[fi]=Kaaos +Comment[gl]=Caos +Comment[he]=תוהו ובוהו +Comment[hi]=अवà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾ +Comment[hr]=Kaos +Comment[hu]=Káosz +Comment[is]=Kaos +Comment[it]=Caos +Comment[ja]=カオス +Comment[mk]=Ð¥Ð°Ð¾Ñ +Comment[nb]=Kaos +Comment[nn]=Kaos +Comment[pt]=Caos +Comment[pt_BR]=Caos +Comment[ro]=Haos +Comment[ru]=Ð¥Ð°Ð¾Ñ +Comment[sl]=Kaos +Comment[sr]=Ð¥Ð°Ð¾Ñ +Comment[sr@Latn]=Haos +Comment[sv]=Kaos +Comment[ta]= கà¯à®´à®ªà¯à®ªà®®à¯ +Comment[tg]=БеÑаруÑомонӣ +Comment[tr]=KarmaÅŸa +Comment[uk]=Ð¥Ð°Ð¾Ñ +Comment[ven]=Thaidzo +Comment[xh]=Ingxwaba ngxwaba +Comment[xx]=xxChaosxx +Comment[zh_CN]=混沌 +Comment[zh_TW]=混沌 +Comment[zu]=Inyakanyaka +botWallVisible=true +height=38 +leftWallVisible=false +rightWallVisible=false +topWallVisible=true +width=151 + +[9-slope@11,355|22] +grade=4 +gradient=Horizontal +height=32 +reversed=true +stuckOnGround=false +width=35 + +[9-slope@12,325|16] +grade=4 +gradient=Opposite Diagonal +height=31 +reversed=false +stuckOnGround=false +width=33 + +[9-slope@127,48|12] +grade=4 +gradient=Elliptic +height=45 +reversed=true +stuckOnGround=false +width=45 + +[9-slope@320,43|3] +grade=7 +gradient=Horizontal +height=41 +reversed=false +stuckOnGround=false +width=32 + +[9-slope@321,83|8] +grade=4 +gradient=Horizontal +height=53 +reversed=false +stuckOnGround=false +width=30 + +[9-slope@326,9|17] +grade=7 +gradient=Opposite Diagonal +height=72 +reversed=true +stuckOnGround=false +width=62 + +[9-slope@50,94|15] +grade=5 +gradient=Elliptic +height=35 +reversed=true +stuckOnGround=false +width=35 + +[9-slope@93,119|9] +grade=8 +gradient=Elliptic +height=125 +reversed=true +stuckOnGround=true +width=125 + +[9-wall@0,0|0] +endPoint=350,330 +startPoint=396,361 + +[9-wall@0,0|1] +endPoint=350,394 +startPoint=320,356 + +[9-wall@0,0|10] +endPoint=225,151 +startPoint=205,239 + +[9-wall@0,0|11] +endPoint=225,151 +startPoint=147,108 + +[9-wall@0,0|12] +endPoint=147,108 +startPoint=77,170 + +[9-wall@0,0|13] +endPoint=77,170 +startPoint=118,254 + +[9-wall@0,0|16] +endPoint=205,239 +startPoint=118,254 + +[9-wall@0,0|18] +endPoint=122,83 +startPoint=82,99 + +[9-wall@0,0|19] +endPoint=352,43 +startPoint=73,43 + +[9-wall@0,0|20] +endPoint=49,98 +startPoint=46,352 + +[9-wall@0,0|3] +endPoint=392,85 +startPoint=177,85 + +[9-wall@0,0|38] +endPoint=177,85 +startPoint=158,93 + +[9-wall@0,0|39] +endPoint=158,93 +startPoint=136,93 + +[9-wall@0,0|40] +endPoint=136,93 +startPoint=122,83 + +[9-windmill@235,171|3] +botWallVisible=true +bottom=false +height=119 +leftWallVisible=true +rightWallVisible=true +speed=2 +topWallVisible=false +width=75 diff --git a/kolf/courses/Makefile.am b/kolf/courses/Makefile.am new file mode 100644 index 00000000..d9953953 --- /dev/null +++ b/kolf/courses/Makefile.am @@ -0,0 +1,4 @@ +coursedir = $(kde_datadir)/kolf/courses +course_DATA = Classic.kolf Hard.kolf Medium.kolf Easy.kolf ReallyEasy Practice Impossible USApro + +EXTRA_DIST=$(course_DATA) diff --git a/kolf/courses/Medium.kolf b/kolf/courses/Medium.kolf new file mode 100644 index 00000000..b232a4d7 --- /dev/null +++ b/kolf/courses/Medium.kolf @@ -0,0 +1,2557 @@ +[0-course@-50,-50] +Name=Medium Course +Name[af]=Medium Natuurlik +Name[bg]=Средно +Name[bn]=মধà§à¦¯à¦® কোরà§à¦¸ +Name[bs]=Srednji teren +Name[ca]=Camp medi +Name[cs]=StÅ™ední kurz +Name[da]=Middelsvær bane +Name[de]=Mittel +Name[el]=ΜέτÏια πίστα +Name[es]=Campo medio +Name[et]=Keskmiselt raske väljak +Name[fi]=Keskivaikea kenttä +Name[fr]=Parcours moyen +Name[gl]=Campo medio +Name[he]=מסלול בינוני +Name[hi]=साधारण कोरà¥à¤¸ +Name[hu]=Közepes nehézségű pálya +Name[is]=Meðal erfið leið +Name[it]=Percorso medio +Name[ja]=普通ã®ã‚³ãƒ¼ã‚¹ +Name[mk]=Среден терен +Name[nb]=Middels vanskelig bane +Name[nl]=Gemiddeld parcours +Name[nn]=Middels vanskeleg bane +Name[nso]=Course ya Magareng +Name[pl]=Åšredni tor +Name[pt]=Percurso Médio +Name[pt_BR]=Curso Médio +Name[ro]=Cale medie +Name[ru]=СреднÑÑ Ð¿Ð»Ð¾Ñ‰Ð°Ð´ÐºÐ° +Name[sk]=Stredne Å¥ažké ihrisko +Name[sl]=Srednje igriÅ¡Äe +Name[sr]=Средњи терен +Name[sr@Latn]=Srednji teren +Name[sv]=Normal bana +Name[ta]=நடà¯à®¤à¯à®¤à®°à®®à®¾à®© மாரà¯à®•à¯à®•à®®à¯ +Name[tg]=Майдони Миёна +Name[tr]=Orta Seviye Pist +Name[uk]=Середній майданчик +Name[ven]= Thero yo Linganelaho +Name[xh]=Indlela ephakathi +Name[xx]=xxMedium Coursexx +Name[zh_CN]=中级路线 +Name[zh_TW]=中等路線 +Name[zu]=Indlela ephakathi +author=Jason Katz-Brown +name=Medium Course + +[1-ball@342,322] +dummykey=true + +[1-cup@325,52|14] +dummykey=true + +[1-floater@100,208|24] +botWallVisible=true +endPoint=100,208 +height=10 +leftWallVisible=true +rightWallVisible=true +speed=2 +startPoint=16,189 +topWallVisible=true +width=19 + +[1-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=3 + +[1-puddle@282,195|9] +changeEnabled=false +changeEvery=50 +height=38 +width=66 + +[1-puddle@350,189|8] +changeEnabled=false +changeEvery=50 +height=40 +width=192 + +[1-puddle@378,206|11] +changeEnabled=false +changeEvery=50 +height=32 +width=232 + +[1-slope@177,86|16] +grade=4 +gradient=Diagonal +height=115 +reversed=false +stuckOnGround=false +width=54 + +[1-slope@193,240|22] +grade=4 +gradient=Diagonal +height=148 +reversed=false +stuckOnGround=false +width=194 + +[1-slope@231,90|15] +grade=4 +gradient=Vertical +height=108 +reversed=false +stuckOnGround=false +width=156 + +[1-slope@291,17|19] +grade=2 +gradient=Elliptic +height=67 +reversed=true +stuckOnGround=false +width=67 + +[1-wall@0,0|13] +endPoint=208,200 +startPoint=180,199 + +[1-wall@0,0|17] +endPoint=180,199 +startPoint=148,213 + +[1-wall@0,0|21] +endPoint=389,236 +startPoint=307,234 + +[1-wall@0,0|22] +endPoint=307,234 +startPoint=255,218 + +[1-wall@0,0|23] +endPoint=255,218 +startPoint=229,215 + +[1-wall@0,0|24] +endPoint=208,200 +startPoint=229,215 + +[1-wall@0,0|3] +endPoint=103,388 +startPoint=11,304 + +[1-wall@0,0|4] +endPoint=126,11 +startPoint=10,128 + +[10-ball@343,254] +dummykey=true + +[10-bumper@114,160|14] +dummykey=true + +[10-bumper@151,79|11] +dummykey=true + +[10-bumper@166,233|9] +dummykey=true + +[10-bumper@179,304|15] +dummykey=true + +[10-bumper@258,99|20] +dummykey=true + +[10-bumper@356,86|19] +dummykey=true + +[10-bumper@43,206|13] +dummykey=true + +[10-bumper@54,90|10] +dummykey=true + +[10-bumper@67,284|12] +dummykey=true + +[10-cup@310,114|16] +dummykey=true + +[10-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[10-slope@11,11|7] +grade=4 +gradient=Vertical +height=47 +reversed=true +stuckOnGround=false +width=379 + +[10-slope@11,121|3] +grade=4 +gradient=Vertical +height=217 +reversed=false +stuckOnGround=false +width=213 + +[10-slope@221,118|6] +grade=2 +gradient=Opposite Diagonal +height=63 +reversed=false +stuckOnGround=false +width=63 + +[10-wall@0,0|17] +endPoint=390,148 +startPoint=354,183 + +[10-wall@0,0|18] +endPoint=355,182 +startPoint=388,251 + +[10-wall@0,0|4] +endPoint=223,335 +startPoint=223,182 + +[10-wall@0,0|5] +endPoint=354,183 +startPoint=223,182 + +[10-wall@0,0|8] +endPoint=388,251 +startPoint=309,389 + +[11-ball@36,295] +dummykey=true + +[11-cup@74,372|72] +dummykey=true + +[11-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[11-sand@270,47|71] +changeEnabled=false +changeEvery=50 +height=40 +width=50 + +[11-sand@279,52|70] +changeEnabled=false +changeEvery=50 +height=46 +width=42 + +[11-slope@12,103|73] +grade=6 +gradient=Opposite Diagonal +height=40 +reversed=false +stuckOnGround=false +width=40 + +[11-slope@121,106|62] +grade=1 +gradient=Elliptic +height=222 +reversed=true +stuckOnGround=false +width=222 + +[11-slope@31,91|73] +grade=6 +gradient=Opposite Diagonal +height=41 +reversed=false +stuckOnGround=false +width=35 + +[11-slope@47,225|59] +grade=6 +gradient=Vertical +height=24 +reversed=false +stuckOnGround=false +width=37 + +[11-slope@50,88|61] +grade=6 +gradient=Horizontal +height=42 +reversed=true +stuckOnGround=false +width=36 + +[11-slope@55,352|74] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[11-slope@8,153|60] +grade=6 +gradient=Vertical +height=28 +reversed=false +stuckOnGround=false +width=48 + +[11-wall@0,0|10] +endPoint=168,354 +startPoint=132,383 + +[11-wall@0,0|11] +endPoint=193,351 +startPoint=168,354 + +[11-wall@0,0|12] +endPoint=239,335 +startPoint=193,351 + +[11-wall@0,0|13] +endPoint=276,340 +startPoint=239,335 + +[11-wall@0,0|14] +endPoint=311,328 +startPoint=276,340 + +[11-wall@0,0|15] +endPoint=335,299 +startPoint=311,328 + +[11-wall@0,0|16] +endPoint=345,270 +startPoint=335,299 + +[11-wall@0,0|17] +endPoint=358,234 +startPoint=345,270 + +[11-wall@0,0|18] +endPoint=374,200 +startPoint=358,234 + +[11-wall@0,0|19] +endPoint=374,200 +startPoint=370,166 + +[11-wall@0,0|20] +endPoint=370,166 +startPoint=358,136 + +[11-wall@0,0|21] +endPoint=358,136 +startPoint=343,109 + +[11-wall@0,0|22] +endPoint=343,109 +startPoint=340,67 + +[11-wall@0,0|23] +endPoint=166,92 +startPoint=168,109 + +[11-wall@0,0|24] +endPoint=166,92 +startPoint=153,82 + +[11-wall@0,0|25] +endPoint=153,82 +startPoint=112,94 + +[11-wall@0,0|26] +endPoint=112,94 +startPoint=80,119 + +[11-wall@0,0|27] +endPoint=80,119 +startPoint=55,128 + +[11-wall@0,0|28] +endPoint=55,128 +startPoint=44,147 + +[11-wall@0,0|29] +endPoint=55,180 +startPoint=44,147 + +[11-wall@0,0|3] +endPoint=159,119 +startPoint=139,125 + +[11-wall@0,0|30] +endPoint=74,196 +startPoint=55,180 + +[11-wall@0,0|32] +endPoint=74,196 +startPoint=86,265 + +[11-wall@0,0|33] +endPoint=86,265 +startPoint=80,290 + +[11-wall@0,0|34] +endPoint=80,290 +startPoint=58,312 + +[11-wall@0,0|35] +endPoint=58,312 +startPoint=31,324 + +[11-wall@0,0|36] +endPoint=168,109 +startPoint=159,119 + +[11-wall@0,0|37] +endPoint=16,318 +startPoint=31,324 + +[11-wall@0,0|38] +endPoint=7,298 +startPoint=16,318 + +[11-wall@0,0|39] +endPoint=17,280 +startPoint=7,298 + +[11-wall@0,0|4] +endPoint=96,273 +startPoint=99,233 + +[11-wall@0,0|40] +endPoint=41,270 +startPoint=17,280 + +[11-wall@0,0|41] +endPoint=51,250 +startPoint=41,270 + +[11-wall@0,0|42] +endPoint=46,234 +startPoint=51,250 + +[11-wall@0,0|43] +endPoint=38,216 +startPoint=46,234 + +[11-wall@0,0|44] +endPoint=21,193 +startPoint=38,216 + +[11-wall@0,0|45] +endPoint=7,164 +startPoint=21,193 + +[11-wall@0,0|46] +endPoint=9,128 +startPoint=7,164 + +[11-wall@0,0|47] +endPoint=26,105 +startPoint=9,128 + +[11-wall@0,0|48] +endPoint=60,89 +startPoint=26,105 + +[11-wall@0,0|49] +endPoint=88,70 +startPoint=60,89 + +[11-wall@0,0|5] +endPoint=112,204 +startPoint=109,160 + +[11-wall@0,0|50] +endPoint=129,47 +startPoint=88,70 + +[11-wall@0,0|51] +endPoint=158,15 +startPoint=129,47 + +[11-wall@0,0|52] +endPoint=177,7 +startPoint=158,15 + +[11-wall@0,0|53] +endPoint=203,7 +startPoint=177,7 + +[11-wall@0,0|54] +endPoint=203,7 +startPoint=244,16 + +[11-wall@0,0|55] +endPoint=279,16 +startPoint=244,16 + +[11-wall@0,0|56] +endPoint=305,19 +startPoint=279,16 + +[11-wall@0,0|57] +endPoint=326,35 +startPoint=305,19 + +[11-wall@0,0|58] +endPoint=326,35 +startPoint=340,67 + +[11-wall@0,0|6] +endPoint=139,125 +startPoint=109,160 + +[11-wall@0,0|63] +endPoint=88,336 +startPoint=64,342 + +[11-wall@0,0|64] +endPoint=23,369 +startPoint=34,357 + +[11-wall@0,0|65] +endPoint=64,342 +startPoint=34,357 + +[11-wall@0,0|66] +endPoint=27,384 +startPoint=23,369 + +[11-wall@0,0|67] +endPoint=55,395 +startPoint=27,384 + +[11-wall@0,0|68] +endPoint=103,391 +startPoint=55,395 + +[11-wall@0,0|69] +endPoint=132,383 +startPoint=103,391 + +[11-wall@0,0|7] +endPoint=112,204 +startPoint=99,233 + +[11-wall@0,0|8] +endPoint=100,309 +startPoint=96,273 + +[11-wall@0,0|9] +endPoint=88,336 +startPoint=100,309 + +[12-ball@47,53] +dummykey=true + +[12-blackhole@375,294|12] +exit=137,36 +exitDeg=30 +maxspeed=5 +minspeed=3 + +[12-bumper@189,94|17] +dummykey=true + +[12-cup@216,137|14] +dummykey=true + +[12-cup@374,244|8] +dummykey=true + +[12-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[12-sand@258,131|15] +changeEnabled=false +changeEvery=50 +height=54 +width=42 + +[12-sand@260,123|16] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[12-slope@10,166|3] +grade=4 +gradient=Vertical +height=221 +reversed=false +stuckOnGround=false +width=381 + +[12-slope@238,7|13] +grade=4 +gradient=Opposite Diagonal +height=124 +reversed=true +stuckOnGround=false +width=150 + +[12-wall@0,0|4] +endPoint=387,219 +startPoint=290,274 + +[12-wall@0,0|5] +endPoint=389,266 +startPoint=284,324 + +[12-wall@0,0|6] +endPoint=117,12 +startPoint=200,165 + +[12-wall@0,0|7] +endPoint=386,165 +startPoint=200,165 + +[13-ball@332,39] +dummykey=true + +[13-cup@44,365|9] +dummykey=true + +[13-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[13-puddle@2,77|18] +changeEnabled=false +changeEvery=50 +height=86 +width=138 + +[13-puddle@33,3|17] +changeEnabled=false +changeEvery=50 +height=134 +width=310 + +[13-puddle@49,53|16] +changeEnabled=false +changeEvery=50 +height=64 +width=98 + +[13-sand@352,347|10] +changeEnabled=false +changeEvery=50 +height=72 +width=104 + +[13-sand@361,324|17] +changeEnabled=false +changeEvery=50 +height=34 +width=56 + +[13-sand@370,408|11] +changeEnabled=false +changeEvery=50 +height=172 +width=290 + +[13-sand@374,321|18] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[13-sand@389,336|13] +changeEnabled=false +changeEvery=50 +height=98 +width=62 + +[13-slope@11,232|23] +grade=1 +gradient=Opposite Diagonal +height=155 +reversed=false +stuckOnGround=false +width=165 + +[13-slope@128,111|5] +grade=4 +gradient=Elliptic +height=94 +reversed=true +stuckOnGround=false +width=94 + +[13-slope@177,207|6] +grade=3 +gradient=Elliptic +height=113 +reversed=false +stuckOnGround=false +width=113 + +[13-slope@235,79|7] +grade=5 +gradient=Elliptic +height=83 +reversed=false +stuckOnGround=false +width=83 + +[13-slope@282,173|8] +grade=4 +gradient=Elliptic +height=65 +reversed=true +stuckOnGround=false +width=65 + +[13-slope@47,105|4] +grade=3 +gradient=Elliptic +height=70 +reversed=false +stuckOnGround=false +width=70 + +[13-slope@72,198|3] +grade=6 +gradient=Elliptic +height=96 +reversed=false +stuckOnGround=false +width=96 + +[14-ball@154,39] +dummykey=true + +[14-blackhole@236,51|5] +exit=384,316 +exitDeg=180 +maxspeed=4 +minspeed=1 + +[14-cup@55,38|8] +dummykey=true + +[14-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[14-slope@10,160|9] +grade=4 +gradient=Opposite Diagonal +height=227 +reversed=false +stuckOnGround=false +width=243 + +[14-slope@257,12|11] +grade=2 +gradient=Opposite Diagonal +height=91 +reversed=true +stuckOnGround=false +width=130 + +[14-slope@31,67|10] +grade=1 +gradient=Elliptic +height=81 +reversed=false +stuckOnGround=false +width=81 + +[14-wall@0,0|4] +endPoint=387,251 +startPoint=174,251 + +[14-wall@0,0|6] +endPoint=174,251 +startPoint=115,199 + +[14-wall@0,0|7] +endPoint=115,199 +startPoint=115,9 + +[14-windmill@192,12|3] +botWallVisible=false +height=166 +leftWallVisible=true +rightWallVisible=false +speed=8 +topWallVisible=false +width=195 + +[15-ball@358,361] +dummykey=true + +[15-cup@43,43|4] +dummykey=true + +[15-floater@180,204|3] +botWallVisible=false +endPoint=180,204 +height=118 +leftWallVisible=false +rightWallVisible=false +speed=5 +startPoint=40,103 +topWallVisible=false +width=149 + +[15-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=2 + +[15-puddle@176,217|8] +changeEnabled=false +changeEvery=50 +height=134 +width=190 + +[15-puddle@192,185|10] +changeEnabled=false +changeEvery=50 +height=136 +width=168 + +[15-puddle@210,196|9] +changeEnabled=false +changeEvery=50 +height=154 +width=132 + +[15-slope@18,274|5] +grade=3 +gradient=Elliptic +height=101 +reversed=false +stuckOnGround=true +width=101 + +[15-slope@93,16|6] +grade=7 +gradient=Elliptic +height=83 +reversed=false +stuckOnGround=true +width=83 + +[15-wall@0,0|34] +endPoint=255,11 +startPoint=391,102 + +[16-ball@214,366] +dummykey=true + +[16-cup@213,190|41] +dummykey=true + +[16-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=4 + +[16-puddle@116,184|40] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[16-puddle@117,179|39] +changeEnabled=false +changeEvery=50 +height=36 +width=34 + +[16-puddle@127,161|37] +changeEnabled=false +changeEvery=50 +height=62 +width=48 + +[16-puddle@140,140|33] +changeEnabled=false +changeEvery=50 +height=46 +width=54 + +[16-puddle@158,378|25] +changeEnabled=false +changeEvery=50 +height=100 +width=58 + +[16-puddle@165,328|27] +changeEnabled=false +changeEvery=50 +height=92 +width=58 + +[16-puddle@187,296|23] +changeEnabled=false +changeEvery=50 +height=72 +width=72 + +[16-puddle@196,297|26] +changeEnabled=false +changeEvery=50 +height=60 +width=98 + +[16-puddle@217,270|24] +changeEnabled=false +changeEvery=50 +height=40 +width=88 + +[16-puddle@220,124|32] +changeEnabled=false +changeEvery=50 +height=44 +width=190 + +[16-puddle@237,289|22] +changeEnabled=false +changeEvery=50 +height=46 +width=40 + +[16-puddle@238,269|21] +changeEnabled=false +changeEvery=50 +height=74 +width=70 + +[16-puddle@257,257|28] +changeEnabled=false +changeEvery=50 +height=54 +width=82 + +[16-puddle@262,134|34] +changeEnabled=false +changeEvery=50 +height=26 +width=34 + +[16-puddle@275,253|41] +changeEnabled=false +changeEvery=50 +height=52 +width=84 + +[16-puddle@283,136|35] +changeEnabled=false +changeEvery=50 +height=48 +width=48 + +[16-puddle@291,238|31] +changeEnabled=false +changeEvery=50 +height=56 +width=58 + +[16-puddle@294,156|36] +changeEnabled=false +changeEvery=50 +height=34 +width=34 + +[16-puddle@309,170|29] +changeEnabled=false +changeEvery=50 +height=104 +width=46 + +[16-puddle@310,202|30] +changeEnabled=false +changeEvery=50 +height=88 +width=42 + +[16-sand@-2,322|58] +changeEnabled=false +changeEvery=50 +height=72 +width=94 + +[16-sand@31,334|41] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[16-sand@41,399|59] +changeEnabled=false +changeEvery=50 +height=150 +width=196 + +[16-slope@11,12|45] +grade=5 +gradient=Diagonal +height=123 +reversed=true +stuckOnGround=false +width=179 + +[16-slope@129,64|50] +grade=4 +gradient=Vertical +height=69 +reversed=false +stuckOnGround=false +width=171 + +[16-slope@174,261|54] +grade=4 +gradient=Vertical +height=48 +reversed=true +stuckOnGround=false +width=137 + +[16-slope@178,153|53] +grade=2 +gradient=Elliptic +height=74 +reversed=true +stuckOnGround=false +width=74 + +[16-slope@278,257|54] +grade=7 +gradient=Diagonal +height=129 +reversed=false +stuckOnGround=false +width=110 + +[16-slope@299,64|51] +grade=4 +gradient=Opposite Diagonal +height=69 +reversed=false +stuckOnGround=false +width=55 + +[16-slope@309,263|53] +grade=4 +gradient=Diagonal +height=42 +reversed=true +stuckOnGround=false +width=48 + +[16-slope@311,133|52] +grade=4 +gradient=Horizontal +height=130 +reversed=true +stuckOnGround=false +width=44 + +[16-slope@87,65|55] +grade=4 +gradient=Diagonal +height=73 +reversed=false +stuckOnGround=false +width=42 + +[16-slope@89,136|38] +grade=4 +gradient=Vertical +height=40 +reversed=false +stuckOnGround=false +width=40 + +[16-wall@0,0|42] +endPoint=177,152 +startPoint=154,181 + +[16-wall@0,0|43] +endPoint=278,188 +startPoint=269,165 + +[16-wall@0,0|44] +endPoint=269,165 +startPoint=244,155 + +[16-wall@0,0|52] +endPoint=200,239 +startPoint=161,256 + +[16-wall@0,0|56] +endPoint=387,77 +startPoint=339,14 + +[17-ball@44,230] +dummykey=true + +[17-cup@307,67|3] +dummykey=true + +[17-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[17-slope@131,218|12] +grade=2 +gradient=Elliptic +height=156 +reversed=true +stuckOnGround=false +width=156 + +[17-slope@32,8|4] +grade=1 +gradient=Opposite Diagonal +height=349 +reversed=true +stuckOnGround=false +width=356 + +[17-slope@58,127|11] +grade=3 +gradient=Elliptic +height=81 +reversed=false +stuckOnGround=false +width=81 + +[17-wall@0,0|10] +endPoint=323,221 +startPoint=260,219 + +[17-wall@0,0|13] +endPoint=296,81 +startPoint=278,12 + +[17-wall@0,0|14] +endPoint=335,85 +startPoint=296,81 + +[17-wall@0,0|5] +endPoint=185,40 +startPoint=190,111 + +[17-wall@0,0|6] +endPoint=236,121 +startPoint=190,111 + +[17-wall@0,0|7] +endPoint=304,135 +startPoint=313,164 + +[17-wall@0,0|8] +endPoint=389,183 +startPoint=313,164 + +[17-wall@0,0|9] +endPoint=260,219 +startPoint=247,173 + +[18-ball@246,341] +dummykey=true + +[18-cup@50,355|3] +dummykey=true + +[18-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[18-slope@-183,-169|5] +grade=2 +gradient=Elliptic +height=400 +reversed=false +stuckOnGround=false +width=400 + +[18-slope@11,297|7] +grade=3 +gradient=Vertical +height=91 +reversed=false +stuckOnGround=false +width=163 + +[18-slope@4,10|8] +grade=2 +gradient=Elliptic +height=125 +reversed=true +stuckOnGround=false +width=125 + +[18-windmill@9,179|6] +botWallVisible=false +bottom=false +height=212 +leftWallVisible=false +rightWallVisible=true +speed=7 +topWallVisible=false +width=166 + +[2-ball@68,357] +dummykey=true + +[2-blackhole@100,268|14] +exit=19,50 +exitDeg=0 +maxspeed=6 +minspeed=3 + +[2-cup@277,370|59] +dummykey=true + +[2-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[2-puddle@133,167|39] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[2-puddle@144,202|32] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[2-puddle@145,190|34] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[2-puddle@163,163|53] +changeEnabled=false +changeEvery=50 +height=40 +width=88 + +[2-puddle@170,203|40] +changeEnabled=false +changeEvery=50 +height=52 +width=62 + +[2-puddle@182,219|56] +changeEnabled=false +changeEvery=50 +height=44 +width=42 + +[2-puddle@184,190|43] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[2-puddle@188,233|36] +changeEnabled=false +changeEvery=50 +height=44 +width=34 + +[2-puddle@190,366|38] +changeEnabled=false +changeEvery=50 +height=60 +width=34 + +[2-puddle@192,387|44] +changeEnabled=false +changeEvery=50 +height=72 +width=54 + +[2-puddle@194,288|45] +changeEnabled=false +changeEvery=50 +height=272 +width=42 + +[2-puddle@195,212|51] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[2-puddle@204,149|54] +changeEnabled=false +changeEvery=50 +height=40 +width=90 + +[2-puddle@205,200|37] +changeEnabled=false +changeEvery=50 +height=32 +width=32 + +[2-puddle@206,391|46] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[2-puddle@207,185|55] +changeEnabled=false +changeEvery=50 +height=34 +width=34 + +[2-puddle@211,154|35] +changeEnabled=false +changeEvery=50 +height=70 +width=92 + +[2-puddle@211,399|47] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[2-puddle@215,180|33] +changeEnabled=false +changeEvery=50 +height=30 +width=36 + +[2-puddle@79,180|31] +changeEnabled=false +changeEvery=50 +height=68 +width=272 + +[2-sand@275,277|65] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[2-sand@363,352|66] +changeEnabled=false +changeEvery=50 +height=64 +width=32 + +[2-sand@366,341|72] +changeEnabled=false +changeEvery=50 +height=44 +width=30 + +[2-slope@113,9|57] +grade=4 +gradient=Horizontal +height=105 +reversed=true +stuckOnGround=false +width=81 + +[2-slope@255,324|74] +grade=4 +gradient=Vertical +height=28 +reversed=false +stuckOnGround=false +width=54 + +[2-slope@263,112|72] +grade=6 +gradient=Diagonal +height=22 +reversed=false +stuckOnGround=false +width=123 + +[2-slope@269,134|71] +grade=6 +gradient=Horizontal +height=31 +reversed=false +stuckOnGround=false +width=117 + +[2-slope@269,163|73] +grade=6 +gradient=Opposite Diagonal +height=36 +reversed=true +stuckOnGround=false +width=117 + +[2-slope@308,322|75] +grade=4 +gradient=Opposite Diagonal +height=30 +reversed=false +stuckOnGround=false +width=27 + +[2-slope@308,349|76] +grade=4 +gradient=Horizontal +height=38 +reversed=true +stuckOnGround=false +width=27 + +[2-slope@313,228|77] +grade=4 +gradient=Elliptic +height=74 +reversed=false +stuckOnGround=false +width=74 + +[2-slope@49,235|74] +grade=4 +gradient=Elliptic +height=107 +reversed=false +stuckOnGround=false +width=107 + +[2-wall@0,0|10] +endPoint=131,367 +startPoint=107,390 + +[2-wall@0,0|11] +endPoint=41,390 +startPoint=13,361 + +[2-wall@0,0|12] +endPoint=55,243 +startPoint=36,272 + +[2-wall@0,0|13] +endPoint=13,288 +startPoint=36,272 + +[2-wall@0,0|15] +endPoint=108,115 +startPoint=88,133 + +[2-wall@0,0|16] +endPoint=88,133 +startPoint=56,134 + +[2-wall@0,0|17] +endPoint=56,134 +startPoint=30,121 + +[2-wall@0,0|18] +endPoint=13,76 +startPoint=30,121 + +[2-wall@0,0|19] +endPoint=45,9 +startPoint=13,27 + +[2-wall@0,0|20] +endPoint=189,114 +startPoint=108,115 + +[2-wall@0,0|21] +endPoint=221,109 +startPoint=189,114 + +[2-wall@0,0|22] +endPoint=244,112 +startPoint=221,109 + +[2-wall@0,0|23] +endPoint=273,139 +startPoint=244,112 + +[2-wall@0,0|24] +endPoint=267,168 +startPoint=273,139 + +[2-wall@0,0|25] +endPoint=261,180 +startPoint=238,197 + +[2-wall@0,0|26] +endPoint=226,238 +startPoint=226,282 + +[2-wall@0,0|27] +endPoint=252,328 +startPoint=226,282 + +[2-wall@0,0|28] +endPoint=262,356 +startPoint=252,328 + +[2-wall@0,0|29] +endPoint=262,356 +startPoint=255,377 + +[2-wall@0,0|30] +endPoint=263,387 +startPoint=255,377 + +[2-wall@0,0|4] +endPoint=87,224 +startPoint=55,243 + +[2-wall@0,0|5] +endPoint=124,224 +startPoint=87,224 + +[2-wall@0,0|57] +endPoint=228,9 +startPoint=388,116 + +[2-wall@0,0|58] +endPoint=238,197 +startPoint=226,238 + +[2-wall@0,0|6] +endPoint=165,255 +startPoint=124,224 + +[2-wall@0,0|64] +endPoint=267,168 +startPoint=261,180 + +[2-wall@0,0|67] +endPoint=107,390 +startPoint=41,390 + +[2-wall@0,0|68] +endPoint=13,361 +startPoint=13,288 + +[2-wall@0,0|69] +endPoint=13,27 +startPoint=13,76 + +[2-wall@0,0|7] +endPoint=165,255 +startPoint=165,314 + +[2-wall@0,0|70] +endPoint=228,9 +startPoint=45,9 + +[2-wall@0,0|71] +endPoint=388,116 +startPoint=386,356 + +[2-wall@0,0|72] +endPoint=372,386 +startPoint=263,387 + +[2-wall@0,0|73] +endPoint=386,356 +startPoint=372,386 + +[2-wall@0,0|8] +endPoint=165,314 +startPoint=134,342 + +[2-wall@0,0|9] +endPoint=134,342 +startPoint=131,367 + +[3-ball@59,317] +dummykey=true + +[3-cup@299,103|5] +dummykey=true + +[3-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[3-puddle@169,234|11] +changeEnabled=false +changeEvery=50 +height=34 +width=10 + +[3-puddle@188,265|6] +changeEnabled=false +changeEvery=50 +height=50 +width=54 + +[3-puddle@196,252|10] +changeEnabled=false +changeEvery=50 +height=72 +width=72 + +[3-puddle@203,227|12] +changeEnabled=false +changeEvery=50 +height=86 +width=78 + +[3-sand@255,57|14] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[3-sand@345,154|15] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[3-slope@170,168|4] +grade=7 +gradient=Diagonal +height=221 +reversed=false +stuckOnGround=false +width=219 + +[3-windmill@12,12|13] +botWallVisible=false +bottom=true +height=198 +leftWallVisible=false +rightWallVisible=false +speed=4 +topWallVisible=false +width=180 + +[4-ball@93,349] +dummykey=true + +[4-bumper@262,91|20] +dummykey=true + +[4-cup@338,325|5] +dummykey=true + +[4-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[4-sand@100,150|11] +changeEnabled=false +changeEvery=50 +height=72 +width=46 + +[4-sand@316,139|18] +changeEnabled=false +changeEvery=50 +height=74 +width=50 + +[4-sand@324,153|14] +changeEnabled=false +changeEvery=50 +height=34 +width=38 + +[4-sand@329,163|14] +changeEnabled=false +changeEvery=50 +height=30 +width=54 + +[4-sand@72,127|10] +changeEnabled=false +changeEvery=50 +height=48 +width=78 + +[4-sand@90,138|12] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@93,135|8] +changeEnabled=false +changeEvery=50 +height=50 +width=46 + +[4-slope@152,20|21] +grade=1 +gradient=Elliptic +height=162 +reversed=true +stuckOnGround=false +width=162 + +[4-slope@278,234|19] +grade=5 +gradient=Elliptic +height=57 +reversed=false +stuckOnGround=false +width=57 + +[4-slope@290,14|20] +grade=2 +gradient=Elliptic +height=105 +reversed=false +stuckOnGround=false +width=105 + +[4-wall@0,0|22] +endPoint=257,355 +startPoint=257,387 + +[4-wall@0,0|3] +endPoint=257,355 +startPoint=221,223 + +[4-wall@0,0|4] +endPoint=10,10 +startPoint=136,109 + +[5-ball@327,224] +dummykey=true + +[5-cup@344,304|42] +dummykey=true + +[5-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[5-puddle@210,199|33] +changeEnabled=false +changeEvery=50 +height=112 +width=110 + +[5-puddle@212,227|35] +changeEnabled=false +changeEvery=50 +height=80 +width=74 + +[5-puddle@224,220|34] +changeEnabled=false +changeEvery=50 +height=148 +width=86 + +[5-puddle@306,368|46] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[5-puddle@331,361|32] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[5-puddle@365,367|45] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[5-sand@188,99|19] +changeEnabled=true +changeEvery=50 +height=40 +width=45 + +[5-slope@21,134|48] +grade=2 +gradient=Vertical +height=84 +reversed=false +stuckOnGround=false +width=207 + +[5-wall@0,0|10] +endPoint=390,257 +startPoint=393,221 + +[5-wall@0,0|11] +endPoint=393,221 +startPoint=368,191 + +[5-wall@0,0|12] +endPoint=368,191 +startPoint=349,113 + +[5-wall@0,0|13] +endPoint=349,113 +startPoint=267,51 + +[5-wall@0,0|14] +endPoint=267,51 +startPoint=232,66 + +[5-wall@0,0|15] +endPoint=232,66 +startPoint=195,52 + +[5-wall@0,0|16] +endPoint=195,52 +startPoint=125,47 + +[5-wall@0,0|17] +endPoint=125,47 +startPoint=36,138 + +[5-wall@0,0|18] +endPoint=19,213 +startPoint=36,138 + +[5-wall@0,0|28] +endPoint=39,240 +startPoint=19,213 + +[5-wall@0,0|3] +endPoint=272,159 +startPoint=223,130 + +[5-wall@0,0|36] +endPoint=44,259 +startPoint=39,240 + +[5-wall@0,0|37] +endPoint=72,267 +startPoint=44,259 + +[5-wall@0,0|38] +endPoint=73,316 +startPoint=72,267 + +[5-wall@0,0|39] +endPoint=132,375 +startPoint=73,316 + +[5-wall@0,0|4] +endPoint=372,267 +startPoint=306,251 + +[5-wall@0,0|40] +endPoint=229,378 +startPoint=132,375 + +[5-wall@0,0|41] +endPoint=259,361 +startPoint=229,378 + +[5-wall@0,0|43] +endPoint=180,313 +startPoint=150,328 + +[5-wall@0,0|5] +endPoint=278,203 +startPoint=272,159 + +[5-wall@0,0|6] +endPoint=223,130 +startPoint=190,135 + +[5-wall@0,0|8] +endPoint=306,251 +startPoint=278,203 + +[5-wall@0,0|9] +endPoint=390,257 +startPoint=372,267 + +[5-windmill@259,284|44] +botWallVisible=false +bottom=true +height=77 +leftWallVisible=false +rightWallVisible=true +speed=5 +topWallVisible=true +width=126 + +[6-ball@28,86] +dummykey=true + +[6-blackhole@169,41|36] +exit=381,349 +exitDeg=200 +maxspeed=5 +minspeed=1 + +[6-blackhole@266,293|43] +exit=26,239 +exitDeg=290 +maxspeed=2 +minspeed=1 + +[6-cup@103,354|44] +dummykey=true + +[6-cup@104,110|3] +dummykey=true + +[6-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[6-puddle@108,218|32] +changeEnabled=false +changeEvery=50 +height=56 +width=118 + +[6-puddle@149,331|40] +changeEnabled=false +changeEvery=50 +height=92 +width=44 + +[6-puddle@151,223|28] +changeEnabled=false +changeEvery=50 +height=44 +width=84 + +[6-puddle@157,369|37] +changeEnabled=false +changeEvery=50 +height=90 +width=62 + +[6-puddle@161,305|41] +changeEnabled=false +changeEvery=50 +height=98 +width=70 + +[6-puddle@167,382|39] +changeEnabled=false +changeEvery=50 +height=52 +width=64 + +[6-puddle@177,232|35] +changeEnabled=false +changeEvery=50 +height=30 +width=42 + +[6-puddle@178,261|36] +changeEnabled=false +changeEvery=50 +height=104 +width=64 + +[6-puddle@178,285|38] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[6-puddle@183,389|42] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[6-puddle@41,208|67] +changeEnabled=false +changeEvery=50 +height=56 +width=222 + +[6-sand@290,48|63] +changeEnabled=false +changeEvery=50 +height=60 +width=58 + +[6-sand@315,31|52] +changeEnabled=false +changeEvery=50 +height=34 +width=142 + +[6-sand@331,73|62] +changeEnabled=false +changeEvery=50 +height=96 +width=76 + +[6-sand@360,99|61] +changeEnabled=false +changeEvery=50 +height=172 +width=54 + +[6-sand@369,24|64] +changeEnabled=false +changeEvery=50 +height=26 +width=24 + +[6-sand@372,25|67] +changeEnabled=false +changeEvery=50 +height=28 +width=34 + +[6-sand@373,40|65] +changeEnabled=false +changeEvery=50 +height=30 +width=24 + +[6-sand@383,36|66] +changeEnabled=false +changeEvery=50 +height=18 +width=12 + +[6-slope@10,11|63] +grade=4 +gradient=Diagonal +height=66 +reversed=true +stuckOnGround=false +width=142 + +[6-slope@11,314|62] +grade=4 +gradient=Opposite Diagonal +height=74 +reversed=false +stuckOnGround=false +width=89 + +[6-slope@13,145|66] +grade=4 +gradient=Vertical +height=67 +reversed=false +stuckOnGround=false +width=133 + +[6-slope@141,14|60] +grade=2 +gradient=Elliptic +height=59 +reversed=true +stuckOnGround=false +width=59 + +[6-slope@147,145|71] +grade=4 +gradient=Opposite Diagonal +height=82 +reversed=false +stuckOnGround=false +width=57 + +[6-slope@182,222|25] +grade=6 +gradient=Vertical +height=168 +reversed=false +stuckOnGround=false +width=208 + +[6-slope@21,275|61] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=false +width=40 + +[6-slope@223,45|72] +grade=4 +gradient=Opposite Diagonal +height=158 +reversed=true +stuckOnGround=false +width=121 + +[6-slope@344,181|73] +grade=4 +gradient=Vertical +height=22 +reversed=true +stuckOnGround=false +width=43 + +[6-slope@58,69|75] +grade=2 +gradient=Elliptic +height=91 +reversed=true +stuckOnGround=false +width=91 + +[6-wall@0,0|10] +endPoint=362,260 +startPoint=388,272 + +[6-wall@0,0|11] +endPoint=344,292 +startPoint=290,254 + +[6-wall@0,0|12] +endPoint=309,302 +startPoint=282,312 + +[6-wall@0,0|13] +endPoint=102,60 +startPoint=70,74 + +[6-wall@0,0|15] +endPoint=360,284 +startPoint=344,292 + +[6-wall@0,0|17] +endPoint=121,66 +startPoint=102,60 + +[6-wall@0,0|18] +endPoint=258,309 +startPoint=229,298 + +[6-wall@0,0|20] +endPoint=176,78 +startPoint=150,88 + +[6-wall@0,0|21] +endPoint=150,88 +startPoint=132,81 + +[6-wall@0,0|22] +endPoint=132,81 +startPoint=121,66 + +[6-wall@0,0|23] +endPoint=200,310 +startPoint=189,350 + +[6-wall@0,0|29] +endPoint=217,80 +startPoint=197,83 + +[6-wall@0,0|30] +endPoint=203,11 +startPoint=225,46 + +[6-wall@0,0|31] +endPoint=38,114 +startPoint=11,114 + +[6-wall@0,0|4] +endPoint=227,46 +startPoint=226,63 + +[6-wall@0,0|45] +endPoint=309,356 +startPoint=277,364 + +[6-wall@0,0|5] +endPoint=38,114 +startPoint=57,98 + +[6-wall@0,0|58] +endPoint=226,63 +startPoint=217,80 + +[6-wall@0,0|59] +endPoint=254,347 +startPoint=231,353 + +[6-wall@0,0|6] +endPoint=197,83 +startPoint=176,78 + +[6-wall@0,0|63] +endPoint=213,388 +startPoint=189,350 + +[6-wall@0,0|66] +endPoint=213,285 +startPoint=200,310 + +[6-wall@0,0|7] +endPoint=57,98 +startPoint=70,74 + +[6-wall@0,0|74] +endPoint=251,10 +startPoint=225,46 + +[6-wall@0,0|8] +endPoint=232,273 +startPoint=213,285 + +[6-wall@0,0|9] +endPoint=116,271 +startPoint=76,296 + +[7-ball@37,357] +dummykey=true + +[7-blackhole@263,340|15] +exit=323,359 +exitDeg=0 +maxspeed=2 +minspeed=2 + +[7-cup@333,50|3] +dummykey=true + +[7-cup@367,355|16] +dummykey=true + +[7-floater@163,347|13] +botWallVisible=true +endPoint=163,347 +height=41 +leftWallVisible=true +rightWallVisible=true +speed=6 +startPoint=158,314 +topWallVisible=true +width=10 + +[7-floater@197,284|14] +botWallVisible=true +endPoint=197,284 +height=37 +leftWallVisible=true +rightWallVisible=true +speed=3 +startPoint=189,222 +topWallVisible=true +width=9 + +[7-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=2 + +[7-slope@111,21|6] +grade=2 +gradient=Opposite Diagonal +height=97 +reversed=false +stuckOnGround=false +width=151 + +[7-slope@112,117|5] +grade=2 +gradient=Diagonal +height=141 +reversed=true +stuckOnGround=false +width=143 + +[7-slope@193,145|10] +grade=3 +gradient=Vertical +height=57 +reversed=true +stuckOnGround=false +width=197 + +[7-slope@239,326|17] +grade=4 +gradient=Diagonal +height=63 +reversed=false +stuckOnGround=false +width=73 + +[7-slope@27,117|8] +grade=2 +gradient=Opposite Diagonal +height=136 +reversed=true +stuckOnGround=false +width=84 + +[7-slope@27,18|7] +grade=2 +gradient=Diagonal +height=100 +reversed=false +stuckOnGround=false +width=87 + +[7-wall@0,0|11] +endPoint=387,326 +startPoint=313,326 + +[7-wall@0,0|12] +endPoint=312,327 +startPoint=312,387 + +[7-wall@0,0|4] +endPoint=313,326 +startPoint=194,204 + +[7-wall@0,0|9] +endPoint=194,148 +startPoint=194,204 + +[8-ball@318,46] +dummykey=true + +[8-blackhole@33,33|9] +exit=384,321 +exitDeg=195 +maxspeed=3 +minspeed=3 + +[8-cup@226,360|11] +dummykey=true + +[8-floater@207,105|3] +botWallVisible=true +endPoint=207,105 +height=105 +leftWallVisible=false +rightWallVisible=true +speed=5 +startPoint=54,225 +topWallVisible=false +width=168 + +[8-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=2 + +[8-puddle@274,284|14] +changeEnabled=false +changeEvery=50 +height=36 +width=106 + +[8-puddle@279,290|17] +changeEnabled=false +changeEvery=50 +height=44 +width=150 + +[8-puddle@311,275|12] +changeEnabled=false +changeEvery=50 +height=58 +width=138 + +[8-puddle@338,267|13] +changeEnabled=false +changeEvery=50 +height=54 +width=114 + +[8-puddle@366,254|15] +changeEnabled=false +changeEvery=50 +height=58 +width=96 + +[8-sand@236,61|20] +changeEnabled=false +changeEvery=50 +height=50 +width=74 + +[8-sand@243,47|19] +changeEnabled=false +changeEvery=50 +height=64 +width=70 + +[8-slope@12,70|10] +grade=3 +gradient=Vertical +height=195 +reversed=false +stuckOnGround=true +width=180 + +[8-slope@131,25|22] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=false +width=40 + +[8-slope@59,19|21] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=false +width=40 + +[8-wall@0,0|18] +endPoint=389,335 +startPoint=312,388 + +[8-wall@0,0|22] +endPoint=387,211 +startPoint=360,252 + +[8-wall@0,0|5] +endPoint=193,9 +startPoint=193,390 + +[9-ball@38,290] +dummykey=true + +[9-cup@318,61|3] +dummykey=true + +[9-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[9-puddle@100,33|8] +changeEnabled=false +changeEvery=50 +height=32 +width=28 + +[9-puddle@112,20|23] +changeEnabled=false +changeEvery=50 +height=10 +width=34 + +[9-puddle@133,37|18] +changeEnabled=false +changeEvery=50 +height=46 +width=88 + +[9-puddle@166,43|19] +changeEnabled=false +changeEvery=50 +height=40 +width=84 + +[9-puddle@178,33|22] +changeEnabled=false +changeEvery=50 +height=38 +width=92 + +[9-puddle@192,40|21] +changeEnabled=false +changeEvery=50 +height=30 +width=48 + +[9-slope@156,-24|24] +grade=4 +gradient=Elliptic +height=160 +reversed=false +stuckOnGround=false +width=160 + +[9-slope@189,216|7] +grade=6 +gradient=Elliptic +height=72 +reversed=false +stuckOnGround=false +width=72 + +[9-wall@0,0|5] +endPoint=220,214 +startPoint=145,131 + +[9-wall@0,0|6] +endPoint=219,293 +startPoint=219,389 + +[9-wall@0,0|9] +endPoint=74,11 +startPoint=11,40 + +[9-windmill@249,13|4] +botWallVisible=false +bottom=true +height=113 +leftWallVisible=true +rightWallVisible=false +speed=5 +topWallVisible=false +width=137 diff --git a/kolf/courses/Practice b/kolf/courses/Practice new file mode 100644 index 00000000..8ff077ec --- /dev/null +++ b/kolf/courses/Practice @@ -0,0 +1,376 @@ +[0-course@-50,-50] +Name=Slope Practice +Name[bg]=Ðаклонено +Name[bn]=ঢালà§à¦¤à§‡ অনà§à¦¶à§€à¦²à¦¨ +Name[bs]=Vježba sa nagibom +Name[ca]=Pràctica de baixada +Name[da]=Træn skrÃ¥ninger +Name[de]=Hügelpraxis +Name[es]=Práctica de pendiente +Name[et]=Kallakute treening +Name[fi]=Kaltevuusharjoitus +Name[fr]=Entraînement sur la pente +Name[gl]=Práctica de pendente +Name[he]=×ימון ×ž×“×¨×•× ×™× +Name[hi]=ढाल अभà¥à¤¯à¤¾à¤¸ +Name[hr]=Nagibna vježba TODO +Name[hu]=Gyakorlás lejtÅ‘n +Name[is]=Æfing í halla +Name[it]=Esercizio su pendio +Name[ja]=æ–œé¢ã®ç·´ç¿’ +Name[lv]=TrennÄ“Å¡anÄs uz nogÄzes +Name[mk]=Вежбање на наклони +Name[nb]=SkrÃ¥ningsøvelse +Name[nl]=Heuveloefening +Name[nn]=Bakkeøving +Name[pl]=Ćwiczenia na stoku +Name[pt]=Praticar Inclinações +Name[pt_BR]=Praticar Ladeira +Name[ru]=Практика на горках +Name[sk]=Nácvik slope +Name[sl]=Vaja za strmine +Name[sr]=Вежба нагиба +Name[sr@Latn]=Vežba nagiba +Name[sv]=Sluttningsövning +Name[ta]=சரிவ௠பயிறà¯à®šà®¿ +Name[tg]=Таҷриба дар Теппачаҳо +Name[tr]=EÄŸim Çalışması +Name[uk]=Ð¢Ñ€ÐµÐ½ÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ð° Ñхилах +Name[ven]=Ndowendowe ya Mutshoni +Name[xh]=Induli yokuzilolonga +Name[xx]=xxSlope Practicexx +Name[zh_CN]=æ–œå¡ç»ƒä¹  +Name[zh_TW]=æ–œå¡ç·´ç¿’ +Name[zu]=Ukuzejwayeza kokusantaba +author=Jason Katz-Brown +name=Slope Practice + +[1-ball@68,257] +dummykey=true + +[1-cup@289,186|3] +dummykey=true + +[1-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=4 +par=2 + +[1-sand@327,8|5] +changeEnabled=false +changeEvery=50 +height=132 +width=310 + +[1-sand@368,64|7] +changeEnabled=false +changeEvery=50 +height=96 +width=98 + +[1-sand@426,127|6] +changeEnabled=false +changeEvery=50 +height=418 +width=162 + +[1-slope@-1,88|4] +grade=2 +gradient=Vertical +height=247 +reversed=false +stuckOnGround=false +width=402 + +[2-ball@306,324] +dummykey=true + +[2-cup@262,69|3] +dummykey=true + +[2-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[2-sand@-62,187|5] +changeEnabled=false +changeEvery=50 +height=300 +width=376 + +[2-sand@11,-10|6] +changeEnabled=false +changeEvery=50 +height=174 +width=400 + +[2-sand@41,74|7] +changeEnabled=false +changeEvery=50 +height=136 +width=158 + +[2-slope@7,-6|4] +grade=2 +gradient=Opposite Diagonal +height=323 +reversed=true +stuckOnGround=false +width=395 + +[3-ball@39,167] +dummykey=true + +[3-cup@340,113|3] +dummykey=true + +[3-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[3-slope@160,-1|4] +grade=3 +gradient=Vertical +height=237 +reversed=true +stuckOnGround=false +width=90 + +[3-slope@247,-1|6] +grade=3 +gradient=Diagonal +height=237 +reversed=true +stuckOnGround=false +width=60 + +[3-slope@87,-1|5] +grade=3 +gradient=Opposite Diagonal +height=236 +reversed=true +stuckOnGround=false +width=74 + +[3-wall@0,0|7] +endPoint=306,13 +startPoint=380,75 + +[3-wall@0,0|8] +endPoint=380,75 +startPoint=386,130 + +[3-wall@0,0|9] +endPoint=386,130 +startPoint=366,246 + +[4-ball@344,292] +dummykey=true + +[4-cup@74,281|3] +dummykey=true + +[4-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[4-puddle@205,406|5] +changeEnabled=false +changeEvery=50 +height=444 +width=112 + +[4-slope@11,43|4] +grade=4 +gradient=Vertical +height=155 +reversed=false +stuckOnGround=false +width=377 + +[5-ball@328,312] +dummykey=true + +[5-cup@96,73|3] +dummykey=true + +[5-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[5-slope@-119,167|4] +grade=2 +gradient=Elliptic +height=345 +reversed=false +stuckOnGround=false +width=345 + +[5-wall@0,0|10] +endPoint=272,380 +startPoint=362,366 + +[5-wall@0,0|11] +endPoint=231,95 +startPoint=202,45 + +[5-wall@0,0|5] +endPoint=231,95 +startPoint=176,214 + +[5-wall@0,0|6] +endPoint=202,45 +startPoint=68,29 + +[5-wall@0,0|7] +endPoint=68,29 +startPoint=30,65 + +[5-wall@0,0|8] +endPoint=30,65 +startPoint=31,264 + +[5-wall@0,0|9] +endPoint=272,380 +startPoint=103,367 + +[6-ball@338,348] +dummykey=true + +[6-cup@80,52|3] +dummykey=true + +[6-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[6-slope@11,226|5] +grade=4 +gradient=Vertical +height=66 +reversed=true +stuckOnGround=false +width=378 + +[6-slope@12,125|4] +grade=4 +gradient=Vertical +height=70 +reversed=false +stuckOnGround=false +width=376 + +[7-ball@370,174] +dummykey=true + +[7-cup@161,231|3] +dummykey=true + +[7-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[7-slope@8,73|4] +grade=4 +gradient=Elliptic +height=311 +reversed=true +stuckOnGround=false +width=311 + +[7-wall@0,0|5] +endPoint=330,174 +startPoint=330,297 + +[7-wall@0,0|6] +endPoint=330,297 +startPoint=406,297 + +[8-ball@331,91] +dummykey=true + +[8-cup@94,267|3] +dummykey=true + +[8-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[8-sand@-8,157|5] +changeEnabled=false +changeEvery=50 +height=142 +width=372 + +[8-slope@11,145|4] +grade=6 +gradient=Vertical +height=254 +reversed=false +stuckOnGround=false +width=391 + +[8-wall@0,0|6] +endPoint=13,398 +startPoint=13,228 + +[9-ball@68,330] +dummykey=true + +[9-cup@320,101|3] +dummykey=true + +[9-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[9-puddle@210,356|6] +changeEnabled=false +changeEvery=50 +height=312 +width=170 + +[9-puddle@305,237|5] +changeEnabled=false +changeEvery=50 +height=120 +width=270 + +[9-puddle@394,360|7] +changeEnabled=false +changeEvery=50 +height=264 +width=300 + +[9-slope@0,0|4] +grade=6 +gradient=Diagonal +height=230 +reversed=true +stuckOnGround=false +width=310 + +[9-wall@0,0|8] +endPoint=366,26 +startPoint=276,24 + +[9-wall@0,0|9] +endPoint=366,26 +startPoint=366,201 diff --git a/kolf/courses/ReallyEasy b/kolf/courses/ReallyEasy new file mode 100644 index 00000000..5e044f97 --- /dev/null +++ b/kolf/courses/ReallyEasy @@ -0,0 +1,2404 @@ +[0-course@-50,-50] +Name=Really Easy +Name[af]=Rerig Maklike +Name[ar]=سهل جدا +Name[bg]=Елементарно +Name[bn]=খà§à¦¬à¦‡ সহজ +Name[br]=Aes-tre +Name[bs]=Stvarno lagano +Name[ca]=Realment fàcil +Name[cs]=Opravdu jednoduchý +Name[da]=Meget nem +Name[de]=Sehr leicht +Name[el]=ΠÏαγματικά εÏκολο +Name[es]=Realmente fácil +Name[et]=Imelihtne +Name[fi]=Todella helppo +Name[fr]=Vraiment facile +Name[gl]=Moi fácil +Name[he]=ממש קל +Name[hi]=सचमà¥à¤š आसान +Name[hr]=Stvarno lagano +Name[hu]=Nagyon könnyű +Name[is]=Verulega auðveld +Name[it]=Facilissimo +Name[ja]=ホントã«ã‚«ãƒ³ã‚¿ãƒ³ +Name[lv]=TieÅ¡Äm viegli +Name[mk]=ÐавиÑтина леÑен +Name[nb]=Veldig lett +Name[nl]=Heel eenvoudig +Name[nn]=Kjempelett +Name[nso]=Bonolo Kannete +Name[pl]=NaprawdÄ™ Å‚atwy tor +Name[pt]=Realmente Fácil +Name[pt_BR]=Realmente Fácil +Name[ro]=Foarte uÅŸor +Name[ru]=Проще некуда +Name[sk]=Naozaj jednoduché +Name[sl]=Zelo lahko +Name[sr]=Баш лако +Name[sr@Latn]=BaÅ¡ lako +Name[sv]=Mycket enkel +Name[ta]=உணà¯à®®à¯ˆà®¯à®¿à®²à¯‡à®¯à¯‡ சà¯à®²à®ªà®®à®¾à®©à®¤à¯ +Name[tg]=Дар Ҳақиқат ОÑон +Name[tr]=Çok Kolay +Name[uk]=Дуже проÑто +Name[ven]=Zwi leluwa nga Maanda +Name[wa]=Vormint Ã¥jhey +Name[xh]=Ilula kakhulu +Name[xx]=xxReally Easyxx +Name[zh_CN]=真的很容易 +Name[zh_TW]=真的簡單 +Name[zu]=Kulula kakhulu +author=Neil Stevens +name=Really Easy + +[1-ball@79,203] +dummykey=true + +[1-cup@301,184|7] +dummykey=true + +[1-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=5 +par=2 + +[1-slope@-3,238|9] +grade=4 +gradient=Vertical +height=170 +reversed=true +stuckOnGround=false +width=407 + +[1-slope@-5,-4|8] +grade=4 +gradient=Vertical +height=146 +reversed=false +stuckOnGround=false +width=399 + +[1-wall@0,0|3] +endPoint=322,148 +startPoint=63,170 + +[1-wall@0,0|4] +endPoint=324,218 +startPoint=63,236 + +[1-wall@0,0|5] +endPoint=63,170 +startPoint=63,236 + +[1-wall@0,0|6] +endPoint=322,148 +startPoint=324,218 + +[10-ball@171,205] +dummykey=true + +[10-cup@209,158|0] +dummykey=true + +[10-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=5 + +[10-puddle@239,231|3] +changeEnabled=false +changeEvery=50 +height=60 +width=70 + +[10-sand@137,128|4] +changeEnabled=false +changeEvery=50 +height=78 +width=90 + +[10-wall@0,0|1] +endPoint=190,39 +startPoint=190,349 + +[10-wall@0,0|2] +endPoint=324,183 +startPoint=72,183 + +[11-ball@189,342] +dummykey=true + +[11-bridge@109,184|4] +botWallVisible=true +height=172 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=167 + +[11-cup@190,138|0] +dummykey=true + +[11-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=5 +par=3 + +[11-wall@0,0|1] +endPoint=308,48 +startPoint=71,48 + +[11-wall@0,0|2] +endPoint=190,51 +startPoint=190,124 + +[11-windmill@109,119|3] +botWallVisible=false +bottom=true +height=64 +leftWallVisible=true +rightWallVisible=true +speed=5 +topWallVisible=true +width=165 + +[12-ball@281,101] +dummykey=true + +[12-cup@208,252|0] +dummykey=true + +[12-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=7 +par=4 + +[12-slope@170,111|2] +grade=6 +gradient=Vertical +height=226 +reversed=true +stuckOnGround=false +width=46 + +[12-slope@80,43|1] +grade=6 +gradient=Diagonal +height=67 +reversed=true +stuckOnGround=false +width=208 + +[12-slope@92,178|3] +grade=6 +gradient=Horizontal +height=42 +reversed=true +stuckOnGround=false +width=200 + +[12-wall@0,0|4] +endPoint=291,111 +startPoint=293,43 + +[12-wall@0,0|6] +endPoint=291,111 +startPoint=218,111 + +[12-wall@0,0|7] +endPoint=293,43 +startPoint=81,43 + +[13-ball@48,40] +dummykey=true + +[13-blackhole@348,38|8] +exit=209,298 +exitDeg=90 +maxspeed=4 +minspeed=4 + +[13-cup@207,143|0] +dummykey=true + +[13-floater@276,198|9] +botWallVisible=true +endPoint=276,198 +height=2 +leftWallVisible=false +rightWallVisible=false +speed=11 +startPoint=45,199 +topWallVisible=false +width=90 + +[13-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=6 +par=3 + +[13-wall@0,0|1] +endPoint=348,72 +startPoint=48,72 + +[13-wall@0,0|2] +endPoint=150,75 +startPoint=151,125 + +[13-wall@0,0|3] +endPoint=150,123 +startPoint=120,291 + +[13-wall@0,0|4] +endPoint=120,291 +startPoint=54,359 + +[13-wall@0,0|5] +endPoint=151,125 +startPoint=281,123 + +[13-wall@0,0|6] +endPoint=281,123 +startPoint=244,332 + +[13-wall@0,0|7] +endPoint=244,332 +startPoint=221,359 + +[14-ball@129,183] +dummykey=true + +[14-cup@308,172|0] +dummykey=true + +[14-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=5 + +[14-puddle@1,10|7] +changeEnabled=false +changeEvery=50 +height=1242 +width=32 + +[14-puddle@11,4|9] +changeEnabled=false +changeEvery=50 +height=24 +width=1062 + +[14-puddle@158,394|8] +changeEnabled=false +changeEvery=50 +height=36 +width=846 + +[14-puddle@407,192|10] +changeEnabled=false +changeEvery=50 +height=602 +width=38 + +[14-wall@-15,1|4] +endPoint=257,100 +startPoint=220,144 + +[14-wall@0,0|1] +endPoint=192,121 +startPoint=41,121 + +[14-wall@0,0|2] +endPoint=150,85 +startPoint=150,300 + +[14-wall@0,0|3] +endPoint=151,120 +startPoint=68,224 + +[14-wall@0,0|5] +endPoint=239,121 +startPoint=357,157 + +[14-wall@0,0|6] +endPoint=357,157 +startPoint=289,302 + +[15-ball@208,165] +dummykey=true + +[15-cup@140,158|0] +dummykey=true + +[15-cup@341,156|10] +dummykey=true + +[15-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=9 +par=4 + +[15-puddle@306,15|14] +changeEnabled=false +changeEvery=50 +height=140 +width=626 + +[15-puddle@386,81|15] +changeEnabled=false +changeEvery=50 +height=82 +width=190 + +[15-puddle@386,97|16] +changeEnabled=false +changeEvery=50 +height=130 +width=114 + +[15-puddle@404,165|17] +changeEnabled=false +changeEvery=50 +height=170 +width=100 + +[15-puddle@408,273|18] +changeEnabled=false +changeEvery=50 +height=260 +width=76 + +[15-puddle@420,348|19] +changeEnabled=false +changeEvery=50 +height=272 +width=98 + +[15-wall@0,0|1] +endPoint=169,30 +startPoint=169,295 + +[15-wall@0,0|2] +endPoint=169,295 +startPoint=93,342 + +[15-wall@0,0|3] +endPoint=167,175 +startPoint=109,213 + +[15-wall@0,0|4] +endPoint=155,141 +startPoint=115,110 + +[15-wall@0,0|5] +endPoint=235,29 +startPoint=239,337 + +[15-wall@0,0|6] +endPoint=239,337 +startPoint=314,336 + +[15-wall@0,0|7] +endPoint=314,336 +startPoint=314,291 + +[15-wall@0,0|8] +endPoint=290,213 +startPoint=237,175 + +[15-wall@0,0|9] +endPoint=237,175 +startPoint=292,122 + +[15-windmill@10,105|13] +botWallVisible=false +bottom=true +height=108 +leftWallVisible=false +rightWallVisible=false +speed=7 +topWallVisible=true +width=99 + +[16-ball@198,27] +dummykey=true + +[16-cup@240,130|0] +dummykey=true + +[16-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=4 + +[16-puddle@197,81|2] +changeEnabled=false +changeEvery=50 +height=34 +width=14 + +[16-puddle@197,93|1] +changeEnabled=false +changeEvery=50 +height=16 +width=236 + +[16-wall@-10,-21|12] +endPoint=289,249 +startPoint=229,210 + +[16-wall@0,0|10] +endPoint=199,332 +startPoint=179,330 + +[16-wall@0,0|9] +endPoint=199,197 +startPoint=199,332 + +[16-wall@3,-18|11] +endPoint=88,284 +startPoint=166,219 + +[16-windmill@142,112|3] +botWallVisible=false +bottom=true +height=81 +leftWallVisible=true +rightWallVisible=true +speed=5 +topWallVisible=true +width=113 + +[17-ball@21,143] +dummykey=true + +[17-cup@255,163|0] +dummykey=true + +[17-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=6 + +[17-sand@100,136|3] +changeEnabled=false +changeEvery=50 +height=48 +width=18 + +[17-sand@104,322|12] +changeEnabled=false +changeEvery=50 +height=32 +width=52 + +[17-sand@112,173|6] +changeEnabled=false +changeEvery=50 +height=26 +width=50 + +[17-sand@119,319|4] +changeEnabled=false +changeEvery=50 +height=28 +width=66 + +[17-sand@126,173|10] +changeEnabled=false +changeEvery=50 +height=26 +width=42 + +[17-sand@134,177|9] +changeEnabled=false +changeEvery=50 +height=32 +width=42 + +[17-sand@137,315|11] +changeEnabled=false +changeEvery=50 +height=24 +width=54 + +[17-sand@65,323|14] +changeEnabled=false +changeEvery=50 +height=20 +width=46 + +[17-sand@75,329|5] +changeEnabled=false +changeEvery=50 +height=28 +width=86 + +[17-sand@88,301|15] +changeEnabled=false +changeEvery=50 +height=78 +width=20 + +[17-sand@90,319|13] +changeEnabled=false +changeEvery=50 +height=20 +width=72 + +[17-sand@93,219|2] +changeEnabled=false +changeEvery=50 +height=238 +width=32 + +[17-sand@94,182|1] +changeEnabled=false +changeEvery=50 +height=38 +width=114 + +[17-sand@96,133|7] +changeEnabled=false +changeEvery=50 +height=44 +width=28 + +[17-sand@97,122|8] +changeEnabled=false +changeEvery=50 +height=46 +width=26 + +[17-slope@206,262|27] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[17-slope@215,253|28] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[17-slope@226,244|29] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[17-slope@234,236|30] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[17-slope@243,228|31] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[17-slope@250,219|32] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[17-slope@259,209|33] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[17-slope@268,202|34] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[17-wall@0,0|16] +endPoint=266,213 +startPoint=201,273 + +[17-wall@0,0|17] +endPoint=266,213 +startPoint=292,174 + +[17-wall@0,0|18] +endPoint=289,253 +startPoint=200,344 + +[17-wall@0,0|19] +endPoint=289,253 +startPoint=328,202 + +[17-wall@0,0|20] +endPoint=347,324 +startPoint=279,264 + +[17-wall@0,0|21] +endPoint=347,324 +startPoint=366,347 + +[17-wall@0,0|22] +endPoint=205,168 +startPoint=252,111 + +[17-wall@0,0|23] +endPoint=205,168 +startPoint=252,222 + +[17-wall@0,0|24] +endPoint=295,108 +startPoint=163,113 + +[17-wall@0,0|25] +endPoint=295,108 +startPoint=374,114 + +[17-wall@0,0|26] +endPoint=263,110 +startPoint=243,62 + +[17-wall@0,0|35] +endPoint=200,344 +startPoint=170,355 + +[17-wall@0,0|36] +endPoint=328,202 +startPoint=333,166 + +[17-wall@0,0|37] +endPoint=201,273 +startPoint=172,283 + +[18-ball@277,299] +dummykey=true + +[18-cup@210,119|0] +dummykey=true + +[18-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=7 +par=4 + +[18-wall@-1,2|18] +endPoint=152,106 +startPoint=89,106 + +[18-wall@0,-3|9] +endPoint=152,134 +startPoint=90,134 + +[18-wall@0,0|1] +endPoint=315,45 +startPoint=83,45 + +[18-wall@0,0|10] +endPoint=51,85 +startPoint=51,138 + +[18-wall@0,0|11] +endPoint=305,105 +startPoint=235,105 + +[18-wall@0,0|12] +endPoint=304,135 +startPoint=235,135 + +[18-wall@0,0|13] +endPoint=267,229 +startPoint=123,229 + +[18-wall@0,0|14] +endPoint=298,277 +startPoint=98,277 + +[18-wall@0,0|15] +endPoint=298,277 +startPoint=298,327 + +[18-wall@0,0|16] +endPoint=172,279 +startPoint=172,345 + +[18-wall@0,0|17] +endPoint=298,327 +startPoint=254,327 + +[18-wall@0,0|2] +endPoint=196,45 +startPoint=196,175 + +[18-wall@0,0|3] +endPoint=192,174 +startPoint=68,248 + +[18-wall@0,0|4] +endPoint=196,175 +startPoint=313,244 + +[18-wall@0,0|5] +endPoint=347,84 +startPoint=51,85 + +[18-wall@0,0|6] +endPoint=347,84 +startPoint=347,136 + +[18-wall@0,0|7] +endPoint=51,85 +startPoint=51,138 + +[18-wall@0,0|8] +endPoint=51,85 +startPoint=51,138 + +[2-ball@252,137] +dummykey=true + +[2-blackhole@155,137|12] +exit=75,308 +exitDeg=0 +maxspeed=4 +minspeed=4 + +[2-cup@353,305|7] +dummykey=true + +[2-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=5 +par=2 + +[2-puddle@-16,98|58] +changeEnabled=false +changeEvery=50 +height=52 +width=128 + +[2-puddle@324,230|73] +changeEnabled=false +changeEvery=50 +height=40 +width=590 + +[2-puddle@36,159|28] +changeEnabled=false +changeEvery=50 +height=148 +width=52 + +[2-puddle@47,196|62] +changeEnabled=false +changeEvery=50 +height=42 +width=28 + +[2-puddle@49,215|53] +changeEnabled=false +changeEvery=50 +height=38 +width=44 + +[2-puddle@67,223|29] +changeEnabled=false +changeEvery=50 +height=22 +width=36 + +[2-sand@13,296|15] +changeEnabled=false +changeEvery=50 +height=48 +width=64 + +[2-sand@29,318|17] +changeEnabled=false +changeEvery=50 +height=84 +width=48 + +[2-slope@283,1|20] +grade=3 +gradient=Elliptic +height=103 +reversed=false +stuckOnGround=false +width=103 + +[2-wall@0,0|10] +endPoint=60,355 +startPoint=58,283 + +[2-wall@0,0|11] +endPoint=374,263 +startPoint=372,338 + +[2-wall@0,0|3] +endPoint=265,101 +startPoint=138,110 + +[2-wall@0,0|4] +endPoint=279,161 +startPoint=133,171 + +[2-wall@0,0|5] +endPoint=133,171 +startPoint=138,110 + +[2-wall@0,0|6] +endPoint=265,101 +startPoint=279,161 + +[2-wall@0,0|8] +endPoint=374,263 +startPoint=58,283 + +[2-wall@0,0|9] +endPoint=372,338 +startPoint=60,355 + +[3-ball@331,335] +dummykey=true + +[3-cup@205,81|3] +dummykey=true + +[3-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=8 +par=5 + +[3-puddle@-5,168|49] +changeEnabled=false +changeEvery=50 +height=44 +width=126 + +[3-puddle@373,294|31] +changeEnabled=false +changeEvery=50 +height=42 +width=76 + +[3-puddle@38,156|32] +changeEnabled=false +changeEvery=50 +height=50 +width=72 + +[3-puddle@397,277|33] +changeEnabled=false +changeEvery=50 +height=68 +width=56 + +[3-puddle@52,143|27] +changeEnabled=false +changeEvery=50 +height=56 +width=52 + +[3-puddle@57,133|19] +changeEnabled=false +changeEvery=50 +height=52 +width=50 + +[3-puddle@64,114|25] +changeEnabled=false +changeEvery=50 +height=68 +width=40 + +[3-puddle@67,111|21] +changeEnabled=false +changeEvery=50 +height=42 +width=48 + +[3-puddle@74,34|41] +changeEnabled=false +changeEvery=50 +height=190 +width=58 + +[3-slope@-188,220|24] +grade=5 +gradient=Elliptic +height=307 +reversed=false +stuckOnGround=false +width=307 + +[3-wall@0,0|4] +endPoint=348,359 +startPoint=59,359 + +[3-wall@0,0|40] +endPoint=123,54 +startPoint=117,109 + +[3-wall@0,0|5] +endPoint=325,298 +startPoint=98,298 + +[3-wall@0,0|6] +endPoint=264,222 +startPoint=153,225 + +[3-wall@0,0|7] +endPoint=264,165 +startPoint=151,172 + +[3-wall@0,0|8] +endPoint=295,51 +startPoint=123,54 + +[3-wall@0,0|9] +endPoint=313,113 +startPoint=117,109 + +[4-ball@257,315] +dummykey=true + +[4-cup@35,38|9] +dummykey=true + +[4-floater@107,209|10] +botWallVisible=false +endPoint=107,209 +height=55 +leftWallVisible=false +rightWallVisible=false +speed=1 +startPoint=71,156 +topWallVisible=false +width=59 + +[4-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=6 +par=3 + +[4-puddle@406,415|54] +changeEnabled=false +changeEvery=50 +height=222 +width=356 + +[4-sand@105,164|35] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@112,151|22] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@115,129|38] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@116,136|34] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@117,119|11] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@118,119|45] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@120,101|10] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@121,84|42] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@122,90|46] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@123,62|44] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@123,72|9] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@124,48|8] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@125,31|7] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@125,36|33] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@238,32|20] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@238,99|29] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@239,118|26] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@239,67|25] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@239,80|18] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@240,50|24] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@241,137|21] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@242,157|23] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@242,174|22] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@245,196|19] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@251,190|36] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@264,199|30] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@278,195|28] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@290,199|14] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@299,194|40] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@313,196|27] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@323,197|31] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@33,209|39] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@332,192|47] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@334,198|12] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@347,192|21] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@364,196|37] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@364,200|15] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@367,194|32] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@41,208|13] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@55,204|41] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@67,198|17] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@77,189|43] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@84,190|48] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@91,180|16] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-wall@0,0|49] +endPoint=128,13 +startPoint=119,134 + +[4-wall@0,0|50] +endPoint=119,134 +startPoint=54,207 + +[4-wall@0,0|51] +endPoint=54,207 +startPoint=12,208 + +[4-wall@0,0|52] +endPoint=240,15 +startPoint=241,201 + +[4-wall@0,0|53] +endPoint=241,201 +startPoint=382,197 + +[5-ball@352,289] +dummykey=true + +[5-cup@203,277|3] +dummykey=true + +[5-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=6 +par=3 + +[5-wall@0,0|4] +endPoint=331,331 +startPoint=43,331 + +[5-wall@0,0|5] +endPoint=267,205 +startPoint=102,204 + +[5-wall@0,0|6] +endPoint=267,205 +startPoint=262,327 + +[5-wall@0,0|7] +endPoint=307,59 +startPoint=82,62 + +[5-wall@0,0|8] +endPoint=164,66 +startPoint=146,329 + +[5-windmill@170,186|9] +botWallVisible=false +bottom=false +height=39 +leftWallVisible=true +rightWallVisible=true +speed=2 +topWallVisible=false +width=88 + +[6-ball@182,57] +dummykey=true + +[6-blackhole@201,82|19] +exit=312,177 +exitDeg=183 +maxspeed=2.32 +minspeed=2.32 + +[6-bridge@90,199|21] +botWallVisible=false +height=79 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=22 + +[6-cup@314,309|20] +dummykey=true + +[6-cup@95,295|3] +dummykey=true + +[6-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[6-wall@0,0|10] +endPoint=323,159 +startPoint=327,193 + +[6-wall@0,0|11] +endPoint=141,261 +startPoint=73,339 + +[6-wall@0,0|12] +endPoint=219,69 +startPoint=205,105 + +[6-wall@0,0|13] +endPoint=205,105 +startPoint=181,84 + +[6-wall@0,0|14] +endPoint=163,32 +startPoint=219,69 + +[6-wall@0,0|15] +endPoint=324,334 +startPoint=293,314 + +[6-wall@0,0|16] +endPoint=323,159 +startPoint=61,169 + +[6-wall@0,0|17] +endPoint=327,193 +startPoint=74,203 + +[6-wall@0,0|18] +endPoint=61,169 +startPoint=74,203 + +[6-wall@0,0|4] +endPoint=256,248 +startPoint=293,314 + +[6-wall@0,0|5] +endPoint=256,248 +startPoint=331,297 + +[6-wall@0,0|6] +endPoint=141,261 +startPoint=101,247 + +[6-wall@0,0|7] +endPoint=324,334 +startPoint=331,297 + +[6-wall@0,0|8] +endPoint=163,32 +startPoint=181,84 + +[6-wall@0,0|9] +endPoint=101,247 +startPoint=73,339 + +[7-ball@31,371] +dummykey=true + +[7-bridge@139,19|134] +botWallVisible=true +height=26 +leftWallVisible=false +rightWallVisible=false +topWallVisible=true +width=94 + +[7-bridge@332,221|135] +botWallVisible=false +height=119 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=50 + +[7-cup@368,30|3] +dummykey=true + +[7-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=5 + +[7-puddle@-1,72|6] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@-10,81|4] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@10,11|15] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@108,11|12] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@11,37|28] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@113,1|10] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@12,39|27] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@128,3|30] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@146,4|17] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@15,31|11] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@15,48|32] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@162,4|19] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@176,19|20] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@179,4|16] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@185,26|77] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@189,192|133] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@190,185|119] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@191,201|132] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@191,37|83] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@193,18|22] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@194,47|23] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@195,175|118] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@195,31|62] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@195,53|109] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@196,211|121] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@198,222|120] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@198,230|90] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@198,234|105] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@199,243|68] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@199,62|21] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@200,167|69] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@201,66|80] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@202,255|82] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@204,261|100] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@204,272|67] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@204,313|74] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@205,156|58] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@205,278|104] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@205,285|76] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@205,292|101] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@205,300|66] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@205,305|102] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@205,79|54] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@206,307|115] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@206,320|103] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@206,87|107] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@207,145|106] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@207,151|56] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@208,122|61] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@208,4|111] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@208,95|59] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@209,111|63] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@209,135|57] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@209,327|64] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@210,105|79] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@214,-4|60] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@216,321|117] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@219,7|112] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@223,332|99] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@236,330|116] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@242,335|72] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@247,0|65] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@25,36|35] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@263,334|78] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@265,340|98] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@267,-2|110] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@272,332|71] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@282,338|97] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@288,-8|108] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@297,320|96] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@300,332|55] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@303,323|95] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@305,314|93] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@306,-12|114] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@314,308|92] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@317,320|81] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@319,316|70] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@32,32|33] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@322,-14|113] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@323,305|89] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@325,301|88] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@328,306|75] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@330,295|73] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@335,284|94] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@344,281|87] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@350,273|91] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@361,269|86] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@37,5|18] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@385,263|85] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@4,63|7] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@403,260|84] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@43,26|31] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@49,16|13] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@54,22|34] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@59,-1|14] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@63,18|26] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@76,11|5] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@77,17|25] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@8,50|8] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@8,58|29] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@81,20|24] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@98,24|9] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-sand@114,219|45] +changeEnabled=false +changeEvery=50 +height=44 +width=62 + +[7-sand@136,211|44] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@151,207|67] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@165,204|78] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@183,202|76] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|122] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|123] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|124] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|125] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|126] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|127] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|129] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|130] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|131] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|72] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|74] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|77] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@208,193|128] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@220,194|75] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@234,189|79] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@247,188|37] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@260,182|53] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@282,176|54] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@302,169|52] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@317,166|70] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@328,162|55] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@71,231|36] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@90,226|64] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[8-ball@78,251] +dummykey=true + +[8-blackhole@111,212|14] +exit=32,30 +exitDeg=0 +maxspeed=1 +minspeed=1 + +[8-blackhole@346,29|15] +exit=96,252 +exitDeg=139 +maxspeed=1 +minspeed=0 + +[8-blackhole@46,280|13] +exit=203,114 +exitDeg=308 +maxspeed=3 +minspeed=3 + +[8-cup@346,301|3] +dummykey=true + +[8-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=6 +par=3 + +[8-slope@23,11|16] +grade=2 +gradient=Horizontal +height=37 +reversed=true +stuckOnGround=false +width=342 + +[8-wall@0,0|10] +endPoint=345,272 +startPoint=212,101 + +[8-wall@0,0|11] +endPoint=388,311 +startPoint=345,272 + +[8-wall@0,0|12] +endPoint=388,311 +startPoint=335,328 + +[8-wall@0,0|4] +endPoint=146,204 +startPoint=54,312 + +[8-wall@0,0|5] +endPoint=19,283 +startPoint=54,312 + +[8-wall@0,0|6] +endPoint=104,195 +startPoint=19,283 + +[8-wall@0,0|7] +endPoint=146,204 +startPoint=104,195 + +[8-wall@0,0|8] +endPoint=335,328 +startPoint=163,88 + +[8-wall@0,0|9] +endPoint=212,101 +startPoint=163,88 + +[9-ball@54,55] +dummykey=true + +[9-bumper@103,164|8] +dummykey=true + +[9-bumper@114,230|11] +dummykey=true + +[9-bumper@128,197|9] +dummykey=true + +[9-bumper@141,161|5] +dummykey=true + +[9-bumper@152,123|0] +dummykey=true + +[9-bumper@156,85|2] +dummykey=true + +[9-bumper@162,25|13] +dummykey=true + +[9-bumper@187,157|6] +dummykey=true + +[9-bumper@221,153|12] +dummykey=true + +[9-bumper@242,329|23] +dummykey=true + +[9-bumper@243,290|18] +dummykey=true + +[9-bumper@248,219|14] +dummykey=true + +[9-bumper@248,254|17] +dummykey=true + +[9-bumper@254,187|16] +dummykey=true + +[9-bumper@257,149|3] +dummykey=true + +[9-bumper@277,339|21] +dummykey=true + +[9-bumper@317,338|20] +dummykey=true + +[9-bumper@346,321|22] +dummykey=true + +[9-bumper@371,295|24] +dummykey=true + +[9-bumper@38,348|19] +dummykey=true + +[9-bumper@58,324|15] +dummykey=true + +[9-bumper@66,170|7] +dummykey=true + +[9-bumper@77,296|10] +dummykey=true + +[9-bumper@97,261|4] +dummykey=true + +[9-cup@281,294|1] +dummykey=true + +[9-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=6 +par=3 diff --git a/kolf/courses/USApro b/kolf/courses/USApro new file mode 100644 index 00000000..bdc48566 --- /dev/null +++ b/kolf/courses/USApro @@ -0,0 +1,1982 @@ +[0-course@-50,-50] +Name=USA Pro +Name[bg]=Ю Ð•Ñ Ð•Ð¹ Про (USA Pro) +Name[bn]=ইউà¦à¦¸à¦ পà§à¦°à§‹ +Name[hi]=यूà¤à¤¸à¤ पà¥à¤°à¥‹ +Name[hu]=Amerikai profi +Name[ja]=USAプロ +Name[mk]=СÐД Профи +Name[pt_BR]=Pro EUA +Name[sr]=СÐД про +Name[sr@Latn]=SAD pro +Name[sv]=USA-proffs +Name[xx]=xxUSA Proxx +Name[zh_TW]=美國è·æ¥­é«˜çƒ +author=Rob Kaper + +[1-ball@22,350] +dummykey=true + +[1-bridge@306,87|2] +botWallVisible=false +height=212 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=68 + +[1-cup@78,38|9] +dummykey=true + +[1-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[1-puddle@-4,79|10] +changeEnabled=false +changeEvery=50 +height=218 +width=98 + +[1-puddle@171,196|1] +changeEnabled=false +changeEvery=50 +height=130 +width=596 + +[1-sand@14,207|0] +changeEnabled=false +changeEvery=50 +height=216 +width=840 + +[1-sign@4,371|14] +Comment=Daytona Beach, FL +Comment[bg]=Дейтона бийч, Флорида +Comment[bn]=ডেটোনা বীচ, ফà§à¦²à§‹à¦°à¦¿à¦¡à¦¾ +Comment[hi]=डायटोना बीच, फà¥à¤²à¥‹. +Comment[hu]=Daytona Beach, Florida +Comment[ja]=デイトナビーãƒ, FL +Comment[lv]=Daitonas pludmale, Florida +Comment[mk]=Дејтона Бич, Флорида +Comment[nb]=Daytona Beach, Florida +Comment[nn]=Daytona Beach, Florida +Comment[pt]=Praia de Daytona, FL +Comment[pt_BR]=Praia de Daytona, FL +Comment[ru]=Дайтон Бич +Comment[sr]=Дејтона бич, Флорида +Comment[sr@Latn]=Dejtona biÄ, Florida +Comment[sv]=Daytona Beach, Florida +Comment[ta]=டேடோனா கடலà¯, FL +Comment[tg]=Дайтон Бич, FL +Comment[tr]=Daytona Plajı, FL +Comment[uk]=Дайтона Біч +Comment[wa]=Daytona Beach, Floride, USA +Comment[xx]=xxDaytona Beach, FLxx +Comment[zh_CN]=代托纳滩,佛罗里达州 +Comment[zh_TW]=Daytona æµ·ç˜ï¼Œä½›ç¾…里é”å·ž +botWallVisible=false +height=34 +leftWallVisible=true +rightWallVisible=false +topWallVisible=true +width=411 + +[1-slope@-3,-4|11] +grade=5 +gradient=Horizontal +height=131 +reversed=true +stuckOnGround=false +width=222 + +[1-slope@139,282|7] +grade=6 +gradient=Vertical +height=117 +reversed=true +stuckOnGround=false +width=53 + +[1-slope@215,273|8] +grade=4 +gradient=Vertical +height=132 +reversed=false +stuckOnGround=false +width=53 + +[1-slope@247,2|12] +grade=5 +gradient=Horizontal +height=152 +reversed=false +stuckOnGround=false +width=56 + +[1-slope@60,281|6] +grade=4 +gradient=Vertical +height=119 +reversed=false +stuckOnGround=false +width=54 + +[1-wall@0,0|13] +endPoint=328,387 +startPoint=38,387 + +[1-wall@0,0|3] +endPoint=374,299 +startPoint=328,387 + +[1-wall@0,0|4] +endPoint=297,3 +startPoint=374,87 + +[1-wall@0,0|5] +endPoint=297,3 +startPoint=32,3 + +[10-ball@364,364] +dummykey=true + +[10-blackhole@142,54|9] +exit=372,376 +exitDeg=170 +maxspeed=5 +minspeed=5 + +[10-blackhole@193,248|7] +exit=196,175 +exitDeg=220 +maxspeed=1 +minspeed=1 + +[10-bridge@169,235|11] +botWallVisible=false +height=95 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=49 + +[10-bumper@122,163|17] +dummykey=true + +[10-bumper@127,179|16] +dummykey=true + +[10-bumper@135,151|19] +dummykey=true + +[10-bumper@136,195|15] +dummykey=true + +[10-bumper@145,212|14] +dummykey=true + +[10-bumper@151,140|20] +dummykey=true + +[10-bumper@157,227|13] +dummykey=true + +[10-bumper@167,130|21] +dummykey=true + +[10-bumper@175,228|12] +dummykey=true + +[10-bumper@195,226|8] +dummykey=true + +[10-bumper@214,227|22] +dummykey=true + +[10-bumper@219,130|28] +dummykey=true + +[10-bumper@232,227|34] +dummykey=true + +[10-bumper@233,143|27] +dummykey=true + +[10-bumper@242,212|18] +dummykey=true + +[10-bumper@246,156|26] +dummykey=true + +[10-bumper@253,198|23] +dummykey=true + +[10-bumper@260,168|25] +dummykey=true + +[10-bumper@262,184|24] +dummykey=true + +[10-cup@193,121|32] +dummykey=true + +[10-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[10-puddle@14,152|30] +changeEnabled=false +changeEvery=50 +height=302 +width=184 + +[10-puddle@140,286|29] +changeEnabled=false +changeEvery=50 +height=86 +width=622 + +[10-puddle@357,89|31] +changeEnabled=false +changeEvery=50 +height=422 +width=130 + +[10-sand@101,111|10] +changeEnabled=false +changeEvery=50 +height=90 +width=146 + +[10-sand@258,141|41] +changeEnabled=false +changeEvery=50 +height=38 +width=36 + +[10-sign@-1,360|42] +Comment=Washington DC (Pentagon) +Comment[bg]=Вашингтон, окръг ÐšÐ¾Ð»ÑƒÐ¼Ð±Ð¸Ñ (Пентагона) +Comment[bn]=ওয়াশিংটন ডিসি (পেনà§à¦Ÿà¦¾à¦—ন) +Comment[es]=Washington DC (Pentágono) +Comment[fr]=Washington DC (Pentagone) +Comment[gl]=Washington DC (Pentágono) +Comment[he]=הפנטגון בוושינגטון העיר +Comment[hi]=वाशिंगटन डीसी (पेंटागन) +Comment[it]=Washington DC (Pentagono) +Comment[ja]=ワシントンDC (ペンタゴン) +Comment[lv]=WaÅ¡ingtona, Kolumbijas apgabals (Pentagons) +Comment[mk]=Вашингтон ДЦ (Пентагон) +Comment[pt]=Washington DC (Pentágono) +Comment[pt_BR]=Washington DC (Pentágono) +Comment[ru]=Пентагон +Comment[sr]=Вашингтон ДЦ (Пентагон) +Comment[sr@Latn]=VaÅ¡ington DC (Pentagon) +Comment[ta]= வாஷிஙà¯à®Ÿà®©à¯ DC (பெனà¯à®Ÿà¯à®Ÿà®•à®©à¯) +Comment[tg]=Вашингтон DC (Пентагон) +Comment[uk]=Пентагон +Comment[wa]=Washington DC (Pentagone), USA +Comment[xx]=xxWashington DC (Pentagon)xx +Comment[zh_CN]=åŽç››é¡¿ç‰¹åŒº(五角大楼) +Comment[zh_TW]=è¯ç››é “特å€(五角大廈) +botWallVisible=true +height=49 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=189 + +[10-slope@136,173|4] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[10-slope@138,131|5] +grade=4 +gradient=Diagonal +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-slope@177,173|2] +grade=4 +gradient=Vertical +height=40 +reversed=true +stuckOnGround=false +width=40 + +[10-slope@178,133|1] +grade=4 +gradient=Vertical +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-slope@217,174|3] +grade=4 +gradient=Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[10-slope@218,133|6] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[10-wall@0,0|33] +endPoint=259,167 +startPoint=283,136 + +[10-wall@0,0|35] +endPoint=283,136 +startPoint=130,25 + +[10-wall@0,0|36] +endPoint=118,161 +startPoint=130,25 + +[10-wall@0,0|37] +endPoint=169,330 +startPoint=215,392 + +[10-wall@0,0|38] +endPoint=215,392 +startPoint=384,392 + +[10-wall@0,0|39] +endPoint=384,392 +startPoint=384,331 + +[10-wall@0,0|40] +endPoint=384,331 +startPoint=218,330 + +[11-ball@36,41] +dummykey=true + +[11-blackhole@140,181|15] +exit=114,265 +exitDeg=30 +maxspeed=3 +minspeed=1 + +[11-blackhole@225,184|13] +exit=77,64 +exitDeg=0 +maxspeed=9 +minspeed=4 + +[11-blackhole@256,272|18] +exit=195,249 +exitDeg=110 +maxspeed=3 +minspeed=3 + +[11-bridge@18,22|16] +botWallVisible=false +height=41 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=36 + +[11-cup@183,170|0] +dummykey=true + +[11-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=5 + +[11-puddle@182,144|3] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[11-puddle@289,3|10] +changeEnabled=false +changeEvery=50 +height=58 +width=242 + +[11-sand@17,316|17] +changeEnabled=false +changeEvery=50 +height=286 +width=318 + +[11-sand@60,174|4] +changeEnabled=false +changeEvery=50 +height=174 +width=84 + +[11-sign@-2,370|24] +Comment=Palm Springs, CO +Comment[bg]=Палм СпрингÑ, Колорадо +Comment[bn]=পাম সà§à¦ªà§à¦°à¦¿à¦™à§â€Œà¦¸, কলোরাডো +Comment[hi]=पॉम सà¥à¤ªà¥à¤°à¤¿à¤‚गà¥à¤¸, सीओ +Comment[hu]=Palm Springs, Connecticut +Comment[ja]=パームスプリング, CO +Comment[lv]=Palmspringsa, Colorado +Comment[mk]=Палм СпрингÑ, Колорадо +Comment[nb]=Palm Springs, Colorado +Comment[nn]=Palm Springs, Colorado +Comment[ru]=Палм Ð¡Ð¿Ñ€Ð¸Ð½Ð³Ñ +Comment[sr]=Палм ÑпрингÑ, Конектикат +Comment[sr@Latn]=Palm springs, Konektikat +Comment[sv]=Palm Springs, Colorado +Comment[ta]=பாம௠ஸà¯à®ªà®¿à®°à®¿à®™à¯à®¸à¯, CO +Comment[tg]=Палм СпрингÑ, CO +Comment[uk]=Палм Ð¡Ð¿Ñ€Ñ–Ð½Ò‘Ñ +Comment[wa]=Palm Springs, Colorado, USA +Comment[xx]=xxPalm Springs, COxx +Comment[zh_CN]=棕榈泉,科罗拉多州 +Comment[zh_TW]=棕櫚泉,科羅拉多州 +botWallVisible=true +height=36 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=146 + +[11-slope@109,150|2] +grade=4 +gradient=Elliptic +height=63 +reversed=true +stuckOnGround=false +width=63 + +[11-slope@120,-2|12] +grade=4 +gradient=Opposite Diagonal +height=51 +reversed=true +stuckOnGround=false +width=46 + +[11-slope@163,191|23] +grade=2 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[11-slope@165,8|11] +grade=4 +gradient=Vertical +height=40 +reversed=true +stuckOnGround=false +width=246 + +[11-slope@195,152|1] +grade=4 +gradient=Elliptic +height=61 +reversed=true +stuckOnGround=false +width=61 + +[11-slope@212,226|19] +grade=8 +gradient=Elliptic +height=91 +reversed=false +stuckOnGround=false +width=91 + +[11-wall@0,0|14] +endPoint=383,96 +startPoint=344,32 + +[11-wall@0,0|20] +endPoint=106,147 +startPoint=84,95 + +[11-wall@0,0|21] +endPoint=84,95 +startPoint=53,81 + +[11-wall@0,0|22] +endPoint=53,81 +startPoint=-1,88 + +[11-wall@0,0|5] +endPoint=189,108 +startPoint=106,147 + +[11-wall@0,0|6] +endPoint=189,108 +startPoint=277,180 + +[11-wall@0,0|7] +endPoint=277,180 +startPoint=181,283 + +[11-wall@0,0|8] +endPoint=181,283 +startPoint=345,375 + +[11-wall@0,0|9] +endPoint=345,375 +startPoint=383,96 + +[2-ball@66,149] +dummykey=true + +[2-bridge@-21,-1|1] +botWallVisible=true +height=19 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=428 + +[2-bumper@103,124|33] +dummykey=true + +[2-bumper@118,101|32] +dummykey=true + +[2-bumper@275,32|23] +dummykey=true + +[2-bumper@276,81|18] +dummykey=true + +[2-bumper@307,139|19] +dummykey=true + +[2-bumper@349,141|27] +dummykey=true + +[2-bumper@88,146|34] +dummykey=true + +[2-cup@272,132|0] +dummykey=true + +[2-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=4 + +[2-puddle@257,212|11] +changeEnabled=false +changeEvery=50 +height=50 +width=52 + +[2-sand@161,120|9] +changeEnabled=false +changeEvery=50 +height=38 +width=40 + +[2-sand@162,201|8] +changeEnabled=false +changeEvery=50 +height=40 +width=38 + +[2-sign@26,345|31] +Comment=Las Vegas, NV +Comment[bg]=Ð›Ð°Ñ Ð’ÐµÐ³Ð°Ñ, Ðевада +Comment[bn]=লাস ভেগাস, নেভাডা +Comment[el]=Λας Βέγκας, NV +Comment[he]=ל×ס ווגס, NV +Comment[hi]=लास वेगास, à¤à¤¨à¤µà¥€. +Comment[hu]=Las Vegas, Nevada +Comment[ja]=ラスベガス, NV +Comment[lv]=Lasvegasa, Nevada +Comment[mk]=Ð›Ð°Ñ Ð’ÐµÐ³Ð°Ñ, Ðевада +Comment[nb]=Las Vegas, Nevada +Comment[nn]=Las Vegas, Nevada +Comment[ru]=Ð›Ð°Ñ Ð’ÐµÐ³Ð°Ñ +Comment[sr]=Ð›Ð°Ñ Ð’ÐµÐ³Ð°Ñ, Ðевада +Comment[sr@Latn]=Las Vegas, Nevada +Comment[sv]=Las Vegas, Nevada +Comment[ta]= லாஸ௠வேகாஸà¯,NV +Comment[tg]=Ð›Ð°Ñ Ð’ÐµÐ³Ð°Ñ, NV +Comment[uk]=Ð›Ð°Ñ Ð’ÐµÐ³Ð°Ñ +Comment[wa]=Las Vegas, Nevada, USA +Comment[xx]=xxLas Vegas, NVxx +Comment[zh_CN]=拉斯维加斯,内åŽè¾¾å·ž +Comment[zh_TW]=拉斯維加斯,內è¯é”å·ž +botWallVisible=true +height=34 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=157 + +[2-slope@-1,296|25] +grade=4 +gradient=Vertical +height=109 +reversed=false +stuckOnGround=false +width=404 + +[2-slope@140,140|4] +grade=4 +gradient=Elliptic +height=41 +reversed=true +stuckOnGround=true +width=41 + +[2-slope@140,18|21] +grade=6 +gradient=Horizontal +height=84 +reversed=true +stuckOnGround=false +width=114 + +[2-slope@181,98|5] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=true +width=40 + +[2-slope@182,140|3] +grade=2 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=true +width=40 + +[2-slope@182,181|7] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=true +width=40 + +[2-slope@219,107|6] +grade=4 +gradient=Elliptic +height=73 +reversed=true +stuckOnGround=true +width=73 + +[2-slope@25,19|30] +grade=4 +gradient=Diagonal +height=152 +reversed=true +stuckOnGround=false +width=43 + +[2-slope@252,18|17] +grade=4 +gradient=Opposite Diagonal +height=64 +reversed=true +stuckOnGround=false +width=113 + +[2-slope@27,166|24] +grade=8 +gradient=Vertical +height=28 +reversed=false +stuckOnGround=false +width=78 + +[2-wall@0,0|10] +endPoint=105,193 +startPoint=58,224 + +[2-wall@0,0|12] +endPoint=290,103 +startPoint=145,97 + +[2-wall@0,0|13] +endPoint=145,97 +startPoint=105,168 + +[2-wall@0,0|14] +endPoint=58,224 +startPoint=248,350 + +[2-wall@0,0|15] +endPoint=248,350 +startPoint=366,224 + +[2-wall@0,0|16] +endPoint=366,224 +startPoint=366,19 + +[2-wall@0,0|2] +endPoint=290,220 +startPoint=290,103 + +[2-wall@0,0|20] +endPoint=24,19 +startPoint=24,193 + +[2-wall@0,0|22] +endPoint=290,220 +startPoint=260,251 + +[2-wall@0,0|26] +endPoint=221,223 +startPoint=260,251 + +[2-wall@0,0|28] +endPoint=105,168 +startPoint=105,193 + +[2-wall@0,0|29] +endPoint=105,193 +startPoint=24,193 + +[3-ball@334,12] +dummykey=true + +[3-cup@366,146|1] +dummykey=true + +[3-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[3-sign@-4,358|9] +Comment=San Francisco, CA +Comment[bg]=Сан ФранциÑко, ÐšÐ°Ð»Ð¸Ñ„Ð¾Ñ€Ð½Ð¸Ñ +Comment[bn]=সান ফà§à¦°à¦¾à¦¨à§à¦¸à¦¿à¦¸à¦•à§‹, কà§à¦¯à¦¾à¦²à¦¿à¦«à§‹à¦°à§à¦¨à¦¿à§Ÿà¦¾ +Comment[el]=Σαν ΦÏανσίσκο, CA +Comment[he]=סן פרנסיסקו,CA +Comment[hi]=सान फà¥à¤°à¤¾à¤‚सिसà¥à¤•à¥‹, सीठ+Comment[hu]=San Francisco, Kalifornia +Comment[ja]=サンフランシスコ, CA +Comment[lv]=Sanfrancisko, Kalifornija +Comment[mk]=Сан ФранциÑко, Калифорнија +Comment[nn]=San Francisco, California +Comment[pt]=São Francisco, CA +Comment[pt_BR]=São Francisco, CA +Comment[ru]=Сан-ФранциÑко +Comment[sr]=Сан ФранциÑко, Калифорнија +Comment[sr@Latn]=San Francisko, Kalifornija +Comment[sv]=San Francisco, Kalifornien +Comment[ta]= சான௠ஃபிரானà¯à®¸à®¿à®¸à¯à®•à¯‹,CA +Comment[tg]=Сан ФрантÑиÑко, CA +Comment[uk]=Сан-ФранциÑко +Comment[wa]=San Francisco, Californeye, USA +Comment[xx]=xxSan Francisco, CAxx +Comment[zh_CN]=æ´›æ‰çŸ¶ï¼ŒåŠ åˆ©ç¦å°¼äºšå·ž +Comment[zh_TW]=舊金山,加州 +botWallVisible=true +height=48 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=436 + +[3-slope@-163,-252|8] +grade=4 +gradient=Elliptic +height=384 +reversed=false +stuckOnGround=false +width=384 + +[3-slope@1,139|6] +grade=4 +gradient=Vertical +height=104 +reversed=false +stuckOnGround=false +width=394 + +[3-slope@195,-158|10] +grade=4 +gradient=Elliptic +height=273 +reversed=false +stuckOnGround=false +width=273 + +[3-slope@32,280|7] +grade=4 +gradient=Horizontal +height=49 +reversed=true +stuckOnGround=false +width=232 + +[3-wall@0,0|0] +endPoint=389,383 +startPoint=389,27 + +[3-wall@0,0|2] +endPoint=389,27 +startPoint=261,279 + +[3-wall@0,0|3] +endPoint=261,279 +startPoint=99,279 + +[3-wall@0,0|4] +endPoint=32,329 +startPoint=261,329 + +[3-wall@0,0|5] +endPoint=32,329 +startPoint=32,24 + +[4-ball@354,354] +dummykey=true + +[4-bridge@-11,172|2] +botWallVisible=false +height=20 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=472 + +[4-cup@182,182|0] +dummykey=true + +[4-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[4-sign@201,171|8] +Comment=Grand Canyon +Comment[ar]=الوادي العظيم +Comment[bg]=Гранд Каньон +Comment[bn]=গà§à¦°à§à¦¯à¦¾à¦¨à§à¦¡ কà§à¦¯à¦¾à¦¨à¦¿à§Ÿà¦¨ +Comment[el]=ΓÏαντ Κάνυον +Comment[es]=Gran Cañón +Comment[gl]=Gran Cañón +Comment[he]=גרנד קניון +Comment[hi]=गà¥à¤°à¤¾à¤‚ड केनà¥à¤¯à¥‰à¤¨ +Comment[ja]=グランドキャニオン +Comment[lv]=Lielais kanjons +Comment[mk]=Гранд кањон +Comment[ru]=Большой Каньон +Comment[sr]=Велики кањон +Comment[sr@Latn]=Veliki kanjon +Comment[ta]= கிராணà¯à®Ÿà¯ கேணà¯à®¯à®¾à®©à¯ +Comment[tg]=Канёни Калон +Comment[tr]=Büyük Kanyon +Comment[uk]=Великий каньйон +Comment[wa]=Grand Canyon, USA +Comment[xx]=xxGrand Canyonxx +Comment[zh_CN]=大峡谷 +Comment[zh_TW]=大峽谷 +botWallVisible=true +height=23 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=223 + +[4-slope@-1,129|1] +grade=4 +gradient=Vertical +height=61 +reversed=true +stuckOnGround=false +width=416 + +[4-slope@-14,192|3] +grade=4 +gradient=Vertical +height=38 +reversed=false +stuckOnGround=false +width=422 + +[4-slope@-2,273|6] +grade=4 +gradient=Vertical +height=45 +reversed=false +stuckOnGround=false +width=445 + +[4-slope@-2,44|7] +grade=4 +gradient=Vertical +height=53 +reversed=true +stuckOnGround=false +width=502 + +[4-slope@-45,230|5] +grade=4 +gradient=Vertical +height=42 +reversed=true +stuckOnGround=false +width=454 + +[4-slope@0,87|4] +grade=4 +gradient=Vertical +height=42 +reversed=false +stuckOnGround=false +width=432 + +[5-ball@23,348] +dummykey=true + +[5-bridge@107,258|9] +botWallVisible=true +height=26 +leftWallVisible=true +rightWallVisible=false +topWallVisible=true +width=112 + +[5-cup@120,271|0] +dummykey=true + +[5-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[5-puddle@117,135|2] +changeEnabled=false +changeEvery=50 +height=84 +width=110 + +[5-puddle@165,297|8] +changeEnabled=false +changeEvery=50 +height=214 +width=102 + +[5-puddle@173,168|7] +changeEnabled=false +changeEvery=50 +height=108 +width=62 + +[5-puddle@180,140|6] +changeEnabled=false +changeEvery=50 +height=50 +width=144 + +[5-sand@65,346|1] +changeEnabled=false +changeEvery=50 +height=438 +width=48 + +[5-sign@219,344|10] +Comment=Lake Tahoe, CA/NV +Comment[bg]=Езерото Тахо, КалифорниÑ/Ðевада +Comment[bn]=লেক টাহো, কà§à¦¯à¦¾à¦²à¦¿à¦«à§‹à¦°à§à¦¨à¦¿à§Ÿà¦¾/নেভাডা +Comment[el]=Λίμνη Tahoe, CA/NV +Comment[es]=Lago Tahoe, CA/NV +Comment[fr]=Lac Tahoe, CA/NV +Comment[gl]=Lago Tahoe, CA/NV +Comment[hi]=लेक ताहोà¤, सीà¤/à¤à¤¨à¤µà¥€ +Comment[hu]=Lake Tahoe, Kalifornia/Nevada +Comment[it]=Lago Tahoe, CA/NV +Comment[ja]=レイク・タホ, CA/NV +Comment[lv]=Taho ezers, Kalifornija/Nevada +Comment[mk]=Езеро Тахо, Калифорнија/Сев. Вирџинија +Comment[nn]=Lake Tahoe, California/Nevada +Comment[pt]=Lago Tahoe, CA/NV +Comment[pt_BR]=Lago Tahoe, CA/NV +Comment[ru]=Озеро Ð¢Ð°Ñ…Ð¾Ñ +Comment[sr]=Језеро Тахо, Кал./Ðев. +Comment[sr@Latn]=Jezero Taho, Kal./Nev. +Comment[sv]=Lake Tahoe, Kalifornien/Nevada +Comment[ta]= லேக௠தஹோ,CA/NV +Comment[tg]=Кӯли ТаҳоÑ, CA/NV +Comment[uk]=Озеро Тахое +Comment[wa]=Lak Tahoe, Californeye/Nevada, USA +Comment[xx]=xxLake Tahoe, CA/NVxx +Comment[zh_CN]=å¡”éœæ¹–,加利ç¦å°¼äºšå·ž/内åŽè¾¾å·ž +Comment[zh_TW]=太浩湖,加州/å…§è¯é”å·ž +botWallVisible=true +height=27 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=202 + +[5-slope@10,9|4] +grade=4 +gradient=Diagonal +height=110 +reversed=true +stuckOnGround=false +width=113 + +[5-slope@171,135|5] +grade=4 +gradient=Horizontal +height=268 +reversed=true +stuckOnGround=false +width=81 + +[5-slope@261,9|11] +grade=4 +gradient=Opposite Diagonal +height=112 +reversed=true +stuckOnGround=false +width=129 + +[5-slope@69,134|3] +grade=5 +gradient=Horizontal +height=281 +reversed=false +stuckOnGround=false +width=102 + +[5-wall@0,0|12] +endPoint=77,99 +startPoint=57,133 + +[5-wall@0,0|13] +endPoint=77,99 +startPoint=119,88 + +[6-ball@102,35] +dummykey=true + +[6-bridge@138,328|27] +botWallVisible=false +height=24 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=23 + +[6-bridge@168,127|8] +botWallVisible=false +height=92 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=21 + +[6-bridge@215,240|13] +botWallVisible=false +height=66 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=21 + +[6-bumper@182,182|15] +dummykey=true + +[6-bumper@182,182|16] +dummykey=true + +[6-cup@107,376|1] +dummykey=true + +[6-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=4 + +[6-puddle@-5,251|2] +changeEnabled=false +changeEvery=50 +height=296 +width=342 + +[6-puddle@186,179|4] +changeEnabled=false +changeEvery=50 +height=68 +width=192 + +[6-puddle@215,274|3] +changeEnabled=false +changeEvery=50 +height=64 +width=222 + +[6-puddle@222,374|7] +changeEnabled=false +changeEvery=50 +height=76 +width=148 + +[6-puddle@230,341|6] +changeEnabled=false +changeEvery=50 +height=20 +width=226 + +[6-puddle@292,354|5] +changeEnabled=false +changeEvery=50 +height=152 +width=144 + +[6-puddle@497,225|0] +changeEnabled=false +changeEvery=50 +height=508 +width=494 + +[6-sand@86,349|34] +changeEnabled=true +changeEvery=51 +height=188 +width=368 + +[6-sign@3,101|17] +Comment=Florida Keys, FL +Comment[bg]=Флорида КийÑ, Флорида +Comment[bn]=ফà§à¦²à§‹à¦°à¦¿à¦¡à¦¾ কিইস, ফà§à¦²à§‹à¦°à¦¿à¦¡à¦¾ +Comment[hi]=फà¥à¤²à¥‹à¤°à¤¿à¤¡à¤¾ कीस, à¤à¤«à¤à¤² +Comment[hu]=Florida Keys, Florida +Comment[ja]=フロリダキーズ, FL +Comment[mk]=Гребени на Флорида, Флорида +Comment[nn]=Florida Keys, Florida +Comment[pt_BR]=Flórida Keys, FL +Comment[ru]=Флорида +Comment[sr]=Флорида киз, Флорида +Comment[sr@Latn]=Florida kiz, Florida +Comment[sv]=Florida Keys, Florida +Comment[ta]=ஃபà¯à®³à¯‹à®°à®¿à®Ÿà®¾ கீஸà¯, FL +Comment[tg]=Флорида, FL +Comment[uk]=Флорида Кіз +Comment[wa]=Florida Keys, Floride, USA +Comment[xx]=xxFlorida Keys, FLxx +Comment[zh_CN]=佛罗里达链岛,佛罗里达州 +Comment[zh_TW]=佛羅里é”群島,佛羅里é”å·ž +botWallVisible=true +height=30 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=117 + +[6-slope@128,300|14] +grade=4 +gradient=Horizontal +height=42 +reversed=false +stuckOnGround=false +width=115 + +[6-slope@158,216|23] +grade=4 +gradient=Horizontal +height=32 +reversed=true +stuckOnGround=false +width=99 + +[6-slope@165,23|21] +grade=4 +gradient=Vertical +height=104 +reversed=false +stuckOnGround=false +width=28 + +[6-slope@193,23|22] +grade=4 +gradient=Horizontal +height=31 +reversed=false +stuckOnGround=false +width=149 + +[6-slope@193,53|35] +grade=4 +gradient=Elliptic +height=101 +reversed=false +stuckOnGround=false +width=101 + +[6-wall@0,0|10] +endPoint=177,244 +startPoint=215,240 + +[6-wall@0,0|11] +endPoint=236,240 +startPoint=224,213 + +[6-wall@0,0|12] +endPoint=224,213 +startPoint=189,219 + +[6-wall@0,0|18] +endPoint=338,24 +startPoint=189,127 + +[6-wall@0,0|19] +endPoint=58,19 +startPoint=168,127 + +[6-wall@0,0|20] +endPoint=58,19 +startPoint=338,24 + +[6-wall@0,0|24] +endPoint=236,306 +startPoint=221,332 + +[6-wall@0,0|25] +endPoint=215,306 +startPoint=152,310 + +[6-wall@0,0|26] +endPoint=221,332 +startPoint=161,328 + +[6-wall@0,0|28] +endPoint=152,310 +startPoint=138,328 + +[6-wall@0,0|29] +endPoint=121,396 +startPoint=161,352 + +[6-wall@0,0|30] +endPoint=121,396 +startPoint=39,396 + +[6-wall@0,0|31] +endPoint=39,396 +startPoint=94,373 + +[6-wall@0,0|32] +endPoint=94,373 +startPoint=127,347 + +[6-wall@0,0|33] +endPoint=127,347 +startPoint=138,352 + +[6-wall@0,0|9] +endPoint=177,244 +startPoint=168,219 + +[7-ball@89,370] +dummykey=true + +[7-cup@375,24|0] +dummykey=true + +[7-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[7-puddle@215,236|1] +changeEnabled=false +changeEvery=50 +height=340 +width=224 + +[7-puddle@245,87|3] +changeEnabled=false +changeEvery=50 +height=44 +width=150 + +[7-puddle@68,170|2] +changeEnabled=false +changeEvery=50 +height=78 +width=178 + +[7-sign@11,116|7] +Comment=Golden Gate +Comment[ar]=البوابة الذهبية +Comment[bg]=Голдън Гейт +Comment[bn]=গোলà§à¦¡à§‡à¦¨ গেট +Comment[cs]=Zlatá brána +Comment[el]=ΧÏυσή Ï€Ïλη +Comment[gl]=Ponte de Rande +Comment[he]=שער הזהב +Comment[hi]=गोलà¥à¤¡à¤¨ गेट +Comment[ja]=ゴールデンゲート +Comment[lv]=Zelta vÄrti +Comment[mk]=Голден Гејт +Comment[ru]=Золотые Ворота +Comment[sr]=Голден гејт +Comment[sr@Latn]=Golden gejt +Comment[ta]=கோலà¯à®Ÿà®©à¯ கேட௠+Comment[tg]=Дарвозаҳои Тиллоӣ +Comment[tr]=Altın Kapı +Comment[uk]=Золоті ворота +Comment[wa]=Golden Gate, USA +Comment[xx]=xxGolden Gatexx +Comment[zh_CN]=金门大桥 +Comment[zh_TW]=金門大橋 +botWallVisible=false +height=97 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=40 + +[7-sign@135,364|25] +Comment=San Francisco, CA +Comment[bg]=Сан ФранциÑко, ÐšÐ°Ð»Ð¸Ñ„Ð¾Ñ€Ð½Ð¸Ñ +Comment[bn]=সান ফà§à¦°à¦¾à¦¨à§à¦¸à¦¿à¦¸à¦•à§‹, কà§à¦¯à¦¾à¦²à¦¿à¦«à§‹à¦°à§à¦¨à¦¿à§Ÿà¦¾ +Comment[el]=Σαν ΦÏανσίσκο, CA +Comment[he]=סן פרנסיסקו,CA +Comment[hi]=सान फà¥à¤°à¤¾à¤‚सिसà¥à¤•à¥‹, सीठ+Comment[hu]=San Francisco, Kalifornia +Comment[ja]=サンフランシスコ, CA +Comment[lv]=Sanfrancisko, Kalifornija +Comment[mk]=Сан ФранциÑко, Калифорнија +Comment[nn]=San Francisco, California +Comment[pt]=São Francisco, CA +Comment[pt_BR]=São Francisco, CA +Comment[ru]=Сан-ФранциÑко +Comment[sr]=Сан ФранциÑко, Калифорнија +Comment[sr@Latn]=San Francisko, Kalifornija +Comment[sv]=San Francisco, Kalifornien +Comment[ta]= சான௠ஃபிரானà¯à®¸à®¿à®¸à¯à®•à¯‹,CA +Comment[tg]=Сан ФрантÑиÑко, CA +Comment[uk]=Сан-ФранциÑко +Comment[wa]=San Francisco, Californeye, USA +Comment[xx]=xxSan Francisco, CAxx +Comment[zh_CN]=æ´›æ‰çŸ¶ï¼ŒåŠ åˆ©ç¦å°¼äºšå·ž +Comment[zh_TW]=舊金山,加州 +botWallVisible=true +height=40 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=159 + +[7-sign@98,209|24] +Comment=Bay Bridge +Comment[ar]=جسر الخليج (باي( +Comment[bg]=Бей Бридж +Comment[bn]=বে বà§à¦°à¦¿à¦œ +Comment[de]=Bay-Brücke +Comment[es]=Puente de la Bahía +Comment[gl]=Ponte da Ría +Comment[he]=גשר המפרץ +Comment[hi]=बे-बà¥à¤°à¤¿à¤œ +Comment[ja]=ベイブリッジ +Comment[lv]=Beibridža +Comment[mk]=Беј Бриџ +Comment[pt_BR]=Ponte da Baía +Comment[ru]=Залив Сан-ФрациÑко +Comment[sr]=Беј бриџ +Comment[sr@Latn]=Bej bridž +Comment[ta]=விரிகà¯à®Ÿà®¾ பாலம௠+Comment[tg]=БÑй Бриҷ +Comment[tr]=Bay Köprüsü +Comment[uk]=Бей-бридж +Comment[xx]=xxBay Bridgexx +Comment[zh_CN]=海湾大桥 +Comment[zh_TW]=æµ·ç£å¤§æ©‹ +botWallVisible=true +height=24 +leftWallVisible=false +rightWallVisible=false +topWallVisible=true +width=241 + +[7-slope@11,212|4] +grade=6 +gradient=Elliptic +height=74 +reversed=false +stuckOnGround=false +width=74 + +[7-slope@13,313|23] +grade=4 +gradient=Vertical +height=34 +reversed=true +stuckOnGround=false +width=150 + +[7-slope@17,23|5] +grade=2 +gradient=Elliptic +height=108 +reversed=false +stuckOnGround=false +width=108 + +[7-slope@293,9|22] +grade=6 +gradient=Opposite Diagonal +height=94 +reversed=true +stuckOnGround=false +width=95 + +[7-slope@333,49|21] +grade=5 +gradient=Vertical +height=339 +reversed=false +stuckOnGround=false +width=56 + +[7-slope@55,211|6] +grade=4 +gradient=Horizontal +height=73 +reversed=true +stuckOnGround=false +width=78 + +[7-wall@0,0|10] +endPoint=88,294 +startPoint=153,386 + +[7-wall@0,0|11] +endPoint=120,132 +startPoint=51,116 + +[7-wall@0,0|12] +endPoint=120,132 +startPoint=169,71 + +[7-wall@0,0|13] +endPoint=169,71 +startPoint=249,59 + +[7-wall@0,0|14] +endPoint=249,59 +startPoint=322,74 + +[7-wall@0,0|15] +endPoint=322,74 +startPoint=321,99 + +[7-wall@0,0|16] +endPoint=321,99 +startPoint=292,107 + +[7-wall@0,0|17] +endPoint=292,107 +startPoint=322,165 + +[7-wall@0,0|18] +endPoint=339,209 +startPoint=322,165 + +[7-wall@0,0|19] +endPoint=339,232 +startPoint=317,325 + +[7-wall@0,0|20] +endPoint=317,325 +startPoint=270,388 + +[7-wall@0,0|8] +endPoint=98,209 +startPoint=51,213 + +[7-wall@0,0|9] +endPoint=98,232 +startPoint=88,294 + +[8-ball@202,333] +dummykey=true + +[8-cup@203,88|1] +dummykey=true + +[8-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[8-puddle@205,213|0] +changeEnabled=false +changeEvery=50 +height=210 +width=76 + +[8-sand@201,35|3] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[8-sand@202,363|2] +changeEnabled=false +changeEvery=50 +height=34 +width=98 + +[8-sign@147,370|19] +Comment=Washington DC +Comment[bg]=Вашингтон, окръг ÐšÐ¾Ð»ÑƒÐ¼Ð±Ð¸Ñ +Comment[bn]=ওয়াশিংটন ডিসি +Comment[el]=Ουάσινγκτον +Comment[he]=וושינגטון הבירה +Comment[hi]=वाशिंगटन डीसी +Comment[ja]=ワシントンDC +Comment[lv]=VaÅ¡ingtona, Kolumbijas apgabals +Comment[mk]=Вашингтон ДЦ +Comment[ru]=Вашингтон +Comment[sr]=Вашингтон ДЦ +Comment[sr@Latn]=VaÅ¡ington DC +Comment[ta]=வாஷிஙà¯à®Ÿà®©à¯ DC +Comment[tg]=Вашингтон DC +Comment[uk]=Вашингтон +Comment[wa]=Washington DC, USA +Comment[xx]=xxWashington DCxx +Comment[zh_CN]=åŽç››é¡¿ç‰¹åŒº +Comment[zh_TW]=è¯ç››é “ç‰¹å€ +botWallVisible=true +height=32 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=111 + +[8-slope@101,291|8] +grade=4 +gradient=Elliptic +height=59 +reversed=true +stuckOnGround=false +width=59 + +[8-slope@11,11|4] +grade=2 +gradient=Vertical +height=376 +reversed=false +stuckOnGround=false +width=88 + +[8-slope@136,8|18] +grade=5 +gradient=Elliptic +height=137 +reversed=true +stuckOnGround=false +width=137 + +[8-slope@174,46|10] +grade=5 +gradient=Elliptic +height=61 +reversed=false +stuckOnGround=false +width=61 + +[8-slope@244,9|7] +grade=3 +gradient=Vertical +height=381 +reversed=true +stuckOnGround=false +width=65 + +[8-slope@245,291|9] +grade=4 +gradient=Elliptic +height=59 +reversed=true +stuckOnGround=false +width=59 + +[8-slope@311,11|5] +grade=2 +gradient=Vertical +height=378 +reversed=false +stuckOnGround=false +width=75 + +[8-slope@99,11|6] +grade=3 +gradient=Vertical +height=377 +reversed=true +stuckOnGround=false +width=65 + +[8-wall@0,0|11] +endPoint=140,296 +startPoint=175,108 + +[8-wall@0,0|12] +endPoint=172,108 +startPoint=235,108 + +[8-wall@0,0|13] +endPoint=237,108 +startPoint=264,297 + +[8-wall@0,0|14] +endPoint=70,54 +startPoint=33,91 + +[8-wall@0,0|15] +endPoint=175,108 +startPoint=142,123 + +[8-wall@0,0|16] +endPoint=271,128 +startPoint=235,108 + +[8-wall@0,0|17] +endPoint=368,128 +startPoint=326,182 + +[9-ball@361,37] +dummykey=true + +[9-bridge@184,29|27] +botWallVisible=false +height=26 +leftWallVisible=false +rightWallVisible=false +topWallVisible=true +width=80 + +[9-bridge@218,278|26] +botWallVisible=true +height=28 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=80 + +[9-cup@125,313|0] +dummykey=true + +[9-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[9-puddle@128,389|12] +changeEnabled=false +changeEvery=50 +height=26 +width=28 + +[9-puddle@166,379|13] +changeEnabled=false +changeEvery=50 +height=10 +width=88 + +[9-puddle@216,370|14] +changeEnabled=false +changeEvery=50 +height=22 +width=30 + +[9-puddle@220,8|25] +changeEnabled=false +changeEvery=50 +height=40 +width=18 + +[9-puddle@222,46|24] +changeEnabled=false +changeEvery=50 +height=58 +width=20 + +[9-puddle@223,356|15] +changeEnabled=false +changeEvery=50 +height=28 +width=18 + +[9-puddle@225,82|23] +changeEnabled=false +changeEvery=50 +height=44 +width=18 + +[9-puddle@227,121|22] +changeEnabled=false +changeEvery=50 +height=74 +width=14 + +[9-puddle@231,152|21] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[9-puddle@240,335|16] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[9-puddle@254,171|20] +changeEnabled=false +changeEvery=50 +height=52 +width=36 + +[9-puddle@256,302|17] +changeEnabled=false +changeEvery=50 +height=100 +width=24 + +[9-puddle@257,205|19] +changeEnabled=false +changeEvery=50 +height=42 +width=22 + +[9-puddle@262,246|18] +changeEnabled=false +changeEvery=50 +height=66 +width=20 + +[9-sign@291,368|28] +Comment=Niagara Falls, NY +Comment[bg]=Ðиагара, ÐÑŽ Йорк +Comment[bn]=নায়েগà§à¦°à¦¾ জলপà§à¦°à¦ªà¦¾à¦¤, নিউ ইয়রà§à¦• +Comment[de]=Niagara-Fälle, NY +Comment[el]=ΚαταÏÏάχτες ÎιαγάÏα, NY +Comment[es]=Cataratas del Niágara, NY +Comment[fr]=Chutes du Niagara, NY +Comment[gl]=Fervenzas do Niagara, NY +Comment[he]=מפלי × ×™×גרה, NY +Comment[hi]=नियागà¥à¤°à¤¾ फालà¥à¤¸, à¤à¤¨à¤µà¤¾à¤¯ +Comment[hu]=Niagara Falls, New York +Comment[ja]=ナイアガラã®æ», NY +Comment[lv]=NiagÄras Å«denskritums, Å…ujorka +Comment[mk]=Ðијагарини водопади, ÐЈ +Comment[nn]=Niagara Falls, New York +Comment[pt]=Catarata do Niagara, NY +Comment[pt_BR]=Cataratas do Niágara, NY +Comment[ru]=ÐиагарÑкий водопад +Comment[sk]=Niagarské vodopády, NY +Comment[sr]=Ðијагарини водопади, Њујорк +Comment[sr@Latn]=Nijagarini vodopadi, Njujork +Comment[sv]=Niagarafallen, Staten New York +Comment[ta]=நயாகரா நீரà¯à®µà¯€à®´à¯à®šà¯à®šà®¿,NY +Comment[tg]=Шаршараи Ðиагара, NY +Comment[tr]=Niagara Åželalesi, NY +Comment[uk]=ÐіагарÑький водоÑпад +Comment[wa]=Niagara, Noû York, USA +Comment[xx]=xxNiagara Falls, NYxx +Comment[zh_CN]=尼亚加拉大瀑布,纽约州 +Comment[zh_TW]=尼加拉瀑布,ç´ç´„å·ž +botWallVisible=true +height=36 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=112 + +[9-slope@-3,148|11] +grade=4 +gradient=Vertical +height=57 +reversed=false +stuckOnGround=false +width=191 + +[9-slope@-5,92|1] +grade=4 +gradient=Vertical +height=45 +reversed=false +stuckOnGround=false +width=232 + +[9-slope@103,215|5] +grade=4 +gradient=Vertical +height=40 +reversed=false +stuckOnGround=false +width=40 + +[9-slope@143,214|6] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=false +stuckOnGround=false +width=40 + +[9-slope@144,254|2] +grade=4 +gradient=Horizontal +height=136 +reversed=true +stuckOnGround=false +width=42 + +[9-slope@189,147|10] +grade=4 +gradient=Opposite Diagonal +height=63 +reversed=false +stuckOnGround=false +width=70 + +[9-slope@226,93|7] +grade=4 +gradient=Opposite Diagonal +height=44 +reversed=false +stuckOnGround=false +width=184 + +[9-slope@252,171|9] +grade=4 +gradient=Diagonal +height=40 +reversed=false +stuckOnGround=false +width=40 + +[9-slope@291,172|8] +grade=4 +gradient=Vertical +height=41 +reversed=false +stuckOnGround=false +width=117 + +[9-slope@333,9|29] +grade=4 +gradient=Elliptic +height=59 +reversed=true +stuckOnGround=true +width=59 + +[9-slope@62,254|3] +grade=4 +gradient=Opposite Diagonal +height=133 +reversed=true +stuckOnGround=false +width=46 + +[9-slope@63,214|4] +grade=4 +gradient=Diagonal +height=40 +reversed=false +stuckOnGround=false +width=40 + +[9-wall@0,0|30] +endPoint=138,14 +startPoint=4,65 + +[9-wall@0,0|31] +endPoint=138,14 +startPoint=184,29 + +[9-wall@0,0|32] +endPoint=264,29 +startPoint=361,8 + +[9-wall@0,0|33] +endPoint=361,8 +startPoint=393,22 + +[9-wall@0,0|34] +endPoint=393,22 +startPoint=393,129 + +[9-wall@0,0|35] +endPoint=393,129 +startPoint=393,171 + +[9-wall@0,0|36] +endPoint=393,171 +startPoint=392,214 + +[9-wall@0,0|37] +endPoint=337,305 +startPoint=392,214 + +[9-wall@0,0|38] +endPoint=337,305 +startPoint=298,305 + +[9-wall@0,0|39] +endPoint=4,208 +startPoint=4,65 + +[9-wall@0,0|40] +endPoint=4,208 +startPoint=46,394 + +[9-wall@0,0|41] +endPoint=46,394 +startPoint=108,385 + +[9-wall@0,0|42] +endPoint=108,385 +startPoint=124,371 + +[9-wall@0,0|43] +endPoint=124,371 +startPoint=142,370 + +[9-wall@0,0|44] +endPoint=190,371 +startPoint=218,305 diff --git a/kolf/editor.cpp b/kolf/editor.cpp new file mode 100644 index 00000000..4cf6e61a --- /dev/null +++ b/kolf/editor.cpp @@ -0,0 +1,60 @@ +#include +#include + +#include +#include +#include +#include + +#include "editor.h" +#include "game.h" + +Editor::Editor(ObjectList *list, QWidget *parent, const char *name) + : QWidget(parent, name) +{ + this->list = list; + config = 0; + + hlayout = new QHBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint()); + + QVBoxLayout *vlayout = new QVBoxLayout(hlayout, KDialog::spacingHint()); + vlayout->addWidget(new QLabel(i18n("Add object:"), this)); + listbox = new KListBox(this, "Listbox"); + vlayout->addWidget(listbox); + hlayout->setStretchFactor(vlayout, 2); + + QStringList items; + Object *obj = 0; + for (obj = list->first(); obj; obj = list->next()) + items.append(obj->name()); + + listbox->insertStringList(items); + + connect(listbox, SIGNAL(executed(QListBoxItem *)), SLOT(listboxExecuted(QListBoxItem *))); +} + +void Editor::listboxExecuted(QListBoxItem * /*item*/) +{ + int curItem = listbox->currentItem(); + if (curItem < 0) + return; + + emit addNewItem(list->at(curItem)); +} + +void Editor::setItem(CanvasItem *item) +{ + delete config; + config = item->config(this); + if (!config) + return; + config->ctorDone(); + hlayout->addWidget(config); + hlayout->setStretchFactor(config, 2); + config->setFrameStyle(QFrame::Box | QFrame::Raised); + config->setLineWidth(1); + config->show(); + connect(config, SIGNAL(modified()), this, SIGNAL(changed())); +} + +#include "editor.moc" diff --git a/kolf/editor.h b/kolf/editor.h new file mode 100644 index 00000000..4d5a3ac5 --- /dev/null +++ b/kolf/editor.h @@ -0,0 +1,37 @@ +#ifndef EDITOR_H_INCLUDED +#define EDITOR_H_INCLUDED + +#include + +#include "game.h" + +class KListBox; +class QHBoxLayout; +class QListBoxItem; +class Config; + +class Editor : public QWidget +{ + Q_OBJECT + +public: + Editor(ObjectList *list, QWidget * = 0, const char * = 0); + +signals: + void changed(); + void addNewItem(Object *); + +public slots: + void setItem(CanvasItem *); + +private slots: + void listboxExecuted(QListBoxItem *); + +private: + ObjectList *list; + QHBoxLayout *hlayout; + KListBox *listbox; + Config *config; +}; + +#endif diff --git a/kolf/floater.cpp b/kolf/floater.cpp new file mode 100644 index 00000000..73612bc8 --- /dev/null +++ b/kolf/floater.cpp @@ -0,0 +1,277 @@ +#include +#include + +#include + +#include "floater.h" + +void FloaterGuide::aboutToDelete() +{ + game->removeItem(floater); + aboutToDie(); + floater->aboutToDie(); + delete floater; + almostDead = true; +} + +void FloaterGuide::aboutToDie() +{ + if (almostDead) + return; + else + Wall::aboutToDie(); +} + +void FloaterGuide::moveBy(double dx, double dy) +{ + Wall::moveBy(dx, dy); + if (floater) + floater->reset(); +} + +void FloaterGuide::setPoints(int xa, int ya, int xb, int yb) +{ + if (fabs(xa - xb) > 0 || fabs(ya - yb) > 0) + { + Wall::setPoints(xa, ya, xb, yb); + if (floater) + floater->reset(); + } +} + +Config *FloaterGuide::config(QWidget *parent) +{ + return floater->config(parent); +} + +///////////////////////// + +Floater::Floater(QRect rect, QCanvas *canvas) + : Bridge(rect, canvas), speedfactor(16) +{ + wall = 0; + setEnabled(true); + noUpdateZ = false; + haventMoved = true; + wall = new FloaterGuide(this, canvas); + wall->setPoints(100, 100, 200, 200); + wall->setPen(QPen(wall->pen().color().light(), wall->pen().width() - 1)); + move(wall->endPoint().x(), wall->endPoint().y()); + + setTopWallVisible(false); + setBotWallVisible(false); + setLeftWallVisible(false); + setRightWallVisible(false); + + newSize(width(), height()); + moveBy(0, 0); + setSpeed(0); + + editModeChanged(false); + reset(); +} + +void Floater::setGame(KolfGame *game) +{ + Bridge::setGame(game); + + wall->setGame(game); +} + +void Floater::editModeChanged(bool changed) +{ + if (changed) + wall->editModeChanged(true); + Bridge::editModeChanged(changed); + wall->setVisible(changed); +} + +void Floater::advance(int phase) +{ + if (!isEnabled()) + return; + + Bridge::advance(phase); + + if (phase == 1 && (xVelocity() || yVelocity())) + { + if (Vector(origin, QPoint(x(), y())).magnitude() > vector.magnitude()) + { + vector.setDirection(vector.direction() + M_PI); + origin = (origin == wall->startPoint()? wall->endPoint() : wall->startPoint()); + + setVelocity(-xVelocity(), -yVelocity()); + } + } +} + +void Floater::reset() +{ + QPoint start = wall->startPoint() + QPoint(wall->x(), wall->y()); + QPoint end = wall->endPoint() + QPoint(wall->x(), wall->y()); + + vector = Vector(end, start); + origin = end; + + move(origin.x(), origin.y()); + setSpeed(speed); +} + +QPtrList Floater::moveableItems() const +{ + QPtrList ret(wall->moveableItems()); + ret.append(wall); + ret.append(point); + return ret; +} + +void Floater::aboutToDie() +{ + if (wall) + wall->setVisible(false); + Bridge::aboutToDie(); + setEnabled(false); +} + +void Floater::setSpeed(int news) +{ + if (!wall || news < 0) + return; + speed = news; + + if (news == 0) + { + setVelocity(0, 0); + return; + } + + const double factor = (double)speed / 3.5; + setVelocity(-cos(vector.direction()) * factor, -sin(vector.direction()) * factor); +} + +void Floater::aboutToSave() +{ + setVelocity(0, 0); + noUpdateZ = true; + move(wall->endPoint().x() + wall->x(), wall->endPoint().y() + wall->y()); + noUpdateZ = false; +} + +void Floater::savingDone() +{ + setSpeed(speed); +} + +void Floater::moveBy(double dx, double dy) +{ + if (!isEnabled()) + return; + + QCanvasItemList l = collisions(false); + for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) + { + CanvasItem *item = dynamic_cast(*it); + + if (!noUpdateZ && item && item->canBeMovedByOthers()) + item->updateZ(this); + + if ((*it)->z() >= z()) + { + if (item && item->canBeMovedByOthers() && collidesWith(*it)) + { + if ((*it)->rtti() == Rtti_Ball) + { + //((Ball *)(*it))->setState(Rolling); + (*it)->moveBy(dx, dy); + if (game && game->hasFocus() && !game->isEditing() && game->curBall() == (Ball *)(*it)) + game->ballMoved(); + } + else if ((*it)->rtti() != Rtti_Putter) + (*it)->moveBy(dx, dy); + } + } + } + + point->dontMove(); + point->move(x() + width(), y() + height()); + + // this call must come after we have tested for collisions, otherwise we skip them when saving! + // that's a bad thing + QCanvasRectangle::moveBy(dx, dy); + + // because we don't do Bridge::moveBy(); + topWall->move(x(), y()); + botWall->move(x(), y() - 1); + leftWall->move(x(), y()); + rightWall->move(x(), y()); + + if (game && game->isEditing()) + game->updateHighlighter(); +} + +void Floater::saveState(StateDB *db) +{ + db->setPoint(QPoint(x(), y())); +} + +void Floater::loadState(StateDB *db) +{ + const QPoint moveTo = db->point(); + move(moveTo.x(), moveTo.y()); +} + +void Floater::save(KConfig *cfg) +{ + cfg->writeEntry("speed", speed); + cfg->writeEntry("startPoint", QPoint(wall->startPoint().x() + wall->x(), wall->startPoint().y() + wall->y())); + cfg->writeEntry("endPoint", QPoint(wall->endPoint().x() + wall->x(), wall->endPoint().y() + wall->y())); + + doSave(cfg); +} + +void Floater::load(KConfig *cfg) +{ + move(firstPoint.x(), firstPoint.y()); + + QPoint start(wall->startPoint() + QPoint(wall->x(), wall->y())); + start = cfg->readPointEntry("startPoint", &start); + QPoint end(wall->endPoint() + QPoint(wall->x(), wall->y())); + end = cfg->readPointEntry("endPoint", &end); + wall->setPoints(start.x(), start.y(), end.x(), end.y()); + wall->move(0, 0); + + setSpeed(cfg->readNumEntry("speed", -1)); + + doLoad(cfg); + reset(); +} + +void Floater::firstMove(int x, int y) +{ + firstPoint = QPoint(x, y); +} + +///////////////////////// + +FloaterConfig::FloaterConfig(Floater *floater, QWidget *parent) + : BridgeConfig(floater, parent) +{ + this->floater = floater; + m_vlayout->addStretch(); + + m_vlayout->addWidget(new QLabel(i18n("Moving speed"), this)); + QHBoxLayout *hlayout = new QHBoxLayout(m_vlayout, spacingHint()); + hlayout->addWidget(new QLabel(i18n("Slow"), this)); + QSlider *slider = new QSlider(0, 20, 2, floater->curSpeed(), Qt::Horizontal, this); + hlayout->addWidget(slider); + hlayout->addWidget(new QLabel(i18n("Fast"), this)); + connect(slider, SIGNAL(valueChanged(int)), this, SLOT(speedChanged(int))); +} + +void FloaterConfig::speedChanged(int news) +{ + floater->setSpeed(news); + changed(); +} + +#include "floater.moc" diff --git a/kolf/floater.h b/kolf/floater.h new file mode 100644 index 00000000..486f3bc0 --- /dev/null +++ b/kolf/floater.h @@ -0,0 +1,81 @@ +#ifndef FLOATER_H +#define FLOATER_H + +#include "game.h" + +class Floater; +class FloaterConfig : public BridgeConfig +{ + Q_OBJECT + +public: + FloaterConfig(Floater *floater, QWidget *parent); + +private slots: + void speedChanged(int news); + +private: + Floater *floater; +}; + +class FloaterGuide : public Wall +{ +public: + FloaterGuide(Floater *floater, QCanvas *canvas) : Wall(canvas) { this->floater = floater; almostDead = false; } + virtual void setPoints(int xa, int ya, int xb, int yb); + virtual void moveBy(double dx, double dy); + virtual Config *config(QWidget *parent); + virtual void aboutToDelete(); + virtual void aboutToDie(); + +private: + Floater *floater; + bool almostDead; +}; + +class Floater : public Bridge +{ +public: + Floater(QRect rect, QCanvas *canvas); + virtual bool collision(Ball *ball, long int id) { Bridge::collision(ball, id); return false; } + virtual void saveState(StateDB *db); + virtual void loadState(StateDB *db); + virtual void save(KConfig *cfg); + virtual void load(KConfig *cfg); + virtual bool loadLast() const { return true; } + virtual void firstMove(int x, int y); + virtual void aboutToSave(); + virtual void aboutToDie(); + virtual void savingDone(); + virtual void setGame(KolfGame *game); + virtual void editModeChanged(bool changed); + virtual bool moveable() const { return false; } + virtual void moveBy(double dx, double dy); + virtual Config *config(QWidget *parent) { return new FloaterConfig(this, parent); } + virtual QPtrList moveableItems() const; + virtual void advance(int phase); + void setSpeed(int news); + int curSpeed() const { return speed; } + + // called by floaterguide when changed; + void reset(); + +private: + int speedfactor; + int speed; + FloaterGuide *wall; + QPoint origin; + Vector vector; + bool noUpdateZ; + bool haventMoved; + QPoint firstPoint; +}; + +class FloaterObj : public Object +{ +public: + FloaterObj() { m_name = i18n("Floater"); m__name = "floater"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Floater(QRect(0, 0, 80, 40), canvas); } +}; + +#endif diff --git a/kolf/game.cpp b/kolf/game.cpp new file mode 100644 index 00000000..55b324ce --- /dev/null +++ b/kolf/game.cpp @@ -0,0 +1,4302 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "kcomboboxdialog.h" +#include "kvolumecontrol.h" +#include "vector.h" +#include "game.h" + + +inline QString makeGroup(int id, int hole, QString name, int x, int y) +{ + return QString("%1-%2@%3,%4|%5").arg(hole).arg(name).arg(x).arg(y).arg(id); +} + +inline QString makeStateGroup(int id, const QString &name) +{ + return QString("%1|%2").arg(name).arg(id); +} + +///////////////////////// + +RectPoint::RectPoint(QColor color, RectItem *rect, QCanvas *canvas) + : QCanvasEllipse(canvas) +{ + setZ(9999); + setSize(10, 10); + this->rect = rect; + setBrush(QBrush(color)); + setSizeFactor(1.0); + dontmove = false; +} + +void RectPoint::moveBy(double dx, double dy) +{ + QCanvasEllipse::moveBy(dx, dy); + + if (dontmove) + { + dontmove = false; + return; + } + + QCanvasItem *qitem = dynamic_cast(rect); + if (!qitem) + return; + + double nw = m_sizeFactor * fabs(x() - qitem->x()); + double nh = m_sizeFactor * fabs(y() - qitem->y()); + if (nw <= 0 || nh <= 0) + return; + + rect->newSize(nw, nh); +} + +Config *RectPoint::config(QWidget *parent) +{ + CanvasItem *citem = dynamic_cast(rect); + if (citem) + return citem->config(parent); + else + return CanvasItem::config(parent); +} + +///////////////////////// + +Arrow::Arrow(QCanvas *canvas) + : QCanvasLine(canvas) +{ + line1 = new QCanvasLine(canvas); + line2 = new QCanvasLine(canvas); + + m_angle = 0; + m_length = 20; + m_reversed = false; + + setPen(black); + + updateSelf(); + setVisible(false); +} + +void Arrow::setPen(QPen p) +{ + QCanvasLine::setPen(p); + line1->setPen(p); + line2->setPen(p); +} + +void Arrow::setZ(double newz) +{ + QCanvasLine::setZ(newz); + line1->setZ(newz); + line2->setZ(newz); +} + +void Arrow::setVisible(bool yes) +{ + QCanvasLine::setVisible(yes); + line1->setVisible(yes); + line2->setVisible(yes); +} + +void Arrow::moveBy(double dx, double dy) +{ + QCanvasLine::moveBy(dx, dy); + line1->moveBy(dx, dy); + line2->moveBy(dx, dy); +} + +void Arrow::aboutToDie() +{ + delete line1; + delete line2; +} + +void Arrow::updateSelf() +{ + QPoint start = startPoint(); + QPoint end(m_length * cos(m_angle), m_length * sin(m_angle)); + + if (m_reversed) + { + QPoint tmp(start); + start = end; + end = tmp; + } + + setPoints(start.x(), start.y(), end.x(), end.y()); + + const double lineLen = m_length / 2; + + const double angle1 = m_angle - M_PI / 2 - 1; + line1->move(end.x() + x(), end.y() + y()); + start = end; + end = QPoint(lineLen * cos(angle1), lineLen * sin(angle1)); + line1->setPoints(0, 0, end.x(), end.y()); + + const double angle2 = m_angle + M_PI / 2 + 1; + line2->move(start.x() + x(), start.y() + y()); + end = QPoint(lineLen * cos(angle2), lineLen * sin(angle2)); + line2->setPoints(0, 0, end.x(), end.y()); +} + +///////////////////////// + +BridgeConfig::BridgeConfig(Bridge *bridge, QWidget *parent) + : Config(parent) +{ + this->bridge = bridge; + + m_vlayout = new QVBoxLayout(this, marginHint(), spacingHint()); + QGridLayout *layout = new QGridLayout(m_vlayout, 2, 3, spacingHint()); + layout->addWidget(new QLabel(i18n("Walls on:"), this), 0, 0); + top = new QCheckBox(i18n("&Top"), this); + layout->addWidget(top, 0, 1); + connect(top, SIGNAL(toggled(bool)), this, SLOT(topWallChanged(bool))); + top->setChecked(bridge->topWallVisible()); + bot = new QCheckBox(i18n("&Bottom"), this); + layout->addWidget(bot, 1, 1); + connect(bot, SIGNAL(toggled(bool)), this, SLOT(botWallChanged(bool))); + bot->setChecked(bridge->botWallVisible()); + left = new QCheckBox(i18n("&Left"), this); + layout->addWidget(left, 1, 0); + connect(left, SIGNAL(toggled(bool)), this, SLOT(leftWallChanged(bool))); + left->setChecked(bridge->leftWallVisible()); + right = new QCheckBox(i18n("&Right"), this); + layout->addWidget(right, 1, 2); + connect(right, SIGNAL(toggled(bool)), this, SLOT(rightWallChanged(bool))); + right->setChecked(bridge->rightWallVisible()); +} + +void BridgeConfig::topWallChanged(bool yes) +{ + bridge->setTopWallVisible(yes); + changed(); +} + +void BridgeConfig::botWallChanged(bool yes) +{ + bridge->setBotWallVisible(yes); + changed(); +} + +void BridgeConfig::leftWallChanged(bool yes) +{ + bridge->setLeftWallVisible(yes); + changed(); +} + +void BridgeConfig::rightWallChanged(bool yes) +{ + bridge->setRightWallVisible(yes); + changed(); +} + +///////////////////////// + +Bridge::Bridge(QRect rect, QCanvas *canvas) + : QCanvasRectangle(rect, canvas) +{ + QColor color("#92772D"); + setBrush(QBrush(color)); + setPen(NoPen); + setZ(998); + + topWall = new Wall(canvas); + topWall->setAlwaysShow(true); + botWall = new Wall(canvas); + botWall->setAlwaysShow(true); + leftWall = new Wall(canvas); + leftWall->setAlwaysShow(true); + rightWall = new Wall(canvas); + rightWall->setAlwaysShow(true); + + setWallZ(z() + 0.01); + setWallColor(color); + + topWall->setVisible(false); + botWall->setVisible(false); + leftWall->setVisible(false); + rightWall->setVisible(false); + + point = new RectPoint(color, this, canvas); + editModeChanged(false); + + newSize(width(), height()); +} + +bool Bridge::collision(Ball *ball, long int /*id*/) +{ + ball->setFrictionMultiplier(.63); + return false; +} + +void Bridge::setWallZ(double newz) +{ + topWall->setZ(newz); + botWall->setZ(newz); + leftWall->setZ(newz); + rightWall->setZ(newz); +} + +void Bridge::setGame(KolfGame *game) +{ + CanvasItem::setGame(game); + topWall->setGame(game); + botWall->setGame(game); + leftWall->setGame(game); + rightWall->setGame(game); +} + +void Bridge::setWallColor(QColor color) +{ + topWall->setPen(QPen(color.dark(), 3)); + botWall->setPen(topWall->pen()); + leftWall->setPen(topWall->pen()); + rightWall->setPen(topWall->pen()); +} + +void Bridge::aboutToDie() +{ + delete point; + topWall->aboutToDie(); + delete topWall; + botWall->aboutToDie(); + delete botWall; + leftWall->aboutToDie(); + delete leftWall; + rightWall->aboutToDie(); + delete rightWall; +} + +void Bridge::editModeChanged(bool changed) +{ + point->setVisible(changed); + moveBy(0, 0); +} + +void Bridge::moveBy(double dx, double dy) +{ + QCanvasRectangle::moveBy(dx, dy); + + point->dontMove(); + point->move(x() + width(), y() + height()); + + topWall->move(x(), y()); + botWall->move(x(), y() - 1); + leftWall->move(x(), y()); + rightWall->move(x(), y()); + + QCanvasItemList list = collisions(true); + for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) + { + CanvasItem *citem = dynamic_cast(*it); + if (citem) + citem->updateZ(); + } +} + +void Bridge::load(KConfig *cfg) +{ + doLoad(cfg); +} + +void Bridge::doLoad(KConfig *cfg) +{ + newSize(cfg->readNumEntry("width", width()), cfg->readNumEntry("height", height())); + setTopWallVisible(cfg->readBoolEntry("topWallVisible", topWallVisible())); + setBotWallVisible(cfg->readBoolEntry("botWallVisible", botWallVisible())); + setLeftWallVisible(cfg->readBoolEntry("leftWallVisible", leftWallVisible())); + setRightWallVisible(cfg->readBoolEntry("rightWallVisible", rightWallVisible())); +} + +void Bridge::save(KConfig *cfg) +{ + doSave(cfg); +} + +void Bridge::doSave(KConfig *cfg) +{ + cfg->writeEntry("width", width()); + cfg->writeEntry("height", height()); + cfg->writeEntry("topWallVisible", topWallVisible()); + cfg->writeEntry("botWallVisible", botWallVisible()); + cfg->writeEntry("leftWallVisible", leftWallVisible()); + cfg->writeEntry("rightWallVisible", rightWallVisible()); +} + +QPtrList Bridge::moveableItems() const +{ + QPtrList ret; + ret.append(point); + return ret; +} + +void Bridge::newSize(int width, int height) +{ + setSize(width, height); +} + +void Bridge::setSize(int width, int height) +{ + QCanvasRectangle::setSize(width, height); + + topWall->setPoints(0, 0, width, 0); + botWall->setPoints(0, height, width, height); + leftWall->setPoints(0, 0, 0, height); + rightWall->setPoints(width, 0, width, height); + + moveBy(0, 0); +} + +///////////////////////// + +WindmillConfig::WindmillConfig(Windmill *windmill, QWidget *parent) + : BridgeConfig(windmill, parent) +{ + this->windmill = windmill; + m_vlayout->addStretch(); + + QCheckBox *check = new QCheckBox(i18n("Windmill on bottom"), this); + check->setChecked(windmill->bottom()); + connect(check, SIGNAL(toggled(bool)), this, SLOT(endChanged(bool))); + m_vlayout->addWidget(check); + + QHBoxLayout *hlayout = new QHBoxLayout(m_vlayout, spacingHint()); + hlayout->addWidget(new QLabel(i18n("Slow"), this)); + QSlider *slider = new QSlider(1, 10, 1, windmill->curSpeed(), Qt::Horizontal, this); + hlayout->addWidget(slider); + hlayout->addWidget(new QLabel(i18n("Fast"), this)); + connect(slider, SIGNAL(valueChanged(int)), this, SLOT(speedChanged(int))); + + endChanged(check->isChecked()); +} + +void WindmillConfig::speedChanged(int news) +{ + windmill->setSpeed(news); + changed(); +} + +void WindmillConfig::endChanged(bool bottom) +{ + windmill->setBottom(bottom); + changed(); + + bot->setEnabled(!bottom); + if (startedUp) + { + bot->setChecked(!bottom); + botWallChanged(bot->isChecked()); + } + top->setEnabled(bottom); + if (startedUp) + { + top->setChecked(bottom); + topWallChanged(top->isChecked()); + } +} + +///////////////////////// + +Windmill::Windmill(QRect rect, QCanvas *canvas) + : Bridge(rect, canvas), speedfactor(16), m_bottom(true) +{ + guard = new WindmillGuard(canvas); + guard->setPen(QPen(black, 5)); + guard->setVisible(true); + guard->setAlwaysShow(true); + setSpeed(5); + guard->setZ(wallZ() + .1); + + left = new Wall(canvas); + left->setPen(wallPen()); + left->setAlwaysShow(true); + right = new Wall(canvas); + right->setPen(wallPen()); + right->setAlwaysShow(true); + left->setZ(wallZ()); + right->setZ(wallZ()); + left->setVisible(true); + right->setVisible(true); + + setTopWallVisible(false); + setBotWallVisible(false); + setLeftWallVisible(true); + setRightWallVisible(true); + + newSize(width(), height()); + moveBy(0, 0); +} + +void Windmill::aboutToDie() +{ + Bridge::aboutToDie(); + guard->aboutToDie(); + delete guard; + left->aboutToDie(); + delete left; + right->aboutToDie(); + delete right; +} + +void Windmill::setSpeed(int news) +{ + if (news < 0) + return; + speed = news; + guard->setXVelocity(((double)news / (double)3) * (guard->xVelocity() > 0? 1 : -1)); +} + +void Windmill::setGame(KolfGame *game) +{ + Bridge::setGame(game); + guard->setGame(game); + left->setGame(game); + right->setGame(game); +} + +void Windmill::save(KConfig *cfg) +{ + cfg->writeEntry("speed", speed); + cfg->writeEntry("bottom", m_bottom); + + doSave(cfg); +} + +void Windmill::load(KConfig *cfg) +{ + setSpeed(cfg->readNumEntry("speed", -1)); + + doLoad(cfg); + + left->editModeChanged(false); + right->editModeChanged(false); + guard->editModeChanged(false); + + setBottom(cfg->readBoolEntry("bottom", true)); +} + +void Windmill::moveBy(double dx, double dy) +{ + Bridge::moveBy(dx, dy); + + left->move(x(), y()); + right->move(x(), y()); + + guard->moveBy(dx, dy); + guard->setBetween(x(), x() + width()); + + update(); +} + +void Windmill::setSize(int width, int height) +{ + newSize(width, height); +} + +void Windmill::setBottom(bool yes) +{ + m_bottom = yes; + newSize(width(), height()); +} + +void Windmill::newSize(int width, int height) +{ + Bridge::newSize(width, height); + + const int indent = width / 4; + + double indentY = m_bottom? height : 0; + left->setPoints(0, indentY, indent, indentY); + right->setPoints(width - indent, indentY, width, indentY); + + guard->setBetween(x(), x() + width); + double guardY = m_bottom? height + 4 : -4; + guard->setPoints(0, guardY, (double)indent / (double)1.07 - 2, guardY); +} + +///////////////////////// + +void WindmillGuard::advance(int phase) +{ + Wall::advance(phase); + + if (phase == 1) + { + if (x() + startPoint().x() <= min) + setXVelocity(fabs(xVelocity())); + else if (x() + endPoint().x() >= max) + setXVelocity(-fabs(xVelocity())); + } +} + +///////////////////////// + +Sign::Sign(QCanvas *canvas) + : Bridge(QRect(0, 0, 110, 40), canvas) +{ + setZ(998.8); + m_text = m_untranslatedText = i18n("New Text"); + setBrush(QBrush(white)); + setWallColor(black); + setWallZ(z() + .01); + + setTopWallVisible(true); + setBotWallVisible(true); + setLeftWallVisible(true); + setRightWallVisible(true); +} + +void Sign::load(KConfig *cfg) +{ + m_text = cfg->readEntry("Comment", m_text); + m_untranslatedText = cfg->readEntryUntranslated("Comment", m_untranslatedText); + + doLoad(cfg); +} + +void Sign::save(KConfig *cfg) +{ + cfg->writeEntry("Comment", m_untranslatedText); + + doSave(cfg); +} + +void Sign::setText(const QString &text) +{ + m_text = text; + m_untranslatedText = text; + + update(); +} + +void Sign::draw(QPainter &painter) +{ + Bridge::draw(painter); + + painter.setPen(QPen(black, 1)); + QSimpleRichText txt(m_text, kapp->font()); + const int indent = wallPen().width() + 3; + txt.setWidth(width() - 2*indent); + QColorGroup colorGroup; + colorGroup.setColor(QColorGroup::Foreground, black); + colorGroup.setColor(QColorGroup::Text, black); + colorGroup.setColor(QColorGroup::Background, black); + colorGroup.setColor(QColorGroup::Base, black); + txt.draw(&painter, x() + indent, y(), QRect(x() + indent, y(), width() - indent, height() - indent), colorGroup); +} + +///////////////////////// + +SignConfig::SignConfig(Sign *sign, QWidget *parent) + : BridgeConfig(sign, parent) +{ + this->sign = sign; + m_vlayout->addStretch(); + m_vlayout->addWidget(new QLabel(i18n("Sign HTML:"), this)); + KLineEdit *name = new KLineEdit(sign->text(), this); + m_vlayout->addWidget(name); + connect(name, SIGNAL(textChanged(const QString &)), this, SLOT(textChanged(const QString &))); +} + +void SignConfig::textChanged(const QString &text) +{ + sign->setText(text); + changed(); +} + +///////////////////////// + +EllipseConfig::EllipseConfig(Ellipse *ellipse, QWidget *parent) + : Config(parent), slow1(0), fast1(0), slow2(0), fast2(0), slider1(0), slider2(0) +{ + this->ellipse = ellipse; + + m_vlayout = new QVBoxLayout(this, marginHint(), spacingHint()); + + QCheckBox *check = new QCheckBox(i18n("Enable show/hide"), this); + m_vlayout->addWidget(check); + connect(check, SIGNAL(toggled(bool)), this, SLOT(check1Changed(bool))); + check->setChecked(ellipse->changeEnabled()); + + QHBoxLayout *hlayout = new QHBoxLayout(m_vlayout, spacingHint()); + slow1 = new QLabel(i18n("Slow"), this); + hlayout->addWidget(slow1); + slider1 = new QSlider(1, 100, 5, 100 - ellipse->changeEvery(), Qt::Horizontal, this); + hlayout->addWidget(slider1); + fast1 = new QLabel(i18n("Fast"), this); + hlayout->addWidget(fast1); + + connect(slider1, SIGNAL(valueChanged(int)), this, SLOT(value1Changed(int))); + + check1Changed(ellipse->changeEnabled()); + + // TODO add slider2 and friends and make it possible for ellipses to grow and contract + + m_vlayout->addStretch(); +} + +void EllipseConfig::value1Changed(int news) +{ + ellipse->setChangeEvery(100 - news); + changed(); +} + +void EllipseConfig::value2Changed(int /*news*/) +{ + changed(); +} + +void EllipseConfig::check1Changed(bool on) +{ + ellipse->setChangeEnabled(on); + if (slider1) + slider1->setEnabled(on); + if (slow1) + slow1->setEnabled(on); + if (fast1) + fast1->setEnabled(on); + + changed(); +} + +void EllipseConfig::check2Changed(bool on) +{ + //ellipse->setChangeEnabled(on); + if (slider2) + slider2->setEnabled(on); + if (slow2) + slow2->setEnabled(on); + if (fast2) + fast2->setEnabled(on); + + changed(); +} + +///////////////////////// + +Ellipse::Ellipse(QCanvas *canvas) + : QCanvasEllipse(canvas) +{ + savingDone(); + setChangeEnabled(false); + setChangeEvery(50); + count = 0; + setVisible(true); + + point = new RectPoint(black, this, canvas); + point->setSizeFactor(2.0); +} + +void Ellipse::aboutToDie() +{ + delete point; +} + +void Ellipse::setChangeEnabled(bool changeEnabled) +{ + m_changeEnabled = changeEnabled; + setAnimated(m_changeEnabled); + + if (!m_changeEnabled) + setVisible(true); +} + +QPtrList Ellipse::moveableItems() const +{ + QPtrList ret; + ret.append(point); + return ret; +} + +void Ellipse::newSize(int width, int height) +{ + QCanvasEllipse::setSize(width, height); +} + +void Ellipse::moveBy(double dx, double dy) +{ + QCanvasEllipse::moveBy(dx, dy); + + point->dontMove(); + point->move(x() + width() / 2, y() + height() / 2); +} + +void Ellipse::editModeChanged(bool changed) +{ + point->setVisible(changed); + moveBy(0, 0); +} + +void Ellipse::advance(int phase) +{ + QCanvasEllipse::advance(phase); + + if (phase == 1 && m_changeEnabled && !dontHide) + { + if (count > (m_changeEvery + 10) * 1.8) + count = 0; + if (count == 0) + setVisible(!isVisible()); + + count++; + } +} + +void Ellipse::load(KConfig *cfg) +{ + setChangeEnabled(cfg->readBoolEntry("changeEnabled", changeEnabled())); + setChangeEvery(cfg->readNumEntry("changeEvery", changeEvery())); + double newWidth = width(), newHeight = height(); + newWidth = cfg->readNumEntry("width", newWidth); + newHeight = cfg->readNumEntry("height", newHeight); + newSize(newWidth, newHeight); +} + +void Ellipse::save(KConfig *cfg) +{ + cfg->writeEntry("changeEvery", changeEvery()); + cfg->writeEntry("changeEnabled", changeEnabled()); + cfg->writeEntry("width", width()); + cfg->writeEntry("height", height()); +} + +Config *Ellipse::config(QWidget *parent) +{ + return new EllipseConfig(this, parent); +} + +void Ellipse::aboutToSave() +{ + setVisible(true); + dontHide = true; +} + +void Ellipse::savingDone() +{ + dontHide = false; +} + +///////////////////////// + +Puddle::Puddle(QCanvas *canvas) + : Ellipse(canvas) +{ + setSize(45, 30); + + QBrush brush; + QPixmap pic; + + if (!QPixmapCache::find("puddle", pic)) + { + pic.load(locate("appdata", "pics/puddle.png")); + QPixmapCache::insert("puddle", pic); + } + + brush.setPixmap(pic); + setBrush(brush); + + KPixmap pointPic(pic); + KPixmapEffect::intensity(pointPic, .45); + brush.setPixmap(pointPic); + point->setBrush(brush); + + setZ(-25); +} + +bool Puddle::collision(Ball *ball, long int /*id*/) +{ + if (ball->isVisible()) + { + QCanvasRectangle i(QRect(ball->x(), ball->y(), 1, 1), canvas()); + i.setVisible(true); + + // is center of ball in? + if (i.collidesWith(this)/* && ball->curVector().magnitude() < 4*/) + { + playSound("puddle"); + ball->setAddStroke(ball->addStroke() + 1); + ball->setPlaceOnGround(true); + ball->setVisible(false); + ball->setState(Stopped); + ball->setVelocity(0, 0); + if (game && game->curBall() == ball) + game->stoppedBall(); + } + else + return true; + } + + return false; +} + +///////////////////////// + +Sand::Sand(QCanvas *canvas) + : Ellipse(canvas) +{ + setSize(45, 40); + + QBrush brush; + QPixmap pic; + + if (!QPixmapCache::find("sand", pic)) + { + pic.load(locate("appdata", "pics/sand.png")); + QPixmapCache::insert("sand", pic); + } + + brush.setPixmap(pic); + setBrush(brush); + + KPixmap pointPic(pic); + KPixmapEffect::intensity(pointPic, .45); + brush.setPixmap(pointPic); + point->setBrush(brush); + + setZ(-26); +} + +bool Sand::collision(Ball *ball, long int /*id*/) +{ + QCanvasRectangle i(QRect(ball->x(), ball->y(), 1, 1), canvas()); + i.setVisible(true); + + // is center of ball in? + if (i.collidesWith(this)/* && ball->curVector().magnitude() < 4*/) + { + if (ball->curVector().magnitude() > 0) + ball->setFrictionMultiplier(7); + else + { + ball->setVelocity(0, 0); + ball->setState(Stopped); + } + } + + return true; +} + +///////////////////////// + +Putter::Putter(QCanvas *canvas) + : QCanvasLine(canvas) +{ + m_showGuideLine = true; + oneDegree = M_PI / 180; + len = 9; + angle = 0; + + guideLine = new QCanvasLine(canvas); + guideLine->setPen(QPen(white, 1, QPen::DotLine)); + guideLine->setZ(998.8); + + setPen(QPen(black, 4)); + putterWidth = 11; + maxAngle = 2 * M_PI; + + hideInfo(); + + // this also sets Z + resetAngles(); +} + +void Putter::showInfo() +{ + guideLine->setVisible(isVisible()); +} + +void Putter::hideInfo() +{ + guideLine->setVisible(m_showGuideLine? isVisible() : false); +} + +void Putter::moveBy(double dx, double dy) +{ + QCanvasLine::moveBy(dx, dy); + guideLine->move(x(), y()); +} + +void Putter::setShowGuideLine(bool yes) +{ + m_showGuideLine = yes; + setVisible(isVisible()); +} + +void Putter::setVisible(bool yes) +{ + QCanvasLine::setVisible(yes); + guideLine->setVisible(m_showGuideLine? yes : false); +} + +void Putter::setOrigin(int _x, int _y) +{ + setVisible(true); + move(_x, _y); + len = 9; + finishMe(); +} + +void Putter::setAngle(Ball *ball) +{ + angle = angleMap.contains(ball)? angleMap[ball] : 0; + finishMe(); +} + +void Putter::go(Direction d, Amount amount) +{ + double addition = (amount == Amount_More? 6 * oneDegree : amount == Amount_Less? .5 * oneDegree : 2 * oneDegree); + + switch (d) + { + case Forwards: + len -= 1; + guideLine->setVisible(false); + break; + case Backwards: + len += 1; + guideLine->setVisible(false); + break; + case D_Left: + angle += addition; + if (angle > maxAngle) + angle -= maxAngle; + break; + case D_Right: + angle -= addition; + if (angle < 0) + angle = maxAngle - fabs(angle); + break; + } + + finishMe(); +} + +void Putter::finishMe() +{ + midPoint.setX(cos(angle) * len); + midPoint.setY(-sin(angle) * len); + + QPoint start; + QPoint end; + + if (midPoint.y() || !midPoint.x()) + { + start.setX(midPoint.x() - putterWidth * sin(angle)); + start.setY(midPoint.y() - putterWidth * cos(angle)); + end.setX(midPoint.x() + putterWidth * sin(angle)); + end.setY(midPoint.y() + putterWidth * cos(angle)); + } + else + { + start.setX(midPoint.x()); + start.setY(midPoint.y() + putterWidth); + end.setY(midPoint.y() - putterWidth); + end.setX(midPoint.x()); + } + + guideLine->setPoints(midPoint.x(), midPoint.y(), -cos(angle) * len * 4, sin(angle) * len * 4); + + setPoints(start.x(), start.y(), end.x(), end.y()); +} + +///////////////////////// + +Bumper::Bumper(QCanvas *canvas) + : QCanvasEllipse(20, 20, canvas) +{ + setZ(-25); + + firstColor = QColor("#E74804"); + secondColor = firstColor.light(); + + count = 0; + setBrush(firstColor); + setAnimated(false); + + inside = new Inside(this, canvas); + inside->setBrush(firstColor.light(109)); + inside->setSize(width() / 2.6, height() / 2.6); + inside->show(); +} + +void Bumper::aboutToDie() +{ + delete inside; +} + +void Bumper::moveBy(double dx, double dy) +{ + QCanvasEllipse::moveBy(dx, dy); + //const double insideLen = (double)(width() - inside->width()) / 2.0; + inside->move(x(), y()); +} + +void Bumper::editModeChanged(bool changed) +{ + inside->setVisible(!changed); +} + +void Bumper::advance(int phase) +{ + QCanvasEllipse::advance(phase); + + if (phase == 1) + { + count++; + if (count > 2) + { + count = 0; + setBrush(firstColor); + update(); + setAnimated(false); + } + } +} + +bool Bumper::collision(Ball *ball, long int /*id*/) +{ + setBrush(secondColor); + + double speed = 1.8 + ball->curVector().magnitude() * .9; + if (speed > 8) + speed = 8; + + const QPoint start(x(), y()); + const QPoint end(ball->x(), ball->y()); + + Vector betweenVector(start, end); + betweenVector.setMagnitude(speed); + + // add some randomness so we don't go indefinetely + betweenVector.setDirection(betweenVector.direction() + deg2rad((kapp->random() % 3) - 1)); + + ball->setVector(betweenVector); + // for some reason, x is always switched... + ball->setXVelocity(-ball->xVelocity()); + ball->setState(Rolling); + + setAnimated(true); + + return true; +} + +///////////////////////// + +Hole::Hole(QColor color, QCanvas *canvas) + : QCanvasEllipse(15, 15, canvas) +{ + setZ(998.1); + setPen(black); + setBrush(color); +} + +bool Hole::collision(Ball *ball, long int /*id*/) +{ + bool wasCenter = false; + + switch (result(QPoint(ball->x(), ball->y()), ball->curVector().magnitude(), &wasCenter)) + { + case Result_Holed: + place(ball, wasCenter); + return false; + + default: + break; + } + + return true; +} + +HoleResult Hole::result(QPoint p, double s, bool * /*wasCenter*/) +{ + const double longestRadius = width() > height()? width() : height(); + if (s > longestRadius / 5.0) + return Result_Miss; + + QCanvasRectangle i(QRect(p, QSize(1, 1)), canvas()); + i.setVisible(true); + + // is center of ball in cup? + if (i.collidesWith(this)) + { + return Result_Holed; + } + else + return Result_Miss; +} + +///////////////////////// + +Cup::Cup(QCanvas *canvas) + : Hole(QColor("#808080"), canvas) +{ + if (!QPixmapCache::find("cup", pixmap)) + { + pixmap.load(locate("appdata", "pics/cup.png")); + QPixmapCache::insert("cup", pixmap); + } +} + +void Cup::draw(QPainter &p) +{ + p.drawPixmap(QPoint(x() - width() / 2, y() - height() / 2), pixmap); +} + +bool Cup::place(Ball *ball, bool /*wasCenter*/) +{ + ball->setState(Holed); + playSound("holed"); + + // the picture's center is a little different + ball->move(x() - 1, y()); + ball->setVelocity(0, 0); + if (game && game->curBall() == ball) + game->stoppedBall(); + return true; +} + +void Cup::save(KConfig *cfg) +{ + cfg->writeEntry("dummykey", true); +} + +///////////////////////// + +BlackHole::BlackHole(QCanvas *canvas) + : Hole(black, canvas), exitDeg(0) +{ + infoLine = 0; + m_minSpeed = 3.0; + m_maxSpeed = 5.0; + runs = 0; + + const QColor myColor((QRgb)(kapp->random() % 0x01000000)); + + outside = new QCanvasEllipse(canvas); + outside->setZ(z() - .001); + + outside->setBrush(QBrush(myColor)); + setBrush(black); + + exitItem = new BlackHoleExit(this, canvas); + exitItem->setPen(QPen(myColor, 6)); + exitItem->setX(300); + exitItem->setY(100); + + setSize(width(), width() / .8); + const float factor = 1.3; + outside->setSize(width() * factor, height() * factor); + outside->setVisible(true); + + moveBy(0, 0); + + finishMe(); +} + +void BlackHole::showInfo() +{ + delete infoLine; + infoLine = new QCanvasLine(canvas()); + infoLine->setVisible(true); + infoLine->setPen(QPen(exitItem->pen().color(), 2)); + infoLine->setZ(10000); + infoLine->setPoints(x(), y(), exitItem->x(), exitItem->y()); + + exitItem->showInfo(); +} + +void BlackHole::hideInfo() +{ + delete infoLine; + infoLine = 0; + + exitItem->hideInfo(); +} + +void BlackHole::aboutToDie() +{ + Hole::aboutToDie(); + delete outside; + exitItem->aboutToDie(); + delete exitItem; +} + +void BlackHole::updateInfo() +{ + if (infoLine) + { + infoLine->setVisible(true); + infoLine->setPoints(x(), y(), exitItem->x(), exitItem->y()); + exitItem->showInfo(); + } +} + +void BlackHole::moveBy(double dx, double dy) +{ + QCanvasEllipse::moveBy(dx, dy); + outside->move(x(), y()); + updateInfo(); +} + +void BlackHole::setExitDeg(int newdeg) +{ + exitDeg = newdeg; + if (game && game->isEditing() && game->curSelectedItem() == exitItem) + game->updateHighlighter(); + + exitItem->updateArrowAngle(); + finishMe(); +} + +QPtrList BlackHole::moveableItems() const +{ + QPtrList ret; + ret.append(exitItem); + return ret; +} + +BlackHoleTimer::BlackHoleTimer(Ball *ball, double speed, int msec) + : m_speed(speed), m_ball(ball) +{ + QTimer::singleShot(msec, this, SLOT(mySlot())); + QTimer::singleShot(msec / 2, this, SLOT(myMidSlot())); +} + +void BlackHoleTimer::mySlot() +{ + emit eject(m_ball, m_speed); + delete this; +} + +void BlackHoleTimer::myMidSlot() +{ + emit halfway(); +} + +bool BlackHole::place(Ball *ball, bool /*wasCenter*/) +{ + // most number is 10 + if (runs > 10 && game && game->isInPlay()) + return false; + + playSound("blackholeputin"); + + const double diff = (m_maxSpeed - m_minSpeed); + const double speed = m_minSpeed + ball->curVector().magnitude() * (diff / 3.75); + + ball->setVelocity(0, 0); + ball->setState(Stopped); + ball->setVisible(false); + ball->setForceStillGoing(true); + + double magnitude = Vector(QPoint(x(), y()), QPoint(exitItem->x(), exitItem->y())).magnitude(); + BlackHoleTimer *timer = new BlackHoleTimer(ball, speed, magnitude * 2.5 - speed * 35 + 500); + + connect(timer, SIGNAL(eject(Ball *, double)), this, SLOT(eject(Ball *, double))); + connect(timer, SIGNAL(halfway()), this, SLOT(halfway())); + + playSound("blackhole"); + return false; +} + +void BlackHole::eject(Ball *ball, double speed) +{ + ball->move(exitItem->x(), exitItem->y()); + + Vector v; + v.setMagnitude(10); + v.setDirection(deg2rad(exitDeg)); + ball->setVector(v); + + // advance ball 10 + ball->doAdvance(); + + v.setMagnitude(speed); + ball->setVector(v); + + ball->setForceStillGoing(false); + ball->setVisible(true); + ball->setState(Rolling); + + runs++; + + playSound("blackholeeject"); +} + +void BlackHole::halfway() +{ + playSound("blackhole"); +} + +void BlackHole::load(KConfig *cfg) +{ + QPoint exit = cfg->readPointEntry("exit", &exit); + exitItem->setX(exit.x()); + exitItem->setY(exit.y()); + exitDeg = cfg->readNumEntry("exitDeg", exitDeg); + m_minSpeed = cfg->readDoubleNumEntry("minspeed", m_minSpeed); + m_maxSpeed = cfg->readDoubleNumEntry("maxspeed", m_maxSpeed); + exitItem->updateArrowAngle(); + exitItem->updateArrowLength(); + + finishMe(); +} + +void BlackHole::finishMe() +{ + double radians = deg2rad(exitDeg); + QPoint midPoint(0, 0); + QPoint start; + QPoint end; + const int width = 15; + + if (midPoint.y() || !midPoint.x()) + { + start.setX(midPoint.x() - width*sin(radians)); + start.setY(midPoint.y() - width*cos(radians)); + end.setX(midPoint.x() + width*sin(radians)); + end.setY(midPoint.y() + width*cos(radians)); + } + else + { + start.setX(midPoint.x()); + start.setY(midPoint.y() + width); + end.setY(midPoint.y() - width); + end.setX(midPoint.x()); + } + + exitItem->setPoints(start.x(), start.y(), end.x(), end.y()); + exitItem->setVisible(true); +} + +void BlackHole::save(KConfig *cfg) +{ + cfg->writeEntry("exit", QPoint(exitItem->x(), exitItem->y())); + cfg->writeEntry("exitDeg", exitDeg); + cfg->writeEntry("minspeed", m_minSpeed); + cfg->writeEntry("maxspeed", m_maxSpeed); +} + +///////////////////////// + +BlackHoleExit::BlackHoleExit(BlackHole *blackHole, QCanvas *canvas) + : QCanvasLine(canvas) +{ + this->blackHole = blackHole; + arrow = new Arrow(canvas); + setZ(blackHole->z()); + arrow->setZ(z() - .00001); + updateArrowLength(); + arrow->setVisible(false); +} + +void BlackHoleExit::aboutToDie() +{ + arrow->aboutToDie(); + delete arrow; +} + +void BlackHoleExit::moveBy(double dx, double dy) +{ + QCanvasLine::moveBy(dx, dy); + arrow->move(x(), y()); + blackHole->updateInfo(); +} + +void BlackHoleExit::setPen(QPen p) +{ + QCanvasLine::setPen(p); + arrow->setPen(QPen(p.color(), 1)); +} + +void BlackHoleExit::updateArrowAngle() +{ + // arrows work in a different angle system + arrow->setAngle(-deg2rad(blackHole->curExitDeg())); + arrow->updateSelf(); +} + +void BlackHoleExit::updateArrowLength() +{ + arrow->setLength(10.0 + 5.0 * (double)(blackHole->minSpeed() + blackHole->maxSpeed()) / 2.0); + arrow->updateSelf(); +} + +void BlackHoleExit::editModeChanged(bool editing) +{ + if (editing) + showInfo(); + else + hideInfo(); +} + +void BlackHoleExit::showInfo() +{ + arrow->setVisible(true); +} + +void BlackHoleExit::hideInfo() +{ + arrow->setVisible(false); +} + +Config *BlackHoleExit::config(QWidget *parent) +{ + return blackHole->config(parent); +} + +///////////////////////// + +BlackHoleConfig::BlackHoleConfig(BlackHole *blackHole, QWidget *parent) + : Config(parent) +{ + this->blackHole = blackHole; + QVBoxLayout *layout = new QVBoxLayout(this, marginHint(), spacingHint()); + layout->addWidget(new QLabel(i18n("Exiting ball angle:"), this)); + QSpinBox *deg = new QSpinBox(0, 359, 10, this); + deg->setSuffix(QString(" ") + i18n("degrees")); + deg->setValue(blackHole->curExitDeg()); + deg->setWrapping(true); + layout->addWidget(deg); + connect(deg, SIGNAL(valueChanged(int)), this, SLOT(degChanged(int))); + + layout->addStretch(); + + QHBoxLayout *hlayout = new QHBoxLayout(layout, spacingHint()); + hlayout->addWidget(new QLabel(i18n("Minimum exit speed:"), this)); + KDoubleNumInput *min = new KDoubleNumInput(this); + min->setRange(0, 8, 1, true); + hlayout->addWidget(min); + connect(min, SIGNAL(valueChanged(double)), this, SLOT(minChanged(double))); + min->setValue(blackHole->minSpeed()); + + hlayout = new QHBoxLayout(layout, spacingHint()); + hlayout->addWidget(new QLabel(i18n("Maximum:"), this)); + KDoubleNumInput *max = new KDoubleNumInput(this); + max->setRange(1, 10, 1, true); + hlayout->addWidget(max); + connect(max, SIGNAL(valueChanged(double)), this, SLOT(maxChanged(double))); + max->setValue(blackHole->maxSpeed()); +} + +void BlackHoleConfig::degChanged(int newdeg) +{ + blackHole->setExitDeg(newdeg); + changed(); +} + +void BlackHoleConfig::minChanged(double news) +{ + blackHole->setMinSpeed(news); + changed(); +} + +void BlackHoleConfig::maxChanged(double news) +{ + blackHole->setMaxSpeed(news); + changed(); +} + +///////////////////////// + +WallPoint::WallPoint(bool start, Wall *wall, QCanvas *canvas) + : QCanvasEllipse(canvas) +{ + this->wall = wall; + this->start = start; + alwaysShow = false; + editing = false; + visible = true; + lastId = INT_MAX - 10; + dontmove = false; + + move(0, 0); + QPoint p; + if (start) + p = wall->startPoint(); + else + p = wall->endPoint(); + setX(p.x()); + setY(p.y()); +} + +void WallPoint::clean() +{ + int oldWidth = width(); + setSize(7, 7); + update(); + + QCanvasItem *onPoint = 0; + QCanvasItemList l = collisions(true); + for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) + if ((*it)->rtti() == rtti()) + onPoint = (*it); + + if (onPoint) + move(onPoint->x(), onPoint->y()); + + setSize(oldWidth, oldWidth); +} + +void WallPoint::moveBy(double dx, double dy) +{ + QCanvasEllipse::moveBy(dx, dy); + if (!editing) + updateVisible(); + + if (dontmove) + { + dontmove = false; + return; + } + + if (!wall) + return; + + if (start) + { + wall->setPoints(x(), y(), wall->endPoint().x() + wall->x(), wall->endPoint().y() + wall->y()); + } + else + { + wall->setPoints(wall->startPoint().x() + wall->x(), wall->startPoint().y() + wall->y(), x(), y()); + } + wall->move(0, 0); +} + +void WallPoint::updateVisible() +{ + if (!wall->isVisible()) + { + visible = false; + return; + } + + if (alwaysShow) + visible = true; + else + { + visible = true; + QCanvasItemList l = collisions(true); + for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) + if ((*it)->rtti() == rtti()) + visible = false; + } +} + +void WallPoint::editModeChanged(bool changed) +{ + editing = changed; + setVisible(true); + if (!editing) + updateVisible(); +} + +bool WallPoint::collision(Ball *ball, long int id) +{ + if (ball->curVector().magnitude() <= 0) + return false; + + long int tempLastId = lastId; + lastId = id; + QCanvasItemList l = collisions(true); + for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) + { + if ((*it)->rtti() == rtti()) + { + WallPoint *point = (WallPoint *)(*it); + point->lastId = id; + } + } + + //kdDebug(12007) << "WallPoint::collision id: " << id << ", tempLastId: " << tempLastId << endl; + Vector ballVector(ball->curVector()); + + //kdDebug(12007) << "Wall::collision ball speed: " << ball->curVector().magnitude() << endl; + int allowableDifference = 1; + if (ballVector.magnitude() < .30) + allowableDifference = 8; + else if (ballVector.magnitude() < .50) + allowableDifference = 6; + else if (ballVector.magnitude() < .65) + allowableDifference = 4; + else if (ballVector.magnitude() < .95) + allowableDifference = 2; + + if (abs(id - tempLastId) <= allowableDifference) + { + //kdDebug(12007) << "WallPoint::collision - SKIP\n"; + } + else + { + bool weirdBounce = visible; + + QPoint relStart(start? wall->startPoint() : wall->endPoint()); + QPoint relEnd(start? wall->endPoint() : wall->startPoint()); + Vector wallVector(relStart, relEnd); + wallVector.setDirection(-wallVector.direction()); + + // find the angle between vectors, between 0 and PI + { + double difference = fabs(wallVector.direction() - ballVector.direction()); + while (difference > 2 * M_PI) + difference -= 2 * M_PI; + + if (difference < M_PI / 2 || difference > 3 * M_PI / 2) + weirdBounce = false; + } + + playSound("wall", ball->curVector().magnitude() / 10.0); + + ballVector /= wall->dampening; + const double ballAngle = ballVector.direction(); + + double wallAngle = wallVector.direction(); + + // opposite bounce, because we're the endpoint + if (weirdBounce) + wallAngle += M_PI / 2; + + const double collisionAngle = ballAngle - wallAngle; + const double leavingAngle = wallAngle - collisionAngle; + + ballVector.setDirection(leavingAngle); + ball->setVector(ballVector); + wall->lastId = id; + + //kdDebug(12007) << "WallPoint::collision - NOT skip, weirdBounce is " << weirdBounce << endl; + } // end if that skips + + wall->lastId = id; + return false; +} + +///////////////////////// + +Wall::Wall(QCanvas *canvas) + : QCanvasLine(canvas) +{ + editing = false; + lastId = INT_MAX - 10; + + dampening = 1.2; + + startItem = 0; + endItem = 0; + + moveBy(0, 0); + setZ(50); + + startItem = new WallPoint(true, this, canvas); + endItem = new WallPoint(false, this, canvas); + startItem->setVisible(true); + endItem->setVisible(true); + setPen(QPen(darkRed, 3)); + + setPoints(-15, 10, 15, -5); + + moveBy(0, 0); + + editModeChanged(false); +} + +void Wall::selectedItem(QCanvasItem *item) +{ + if (item->rtti() == Rtti_WallPoint) + { + WallPoint *wallPoint = dynamic_cast(item); + if (wallPoint) { + setPoints(startPoint().x(), startPoint().y(), wallPoint->x() - x(), wallPoint->y() - y()); + } + } +} + +void Wall::clean() +{ + startItem->clean(); + endItem->clean(); +} + +void Wall::setAlwaysShow(bool yes) +{ + startItem->setAlwaysShow(yes); + endItem->setAlwaysShow(yes); +} + +void Wall::setVisible(bool yes) +{ + QCanvasLine::setVisible(yes); + + startItem->setVisible(yes); + endItem->setVisible(yes); + startItem->updateVisible(); + endItem->updateVisible(); +} + +void Wall::setZ(double newz) +{ + QCanvasLine::setZ(newz); + if (startItem) + startItem->setZ(newz + .002); + if (endItem) + endItem->setZ(newz + .001); +} + +void Wall::setPen(QPen p) +{ + QCanvasLine::setPen(p); + + if (startItem) + startItem->setBrush(QBrush(p.color())); + if (endItem) + endItem->setBrush(QBrush(p.color())); +} + +void Wall::aboutToDie() +{ + delete startItem; + delete endItem; +} + +void Wall::setGame(KolfGame *game) +{ + CanvasItem::setGame(game); + startItem->setGame(game); + endItem->setGame(game); +} + +QPtrList Wall::moveableItems() const +{ + QPtrList ret; + ret.append(startItem); + ret.append(endItem); + return ret; +} + +void Wall::moveBy(double dx, double dy) +{ + QCanvasLine::moveBy(dx, dy); + + if (!startItem || !endItem) + return; + + startItem->dontMove(); + endItem->dontMove(); + startItem->move(startPoint().x() + x(), startPoint().y() + y()); + endItem->move(endPoint().x() + x(), endPoint().y() + y()); +} + +void Wall::setVelocity(double vx, double vy) +{ + QCanvasLine::setVelocity(vx, vy); + /* + startItem->setVelocity(vx, vy); + endItem->setVelocity(vx, vy); + */ +} + +QPointArray Wall::areaPoints() const +{ + // editing we want full width for easy moving + if (editing) + return QCanvasLine::areaPoints(); + + // lessen width, for QCanvasLine::areaPoints() likes + // to make lines _very_ fat :( + // from qcanvas.cpp, only the stuff for a line width of 1 taken + + // it's all squished because I don't want my + // line counts to count code I didn't write! + QPointArray p(4); const int xi = int(x()); const int yi = int(y()); const QPoint start = startPoint(); const QPoint end = endPoint(); const int x1 = start.x(); const int x2 = end.x(); const int y1 = start.y(); const int y2 = end.y(); const int dx = QABS(x1-x2); const int dy = QABS(y1-y2); if ( dx > dy ) { p[0] = QPoint(x1+xi,y1+yi-1); p[1] = QPoint(x2+xi,y2+yi-1); p[2] = QPoint(x2+xi,y2+yi+1); p[3] = QPoint(x1+xi,y1+yi+1); } else { p[0] = QPoint(x1+xi-1,y1+yi); p[1] = QPoint(x2+xi-1,y2+yi); p[2] = QPoint(x2+xi+1,y2+yi); p[3] = QPoint(x1+xi+1,y1+yi); } return p; +} + +void Wall::editModeChanged(bool changed) +{ + // make big for debugging? + const bool debugPoints = false; + + editing = changed; + + startItem->setZ(z() + .002); + endItem->setZ(z() + .001); + startItem->editModeChanged(editing); + endItem->editModeChanged(editing); + + int neww = 0; + if (changed || debugPoints) + neww = 10; + else + neww = pen().width(); + + startItem->setSize(neww, neww); + endItem->setSize(neww, neww); + + moveBy(0, 0); +} + +bool Wall::collision(Ball *ball, long int id) +{ + if (ball->curVector().magnitude() <= 0) + return false; + + long int tempLastId = lastId; + lastId = id; + startItem->lastId = id; + endItem->lastId = id; + + //kdDebug(12007) << "Wall::collision id: " << id << ", tempLastId: " << tempLastId << endl; + Vector ballVector(ball->curVector()); + + //kdDebug(12007) << "Wall::collision ball speed: " << ball->curVector().magnitude() << endl; + int allowableDifference = 1; + if (ballVector.magnitude() < .30) + allowableDifference = 8; + else if (ballVector.magnitude() < .50) + allowableDifference = 6; + else if (ballVector.magnitude() < .75) + allowableDifference = 4; + else if (ballVector.magnitude() < .95) + allowableDifference = 2; + //kdDebug(12007) << "Wall::collision allowableDifference is " << allowableDifference << endl; + if (abs(id - tempLastId) <= allowableDifference) + { + //kdDebug(12007) << "Wall::collision - SKIP\n"; + return false; + } + + playSound("wall", ball->curVector().magnitude() / 10.0); + + ballVector /= dampening; + const double ballAngle = ballVector.direction(); + + const double wallAngle = -Vector(startPoint(), endPoint()).direction(); + const double collisionAngle = ballAngle - wallAngle; + const double leavingAngle = wallAngle - collisionAngle; + + ballVector.setDirection(leavingAngle); + ball->setVector(ballVector); + + //kdDebug(12007) << "Wall::collision - NOT skip\n"; + return false; +} + +void Wall::load(KConfig *cfg) +{ + QPoint start(startPoint()); + start = cfg->readPointEntry("startPoint", &start); + QPoint end(endPoint()); + end = cfg->readPointEntry("endPoint", &end); + + setPoints(start.x(), start.y(), end.x(), end.y()); + + moveBy(0, 0); + startItem->move(start.x(), start.y()); + endItem->move(end.x(), end.y()); +} + +void Wall::save(KConfig *cfg) +{ + cfg->writeEntry("startPoint", QPoint(startItem->x(), startItem->y())); + cfg->writeEntry("endPoint", QPoint(endItem->x(), endItem->y())); +} + +///////////////////////// + +HoleConfig::HoleConfig(HoleInfo *holeInfo, QWidget *parent) + : Config(parent) +{ + this->holeInfo = holeInfo; + + QVBoxLayout *layout = new QVBoxLayout(this, marginHint(), spacingHint()); + + QHBoxLayout *hlayout = new QHBoxLayout(layout, spacingHint()); + hlayout->addWidget(new QLabel(i18n("Course name: "), this)); + KLineEdit *nameEdit = new KLineEdit(holeInfo->untranslatedName(), this); + hlayout->addWidget(nameEdit); + connect(nameEdit, SIGNAL(textChanged(const QString &)), this, SLOT(nameChanged(const QString &))); + + hlayout = new QHBoxLayout(layout, spacingHint()); + hlayout->addWidget(new QLabel(i18n("Course author: "), this)); + KLineEdit *authorEdit = new KLineEdit(holeInfo->author(), this); + hlayout->addWidget(authorEdit); + connect(authorEdit, SIGNAL(textChanged(const QString &)), this, SLOT(authorChanged(const QString &))); + + layout->addStretch(); + + hlayout = new QHBoxLayout(layout, spacingHint()); + hlayout->addWidget(new QLabel(i18n("Par:"), this)); + QSpinBox *par = new QSpinBox(1, 15, 1, this); + par->setValue(holeInfo->par()); + hlayout->addWidget(par); + connect(par, SIGNAL(valueChanged(int)), this, SLOT(parChanged(int))); + hlayout->addStretch(); + + hlayout->addWidget(new QLabel(i18n("Maximum:"), this)); + QSpinBox *maxstrokes = new QSpinBox(holeInfo->lowestMaxStrokes(), 30, 1, this); + QWhatsThis::add(maxstrokes, i18n("Maximum number of strokes player can take on this hole.")); + QToolTip::add(maxstrokes, i18n("Maximum number of strokes")); + maxstrokes->setSpecialValueText(i18n("Unlimited")); + maxstrokes->setValue(holeInfo->maxStrokes()); + hlayout->addWidget(maxstrokes); + connect(maxstrokes, SIGNAL(valueChanged(int)), this, SLOT(maxStrokesChanged(int))); + + QCheckBox *check = new QCheckBox(i18n("Show border walls"), this); + check->setChecked(holeInfo->borderWalls()); + layout->addWidget(check); + connect(check, SIGNAL(toggled(bool)), this, SLOT(borderWallsChanged(bool))); +} + +void HoleConfig::authorChanged(const QString &newauthor) +{ + holeInfo->setAuthor(newauthor); + changed(); +} + +void HoleConfig::nameChanged(const QString &newname) +{ + holeInfo->setName(newname); + holeInfo->setUntranslatedName(newname); + changed(); +} + +void HoleConfig::parChanged(int newpar) +{ + holeInfo->setPar(newpar); + changed(); +} + +void HoleConfig::maxStrokesChanged(int newms) +{ + holeInfo->setMaxStrokes(newms); + changed(); +} + +void HoleConfig::borderWallsChanged(bool yes) +{ + holeInfo->borderWallsChanged(yes); + changed(); +} + +///////////////////////// + +StrokeCircle::StrokeCircle(QCanvas *canvas) + : QCanvasItem(canvas) +{ + dvalue = 0; + dmax = 360; + iwidth = 100; + iheight = 100; + ithickness = 8; + setZ(10000); +} + +void StrokeCircle::setValue(double v) +{ + dvalue = v; + if (dvalue > dmax) + dvalue = dmax; + + update(); +} + +double StrokeCircle::value() +{ + return dvalue; +} + +bool StrokeCircle::collidesWith(const QCanvasItem*) const { return false; } + +bool StrokeCircle::collidesWith(const QCanvasSprite*, const QCanvasPolygonalItem*, const QCanvasRectangle*, const QCanvasEllipse*, const QCanvasText*) const { return false; } + +QRect StrokeCircle::boundingRect() const { return QRect(x(), y(), iwidth, iheight); } + +void StrokeCircle::setMaxValue(double m) +{ + dmax = m; + if (dvalue > dmax) + dvalue = dmax; + + update(); +} +void StrokeCircle::setSize(int w, int h) +{ + if (w > 0) + iwidth = w; + if (h > 0) + iheight = h; + + update(); +} +void StrokeCircle::setThickness(int t) +{ + if (t > 0) + ithickness = t; + + update(); +} + +int StrokeCircle::thickness() const +{ + return ithickness; +} + +int StrokeCircle::width() const +{ + return iwidth; +} + +int StrokeCircle::height() const +{ + return iheight; +} + +void StrokeCircle::draw(QPainter &p) +{ + int al = (int)((dvalue * 360 * 16) / dmax); + int length, deg; + if (al < 0) + { + deg = 270 * 16; + length = -al; + } + else if (al <= (270 * 16)) + { + deg = 270 * 16 - al; + length = al; + } + else + { + deg = (360 * 16) - (al - (270 * 16)); + length = al; + } + + p.setBrush(QBrush(black, Qt::NoBrush)); + p.setPen(QPen(white, ithickness / 2)); + p.drawEllipse(x() + ithickness / 2, y() + ithickness / 2, iwidth - ithickness, iheight - ithickness); + p.setPen(QPen(QColor((int)(0xff * dvalue) / dmax, 0, 0xff - (int)(0xff * dvalue) / dmax), ithickness)); + p.drawArc(x() + ithickness / 2, y() + ithickness / 2, iwidth - ithickness, iheight - ithickness, deg, length); + + p.setPen(QPen(white, 1)); + p.drawEllipse(x(), y(), iwidth, iheight); + p.drawEllipse(x() + ithickness, y() + ithickness, iwidth - ithickness * 2, iheight - ithickness * 2); + p.setPen(QPen(white, 3)); + p.drawLine(x() + iwidth / 2, y() + iheight - ithickness * 1.5, x() + iwidth / 2, y() + iheight); + p.drawLine(x() + iwidth / 4 - iwidth / 20, y() + iheight - iheight / 4 + iheight / 20, x() + iwidth / 4 + iwidth / 20, y() + iheight - iheight / 4 - iheight / 20); + p.drawLine(x() + iwidth - iwidth / 4 + iwidth / 20, y() + iheight - iheight / 4 + iheight / 20, x() + iwidth - iwidth / 4 - iwidth / 20, y() + iheight - iheight / 4 - iheight / 20); +} + +///////////////////////////////////////// + +KolfGame::KolfGame(ObjectList *obj, PlayerList *players, QString filename, QWidget *parent, const char *name ) + : QCanvasView(parent, name) +{ + // for mouse control + setMouseTracking(true); + viewport()->setMouseTracking(true); + setFrameShape(NoFrame); + + regAdv = false; + curHole = 0; // will get ++'d + cfg = 0; + setFilename(filename); + this->players = players; + this->obj = obj; + curPlayer = players->end(); + curPlayer--; // will get ++'d to end and sent back + // to beginning + paused = false; + modified = false; + inPlay = false; + putting = false; + stroking = false; + editing = false; + strict = false; + lastDelId = -1; + m_showInfo = false; + ballStateList.canUndo = false; + fastAdvancedExist = false; + soundDir = locate("appdata", "sounds/"); + dontAddStroke = false; + addingNewHole = false; + scoreboardHoles = 0; + infoShown = false; + m_useMouse = true; + m_useAdvancedPutting = false; + m_useAdvancedPutting = true; + m_sound = true; + m_ignoreEvents = false; + soundedOnce = false; + oldPlayObjects.setAutoDelete(true); + highestHole = 0; + recalcHighestHole = false; + + holeInfo.setGame(this); + holeInfo.setAuthor(i18n("Course Author")); + holeInfo.setName(i18n("Course Name")); + holeInfo.setUntranslatedName(i18n("Course Name")); + holeInfo.setMaxStrokes(10); + holeInfo.borderWallsChanged(true); + + // width and height are the width and height of the canvas + // in easy storage + width = 400; + height = 400; + grass = QColor("#35760D"); + + margin = 10; + + setFocusPolicy(QWidget::StrongFocus); + setFixedSize(width + 2 * margin, height + 2 * margin); + + setMargins(margin, margin, margin, margin); + + course = new QCanvas(this); + course->setBackgroundColor(white); + course->resize(width, height); + + QPixmap pic; + if (!QPixmapCache::find("grass", pic)) + { + pic.load(locate("appdata", "pics/grass.png")); + QPixmapCache::insert("grass", pic); + } + course->setBackgroundPixmap(pic); + + setCanvas(course); + move(0, 0); + adjustSize(); + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->setCanvas(course); + + // highlighter shows current item + highlighter = new QCanvasRectangle(course); + highlighter->setPen(QPen(yellow, 1)); + highlighter->setBrush(QBrush(NoBrush)); + highlighter->setVisible(false); + highlighter->setZ(10000); + + // shows some info about hole + infoText = new QCanvasText(course); + infoText->setText(""); + infoText->setColor(white); + QFont font = kapp->font(); + font.setPixelSize(12); + infoText->move(15, width/2); + infoText->setZ(10001); + infoText->setFont(font); + infoText->setVisible(false); + + // create the advanced putting indicator + strokeCircle = new StrokeCircle(course); + strokeCircle->move(width - 90, height - 90); + strokeCircle->setSize(80, 80); + strokeCircle->setThickness(8); + strokeCircle->setVisible(false); + strokeCircle->setValue(0); + strokeCircle->setMaxValue(360); + + // whiteBall marks the spot of the whole whilst editing + whiteBall = new Ball(course); + whiteBall->setGame(this); + whiteBall->setColor(white); + whiteBall->setVisible(false); + whiteBall->setDoDetect(false); + + int highestLog = 0; + + // if players have scores from loaded game, move to last hole + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + { + if ((int)(*it).scores().count() > highestLog) + highestLog = (*it).scores().count(); + + (*it).ball()->setGame(this); + (*it).ball()->setAnimated(true); + } + + // here only for saved games + if (highestLog) + curHole = highestLog; + + putter = new Putter(course); + + // border walls: + + // horiz + addBorderWall(QPoint(margin, margin), QPoint(width - margin, margin)); + addBorderWall(QPoint(margin, height - margin - 1), QPoint(width - margin, height - margin - 1)); + + // vert + addBorderWall(QPoint(margin, margin), QPoint(margin, height - margin)); + addBorderWall(QPoint(width - margin - 1, margin), QPoint(width - margin - 1, height - margin)); + + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(timeout())); + timerMsec = 300; + + fastTimer = new QTimer(this); + connect(fastTimer, SIGNAL(timeout()), this, SLOT(fastTimeout())); + fastTimerMsec = 11; + + autoSaveTimer = new QTimer(this); + connect(autoSaveTimer, SIGNAL(timeout()), this, SLOT(autoSaveTimeout())); + autoSaveMsec = 5 * 1000 * 60; // 5 min autosave + + // setUseAdvancedPutting() sets maxStrength! + setUseAdvancedPutting(false); + + putting = false; + putterTimer = new QTimer(this); + connect(putterTimer, SIGNAL(timeout()), this, SLOT(putterTimeout())); + putterTimerMsec = 20; +} + +void KolfGame::startFirstHole(int hole) +{ + if (curHole > 0) // if there was saved game, sync scoreboard + // with number of holes + { + for (; scoreboardHoles < curHole; ++scoreboardHoles) + { + cfg->setGroup(QString("%1-hole@-50,-50|0").arg(scoreboardHoles + 1)); + emit newHole(cfg->readNumEntry("par", 3)); + } + + // lets load all of the scores from saved game if there are any + for (int hole = 1; hole <= curHole; ++hole) + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + emit scoreChanged((*it).id(), hole, (*it).score(hole)); + } + + curHole = hole - 1; + + // this increments curHole, etc + recalcHighestHole = true; + startNextHole(); + paused = true; + unPause(); +} + +void KolfGame::setFilename(const QString &filename) +{ + this->filename = filename; + delete cfg; + cfg = new KConfig(filename, false, false); +} + +KolfGame::~KolfGame() +{ + oldPlayObjects.clear(); + delete cfg; +} + +void KolfGame::setModified(bool mod) +{ + modified = mod; + emit modifiedChanged(mod); +} + +void KolfGame::pause() +{ + if (paused) + { + // play along with people who call pause() again, instead of unPause() + unPause(); + return; + } + + paused = true; + timer->stop(); + fastTimer->stop(); + putterTimer->stop(); +} + +void KolfGame::unPause() +{ + if (!paused) + return; + + paused = false; + + timer->start(timerMsec); + fastTimer->start(fastTimerMsec); + + if (putting || stroking) + putterTimer->start(putterTimerMsec); +} + +void KolfGame::addBorderWall(QPoint start, QPoint end) +{ + Wall *wall = new Wall(course); + wall->setPoints(start.x(), start.y(), end.x(), end.y()); + wall->setVisible(true); + wall->setGame(this); + wall->setZ(998.7); + borderWalls.append(wall); +} + +void KolfGame::updateHighlighter() +{ + if (!selectedItem) + return; + QRect rect = selectedItem->boundingRect(); + highlighter->move(rect.x() + 1, rect.y() + 1); + highlighter->setSize(rect.width(), rect.height()); +} + +void KolfGame::handleMouseDoubleClickEvent(QMouseEvent *e) +{ + // allow two fast single clicks + handleMousePressEvent(e); +} + +void KolfGame::handleMousePressEvent(QMouseEvent *e) +{ + if (m_ignoreEvents) + return; + + if (editing) + { + if (inPlay) + return; + + storedMousePos = e->pos(); + + QCanvasItemList list = course->collisions(e->pos()); + if (list.first() == highlighter) + list.pop_front(); + + moving = false; + highlighter->setVisible(false); + selectedItem = 0; + movingItem = 0; + + if (list.count() < 1) + { + emit newSelectedItem(&holeInfo); + return; + } + // only items we keep track of + if ((!(items.containsRef(list.first()) || list.first() == whiteBall || extraMoveable.containsRef(list.first())))) + { + emit newSelectedItem(&holeInfo); + return; + } + + CanvasItem *citem = dynamic_cast(list.first()); + if (!citem || !citem->moveable()) + { + emit newSelectedItem(&holeInfo); + return; + } + + switch (e->button()) + { + // select AND move now :) + case LeftButton: + { + selectedItem = list.first(); + movingItem = selectedItem; + moving = true; + + if (citem->cornerResize()) + setCursor(KCursor::sizeFDiagCursor()); + else + setCursor(KCursor::sizeAllCursor()); + + emit newSelectedItem(citem); + highlighter->setVisible(true); + QRect rect = selectedItem->boundingRect(); + highlighter->move(rect.x() + 1, rect.y() + 1); + highlighter->setSize(rect.width(), rect.height()); + } + break; + + default: + break; + } + } + else + { + if (m_useMouse) + { + if (!inPlay && e->button() == LeftButton) + puttPress(); + else if (e->button() == RightButton) + toggleShowInfo(); + } + } + + setFocus(); +} + +QPoint KolfGame::viewportToViewport(const QPoint &p) +{ + // for some reason viewportToContents doesn't work right + return p - QPoint(margin, margin); +} + +// the following four functions are needed to handle both +// border presses and regular in-course presses + +void KolfGame::mouseReleaseEvent(QMouseEvent * e) +{ + QMouseEvent fixedEvent (QEvent::MouseButtonRelease, viewportToViewport(viewportToContents(e->pos())), e->button(), e->state()); + handleMouseReleaseEvent(&fixedEvent); +} + +void KolfGame::mousePressEvent(QMouseEvent * e) +{ + QMouseEvent fixedEvent (QEvent::MouseButtonPress, viewportToViewport(viewportToContents(e->pos())), e->button(), e->state()); + handleMousePressEvent(&fixedEvent); +} + +void KolfGame::mouseDoubleClickEvent(QMouseEvent * e) +{ + QMouseEvent fixedEvent (QEvent::MouseButtonDblClick, viewportToViewport(viewportToContents(e->pos())), e->button(), e->state()); + handleMouseDoubleClickEvent(&fixedEvent); +} + +void KolfGame::mouseMoveEvent(QMouseEvent * e) +{ + QMouseEvent fixedEvent (QEvent::MouseMove, viewportToViewport(viewportToContents(e->pos())), e->button(), e->state()); + handleMouseMoveEvent(&fixedEvent); +} + +void KolfGame::handleMouseMoveEvent(QMouseEvent *e) +{ + if (inPlay || !putter || m_ignoreEvents) + return; + + QPoint mouse = e->pos(); + + // mouse moving of putter + if (!editing) + { + updateMouse(); + return; + } + + if (!moving) + { + // lets change the cursor to a hand + // if we're hovering over something + + QCanvasItemList list = course->collisions(e->pos()); + if (list.count() > 0) + setCursor(KCursor::handCursor()); + else + setCursor(KCursor::arrowCursor()); + return; + } + + int moveX = storedMousePos.x() - mouse.x(); + int moveY = storedMousePos.y() - mouse.y(); + + // moving counts as modifying + if (moveX || moveY) + setModified(true); + + highlighter->moveBy(-(double)moveX, -(double)moveY); + movingItem->moveBy(-(double)moveX, -(double)moveY); + QRect brect = movingItem->boundingRect(); + emit newStatusText(QString("%1x%2").arg(brect.x()).arg(brect.y())); + storedMousePos = mouse; +} + +void KolfGame::updateMouse() +{ + // don't move putter if in advanced putting sequence + if (!m_useMouse || ((stroking || putting) && m_useAdvancedPutting)) + return; + + const QPoint cursor = viewportToViewport(viewportToContents(mapFromGlobal(QCursor::pos()))); + const QPoint ball((*curPlayer).ball()->x(), (*curPlayer).ball()->y()); + putter->setAngle(-Vector(cursor, ball).direction()); +} + +void KolfGame::handleMouseReleaseEvent(QMouseEvent *e) +{ + setCursor(KCursor::arrowCursor()); + + if (editing) + { + emit newStatusText(QString::null); + moving = false; + } + + if (m_ignoreEvents) + return; + + if (!editing && m_useMouse) + { + if (!inPlay && e->button() == LeftButton) + puttRelease(); + else if (e->button() == RightButton) + toggleShowInfo(); + } + + setFocus(); +} + +void KolfGame::keyPressEvent(QKeyEvent *e) +{ + if (inPlay || editing || m_ignoreEvents) + return; + + switch (e->key()) + { + case Key_Up: + if (!e->isAutoRepeat()) + toggleShowInfo(); + break; + + case Key_Escape: + putting = false; + stroking = false; + finishStroking = false; + strokeCircle->setVisible(false); + putterTimer->stop(); + putter->setOrigin((*curPlayer).ball()->x(), (*curPlayer).ball()->y()); + break; + + case Key_Left: + case Key_Right: + // don't move putter if in advanced putting sequence + if ((!stroking && !putting) || !m_useAdvancedPutting) + putter->go(e->key() == Key_Left? D_Left : D_Right, e->state() & ShiftButton? Amount_More : e->state() & ControlButton? Amount_Less : Amount_Normal); + break; + + case Key_Space: case Key_Down: + puttPress(); + break; + + default: + break; + } +} + +void KolfGame::toggleShowInfo() +{ + setShowInfo(!m_showInfo); +} + +void KolfGame::updateShowInfo() +{ + setShowInfo(m_showInfo); +} + +void KolfGame::setShowInfo(bool yes) +{ + m_showInfo = yes; + + if (m_showInfo) + { + QCanvasItem *item = 0; + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + citem->showInfo(); + } + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->showInfo(); + + showInfo(); + } + else + { + QCanvasItem *item = 0; + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + citem->hideInfo(); + } + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->hideInfo(); + + hideInfo(); + } +} + +void KolfGame::puttPress() +{ + // Advanced putting: 1st click start putting sequence, 2nd determine strength, 3rd determine precision + + if (!putting && !stroking && !inPlay) + { + puttCount = 0; + puttReverse = false; + putting = true; + stroking = false; + strength = 0; + if (m_useAdvancedPutting) + { + strokeCircle->setValue(0); + int pw = putter->endPoint().x() - putter->startPoint().x(); + if (pw < 0) pw = -pw; + int px = (int)putter->x() + pw / 2; + int py = (int)putter->y(); + if (px > width / 2 && py < height / 2) + strokeCircle->move(px - pw / 2 - 10 - strokeCircle->width(), py + 10); + else if (px > width / 2) + strokeCircle->move(px - pw / 2 - 10 - strokeCircle->width(), py - 10 - strokeCircle->height()); + else if (py < height / 2) + strokeCircle->move(px + pw / 2 + 10, py + 10); + else + strokeCircle->move(px + pw / 2 + 10, py - 10 - strokeCircle->height()); + strokeCircle->setVisible(true); + } + putterTimer->start(putterTimerMsec); + } + else if (m_useAdvancedPutting && putting && !editing) + { + putting = false; + stroking = true; + puttReverse = false; + finishStroking = false; + } + else if (m_useAdvancedPutting && stroking) + { + finishStroking = true; + putterTimeout(); + } +} + +void KolfGame::keyReleaseEvent(QKeyEvent *e) +{ + if (e->isAutoRepeat() || m_ignoreEvents) + return; + + if (e->key() == Key_Space || e->key() == Key_Down) + puttRelease(); + else if ((e->key() == Key_Backspace || e->key() == Key_Delete) && !(e->state() & ControlButton)) + { + if (editing && !moving && selectedItem) + { + CanvasItem *citem = dynamic_cast(selectedItem); + if (!citem) + return; + citem = citem->itemToDelete(); + if (!citem) + return; + QCanvasItem *item = dynamic_cast(citem); + if (citem && citem->deleteable()) + { + lastDelId = citem->curId(); + + highlighter->setVisible(false); + items.removeRef(item); + citem->hideInfo(); + citem->aboutToDelete(); + citem->aboutToDie(); + delete citem; + selectedItem = 0; + emit newSelectedItem(&holeInfo); + + setModified(true); + } + } + } + else if (e->key() == Key_I || e->key() == Key_Up) + toggleShowInfo(); +} + +void KolfGame::puttRelease() +{ + if (!m_useAdvancedPutting && putting && !editing) + { + putting = false; + stroking = true; + } +} + +void KolfGame::stoppedBall() +{ + if (!inPlay) + { + inPlay = true; + dontAddStroke = true; + } +} + +void KolfGame::timeout() +{ + Ball *curBall = (*curPlayer).ball(); + + // test if the ball is gone + // in this case we want to stop the ball and + // later undo the shot + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + { + if (!course->rect().contains(QPoint((*it).ball()->x(), (*it).ball()->y()))) + { + (*it).ball()->setState(Stopped); + + // don't do it if he's past maxStrokes + if ((*it).score(curHole) < holeInfo.maxStrokes() - 1 || !holeInfo.hasMaxStrokes()) + { + loadStateList(); + } + shotDone(); + + return; + } + } + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + if ((*it).ball()->forceStillGoing() || ((*it).ball()->curState() == Rolling && (*it).ball()->curVector().magnitude() > 0 && (*it).ball()->isVisible())) + return; + + int curState = curBall->curState(); + if (curState == Stopped && inPlay) + { + inPlay = false; + QTimer::singleShot(0, this, SLOT(shotDone())); + } + + if (curState == Holed && inPlay) + { + emit inPlayEnd(); + emit playerHoled(&(*curPlayer)); + + int curScore = (*curPlayer).score(curHole); + if (!dontAddStroke) + curScore++; + + if (curScore == 1) + { + playSound("holeinone"); + } + else if (curScore <= holeInfo.par()) + { + // I don't have a sound!! + // *sob* + // playSound("woohoo"); + } + + (*curPlayer).ball()->setZ((*curPlayer).ball()->z() + .1 - (.1)/(curScore)); + + if (allPlayersDone()) + { + inPlay = false; + + if (curHole > 0 && !dontAddStroke) + { + (*curPlayer).addStrokeToHole(curHole); + emit scoreChanged((*curPlayer).id(), curHole, (*curPlayer).score(curHole)); + } + QTimer::singleShot(600, this, SLOT(holeDone())); + } + else + { + inPlay = false; + QTimer::singleShot(0, this, SLOT(shotDone())); + } + } +} + +void KolfGame::fastTimeout() +{ + // do regular advance every other time + if (regAdv) + course->advance(); + regAdv = !regAdv; + + if (!editing) + { + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->doAdvance(); + + if (fastAdvancedExist) + { + CanvasItem *citem = 0; + for (citem = fastAdvancers.first(); citem; citem = fastAdvancers.next()) + citem->doAdvance(); + } + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->fastAdvanceDone(); + + if (fastAdvancedExist) + { + CanvasItem *citem = 0; + for (citem = fastAdvancers.first(); citem; citem = fastAdvancers.next()) + citem->fastAdvanceDone(); + } + } +} + +void KolfGame::ballMoved() +{ + if (putter->isVisible()) + { + putter->move((*curPlayer).ball()->x(), (*curPlayer).ball()->y()); + updateMouse(); + } +} + +void KolfGame::putterTimeout() +{ + if (inPlay || editing) + return; + + if (m_useAdvancedPutting) + { + if (putting) + { + const float base = 2.0; + + if (puttReverse && strength <= 0) + { + // aborted + putting = false; + strokeCircle->setVisible(false); + } + else if (strength > maxStrength || puttReverse) + { + // decreasing strength as we've reached the top + puttReverse = true; + strength -= pow(base, strength / maxStrength) - 1.8; + if ((int)strength < puttCount * 2) + { + puttCount--; + if (puttCount >= 0) + putter->go(Forwards); + } + } + else + { + // make the increase at high strength faster + strength += pow(base, strength / maxStrength) - .3; + if ((int)strength > puttCount * 2) + { + putter->go(Backwards); + puttCount++; + } + } + // make the visible steps at high strength smaller + strokeCircle->setValue(pow(strength / maxStrength, 0.8) * 360); + } + else if (stroking) + { + double al = strokeCircle->value(); + if (al >= 45) + al -= 0.2 + strength / 50 + al / 100; + else + al -= 0.2 + strength / 50; + + if (puttReverse) + { + // show the stroke + puttCount--; + if (puttCount >= 0) + putter->go(Forwards); + else + { + strokeCircle->setVisible(false); + finishStroking = false; + putterTimer->stop(); + putting = false; + stroking = false; + shotStart(); + } + } + else if (al < -45 || finishStroking) + { + strokeCircle->setValue(al); + int deg; + // if > 45 or < -45 then bad stroke + if (al > 45) + { + deg = putter->curDeg() - 45 + rand() % 90; + strength -= rand() % (int)strength; + } + else if (!finishStroking) + { + deg = putter->curDeg() - 45 + rand() % 90; + strength -= rand() % (int)strength; + } + else + deg = putter->curDeg() + (int)(strokeCircle->value() / 3); + + if (deg < 0) + deg += 360; + else if (deg > 360) + deg -= 360; + + putter->setDeg(deg); + puttReverse = true; + } + else + { + strokeCircle->setValue(al); + putterTimer->changeInterval(putterTimerMsec/10); + } + } + + } + else + { + if (putting) + { + putter->go(Backwards); + puttCount++; + strength += 1.5; + if (strength > maxStrength) + { + putting = false; + stroking = true; + } + } + else if (stroking) + { + if (putter->curLen() < (*curPlayer).ball()->height() + 2) + { + stroking = false; + putterTimer->stop(); + putting = false; + stroking = false; + shotStart(); + } + + putter->go(Forwards); + putterTimer->changeInterval(putterTimerMsec/10); + } + } +} + +void KolfGame::autoSaveTimeout() +{ + // this should be a config option + // until it is i'll disable it + if (editing) + { + //save(); + } +} + +void KolfGame::recreateStateList() +{ + stateDB.clear(); + + QCanvasItem *item = 0; + + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + { + stateDB.setName(makeStateGroup(citem->curId(), citem->name())); + citem->saveState(&stateDB); + } + } + + ballStateList.clear(); + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + ballStateList.append((*it).stateInfo(curHole)); + + ballStateList.canUndo = true; +} + +void KolfGame::undoShot() +{ + if (ballStateList.canUndo) + loadStateList(); +} + +void KolfGame::loadStateList() +{ + QCanvasItem *item = 0; + + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + { + stateDB.setName(makeStateGroup(citem->curId(), citem->name())); + citem->loadState(&stateDB); + } + } + + for (BallStateList::Iterator it = ballStateList.begin(); it != ballStateList.end(); ++it) + { + BallStateInfo info = (*it); + Player &player = (*players->at(info.id - 1)); + player.ball()->move(info.spot.x(), info.spot.y()); + player.ball()->setBeginningOfHole(info.beginningOfHole); + if ((*curPlayer).id() == info.id) + ballMoved(); + else + player.ball()->setVisible(!info.beginningOfHole); + player.setScoreForHole(info.score, curHole); + player.ball()->setState(info.state); + emit scoreChanged(info.id, curHole, info.score); + } +} + +void KolfGame::shotDone() +{ + inPlay = false; + emit inPlayEnd(); + setFocus(); + + Ball *ball = (*curPlayer).ball(); + double oldx = ball->x(), oldy = ball->y(); + + if (!dontAddStroke && (*curPlayer).numHoles()) + (*curPlayer).addStrokeToHole(curHole); + + dontAddStroke = false; + + // do hack stuff, shouldn't be done here + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + { + if ((*it).ball()->addStroke()) + { + for (int i = 1; i <= (*it).ball()->addStroke(); ++i) + (*it).addStrokeToHole(curHole); + + // emit that we have a new stroke count + emit scoreChanged((*it).id(), curHole, (*it).score(curHole)); + } + (*it).ball()->setAddStroke(0); + } + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + { + Ball *ball = (*it).ball(); + + if (ball->curState() == Holed) + continue; + + Vector v; + if (ball->placeOnGround(v)) + { + ball->setPlaceOnGround(false); + + QStringList options; + const QString placeOutside = i18n("Drop Outside of Hazard"); + const QString rehit = i18n("Rehit From Last Location"); + options << placeOutside << rehit; + const QString choice = KComboBoxDialog::getItem(i18n("What would you like to do for your next shot?"), i18n("%1 is in a Hazard").arg((*it).name()), options, placeOutside, "hazardOptions"); + + if (choice == placeOutside) + { + (*it).ball()->setDoDetect(false); + + double x = ball->x(), y = ball->y(); + + while (1) + { + QCanvasItemList list = ball->collisions(true); + bool keepMoving = false; + while (!list.isEmpty()) + { + QCanvasItem *item = list.first(); + if (item->rtti() == Rtti_DontPlaceOn) + keepMoving = true; + + list.pop_front(); + } + if (!keepMoving) + break; + + const float movePixel = 3.0; + x -= cos(v.direction()) * movePixel; + y += sin(v.direction()) * movePixel; + + ball->move(x, y); + } + + // move another two pixels away + x -= cos(v.direction()) * 2; + y += sin(v.direction()) * 2; + } + else if (choice == rehit) + { + for (BallStateList::Iterator it = ballStateList.begin(); it != ballStateList.end(); ++it) + { + if ((*it).id == (*curPlayer).id()) + { + if ((*it).beginningOfHole) + ball->move(whiteBall->x(), whiteBall->y()); + else + ball->move((*it).spot.x(), (*it).spot.y()); + + break; + } + } + } + + ball->setVisible(true); + ball->setState(Stopped); + + (*it).ball()->setDoDetect(true); + ball->collisionDetect(oldx, oldy); + } + } + + // emit again + emit scoreChanged((*curPlayer).id(), curHole, (*curPlayer).score(curHole)); + + ball->setVelocity(0, 0); + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + { + Ball *ball = (*it).ball(); + + int curStrokes = (*it).score(curHole); + if (curStrokes >= holeInfo.maxStrokes() && holeInfo.hasMaxStrokes()) + { + ball->setState(Holed); + ball->setVisible(false); + + // move to center in case he/she hit out + ball->move(width / 2, height / 2); + playerWhoMaxed = (*it).name(); + + if (allPlayersDone()) + { + startNextHole(); + QTimer::singleShot(100, this, SLOT(emitMax())); + return; + } + + QTimer::singleShot(100, this, SLOT(emitMax())); + } + } + + // change player to next player + // skip player if he's Holed + do + { + curPlayer++; + if (curPlayer == players->end()) + curPlayer = players->begin(); + } + while ((*curPlayer).ball()->curState() == Holed); + + emit newPlayersTurn(&(*curPlayer)); + + (*curPlayer).ball()->setVisible(true); + + putter->setAngle((*curPlayer).ball()); + putter->setOrigin((*curPlayer).ball()->x(), (*curPlayer).ball()->y()); + updateMouse(); + + inPlay = false; + (*curPlayer).ball()->collisionDetect(oldx, oldy); +} + +void KolfGame::emitMax() +{ + emit maxStrokesReached(playerWhoMaxed); +} + +void KolfGame::startBall(const Vector &vector) +{ + playSound("hit"); + + emit inPlayStart(); + putter->setVisible(false); + + (*curPlayer).ball()->setState(Rolling); + (*curPlayer).ball()->setVector(vector); + + QCanvasItem *item = 0; + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + citem->shotStarted(); + } + + inPlay = true; +} + +void KolfGame::shotStart() +{ + // ensure we never hit the ball back into the hole which + // can cause hole skippage + if ((*curPlayer).ball()->curState() == Holed) + return; + + // save state + recreateStateList(); + + putter->saveAngle((*curPlayer).ball()); + strength /= 8; + if (!strength) + strength = 1; + + startBall(Vector(strength, putter->curAngle() + M_PI)); + + addHoleInfo(ballStateList); +} + +void KolfGame::addHoleInfo(BallStateList &list) +{ + list.player = (*curPlayer).id(); + list.vector = (*curPlayer).ball()->curVector(); + list.hole = curHole; +} + +void KolfGame::sayWhosGoing() +{ + if (players->count() >= 2) + { + KMessageBox::information(this, i18n("%1 will start off.").arg((*curPlayer).name()), i18n("New Hole"), "newHole"); + } +} + +void KolfGame::holeDone() +{ + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->setVisible(false); + startNextHole(); + sayWhosGoing(); +} + +// this function is WAY too smart for it's own good +// ie, bad design :-( +void KolfGame::startNextHole() +{ + setFocus(); + + bool reset = true; + if (askSave(true)) + { + if (allPlayersDone()) + { + // we'll reload this hole, but not reset + curHole--; + reset = false; + } + else + return; + } + else + setModified(false); + + pause(); + + dontAddStroke = false; + + inPlay = false; + timer->stop(); + putter->resetAngles(); + + int oldCurHole = curHole; + curHole++; + emit currentHole(curHole); + + if (reset) + { + whiteBall->move(width/2, height/2); + holeInfo.borderWallsChanged(true); + } + + int leastScore = INT_MAX; + + // to get the first player to go first on every hole, + // don't do the score stuff below + curPlayer = players->begin(); + double oldx=(*curPlayer).ball()->x(), oldy=(*curPlayer).ball()->y(); + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + { + if (curHole > 1) + { + bool ahead = false; + if ((*it).lastScore() != 0) + { + if ((*it).lastScore() < leastScore) + ahead = true; + else if ((*it).lastScore() == leastScore) + { + for (int i = curHole - 1; i > 0; --i) + { + const int thisScore = (*it).score(i); + const int thatScore = (*curPlayer).score(i); + if (thisScore < thatScore) + { + ahead = true; + break; + } + else if (thisScore > thatScore) + break; + } + } + } + + if (ahead) + { + curPlayer = it; + leastScore = (*it).lastScore(); + } + } + + if (reset) + (*it).ball()->move(width / 2, height / 2); + else + (*it).ball()->move(whiteBall->x(), whiteBall->y()); + + (*it).ball()->setState(Stopped); + + // this gets set to false when the ball starts + // to move by the Mr. Ball himself. + (*it).ball()->setBeginningOfHole(true); + if ((int)(*it).scores().count() < curHole) + (*it).addHole(); + (*it).ball()->setVelocity(0, 0); + (*it).ball()->setVisible(false); + } + + emit newPlayersTurn(&(*curPlayer)); + + if (reset) + openFile(); + + inPlay = false; + timer->start(timerMsec); + + // if (false) { we're done with the round! } + if (oldCurHole != curHole) + { + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->setPlaceOnGround(false); + + // here we have to make sure the scoreboard shows + // all of the holes up until now; + + for (; scoreboardHoles < curHole; ++scoreboardHoles) + { + cfg->setGroup(QString("%1-hole@-50,-50|0").arg(scoreboardHoles + 1)); + emit newHole(cfg->readNumEntry("par", 3)); + } + + resetHoleScores(); + updateShowInfo(); + + // this is from shotDone() + (*curPlayer).ball()->setVisible(true); + putter->setOrigin((*curPlayer).ball()->x(), (*curPlayer).ball()->y()); + updateMouse(); + + ballStateList.canUndo = false; + + (*curPlayer).ball()->collisionDetect(oldx, oldy); + } + + unPause(); +} + +void KolfGame::showInfo() +{ + QString text = i18n("Hole %1: par %2, maximum %3 strokes").arg(curHole).arg(holeInfo.par()).arg(holeInfo.maxStrokes()); + infoText->move((width - QFontMetrics(infoText->font()).width(text)) / 2, infoText->y()); + infoText->setText(text); + // I hate this text! Let's not show it + //infoText->setVisible(true); + + emit newStatusText(text); +} + +void KolfGame::showInfoDlg(bool addDontShowAgain) +{ + KMessageBox::information(parentWidget(), + i18n("Course name: %1").arg(holeInfo.name()) + QString("\n") + + i18n("Created by %1").arg(holeInfo.author()) + QString("\n") + + i18n("%1 holes").arg(highestHole), + i18n("Course Information"), + addDontShowAgain? holeInfo.name() + QString(" ") + holeInfo.author() : QString::null); +} + +void KolfGame::hideInfo() +{ + infoText->setText(""); + infoText->setVisible(false); + + emit newStatusText(QString::null); +} + +void KolfGame::openFile() +{ + Object *curObj = 0; + + QCanvasItem *item = 0; + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + { + // sometimes info is still showing + citem->hideInfo(); + citem->aboutToDie(); + } + } + + items.setAutoDelete(true); + items.clear(); + items.setAutoDelete(false); + + extraMoveable.setAutoDelete(false); + extraMoveable.clear(); + fastAdvancers.setAutoDelete(false); + fastAdvancers.clear(); + selectedItem = 0; + + // will tell basic course info + // we do this here for the hell of it. + // there is no fake id, by the way, + // because it's old and when i added ids i forgot to change it. + cfg->setGroup("0-course@-50,-50"); + holeInfo.setAuthor(cfg->readEntry("author", holeInfo.author())); + holeInfo.setName(cfg->readEntry("Name", holeInfo.name())); + holeInfo.setUntranslatedName(cfg->readEntryUntranslated("Name", holeInfo.untranslatedName())); + emit titleChanged(holeInfo.name()); + + cfg->setGroup(QString("%1-hole@-50,-50|0").arg(curHole)); + curPar = cfg->readNumEntry("par", 3); + holeInfo.setPar(curPar); + holeInfo.borderWallsChanged(cfg->readBoolEntry("borderWalls", holeInfo.borderWalls())); + holeInfo.setMaxStrokes(cfg->readNumEntry("maxstrokes", 10)); + bool hasFinalLoad = cfg->readBoolEntry("hasFinalLoad", true); + + QStringList missingPlugins; + QStringList groups = cfg->groupList(); + + int numItems = 0; + int _highestHole = 0; + + for (QStringList::Iterator it = groups.begin(); it != groups.end(); ++it) + { + // [-@,|] + cfg->setGroup(*it); + + const int len = (*it).length(); + const int dashIndex = (*it).find("-"); + const int holeNum = (*it).left(dashIndex).toInt(); + if (holeNum > _highestHole) + _highestHole = holeNum; + + const int atIndex = (*it).find("@"); + const QString name = (*it).mid(dashIndex + 1, atIndex - (dashIndex + 1)); + + if (holeNum != curHole) + { + // if we've had one, break, cause list is sorted + // erps, no, cause we need to know highest hole! + if (numItems && !recalcHighestHole) + break; + continue; + } + numItems++; + + + const int commaIndex = (*it).find(","); + const int pipeIndex = (*it).find("|"); + const int x = (*it).mid(atIndex + 1, commaIndex - (atIndex + 1)).toInt(); + const int y = (*it).mid(commaIndex + 1, pipeIndex - (commaIndex + 1)).toInt(); + + // will tell where ball is + if (name == "ball") + { + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->move(x, y); + whiteBall->move(x, y); + continue; + } + + const int id = (*it).right(len - (pipeIndex + 1)).toInt(); + + bool loaded = false; + + for (curObj = obj->first(); curObj; curObj = obj->next()) + { + if (name != curObj->_name()) + continue; + + QCanvasItem *newItem = curObj->newObject(course); + items.append(newItem); + + CanvasItem *canvasItem = dynamic_cast(newItem); + if (!canvasItem) + continue; + + canvasItem->setId(id); + canvasItem->setGame(this); + canvasItem->editModeChanged(editing); + canvasItem->setName(curObj->_name()); + addItemsToMoveableList(canvasItem->moveableItems()); + if (canvasItem->fastAdvance()) + addItemToFastAdvancersList(canvasItem); + + newItem->move(x, y); + canvasItem->firstMove(x, y); + + newItem->setVisible(true); + + // make things actually show + if (!hasFinalLoad) + { + cfg->setGroup(makeGroup(id, curHole, canvasItem->name(), x, y)); + canvasItem->load(cfg); + course->update(); + } + + // we don't allow multiple items for the same thing in + // the file! + + loaded = true; + break; + } + + if (!loaded && name != "hole" && missingPlugins.contains(name) <= 0) + missingPlugins.append(name); + } + + if (!missingPlugins.empty()) + { + KMessageBox::informationList(this, QString("

<http://katzbrown.com/kolf/Plugins/>

") + i18n("This hole uses the following plugins, which you do not have installed:") + QString("

"), missingPlugins, QString::null, QString("%1 warning").arg(holeInfo.untranslatedName() + QString::number(curHole))); + } + + lastDelId = -1; + + // if it's the first hole let's not + if (!numItems && curHole > 1 && !addingNewHole && curHole >= _highestHole) + { + // we're done, let's quit + curHole--; + pause(); + emit holesDone(); + + // tidy things up + setBorderWalls(false); + clearHole(); + setModified(false); + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->setVisible(false); + + return; + } + + // do it down here; if !hasFinalLoad, do it up there! + QCanvasItem *qcanvasItem = 0; + QPtrList todo; + QPtrList qtodo; + if (hasFinalLoad) + { + for (qcanvasItem = items.first(); qcanvasItem; qcanvasItem = items.next()) + { + CanvasItem *item = dynamic_cast(qcanvasItem); + if (item) + { + if (item->loadLast()) + { + qtodo.append(qcanvasItem); + todo.append(item); + } + else + { + QString group = makeGroup(item->curId(), curHole, item->name(), (int)qcanvasItem->x(), (int)qcanvasItem->y()); + cfg->setGroup(group); + item->load(cfg); + } + } + } + + CanvasItem *citem = 0; + qcanvasItem = qtodo.first(); + for (citem = todo.first(); citem; citem = todo.next()) + { + cfg->setGroup(makeGroup(citem->curId(), curHole, citem->name(), (int)qcanvasItem->x(), (int)qcanvasItem->y())); + citem->load(cfg); + + qcanvasItem = qtodo.next(); + } + } + + for (qcanvasItem = items.first(); qcanvasItem; qcanvasItem = items.next()) + { + CanvasItem *citem = dynamic_cast(qcanvasItem); + if (citem) + citem->updateZ(); + } + + if (curHole > _highestHole) + _highestHole = curHole; + + if (recalcHighestHole) + { + highestHole = _highestHole; + recalcHighestHole = false; + emit largestHole(highestHole); + } + + if (curHole == 1 && !filename.isNull() && !infoShown) + { + // let's not now, because they see it when they choose course + //showInfoDlg(true); + infoShown = true; + } + + setModified(false); +} + +void KolfGame::addItemsToMoveableList(QPtrList list) +{ + QCanvasItem *item = 0; + for (item = list.first(); item; item = list.next()) + extraMoveable.append(item); +} + +void KolfGame::addItemToFastAdvancersList(CanvasItem *item) +{ + fastAdvancers.append(item); + fastAdvancedExist = fastAdvancers.count() > 0; +} + +void KolfGame::addNewObject(Object *newObj) +{ + QCanvasItem *newItem = newObj->newObject(course); + items.append(newItem); + newItem->setVisible(true); + + CanvasItem *canvasItem = dynamic_cast(newItem); + if (!canvasItem) + return; + + // we need to find a number that isn't taken + int i = lastDelId > 0? lastDelId : items.count() - 30; + if (i <= 0) + i = 0; + + for (;; ++i) + { + bool found = false; + QCanvasItem *item = 0; + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + { + if (citem->curId() == i) + { + found = true; + break; + } + } + } + + + if (!found) + break; + } + canvasItem->setId(i); + + canvasItem->setGame(this); + + if (m_showInfo) + canvasItem->showInfo(); + else + canvasItem->hideInfo(); + + canvasItem->editModeChanged(editing); + + canvasItem->setName(newObj->_name()); + addItemsToMoveableList(canvasItem->moveableItems()); + + if (canvasItem->fastAdvance()) + addItemToFastAdvancersList(canvasItem); + + newItem->move(width/2 - 18, height / 2 - 18); + + if (selectedItem) + canvasItem->selectedItem(selectedItem); + + setModified(true); +} + +bool KolfGame::askSave(bool noMoreChances) +{ + if (!modified) + // not cancel, don't save + return false; + + int result = KMessageBox::warningYesNoCancel(this, i18n("There are unsaved changes to current hole. Save them?"), i18n("Unsaved Changes"), KStdGuiItem::save(), noMoreChances? KStdGuiItem::discard() : i18n("Save &Later"), noMoreChances? "DiscardAsk" : "SaveAsk", true); + switch (result) + { + case KMessageBox::Yes: + save(); + // fallthrough + + case KMessageBox::No: + return false; + break; + + case KMessageBox::Cancel: + return true; + break; + + default: + break; + } + + return false; +} + +void KolfGame::addNewHole() +{ + if (askSave(true)) + return; + + // either it's already false + // because it was saved by askSave(), + // or the user pressed the 'discard' button + setModified(false); + + // find highest hole num, and create new hole + // now openFile makes highest hole for us + + addingNewHole = true; + curHole = highestHole; + recalcHighestHole = true; + startNextHole(); + addingNewHole = false; + emit currentHole(curHole); + + // make sure even the current player isn't showing + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->setVisible(false); + + whiteBall->setVisible(editing); + highlighter->setVisible(false); + putter->setVisible(!editing); + inPlay = false; + + // add default objects + Object *curObj = 0; + for (curObj = obj->first(); curObj; curObj = obj->next()) + if (curObj->addOnNewHole()) + addNewObject(curObj); + + save(); +} + +// kantan deshou ;-) +void KolfGame::resetHole() +{ + if (askSave(true)) + return; + setModified(false); + curHole--; + startNextHole(); + resetHoleScores(); +} + +void KolfGame::resetHoleScores() +{ + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + { + (*it).resetScore(curHole); + emit scoreChanged((*it).id(), curHole, 0); + } +} + +void KolfGame::clearHole() +{ + QCanvasItem *qcanvasItem = 0; + for (qcanvasItem = items.first(); qcanvasItem; qcanvasItem = items.next()) + { + CanvasItem *citem = dynamic_cast(qcanvasItem); + if (citem) + citem->aboutToDie(); + } + items.setAutoDelete(true); + items.clear(); + items.setAutoDelete(false); + emit newSelectedItem(&holeInfo); + + // add default objects + Object *curObj = 0; + for (curObj = obj->first(); curObj; curObj = obj->next()) + if (curObj->addOnNewHole()) + addNewObject(curObj); + + setModified(true); +} + +void KolfGame::switchHole(int hole) +{ + if (inPlay) + return; + if (hole < 1 || hole > highestHole) + return; + + bool wasEditing = editing; + if (editing) + toggleEditMode(); + + if (askSave(true)) + return; + setModified(false); + + curHole = hole; + resetHole(); + + if (wasEditing) + toggleEditMode(); +} + +void KolfGame::switchHole(const QString &holestring) +{ + bool ok; + int hole = holestring.toInt(&ok); + if (!ok) + return; + switchHole(hole); +} + +void KolfGame::nextHole() +{ + switchHole(curHole + 1); +} + +void KolfGame::prevHole() +{ + switchHole(curHole - 1); +} + +void KolfGame::firstHole() +{ + switchHole(1); +} + +void KolfGame::lastHole() +{ + switchHole(highestHole); +} + +void KolfGame::randHole() +{ + int newHole = 1 + (int)((double)kapp->random() * ((double)(highestHole - 1) / (double)RAND_MAX)); + switchHole(newHole); +} + +void KolfGame::save() +{ + if (filename.isNull()) + { + QString newfilename = KFileDialog::getSaveFileName(":kourses", "application/x-kourse", this, i18n("Pick Kolf Course to Save To")); + if (newfilename.isNull()) + return; + + setFilename(newfilename); + } + + emit parChanged(curHole, holeInfo.par()); + emit titleChanged(holeInfo.name()); + + // we use this bool for optimization + // in openFile(). + bool hasFinalLoad = false; + fastAdvancedExist = false; + + QCanvasItem *item = 0; + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + { + citem->aboutToSave(); + if (citem->loadLast()) + hasFinalLoad = true; + } + } + + QStringList groups = cfg->groupList(); + + // wipe out all groups from this hole + for (QStringList::Iterator it = groups.begin(); it != groups.end(); ++it) + { + int holeNum = (*it).left((*it).find("-")).toInt(); + if (holeNum == curHole) + cfg->deleteGroup(*it); + } + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + { + citem->clean(); + + cfg->setGroup(makeGroup(citem->curId(), curHole, citem->name(), (int)item->x(), (int)item->y())); + citem->save(cfg); + } + } + + // save where ball starts (whiteBall tells all) + cfg->setGroup(QString("%1-ball@%2,%3").arg(curHole).arg((int)whiteBall->x()).arg((int)whiteBall->y())); + cfg->writeEntry("dummykey", true); + + cfg->setGroup("0-course@-50,-50"); + cfg->writeEntry("author", holeInfo.author()); + cfg->writeEntry("Name", holeInfo.untranslatedName()); + + // save hole info + cfg->setGroup(QString("%1-hole@-50,-50|0").arg(curHole)); + cfg->writeEntry("par", holeInfo.par()); + cfg->writeEntry("maxstrokes", holeInfo.maxStrokes()); + cfg->writeEntry("borderWalls", holeInfo.borderWalls()); + cfg->writeEntry("hasFinalLoad", hasFinalLoad); + + cfg->sync(); + + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + citem->savingDone(); + } + + setModified(false); +} + +void KolfGame::toggleEditMode() +{ + // won't be editing anymore, and user wants to cancel, we return + // this is pretty useless. when the person leaves the hole, + // he gets asked again + /* + if (editing && modified) + { + if (askSave(false)) + { + emit checkEditing(); + return; + } + } + */ + + moving = false; + selectedItem = 0; + + editing = !editing; + + if (editing) + { + emit editingStarted(); + emit newSelectedItem(&holeInfo); + } + else + { + emit editingEnded(); + setCursor(KCursor::arrowCursor()); + } + + // alert our items + QCanvasItem *item = 0; + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + citem->editModeChanged(editing); + } + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + { + // curplayer shouldn't be hidden no matter what + if ((*it).ball()->beginningOfHole() && it != curPlayer) + (*it).ball()->setVisible(false); + else + (*it).ball()->setVisible(!editing); + } + + whiteBall->setVisible(editing); + highlighter->setVisible(false); + + // shouldn't see putter whilst editing + putter->setVisible(!editing); + + if (editing) + autoSaveTimer->start(autoSaveMsec); + else + autoSaveTimer->stop(); + + inPlay = false; +} + +void KolfGame::playSound(QString file, double vol) +{ + if (m_sound) + { + KPlayObject *oldPlayObject = 0; + for (oldPlayObject = oldPlayObjects.first(); oldPlayObject; oldPlayObject = oldPlayObjects.next()) + { + if (oldPlayObject && oldPlayObject->state() != Arts::posPlaying) + { + oldPlayObjects.remove(); + + // because we will go to next() next time + // and after remove current item is one after + // removed item + (void) oldPlayObjects.prev(); + } + } + + file = soundDir + file + QString::fromLatin1(".wav"); + + // not needed when all of the files are in the distribution + //if (!QFile::exists(file)) + //return; + + KPlayObjectFactory factory(artsServer.server()); + KPlayObject *playObject = factory.createPlayObject(KURL(file), true); + + if (playObject && !playObject->isNull()) + { + if (vol > 1) + vol = 1; + else if (vol <= .01) + { + delete playObject; + return; + } + + if (vol < .99) + { + //new KVolumeControl(vol, artsServer.server(), playObject); + } + + playObject->play(); + oldPlayObjects.append(playObject); + } + } +} + +void HoleInfo::borderWallsChanged(bool yes) +{ + m_borderWalls = yes; + game->setBorderWalls(yes); +} + +void KolfGame::print(KPrinter &pr) +{ + QPainter p(&pr); + + QPaintDeviceMetrics metrics(&pr); + + // translate to center + p.translate(metrics.width() / 2 - course->rect().width() / 2, metrics.height() / 2 - course->rect().height() / 2); + + QPixmap pix(width, height); + QPainter pixp(&pix); + course->drawArea(course->rect(), &pixp); + p.drawPixmap(0, 0, pix); + + p.setPen(QPen(black, 2)); + p.drawRect(course->rect()); + + p.resetXForm(); + + if (pr.option("kde-kolf-title") == "true") + { + QString text = i18n("%1 - Hole %2; by %3").arg(holeInfo.name()).arg(curHole).arg(holeInfo.author()); + QFont font(kapp->font()); + font.setPointSize(18); + QRect rect = QFontMetrics(font).boundingRect(text); + p.setFont(font); + + p.drawText(metrics.width() / 2 - rect.width() / 2, metrics.height() / 2 - course->rect().height() / 2 -20 - rect.height(), text); + } +} + +bool KolfGame::allPlayersDone() +{ + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + if ((*it).ball()->curState() != Holed) + return false; + + return true; +} + +void KolfGame::setBorderWalls(bool showing) +{ + Wall *wall = 0; + for (wall = borderWalls.first(); wall; wall = borderWalls.next()) + wall->setVisible(showing); +} + +void KolfGame::setUseAdvancedPutting(bool yes) +{ + m_useAdvancedPutting = yes; + + // increase maxStrength in advanced putting mode + if (yes) + maxStrength = 65; + else + maxStrength = 55; +} + +void KolfGame::setShowGuideLine(bool yes) +{ + putter->setShowGuideLine(yes); +} + +void KolfGame::setSound(bool yes) +{ + m_sound = yes; +} + +void KolfGame::courseInfo(CourseInfo &info, const QString& filename) +{ + KConfig cfg(filename); + cfg.setGroup("0-course@-50,-50"); + info.author = cfg.readEntry("author", info.author); + info.name = cfg.readEntry("Name", cfg.readEntry("name", info.name)); + info.untranslatedName = cfg.readEntryUntranslated("Name", cfg.readEntryUntranslated("name", info.name)); + + unsigned int hole = 1; + unsigned int par= 0; + while (1) + { + QString group = QString("%1-hole@-50,-50|0").arg(hole); + if (!cfg.hasGroup(group)) + { + hole--; + break; + } + + cfg.setGroup(group); + par += cfg.readNumEntry("par", 3); + + hole++; + } + + info.par = par; + info.holes = hole; +} + +void KolfGame::scoresFromSaved(KConfig *config, PlayerList &players) +{ + config->setGroup("0 Saved Game"); + int numPlayers = config->readNumEntry("Players", 0); + if (numPlayers <= 0) + return; + + for (int i = 1; i <= numPlayers; ++i) + { + // this is same as in kolf.cpp, but we use saved game values + config->setGroup(QString::number(i)); + players.append(Player()); + players.last().ball()->setColor(config->readEntry("Color", "#ffffff")); + players.last().setName(config->readEntry("Name")); + players.last().setId(i); + + QStringList scores(config->readListEntry("Scores")); + QValueList intscores; + for (QStringList::Iterator it = scores.begin(); it != scores.end(); ++it) + intscores.append((*it).toInt()); + + players.last().setScores(intscores); + } +} + +void KolfGame::saveScores(KConfig *config) +{ + // wipe out old player info + QStringList groups = config->groupList(); + for (QStringList::Iterator it = groups.begin(); it != groups.end(); ++it) + { + // this deletes all int groups, ie, the player info groups + bool ok = false; + (*it).toInt(&ok); + if (ok) + config->deleteGroup(*it); + } + + config->setGroup("0 Saved Game"); + config->writeEntry("Players", players->count()); + config->writeEntry("Course", filename); + config->writeEntry("Current Hole", curHole); + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + { + config->setGroup(QString::number((*it).id())); + config->writeEntry("Name", (*it).name()); + config->writeEntry("Color", (*it).ball()->color().name()); + + QStringList scores; + QValueList intscores = (*it).scores(); + for (QValueList::Iterator it = intscores.begin(); it != intscores.end(); ++it) + scores.append(QString::number(*it)); + + config->writeEntry("Scores", scores); + } +} + +CourseInfo::CourseInfo() +: name(i18n("Course Name")), author(i18n("Course Author")), holes(0), par(0) +{ +} + +#include "game.moc" diff --git a/kolf/game.h b/kolf/game.h new file mode 100644 index 00000000..afb13b3c --- /dev/null +++ b/kolf/game.h @@ -0,0 +1,1026 @@ +#ifndef GAME_H +#define GAME_H + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "object.h" +#include "config.h" +#include "canvasitem.h" +#include "ball.h" +#include "statedb.h" +#include "rtti.h" +#include + +class QLabel; +class QSlider; +class QCheckBox; +class QTimer; +class QKeyEvent; +class QMouseEvent; +class QPainter; +class KConfig; +class KPrinter; +class KolfGame; + +enum Direction { D_Left, D_Right, Forwards, Backwards }; +enum Amount { Amount_Less, Amount_Normal, Amount_More }; +enum HoleResult { Result_Holed, Result_Miss, Result_LipOut }; + +class Player; + +class BallStateInfo +{ +public: + void saveState(KConfig *cfg); + void loadState(KConfig *cfg); + + int id; + QPoint spot; + BallState state; + bool beginningOfHole; + int score; +}; +class BallStateList : public QValueList +{ +public: + int hole; + int player; + bool canUndo; + Vector vector; +}; + +class Player +{ +public: + Player() : m_ball(new Ball(0)) {}; + Ball *ball() const { return m_ball; } + void setBall(Ball *ball) { m_ball = ball; } + BallStateInfo stateInfo(int hole) const { BallStateInfo ret; ret.spot = QPoint(m_ball->x(), m_ball->y()); ret.state = m_ball->curState(); ret.score = score(hole); ret.beginningOfHole = m_ball->beginningOfHole(); ret.id = m_id; return ret; } + + QValueList scores() const { return m_scores; } + void setScores(const QValueList &newScores) { m_scores = newScores; } + int score(int hole) const { return (*m_scores.at(hole - 1)); } + int lastScore() const { return m_scores.last(); } + int firstScore() const { return m_scores.first(); } + + void addStrokeToHole(int hole) { (*m_scores.at(hole - 1))++; } + void setScoreForHole(int score, int hole) { (*m_scores.at(hole - 1)) = score; } + void subtractStrokeFromHole(int hole) { (*m_scores.at(hole - 1))--; } + void resetScore(int hole) { (*m_scores.at(hole - 1)) = 0; } + void addHole() { m_scores.append(0); } + unsigned int numHoles() const { return m_scores.count(); } + + QString name() const { return m_name; } + void setName(const QString &name) { m_name = name; m_ball->setName(name); } + + void setId(int id) { m_id = id; } + int id() const { return m_id; } + +private: + Ball *m_ball; + QValueList m_scores; + QString m_name; + int m_id; +}; +typedef QValueList PlayerList; + +class Arrow : public QCanvasLine +{ +public: + Arrow(QCanvas *canvas); + void setAngle(double newAngle) { m_angle = newAngle; } + double angle() const { return m_angle; } + void setLength(double newLength) { m_length = newLength; } + double length() const { return m_length; } + void setReversed(bool yes) { m_reversed = yes; } + bool reversed() const { return m_reversed; } + virtual void setVisible(bool); + virtual void setPen(QPen p); + void aboutToDie(); + virtual void moveBy(double, double); + void updateSelf(); + virtual void setZ(double newz); + +private: + double m_angle; + double m_length; + bool m_reversed; + QCanvasLine *line1; + QCanvasLine *line2; +}; + +class RectPoint; +class RectItem +{ +public: + virtual void newSize(int /*width*/, int /*height*/) {}; +}; + +class RectPoint : public QCanvasEllipse, public CanvasItem +{ +public: + RectPoint(QColor color, RectItem *, QCanvas *canvas); + void dontMove() { dontmove = true; } + virtual void moveBy(double dx, double dy); + virtual Config *config(QWidget *parent); + virtual bool deleteable() const { return false; } + virtual bool cornerResize() const { return true; } + virtual CanvasItem *itemToDelete() { return dynamic_cast(rect); } + void setSizeFactor(double newFactor) { m_sizeFactor = newFactor; } + +protected: + RectItem *rect; + double m_sizeFactor; + +private: + bool dontmove; +}; + +class Ellipse : public QCanvasEllipse, public CanvasItem, public RectItem +{ +public: + Ellipse(QCanvas *canvas); + virtual void advance(int phase); + + int changeEvery() const { return m_changeEvery; } + void setChangeEvery(int news) { m_changeEvery = news; } + bool changeEnabled() const { return m_changeEnabled; } + void setChangeEnabled(bool news); + + virtual void aboutToDie(); + virtual void aboutToSave(); + virtual void savingDone(); + + virtual QPtrList moveableItems() const; + + virtual void newSize(int width, int height); + virtual void moveBy(double dx, double dy); + + virtual void editModeChanged(bool changed); + + virtual void save(KConfig *cfg); + virtual void load(KConfig *cfg); + + virtual Config *config(QWidget *parent); + +protected: + RectPoint *point; + int m_changeEvery; + bool m_changeEnabled; + +private: + int count; + bool dontHide; +}; +class EllipseConfig : public Config +{ + Q_OBJECT + +public: + EllipseConfig(Ellipse *ellipse, QWidget *); + +private slots: + void value1Changed(int news); + void value2Changed(int news); + void check1Changed(bool on); + void check2Changed(bool on); + +protected: + QVBoxLayout *m_vlayout; + +private: + QLabel *slow1; + QLabel *fast1; + QLabel *slow2; + QLabel *fast2; + QSlider *slider1; + QSlider *slider2; + Ellipse *ellipse; +}; + +class Puddle : public Ellipse +{ +public: + Puddle(QCanvas *canvas); + virtual bool collision(Ball *ball, long int id); + virtual int rtti() const { return Rtti_DontPlaceOn; } +}; +class PuddleObj : public Object +{ +public: + PuddleObj() { m_name = i18n("Puddle"); m__name = "puddle"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Puddle(canvas); } +}; + +class Sand : public Ellipse +{ +public: + Sand(QCanvas *canvas); + virtual bool collision(Ball *ball, long int id); +}; +class SandObj : public Object +{ +public: + SandObj() { m_name = i18n("Sand"); m__name = "sand"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Sand(canvas); } +}; + +class Inside : public QCanvasEllipse, public CanvasItem +{ +public: + Inside(CanvasItem *item, QCanvas *canvas) : QCanvasEllipse(canvas) { this->item = item; } + virtual bool collision(Ball *ball, long int id) { return item->collision(ball, id); } + +protected: + CanvasItem *item; +}; + +class Bumper : public QCanvasEllipse, public CanvasItem +{ +public: + Bumper(QCanvas *canvas); + + virtual void advance(int phase); + virtual void aboutToDie(); + virtual void moveBy(double dx, double dy); + virtual void editModeChanged(bool changed); + + virtual bool collision(Ball *ball, long int id); + +protected: + QColor firstColor; + QColor secondColor; + Inside *inside; + +private: + int count; +}; +class BumperObj : public Object +{ +public: + BumperObj() { m_name = i18n("Bumper"); m__name = "bumper"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Bumper(canvas); } +}; + +class Hole : public QCanvasEllipse, public CanvasItem +{ +public: + Hole(QColor color, QCanvas *canvas); + virtual bool place(Ball * /*ball*/, bool /*wasCenter*/) { return true; }; + + virtual bool collision(Ball *ball, long int id); + +protected: + virtual HoleResult result(const QPoint, double, bool *wasCenter); +}; + +class Cup : public Hole +{ +public: + Cup(QCanvas *canvas); + virtual bool place(Ball *ball, bool wasCenter); + virtual void save(KConfig *cfg); + virtual bool canBeMovedByOthers() const { return true; } + virtual void draw(QPainter &painter); + +protected: + QPixmap pixmap; +}; +class CupObj : public Object +{ +public: + CupObj() { m_name = i18n("Cup"); m__name = "cup"; m_addOnNewHole = true; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Cup(canvas); } +}; + +class BlackHole; +class BlackHoleConfig : public Config +{ + Q_OBJECT + +public: + BlackHoleConfig(BlackHole *blackHole, QWidget *parent); + +private slots: + void degChanged(int); + void minChanged(double); + void maxChanged(double); + +private: + BlackHole *blackHole; +}; +class BlackHoleExit : public QCanvasLine, public CanvasItem +{ +public: + BlackHoleExit(BlackHole *blackHole, QCanvas *canvas); + virtual int rtti() const { return Rtti_NoCollision; } + virtual void aboutToDie(); + virtual void moveBy(double dx, double dy); + virtual bool deleteable() const { return false; } + virtual bool canBeMovedByOthers() const { return true; } + virtual void editModeChanged(bool editing); + virtual void setPen(QPen p); + virtual void showInfo(); + virtual void hideInfo(); + void updateArrowAngle(); + void updateArrowLength(); + virtual Config *config(QWidget *parent); + BlackHole *blackHole; + +protected: + Arrow *arrow; +}; +class BlackHoleTimer : public QObject +{ +Q_OBJECT + +public: + BlackHoleTimer(Ball *ball, double speed, int msec); + +signals: + void eject(Ball *ball, double speed); + void halfway(); + +protected slots: + void mySlot(); + void myMidSlot(); + +protected: + double m_speed; + Ball *m_ball; +}; +class BlackHole : public QObject, public Hole +{ +Q_OBJECT + +public: + BlackHole(QCanvas *canvas); + virtual bool canBeMovedByOthers() const { return true; } + virtual void aboutToDie(); + virtual void showInfo(); + virtual void hideInfo(); + virtual bool place(Ball *ball, bool wasCenter); + virtual void save(KConfig *cfg); + virtual void load(KConfig *cfg); + virtual Config *config(QWidget *parent) { return new BlackHoleConfig(this, parent); } + virtual QPtrList moveableItems() const; + double minSpeed() const { return m_minSpeed; } + double maxSpeed() const { return m_maxSpeed; } + void setMinSpeed(double news) { m_minSpeed = news; exitItem->updateArrowLength(); } + void setMaxSpeed(double news) { m_maxSpeed = news; exitItem->updateArrowLength(); } + + int curExitDeg() const { return exitDeg; } + void setExitDeg(int newdeg); + + virtual void editModeChanged(bool editing) { exitItem->editModeChanged(editing); } + void updateInfo(); + + virtual void shotStarted() { runs = 0; }; + + virtual void moveBy(double dx, double dy); + +public slots: + void eject(Ball *ball, double speed); + void halfway(); + +protected: + int exitDeg; + BlackHoleExit *exitItem; + double m_minSpeed; + double m_maxSpeed; + +private: + int runs; + QCanvasLine *infoLine; + QCanvasEllipse *outside; + void finishMe(); +}; +class BlackHoleObj : public Object +{ +public: + BlackHoleObj() { m_name = i18n("Black Hole"); m__name = "blackhole"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new BlackHole(canvas); } +}; + +class WallPoint; +class Wall : public QCanvasLine, public CanvasItem +{ +public: + Wall(QCanvas *canvas); + virtual void aboutToDie(); + double dampening; + + void setAlwaysShow(bool yes); + virtual void setZ(double newz); + virtual void setPen(QPen p); + virtual bool collision(Ball *ball, long int id); + virtual void save(KConfig *cfg); + virtual void load(KConfig *cfg); + virtual void selectedItem(QCanvasItem *item); + virtual void editModeChanged(bool changed); + virtual void moveBy(double dx, double dy); + virtual void setVelocity(double vx, double vy); + virtual void clean(); + + // must reimp because we gotta move the end items, + // and we do that in moveBy() + virtual void setPoints(int xa, int ya, int xb, int yb) { QCanvasLine::setPoints(xa, ya, xb, yb); moveBy(0, 0); } + + virtual int rtti() const { return Rtti_Wall; } + virtual QPtrList moveableItems() const; + virtual void setGame(KolfGame *game); + virtual void setVisible(bool); + + virtual QPointArray areaPoints() const; + +protected: + WallPoint *startItem; + WallPoint *endItem; + bool editing; + +private: + long int lastId; + + friend class WallPoint; +}; +class WallPoint : public QCanvasEllipse, public CanvasItem +{ +public: + WallPoint(bool start, Wall *wall, QCanvas *canvas); + void setAlwaysShow(bool yes) { alwaysShow = yes; updateVisible(); } + virtual void editModeChanged(bool changed); + virtual void moveBy(double dx, double dy); + virtual int rtti() const { return Rtti_WallPoint; } + virtual bool deleteable() const { return false; } + virtual bool collision(Ball *ball, long int id); + virtual CanvasItem *itemToDelete() { return wall; } + virtual void clean(); + virtual Config *config(QWidget *parent) { return wall->config(parent); } + void dontMove() { dontmove = true; }; + void updateVisible(); + + Wall *parentWall() { return wall; } + +protected: + Wall *wall; + bool editing; + bool visible; + +private: + bool alwaysShow; + bool start; + bool dontmove; + long int lastId; + + friend class Wall; +}; +class WallObj : public Object +{ +public: + WallObj() { m_name = i18n("Wall"); m__name = "wall"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Wall(canvas); } +}; + +class Putter : public QCanvasLine, public CanvasItem +{ +public: + Putter(QCanvas *canvas); + void go(Direction, Amount amount = Amount_Normal); + void setOrigin(int x, int y); + int curLen() const { return len; } + double curAngle() const { return angle; } + int curDeg() const { return rad2deg(angle); } + virtual void showInfo(); + virtual void hideInfo(); + void setAngle(double news) { angle = news; finishMe(); } + void setDeg(int news) { angle = deg2rad(news); finishMe(); } + double curMaxAngle() const { return maxAngle; } + virtual int rtti() const { return Rtti_Putter; } + virtual void setVisible(bool yes); + void saveAngle(Ball *ball) { angleMap[ball] = angle; } + void setAngle(Ball *ball); + void resetAngles() { angleMap.clear(); setZ(999999); } + virtual bool canBeMovedByOthers() const { return true; } + virtual void moveBy(double dx, double dy); + void setShowGuideLine(bool yes); + +private: + QPoint midPoint; + double maxAngle; + double angle; + double oneDegree; + QMap angleMap; + int len; + void finishMe(); + int putterWidth; + QCanvasLine *guideLine; + bool m_showGuideLine; +}; + +class Bridge; +class BridgeConfig : public Config +{ + Q_OBJECT + +public: + BridgeConfig(Bridge *bridge, QWidget *); + +protected slots: + void topWallChanged(bool); + void botWallChanged(bool); + void leftWallChanged(bool); + void rightWallChanged(bool); + +protected: + QVBoxLayout *m_vlayout; + QCheckBox *top; + QCheckBox *bot; + QCheckBox *left; + QCheckBox *right; + +private: + Bridge *bridge; +}; +class Bridge : public QCanvasRectangle, public CanvasItem, public RectItem +{ +public: + Bridge(QRect rect, QCanvas *canvas); + virtual bool collision(Ball *ball, long int id); + virtual void aboutToDie(); + virtual void editModeChanged(bool changed); + virtual void moveBy(double dx, double dy); + virtual void load(KConfig *cfg); + virtual void save(KConfig *cfg); + virtual bool vStrut() const { return true; } + void doLoad(KConfig *cfg); + void doSave(KConfig *cfg); + virtual void newSize(int width, int height); + virtual void setGame(KolfGame *game); + virtual Config *config(QWidget *parent) { return new BridgeConfig(this, parent); } + void setSize(int width, int height); + virtual QPtrList moveableItems() const; + + void setWallColor(QColor color); + QPen wallPen() const { return topWall->pen(); } + + double wallZ() const { return topWall->z(); } + void setWallZ(double); + + void setTopWallVisible(bool yes) { topWall->setVisible(yes); } + void setBotWallVisible(bool yes) { botWall->setVisible(yes); } + void setLeftWallVisible(bool yes) { leftWall->setVisible(yes); } + void setRightWallVisible(bool yes) { rightWall->setVisible(yes); } + bool topWallVisible() const { return topWall->isVisible(); } + bool botWallVisible() const { return botWall->isVisible(); } + bool leftWallVisible() const { return leftWall->isVisible(); } + bool rightWallVisible() const { return rightWall->isVisible(); } + +protected: + Wall *topWall; + Wall *botWall; + Wall *leftWall; + Wall *rightWall; + RectPoint *point; +}; +class BridgeObj : public Object +{ +public: + BridgeObj() { m_name = i18n("Bridge"); m__name = "bridge"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Bridge(QRect(0, 0, 80, 40), canvas); } +}; + +class Sign; +class SignConfig : public BridgeConfig +{ + Q_OBJECT + +public: + SignConfig(Sign *sign, QWidget *parent); + +private slots: + void textChanged(const QString &); + +private: + Sign *sign; +}; +class Sign : public Bridge +{ +public: + Sign(QCanvas *canvas); + void setText(const QString &text); + QString text() const { return m_text; } + virtual void draw(QPainter &painter); + virtual bool vStrut() const { return false; } + virtual Config *config(QWidget *parent) { return new SignConfig(this, parent); } + virtual void save(KConfig *cfg); + virtual void load(KConfig *cfg); + +protected: + QString m_text; + QString m_untranslatedText; +}; +class SignObj : public Object +{ +public: + SignObj() { m_name = i18n("Sign"); m__name = "sign"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Sign(canvas); } +}; + +class Windmill; +class WindmillGuard : public Wall +{ +public: + WindmillGuard(QCanvas *canvas) : Wall(canvas) {}; + void setBetween(int newmin, int newmax) { max = newmax; min = newmin; } + virtual void advance(int phase); + +protected: + int max; + int min; +}; +class WindmillConfig : public BridgeConfig +{ + Q_OBJECT + +public: + WindmillConfig(Windmill *windmill, QWidget *parent); + +private slots: + void speedChanged(int news); + void endChanged(bool yes); + +private: + Windmill *windmill; +}; +class Windmill : public Bridge +{ +public: + Windmill(QRect rect, QCanvas *canvas); + virtual void aboutToDie(); + virtual void newSize(int width, int height); + virtual void save(KConfig *cfg); + virtual void load(KConfig *cfg); + virtual void setGame(KolfGame *game); + virtual Config *config(QWidget *parent) { return new WindmillConfig(this, parent); } + void setSize(int width, int height); + virtual void moveBy(double dx, double dy); + void setSpeed(int news); + int curSpeed() const { return speed; } + void setBottom(bool yes); + bool bottom() const { return m_bottom; } + +protected: + WindmillGuard *guard; + Wall *left; + Wall *right; + int speedfactor; + int speed; + bool m_bottom; +}; +class WindmillObj : public Object +{ +public: + WindmillObj() { m_name = i18n("Windmill"); m__name = "windmill"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Windmill(QRect(0, 0, 80, 40), canvas); } +}; + +class HoleInfo; +class HoleConfig : public Config +{ + Q_OBJECT + +public: + HoleConfig(HoleInfo *holeInfo, QWidget *); + +private slots: + void authorChanged(const QString &); + void parChanged(int); + void maxStrokesChanged(int); + void nameChanged(const QString &); + void borderWallsChanged(bool); + +private: + HoleInfo *holeInfo; +}; +class HoleInfo : public CanvasItem +{ +public: + HoleInfo() { m_lowestMaxStrokes = 4; } + virtual ~HoleInfo() {} + void setPar(int newpar) { m_par = newpar; } + int par() const { return m_par; } + void setMaxStrokes(int newMaxStrokes) { m_maxStrokes = newMaxStrokes; } + int lowestMaxStrokes() const { return m_lowestMaxStrokes; } + int maxStrokes() const { return m_maxStrokes; } + bool hasMaxStrokes() const { return m_maxStrokes != m_lowestMaxStrokes; } + void setAuthor(QString newauthor) { m_author = newauthor; } + QString author() const { return m_author; } + + void setName(QString newname) { m_name = newname; } + void setUntranslatedName(QString newname) { m_untranslatedName = newname; } + QString name() const { return m_name; } + QString untranslatedName() const { return m_untranslatedName; } + + virtual Config *config(QWidget *parent) { return new HoleConfig(this, parent); } + void borderWallsChanged(bool yes); + bool borderWalls() const { return m_borderWalls; } + +private: + QString m_author; + QString m_name; + QString m_untranslatedName; + bool m_borderWalls; + int m_par; + int m_maxStrokes; + int m_lowestMaxStrokes; +}; + +class StrokeCircle : public QCanvasItem +{ +public: + StrokeCircle(QCanvas *canvas); + + void setValue(double v); + double value(); + void setMaxValue(double m); + void setSize(int w, int h); + void setThickness(int t); + int thickness() const; + int width() const; + int height() const; + virtual void draw(QPainter &p); + virtual QRect boundingRect() const; + virtual bool collidesWith(const QCanvasItem*) const; + virtual bool collidesWith(const QCanvasSprite*, const QCanvasPolygonalItem*, const QCanvasRectangle*, const QCanvasEllipse*, const QCanvasText*) const; + +private: + double dvalue, dmax; + int ithickness, iwidth, iheight; +}; + +struct KDE_EXPORT CourseInfo +{ + CourseInfo(const QString &_name, const QString &_untranslatedName, const QString &_author, unsigned int _holes, unsigned int _par) { name = _name; untranslatedName = _untranslatedName, author = _author; holes = _holes; par = _par; } + CourseInfo(); + + QString name; + QString untranslatedName; + QString author; + unsigned int holes; + unsigned int par; +}; + +class KDE_EXPORT KolfGame : public QCanvasView +{ + Q_OBJECT + +public: + KolfGame(ObjectList *obj, PlayerList *players, QString filename, QWidget *parent=0, const char *name=0 ); + ~KolfGame(); + void setObjects(ObjectList *obj) { this->obj = obj; } + void setFilename(const QString &filename); + QString curFilename() const { return filename; } + void emitLargestHole() { emit largestHole(highestHole); } + QCanvas *canvas() const { return course; } + void removeItem(QCanvasItem *item) { items.setAutoDelete(false); items.removeRef(item); } + // returns whether it was a cancel + bool askSave(bool); + bool isEditing() const { return editing; } + int currentHole() { return curHole; } + void setStrict(bool yes) { strict = yes; } + // returns true when you shouldn't do anything + bool isPaused() const { return paused; } + Ball *curBall() const { return (*curPlayer).ball(); } + void updateMouse(); + void ballMoved(); + void updateHighlighter(); + void updateCourse() { course->update(); } + QCanvasItem *curSelectedItem() const { return selectedItem; } + void setBorderWalls(bool); + void setInPlay(bool yes) { inPlay = yes; } + bool isInPlay() { return inPlay; } + bool isInfoShowing() { return m_showInfo; } + void stoppedBall(); + QString courseName() const { return holeInfo.name(); } + void hidePutter() { putter->setVisible(false); } + void ignoreEvents(bool ignore) { m_ignoreEvents = ignore; } + + static void scoresFromSaved(KConfig *, PlayerList &players); + static void courseInfo(CourseInfo &info, const QString &filename); + +public slots: + void pause(); + void unPause(); + void save(); + void toggleEditMode(); + void setModified(bool mod = true); + void addNewObject(Object *newObj); + void addNewHole(); + void switchHole(int); + void switchHole(const QString &); + void nextHole(); + void prevHole(); + void firstHole(); + void lastHole(); + void randHole(); + void playSound(QString file, double vol = 1); + void showInfoDlg(bool = false); + void resetHole(); + void clearHole(); + void print(KPrinter &); + void setShowInfo(bool yes); + void toggleShowInfo(); + void updateShowInfo(); + void setUseMouse(bool yes) { m_useMouse = yes; } + void setUseAdvancedPutting(bool yes); + void setShowGuideLine(bool yes); + void setSound(bool yes); + void undoShot(); + void timeout(); + void saveScores(KConfig *); + void startFirstHole(int hole); + void sayWhosGoing(); + +signals: + void holesDone(); + void newHole(int); + void parChanged(int, int); + void titleChanged(const QString &); + void largestHole(int); + void scoreChanged(int, int, int); + void newPlayersTurn(Player *); + void playerHoled(Player *); + void newSelectedItem(CanvasItem *); + void checkEditing(); + void editingStarted(); + void editingEnded(); + void inPlayStart(); + void inPlayEnd(); + void maxStrokesReached(const QString &); + void currentHole(int); + void modifiedChanged(bool); + void newStatusText(const QString &); + +private slots: + void shotDone(); + void holeDone(); + void startNextHole(); + void fastTimeout(); + void putterTimeout(); + void autoSaveTimeout(); + void addItemsToMoveableList(QPtrList); + void addItemToFastAdvancersList(CanvasItem *); + void hideInfo(); + + void emitMax(); + +protected: + void mouseMoveEvent(QMouseEvent *e); + void mousePressEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void mouseDoubleClickEvent(QMouseEvent *e); + + void handleMousePressEvent(QMouseEvent *e); + void handleMouseDoubleClickEvent(QMouseEvent *e); + void handleMouseMoveEvent(QMouseEvent *e); + void handleMouseReleaseEvent(QMouseEvent *e); + void keyPressEvent(QKeyEvent *e); + void keyReleaseEvent(QKeyEvent *e); + + QPoint viewportToViewport(const QPoint &p); + +private: + QCanvas *course; + Putter *putter; + PlayerList *players; + PlayerList::Iterator curPlayer; + Ball *whiteBall; + StrokeCircle *strokeCircle; + + QTimer *timer; + QTimer *autoSaveTimer; + QTimer *fastTimer; + QTimer *putterTimer; + bool regAdv; + + ObjectList *obj; + QPtrList items; + QPtrList extraMoveable; + QPtrList borderWalls; + + int timerMsec; + int autoSaveMsec; + int fastTimerMsec; + int putterTimerMsec; + + void puttPress(); + void puttRelease(); + bool inPlay; + bool putting; + bool stroking; + bool finishStroking; + double strength; + double maxStrength; + int puttCount; + bool puttReverse; + + int curHole; + int highestHole; + int curPar; + + int wallWidth; + int height; + int width; + int margin; + QColor grass; + + int advancePeriod; + + int lastDelId; + + bool paused; + + QString filename; + bool recalcHighestHole; + void openFile(); + + bool strict; + + bool editing; + QPoint storedMousePos; + bool moving; + bool dragging; + QCanvasItem *movingItem; + QCanvasItem *selectedItem; + QCanvasRectangle *highlighter; + + // sound + KArtsDispatcher artsDispatcher; + KArtsServer artsServer; + QPtrList oldPlayObjects; + bool m_sound; + bool soundedOnce; + QString soundDir; + + bool m_ignoreEvents; + + HoleInfo holeInfo; + QCanvasText *infoText; + void showInfo(); + StateDB stateDB; + + BallStateList ballStateList; + void loadStateList(); + void recreateStateList(); + void addHoleInfo(BallStateList &list); + + bool dontAddStroke; + + bool addingNewHole; + int scoreboardHoles; + inline void resetHoleScores(); + + bool m_showInfo; + + bool infoShown; + + KConfig *cfg; + + inline void addBorderWall(QPoint start, QPoint end); + void shotStart(); + void startBall(const Vector &vector); + + bool modified; + + inline bool allPlayersDone(); + + bool m_useMouse; + bool m_useAdvancedPutting; + + QPtrList fastAdvancers; + bool fastAdvancedExist; + + QString playerWhoMaxed; +}; + +#endif diff --git a/kolf/graphics/Makefile.am b/kolf/graphics/Makefile.am new file mode 100644 index 00000000..45bda9e0 --- /dev/null +++ b/kolf/graphics/Makefile.am @@ -0,0 +1,4 @@ +picdir = $(kde_datadir)/kolf/pics/ +pic_DATA = cup.png grass.png puddle.png sand.png + +EXTRA_DIST=$(pic_DATA) diff --git a/kolf/graphics/cup.png b/kolf/graphics/cup.png new file mode 100644 index 00000000..2da6c70a Binary files /dev/null and b/kolf/graphics/cup.png differ diff --git a/kolf/graphics/grass.png b/kolf/graphics/grass.png new file mode 100644 index 00000000..c49475c7 Binary files /dev/null and b/kolf/graphics/grass.png differ diff --git a/kolf/graphics/puddle.png b/kolf/graphics/puddle.png new file mode 100644 index 00000000..f8ecd7f7 Binary files /dev/null and b/kolf/graphics/puddle.png differ diff --git a/kolf/graphics/sand.png b/kolf/graphics/sand.png new file mode 100644 index 00000000..860e1099 Binary files /dev/null and b/kolf/graphics/sand.png differ diff --git a/kolf/intro b/kolf/intro new file mode 100644 index 00000000..6377a8a0 --- /dev/null +++ b/kolf/intro @@ -0,0 +1,283 @@ +[0-course@-50,-50] +Name=Intro +author=Jason Katz-Brown +name=Intro + +[1-ball@258,281] +dummykey=true + +[1-blackhole@126,146|6] +exit=385,3 +exitDeg=270 +maxspeed=5 +minspeed=5 + +[1-blackhole@258,282|5] +exit=3,19 +exitDeg=0 +maxspeed=3.7 +minspeed=3.7 + +[1-blackhole@48,356|7] +exit=3,281 +exitDeg=0 +maxspeed=4 +minspeed=4 + +[1-bridge@-15,261|13] +botWallVisible=true +height=43 +leftWallVisible=false +rightWallVisible=true +topWallVisible=true +width=300 + +[1-bridge@23,339|14] +botWallVisible=true +height=35 +leftWallVisible=true +rightWallVisible=false +topWallVisible=true +width=232 + +[1-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=4 +par=3 + +[1-puddle@-121,390|12] +changeEnabled=false +changeEvery=50 +height=354 +width=750 + +[1-sand@268,-7|16] +changeEnabled=false +changeEvery=50 +height=110 +width=124 + +[1-sand@296,60|15] +changeEnabled=false +changeEvery=50 +height=186 +width=90 + +[1-sand@300,107|17] +changeEnabled=false +changeEvery=50 +height=134 +width=104 + +[1-sand@329,6|24] +changeEnabled=false +changeEvery=50 +height=118 +width=22 + +[1-sand@340,83|25] +changeEnabled=false +changeEvery=50 +height=54 +width=10 + +[1-slope@132,237|28] +grade=4 +gradient=Vertical +height=23 +reversed=false +stuckOnGround=false +width=150 + +[1-slope@147,263|10] +grade=5 +gradient=Horizontal +height=40 +reversed=false +stuckOnGround=false +width=40 + +[1-slope@16,22|3] +grade=6 +gradient=Elliptic +height=221 +reversed=true +stuckOnGround=true +width=221 + +[1-slope@163,-9|18] +grade=5 +gradient=Opposite Diagonal +height=118 +reversed=true +stuckOnGround=true +width=105 + +[1-slope@193,263|11] +grade=4 +gradient=Horizontal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[1-slope@206,305|32] +grade=4 +gradient=Opposite Diagonal +height=19 +reversed=true +stuckOnGround=false +width=27 + +[1-slope@222,143|20] +grade=5 +gradient=Opposite Diagonal +height=64 +reversed=true +stuckOnGround=true +width=60 + +[1-slope@225,63|19] +grade=5 +gradient=Horizontal +height=80 +reversed=false +stuckOnGround=true +width=34 + +[1-slope@231,304|30] +grade=4 +gradient=Vertical +height=21 +reversed=true +stuckOnGround=false +width=53 + +[1-slope@232,224|6] +grade=7 +gradient=Diagonal +height=177 +reversed=false +stuckOnGround=true +width=170 + +[1-slope@281,234|27] +grade=4 +gradient=Opposite Diagonal +height=28 +reversed=false +stuckOnGround=false +width=43 + +[1-slope@283,262|26] +grade=4 +gradient=Horizontal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[1-slope@284,301|29] +grade=4 +gradient=Diagonal +height=23 +reversed=true +stuckOnGround=false +width=37 + +[1-slope@286,141|21] +grade=5 +gradient=Vertical +height=67 +reversed=true +stuckOnGround=true +width=40 + +[1-slope@322,-13|23] +grade=5 +gradient=Horizontal +height=127 +reversed=true +stuckOnGround=true +width=51 + +[1-slope@325,110|22] +grade=5 +gradient=Diagonal +height=98 +reversed=true +stuckOnGround=true +width=48 + +[1-slope@43,263|8] +grade=5 +gradient=Horizontal +height=40 +reversed=false +stuckOnGround=false +width=40 + +[1-slope@88,263|9] +grade=4 +gradient=Horizontal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[1-slope@92,236|31] +grade=4 +gradient=Diagonal +height=23 +reversed=false +stuckOnGround=false +width=40 + +[1-wall@0,0|33] +endPoint=276,8 +startPoint=276,42 + +[1-wall@0,0|34] +endPoint=279,26 +startPoint=301,9 + +[1-wall@0,0|35] +endPoint=306,43 +startPoint=279,26 + +[1-wall@0,0|36] +endPoint=298,46 +startPoint=273,65 + +[1-wall@0,0|37] +endPoint=298,46 +startPoint=313,69 + +[1-wall@0,0|38] +endPoint=313,69 +startPoint=291,85 + +[1-wall@0,0|39] +endPoint=291,85 +startPoint=273,65 + +[1-wall@0,0|40] +endPoint=278,82 +startPoint=279,109 + +[1-wall@0,0|41] +endPoint=279,109 +startPoint=310,108 + +[1-wall@0,0|42] +endPoint=279,120 +startPoint=281,155 + +[1-wall@0,0|43] +endPoint=279,120 +startPoint=309,119 + +[1-wall@0,0|44] +endPoint=282,136 +startPoint=305,136 diff --git a/kolf/kcomboboxdialog.cpp b/kolf/kcomboboxdialog.cpp new file mode 100644 index 00000000..03e0701b --- /dev/null +++ b/kolf/kcomboboxdialog.cpp @@ -0,0 +1,152 @@ +// Copyright (C) 2002 Jason Katz-Brown +// Copyright (C) 2002 Neil Stevens +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name(s) of the author(s) shall not be +// used in advertising or otherwise to promote the sale, use or other dealings +// in this Software without prior written authorization from the author(s). + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "kcomboboxdialog.h" + +KComboBoxDialog::KComboBoxDialog( const QString &_text, const QStringList &_items, const QString& _value, bool showDontAskAgain, QWidget *parent ) + : KDialogBase( Plain, QString::null, Ok, Ok, parent, 0L, true, true ) +{ + QVBoxLayout *topLayout = new QVBoxLayout( plainPage(), marginHint(), spacingHint() ); + QLabel *label = new QLabel(_text, plainPage() ); + topLayout->addWidget( label, 1 ); + + combo = new KHistoryCombo( plainPage() ); + combo->setEditable(false); + combo->insertStringList( _items ); + topLayout->addWidget( combo, 1 ); + + if (showDontAskAgain) + { + dontAskAgainCheckBox = new QCheckBox( i18n("&Do not ask again"), plainPage() ); + topLayout->addWidget( dontAskAgainCheckBox, 1 ); + } + else + dontAskAgainCheckBox = 0; + + if ( !_value.isNull() ) + combo->setCurrentText( _value ); + combo->setFocus(); +} + +KComboBoxDialog::~KComboBoxDialog() +{ +} + +QString KComboBoxDialog::text() const +{ + return combo->currentText(); +} + +bool KComboBoxDialog::dontAskAgainChecked() +{ + if (dontAskAgainCheckBox) + return dontAskAgainCheckBox->isChecked(); + + return false; +} + +QString KComboBoxDialog::getItem( const QString &_text, const QStringList &_items, const QString& _value, const QString &dontAskAgainName, QWidget *parent ) +{ + return getItem( _text, QString::null, _items, _value, dontAskAgainName, parent ); +} + +QString KComboBoxDialog::getItem( const QString &_text, const QString &_caption, const QStringList &_items, const QString& _value, const QString &dontAskAgainName, QWidget *parent ) +{ + QString prevAnswer; + if ( !dontAskAgainName.isEmpty() ) + { + KConfig *config = KGlobal::config(); + config->setGroup( "Notification Messages" ); + prevAnswer = config->readEntry( dontAskAgainName ); + if ( !prevAnswer.isEmpty() ) + if ( _items.contains( prevAnswer ) > 0 ) + return prevAnswer; + } + + KComboBoxDialog dlg( _text, _items, _value, !dontAskAgainName.isNull(), parent ); + if ( !_caption.isNull() ) + dlg.setCaption( _caption ); + + dlg.exec(); + + const QString text = dlg.text(); + + if (dlg.dontAskAgainChecked()) + { + if ( !dontAskAgainName.isEmpty() && !text.isEmpty() ) + { + KConfig *config = KGlobal::config(); + config->setGroup ( "Notification Messages" ); + config->writeEntry( dontAskAgainName, text ); + } + } + + return text; +} + +QString KComboBoxDialog::getText(const QString &_caption, const QString &_text, const QString &_value, bool *ok, QWidget *parent, const QString &configName, KConfig *config) +{ + KComboBoxDialog dlg(_text, QStringList(), _value, false, parent); + if ( !_caption.isNull() ) + dlg.setCaption( _caption ); + + KHistoryCombo * const box = dlg.comboBox(); + box->setEditable(true); + + const QString historyItem = QString("%1History").arg(configName); + const QString completionItem = QString("%1Completion").arg(configName); + + if(!configName.isNull()) + { + config->setGroup("KComboBoxDialog"); + box->setHistoryItems(config->readListEntry(historyItem)); + box->completionObject()->setItems(config->readListEntry(completionItem)); + } + + bool result = dlg.exec(); + if(ok) *ok = result; + + if(!configName.isNull() && result) + { + box->addToHistory(dlg.text()); + box->completionObject()->addItem(dlg.text()); + config->setGroup("KComboBoxDialog"); + config->writeEntry(historyItem, box->historyItems()); + config->writeEntry(completionItem, box->completionObject()->items()); + } + + return dlg.text(); +} + +#include "kcomboboxdialog.moc" diff --git a/kolf/kcomboboxdialog.h b/kolf/kcomboboxdialog.h new file mode 100644 index 00000000..e835c0aa --- /dev/null +++ b/kolf/kcomboboxdialog.h @@ -0,0 +1,114 @@ +// Copyright (C) 2002 Jason Katz-Brown +// Copyright (C) 2002 Neil Stevens +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name(s) of the author(s) shall not be +// used in advertising or otherwise to promote the sale, use or other dealings +// in this Software without prior written authorization from the author(s). + +#ifndef KCOMBOBOX_DIALOG_H +#define KCOMBOBOX_DIALOG_H + +#include + +#include +#include + +class QCheckBox; +class KHistoryCombo; + +/** + * Dialog for user to choose an item from a QStringList. + */ + +class KComboBoxDialog : public KDialogBase +{ +Q_OBJECT + +public: + /** + * Create a dialog that asks for a single line of text. _value is + * the initial value of the line. _text appears as the current text + * of the combobox. + * + * @param _items Items in the combobox + * @param _text Text of the label + * @param _value Initial value of the combobox + */ + KComboBoxDialog( const QString &_text, const QStringList& _items, const QString& _value = QString::null, bool showDontAskAgain = false, QWidget *parent = 0 ); + virtual ~KComboBoxDialog(); + + /** + * @return the value the user chose + */ + QString text() const; + + /** + * @return the line edit widget + */ + KHistoryCombo *comboBox() const { return combo; } + + /** + * Static convenience function to get input from the user. + * + * @param _text Text of the label + * @param _items Items in the combobox + * @param _value Initial value of the inputline + * @param dontAskAgainName Name for saving whether the user doesn't want to be asked again; use QString::null to disable + */ + static QString getItem( const QString &_text, const QStringList &_items, const QString& _value = QString::null, const QString &dontAskAgainName = QString::null, QWidget *parent = 0 ); + + /** + * Static convenience function to get input from the user. + * This method includes a caption. + * + * @param _caption Caption of the dialog + * @param _text Text of the label + * @param _items Items in the combobox + * @param _value Initial value of the inputline + * @param dontAskAgainName Name for saving whether the user doesn't want to be asked again; use QString::null to disable + */ + static QString getItem( const QString &_text, const QString &_caption, const QStringList &_items, const QString& _value = QString::null, const QString &dontAskAgainName = QString::null, QWidget *parent = 0 ); + + /** + * Static convenience method. + * This method is meant as a replacement for KLineEditDlg::getText() for cases + * when a history and autocompletion are desired. + * + * @param _caption Caption of the dialog + * @param _text Text of the label + * @param _value Initial value of the inputline + * @param ok Variable to store whether the user hit OK + * @param parent Parent widget for the dialog + * @param configName Name of the dialog for saving the completion and history + * @parma config KConfig for saving the completion and history + */ + static QString getText(const QString &_caption, const QString &_text, + const QString &_value = QString::null, + bool *ok = 0, QWidget *parent = 0, + const QString &configName = QString::null, + KConfig *config = KGlobal::config()); + +protected: + KHistoryCombo *combo; + QCheckBox *dontAskAgainCheckBox; + bool dontAskAgainChecked(); +}; + +#endif diff --git a/kolf/kolf.cpp b/kolf/kolf.cpp new file mode 100644 index 00000000..33155b6a --- /dev/null +++ b/kolf/kolf.cpp @@ -0,0 +1,815 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "game.h" +#include "floater.h" +#include "slope.h" +#include "newgame.h" +#include "scoreboard.h" +#include "editor.h" +#include "pluginloader.h" +#include "printdialogpage.h" +#include "kolf.h" + +Kolf::Kolf() + : KMainWindow(0, "Kolf") +{ + competition = false; + game = 0; + editor = 0; + spacer = 0; + scoreboard = 0; + isTutorial = false; + + initGUI(); + + obj = new ObjectList; + initPlugins(); + + filename = QString::null; + dummy = new QWidget(this); + setCentralWidget(dummy); + layout = new QGridLayout(dummy, 3, 1); + + resize(420, 480); +} + +Kolf::~Kolf() +{ + // wipe out our objects + obj->setAutoDelete(true); + delete obj; +} + +void Kolf::initGUI() +{ + newAction = KStdGameAction::gameNew(this, SLOT(newGame()), actionCollection()); + newAction->setText(newAction->text() + QString("...")); + + endAction = KStdGameAction::end(this, SLOT(closeGame()), actionCollection()); + printAction = KStdGameAction::print(this, SLOT(print()), actionCollection()); + + (void) KStdGameAction::quit(this, SLOT(close()), actionCollection()); + saveAction = KStdAction::save(this, SLOT(save()), actionCollection(), "game_save"); + saveAction->setText(i18n("Save &Course")); + saveAsAction = KStdAction::saveAs(this, SLOT(saveAs()), actionCollection(), "game_save_as"); + saveAsAction->setText(i18n("Save &Course As...")); + + saveGameAction = new KAction(i18n("&Save Game"), 0, this, SLOT(saveGame()), actionCollection(), "savegame"); + saveGameAsAction = new KAction(i18n("&Save Game As..."), 0, this, SLOT(saveGameAs()), actionCollection(), "savegameas"); + + loadGameAction = KStdGameAction::load(this, SLOT(loadGame()), actionCollection()); + loadGameAction->setText(i18n("Load Saved Game...")); + + highScoreAction = KStdGameAction::highscores(this, SLOT(showHighScores()), actionCollection()); + + editingAction = new KToggleAction(i18n("&Edit"), "pencil", CTRL+Key_E, this, SLOT(emptySlot()), actionCollection(), "editing"); + newHoleAction = new KAction(i18n("&New"), "filenew", CTRL+SHIFT+Key_N, this, SLOT(emptySlot()), actionCollection(), "newhole"); + clearHoleAction = new KAction(KStdGuiItem::clear().text(), "locationbar_erase", CTRL+Key_Delete, this, SLOT(emptySlot()), actionCollection(), "clearhole"); + resetHoleAction = new KAction(i18n("&Reset"), CTRL+Key_R, this, SLOT(emptySlot()), actionCollection(), "resethole"); + undoShotAction = KStdAction::undo(this, SLOT(emptySlot()), actionCollection(), "undoshot"); + undoShotAction->setText(i18n("&Undo Shot")); + //replayShotAction = new KAction(i18n("&Replay Shot"), 0, this, SLOT(emptySlot()), actionCollection(), "replay"); + + holeAction = new KListAction(i18n("Switch to Hole"), 0, this, SLOT(emptySlot()), actionCollection(), "switchhole"); + nextAction = new KAction(i18n("&Next Hole"), "forward", KStdAccel::shortcut(KStdAccel::Forward), this, SLOT(emptySlot()), actionCollection(), "nexthole"); + prevAction = new KAction(i18n("&Previous Hole"), "back", KStdAccel::shortcut(KStdAccel::Back), this, SLOT(emptySlot()), actionCollection(), "prevhole"); + firstAction = new KAction(i18n("&First Hole"), "gohome", KStdAccel::shortcut(KStdAccel::Home), this, SLOT(emptySlot()), actionCollection(), "firsthole"); + lastAction = new KAction(i18n("&Last Hole"), CTRL+SHIFT+Key_End, this, SLOT(emptySlot()), actionCollection(), "lasthole"); + randAction = new KAction(i18n("&Random Hole"), "goto", 0, this, SLOT(emptySlot()), actionCollection(), "randhole"); + + useMouseAction = new KToggleAction(i18n("Enable &Mouse for Moving Putter"), 0, this, SLOT(emptySlot()), actionCollection(), "usemouse"); + useMouseAction->setCheckedState(i18n("Disable &Mouse for Moving Putter")); + connect(useMouseAction, SIGNAL(toggled(bool)), this, SLOT(useMouseChanged(bool))); + KConfig *config = kapp->config(); + config->setGroup("Settings"); + useMouseAction->setChecked(config->readBoolEntry("useMouse", true)); + + useAdvancedPuttingAction = new KToggleAction(i18n("Enable &Advanced Putting"), 0, this, SLOT(emptySlot()), actionCollection(), "useadvancedputting"); + useAdvancedPuttingAction->setCheckedState(i18n("Disable &Advanced Putting")); + connect(useAdvancedPuttingAction, SIGNAL(toggled(bool)), this, SLOT(useAdvancedPuttingChanged(bool))); + useAdvancedPuttingAction->setChecked(config->readBoolEntry("useAdvancedPutting", false)); + + showInfoAction = new KToggleAction(i18n("Show &Info"), "info", CTRL+Key_I, this, SLOT(emptySlot()), actionCollection(), "showinfo"); + showInfoAction->setCheckedState(i18n("Hide &Info")); + connect(showInfoAction, SIGNAL(toggled(bool)), this, SLOT(showInfoChanged(bool))); + showInfoAction->setChecked(config->readBoolEntry("showInfo", false)); + + showGuideLineAction = new KToggleAction(i18n("Show Putter &Guideline"), 0, this, SLOT(emptySlot()), actionCollection(), "showguideline"); + showGuideLineAction->setCheckedState(i18n("Hide Putter &Guideline")); + connect(showGuideLineAction, SIGNAL(toggled(bool)), this, SLOT(showGuideLineChanged(bool))); + showGuideLineAction->setChecked(config->readBoolEntry("showGuideLine", true)); + + KToggleAction *act=new KToggleAction(i18n("Enable All Dialog Boxes"), 0, this, SLOT(enableAllMessages()), actionCollection(), "enableAll"); + act->setCheckedState(i18n("Disable All Dialog Boxes")); + + soundAction = new KToggleAction(i18n("Play &Sounds"), 0, this, SLOT(emptySlot()), actionCollection(), "sound"); + connect(soundAction, SIGNAL(toggled(bool)), this, SLOT(soundChanged(bool))); + soundAction->setChecked(config->readBoolEntry("sound", true)); + + (void) new KAction(i18n("&Reload Plugins"), 0, this, SLOT(initPlugins()), actionCollection(), "reloadplugins"); + (void) new KAction(i18n("Show &Plugins"), 0, this, SLOT(showPlugins()), actionCollection(), "showplugins"); + + aboutAction = new KAction(i18n("&About Course"), 0, this, SLOT(emptySlot()), actionCollection(), "aboutcourse"); + tutorialAction = new KAction(i18n("&Tutorial"), 0, this, SLOT(tutorial()), actionCollection(), "tutorial"); + + statusBar(); + setupGUI(); +} + +bool Kolf::queryClose() +{ + if (game) + if (game->askSave(true)) + return false; + return true; +} + +void Kolf::startNewGame() +{ + NewGameDialog *dialog = 0; + int firstHole = 1; + + if (loadedGame.isNull()) + { + dialog = new NewGameDialog(filename.isNull(), dummy, "New Game Dialog"); + if (dialog->exec() != QDialog::Accepted) + goto end; + } + + players.clear(); + delete scoreboard; + scoreboard = new ScoreBoard(dummy, "Score Board"); + layout->addWidget(scoreboard, 1, 0); + scoreboard->show(); + + if (loadedGame.isNull()) + { + PlayerEditor *curEditor = 0; + int newId = 1; + for (curEditor = dialog->players()->first(); curEditor; curEditor = dialog->players()->next(), ++newId) + { + players.append(Player()); + players.last().ball()->setColor(curEditor->color()); + players.last().setName(curEditor->name()); + players.last().setId(newId); + } + + competition = dialog->competition(); + filename = filename.isNull()? dialog->course() : filename; + } + else + { + KConfig config(loadedGame); + config.setGroup("0 Saved Game"); + + if (isTutorial) + filename = KGlobal::dirs()->findResource("appdata", "tutorial.kolf"); + else + filename = config.readEntry("Course", QString::null); + + if (filename.isNull()) + return; + + competition = config.readBoolEntry("Competition", false); + firstHole = config.readNumEntry("Current Hole", 1); + + players.clear(); + KolfGame::scoresFromSaved(&config, players); + } + + for (PlayerList::Iterator it = players.begin(); it != players.end(); ++it) + scoreboard->newPlayer((*it).name()); + + delete spacer; + spacer = 0; + delete game; + game = new KolfGame(obj, &players, filename, dummy); + game->setStrict(competition); + + connect(game, SIGNAL(newHole(int)), scoreboard, SLOT(newHole(int))); + connect(game, SIGNAL(scoreChanged(int, int, int)), scoreboard, SLOT(setScore(int, int, int))); + connect(game, SIGNAL(parChanged(int, int)), scoreboard, SLOT(parChanged(int, int))); + connect(game, SIGNAL(modifiedChanged(bool)), this, SLOT(updateModified(bool))); + connect(game, SIGNAL(newPlayersTurn(Player *)), this, SLOT(newPlayersTurn(Player *))); + connect(game, SIGNAL(holesDone()), this, SLOT(gameOver())); + connect(game, SIGNAL(checkEditing()), this, SLOT(checkEditing())); + connect(game, SIGNAL(editingStarted()), this, SLOT(editingStarted())); + connect(game, SIGNAL(editingEnded()), this, SLOT(editingEnded())); + connect(game, SIGNAL(inPlayStart()), this, SLOT(inPlayStart())); + connect(game, SIGNAL(inPlayEnd()), this, SLOT(inPlayEnd())); + connect(game, SIGNAL(maxStrokesReached(const QString &)), this, SLOT(maxStrokesReached(const QString &))); + connect(game, SIGNAL(largestHole(int)), this, SLOT(updateHoleMenu(int))); + connect(game, SIGNAL(titleChanged(const QString &)), this, SLOT(titleChanged(const QString &))); + connect(game, SIGNAL(newStatusText(const QString &)), this, SLOT(newStatusText(const QString &))); + connect(game, SIGNAL(currentHole(int)), this, SLOT(setCurrentHole(int))); + connect(holeAction, SIGNAL(activated(const QString &)), game, SLOT(switchHole(const QString &))); + connect(nextAction, SIGNAL(activated()), game, SLOT(nextHole())); + connect(prevAction, SIGNAL(activated()), game, SLOT(prevHole())); + connect(firstAction, SIGNAL(activated()), game, SLOT(firstHole())); + connect(lastAction, SIGNAL(activated()), game, SLOT(lastHole())); + connect(randAction, SIGNAL(activated()), game, SLOT(randHole())); + connect(editingAction, SIGNAL(activated()), game, SLOT(toggleEditMode())); + connect(newHoleAction, SIGNAL(activated()), game, SLOT(addNewHole())); + connect(clearHoleAction, SIGNAL(activated()), game, SLOT(clearHole())); + connect(resetHoleAction, SIGNAL(activated()), game, SLOT(resetHole())); + connect(undoShotAction, SIGNAL(activated()), game, SLOT(undoShot())); + //connect(replayShotAction, SIGNAL(activated()), game, SLOT(replay())); + connect(aboutAction, SIGNAL(activated()), game, SLOT(showInfoDlg())); + connect(useMouseAction, SIGNAL(toggled(bool)), game, SLOT(setUseMouse(bool))); + connect(useAdvancedPuttingAction, SIGNAL(toggled(bool)), game, SLOT(setUseAdvancedPutting(bool))); + connect(soundAction, SIGNAL(toggled(bool)), game, SLOT(setSound(bool))); + connect(showGuideLineAction, SIGNAL(toggled(bool)), game, SLOT(setShowGuideLine(bool))); + connect(showInfoAction, SIGNAL(toggled(bool)), game, SLOT(setShowInfo(bool))); + + game->setUseMouse(useMouseAction->isChecked()); + game->setUseAdvancedPutting(useAdvancedPuttingAction->isChecked()); + game->setShowInfo(showInfoAction->isChecked()); + game->setShowGuideLine(showGuideLineAction->isChecked()); + game->setSound(soundAction->isChecked()); + + layout->addWidget(game, 0, 0, AlignCenter); + + game->show(); + game->setFocus(); + + setEditingEnabled(true); + endAction->setEnabled(true); + setHoleMovementEnabled(true); + setHoleOtherEnabled(true); + aboutAction->setEnabled(true); + highScoreAction->setEnabled(true); + printAction->setEnabled(true); + saveAction->setEnabled(true); + saveAsAction->setEnabled(true); + saveGameAction->setEnabled(true); + saveGameAsAction->setEnabled(true); + + clearHoleAction->setEnabled(false); + newHoleAction->setEnabled(false); + newAction->setEnabled(false); + loadGameAction->setEnabled(false); + tutorialAction->setEnabled(false); + + + // so game can do stuff that needs to be done + // after things above are connected + game->startFirstHole(firstHole); + + end: + delete dialog; +} + +void Kolf::newGame() +{ + isTutorial = false; + filename = QString::null; + startNewGame(); +} + +void Kolf::tutorial() +{ + QString newfilename = KGlobal::dirs()->findResource("appdata", "tutorial.kolfgame"); + if (newfilename.isNull()) + return; + + filename = QString::null; + loadedGame = newfilename; + isTutorial = true; + + startNewGame(); + + loadedGame = QString::null; +} + +void Kolf::closeGame() +{ + if (game) + { + if (game->askSave(true)) + return; + game->pause(); + } + + filename = QString::null; + + editingEnded(); + delete game; + game = 0; + loadedGame = QString::null; + + editingAction->setChecked(false); + setEditingEnabled(false); + endAction->setEnabled(false); + aboutAction->setEnabled(false); + highScoreAction->setEnabled(false); + printAction->setEnabled(false); + saveAction->setEnabled(false); + saveAsAction->setEnabled(false); + saveGameAction->setEnabled(false); + saveGameAsAction->setEnabled(false); + setHoleMovementEnabled(false); + setHoleOtherEnabled(false); + + clearHoleAction->setEnabled(false); + newHoleAction->setEnabled(false); + newAction->setEnabled(true); + loadGameAction->setEnabled(true); + tutorialAction->setEnabled(true); + + titleChanged(QString::null); + updateModified(false); + + QTimer::singleShot(100, this, SLOT(createSpacer())); +} + +void Kolf::createSpacer() +{ + // make a player to play the spacer hole + spacerPlayers.clear(); + spacerPlayers.append(Player()); + spacerPlayers.last().ball()->setColor(yellow); + spacerPlayers.last().setName("player"); + spacerPlayers.last().setId(1); + + delete spacer; + spacer = new KolfGame(obj, &spacerPlayers, KGlobal::dirs()->findResource("appdata", "intro"), dummy); + spacer->setSound(false); + spacer->startFirstHole(1); + layout->addWidget(spacer, 0, 0, AlignCenter); + spacer->hidePutter(); + spacer->ignoreEvents(true); + + spacer->show(); +} + +void Kolf::gameOver() +{ + int curPar = 0; + int lowScore = INT_MAX; // let's hope it doesn't stay this way! + int curScore = 1; + + // names of people who had the lowest score + QStringList names; + + HighScoreList highScores; + int scoreBoardIndex = 1; + + while (curScore != 0) + { + QString curName; + + // name taken as a reference and filled out + curScore = scoreboard->total(scoreBoardIndex, curName); + + scoreBoardIndex++; + + if (curName == i18n("Par")) + { + curPar = curScore; + continue; + } + + if (curScore == 0) + continue; + + // attempt to add everybody to the highscore list + // (ignored if we aren't competing down below) + highScores.append(HighScore(curName, curScore)); + + if (curScore < lowScore) + { + names.clear(); + lowScore = curScore; + names.append(curName); + } + else if (curScore == lowScore) + names.append(curName); + } + + // only announce a winner if more than two entries + // (player and par) are on the scoreboard + one to go past end + // + 1 for koodoo + if (scoreBoardIndex > 4) + { + if (names.count() > 1) + { + QString winners = names.join(i18n(" and ")); + KMessageBox::information(this, i18n("%1 tied").arg(winners)); + } + else + KMessageBox::information(this, i18n("%1 won!").arg(names.first())); + } + + if (competition) + { + // deal with highscores + // KScoreDialog makes it very easy :-)) + + KScoreDialog *scoreDialog = new KScoreDialog(KScoreDialog::Name | KScoreDialog::Custom1 | KScoreDialog::Score, this); + scoreDialog->addField(KScoreDialog::Custom1, i18n("Par"), "Par"); + + CourseInfo courseInfo; + game->courseInfo(courseInfo, game->curFilename()); + + scoreDialog->setConfigGroup(courseInfo.untranslatedName + QString(" Highscores")); + + for (HighScoreList::Iterator it = highScores.begin(); it != highScores.end(); ++it) + { + KScoreDialog::FieldInfo info; + info[KScoreDialog::Name] = (*it).name; + info[KScoreDialog::Custom1] = QString::number(curPar); + + scoreDialog->addScore((*it).score, info, false, true); + } + + scoreDialog->setComment(i18n("High Scores for %1").arg(courseInfo.name)); + scoreDialog->show(); + } + + QTimer::singleShot(700, this, SLOT(closeGame())); +} + +void Kolf::showHighScores() +{ + KScoreDialog *scoreDialog = new KScoreDialog(KScoreDialog::Name | KScoreDialog::Custom1 | KScoreDialog::Score, this); + scoreDialog->addField(KScoreDialog::Custom1, i18n("Par"), "Par"); + + CourseInfo courseInfo; + game->courseInfo(courseInfo, game->curFilename()); + + scoreDialog->setConfigGroup(courseInfo.untranslatedName + QString(" Highscores")); + scoreDialog->setComment(i18n("High Scores for %1").arg(courseInfo.name)); + scoreDialog->show(); +} + +void Kolf::save() +{ + if (filename.isNull()) + { + saveAs(); + return; + } + + if (game) + game->save(); + + game->setFocus(); +} + +void Kolf::saveAs() +{ + QString newfilename = KFileDialog::getSaveFileName(":kourses", "application/x-kourse", this, i18n("Pick Kolf Course to Save To")); + if (!newfilename.isNull()) + { + filename = newfilename; + game->setFilename(filename); + game->save(); + game->setFocus(); + } +} + +void Kolf::saveGameAs() +{ + QString newfilename = KFileDialog::getSaveFileName(":savedkolf", "application/x-kolf", this, i18n("Pick Saved Game to Save To")); + if (newfilename.isNull()) + return; + + loadedGame = newfilename; + + saveGame(); +} + +void Kolf::saveGame() +{ + if (loadedGame.isNull()) + { + saveGameAs(); + return; + } + + KConfig config(loadedGame); + config.setGroup("0 Saved Game"); + + config.writeEntry("Competition", competition); + config.writeEntry("Course", filename); + + game->saveScores(&config); + + config.sync(); +} + +void Kolf::loadGame() +{ + loadedGame = KFileDialog::getOpenFileName(":savedkolf", QString::fromLatin1("application/x-kolf"), this, i18n("Pick Kolf Saved Game")); + + if (loadedGame.isNull()) + return; + + isTutorial = false; + startNewGame(); +} + +// called by main for commmand line files +void Kolf::openURL(KURL url) +{ + QString target; + if (KIO::NetAccess::download(url, target, this)) + { + isTutorial = false; + QString mimeType = KMimeType::findByPath(target)->name(); + if (mimeType == "application/x-kourse") + filename = target; + else if (mimeType == "application/x-kolf") + loadedGame = target; + else + { + closeGame(); + return; + } + + QTimer::singleShot(10, this, SLOT(startNewGame())); + } + else + closeGame(); +} + +void Kolf::newPlayersTurn(Player *player) +{ + tempStatusBarText = i18n("%1's turn").arg(player->name()); + + if (showInfoAction->isChecked()) + statusBar()->message(tempStatusBarText, 5 * 1000); + else + statusBar()->message(tempStatusBarText); + + scoreboard->setCurrentCell(player->id() - 1, game->currentHole() - 1); +} + +void Kolf::newStatusText(const QString &text) +{ + if (text.isEmpty()) + statusBar()->message(tempStatusBarText); + else + statusBar()->message(text); +} + +void Kolf::editingStarted() +{ + delete editor; + editor = new Editor(obj, dummy, "Editor"); + connect(editor, SIGNAL(addNewItem(Object *)), game, SLOT(addNewObject(Object *))); + connect(editor, SIGNAL(changed()), game, SLOT(setModified())); + connect(editor, SIGNAL(addNewItem(Object *)), this, SLOT(setHoleFocus())); + connect(game, SIGNAL(newSelectedItem(CanvasItem *)), editor, SLOT(setItem(CanvasItem *))); + + scoreboard->hide(); + + layout->addWidget(editor, 1, 0); + editor->show(); + + clearHoleAction->setEnabled(true); + newHoleAction->setEnabled(true); + setHoleOtherEnabled(false); + + game->setFocus(); +} + +void Kolf::editingEnded() +{ + delete editor; + editor = 0; + + if (scoreboard) + scoreboard->show(); + + clearHoleAction->setEnabled(false); + newHoleAction->setEnabled(false); + setHoleOtherEnabled(true); + + if (game) + game->setFocus(); +} + +void Kolf::inPlayStart() +{ + setEditingEnabled(false); + setHoleOtherEnabled(false); + setHoleMovementEnabled(false); +} + +void Kolf::inPlayEnd() +{ + setEditingEnabled(true); + setHoleOtherEnabled(true); + setHoleMovementEnabled(true); +} + +void Kolf::maxStrokesReached(const QString &name) +{ + KMessageBox::sorry(this, i18n("%1's score has reached the maximum for this hole.").arg(name)); +} + +void Kolf::updateHoleMenu(int largest) +{ + QStringList items; + for (int i = 1; i <= largest; ++i) + items.append(QString::number(i)); + + // setItems for some reason enables the action + bool shouldbe = holeAction->isEnabled(); + holeAction->setItems(items); + holeAction->setEnabled(shouldbe); +} + +void Kolf::setHoleMovementEnabled(bool yes) +{ + if (competition) + yes = false; + + holeAction->setEnabled(yes); + + nextAction->setEnabled(yes); + prevAction->setEnabled(yes); + firstAction->setEnabled(yes); + lastAction->setEnabled(yes); + randAction->setEnabled(yes); +} + +void Kolf::setHoleOtherEnabled(bool yes) +{ + if (competition) + yes = false; + + resetHoleAction->setEnabled(yes); + undoShotAction->setEnabled(yes); + //replayShotAction->setEnabled(yes); +} + +void Kolf::setEditingEnabled(bool yes) +{ + editingAction->setEnabled(competition? false : yes); +} + +void Kolf::checkEditing() +{ + editingAction->setChecked(true); +} + +void Kolf::print() +{ + KPrinter pr; + pr.addDialogPage(new PrintDialogPage()); + + if (pr.setup(this, i18n("Print %1 - Hole %2").arg(game->courseName()).arg(game->currentHole()))) + { + pr.newPage(); + if (game) + game->print(pr); + } +} + +void Kolf::updateModified(bool mod) +{ + courseModified = mod; + titleChanged(title); +} + +void Kolf::titleChanged(const QString &newTitle) +{ + title = newTitle; + setCaption(title, courseModified); +} + +void Kolf::useMouseChanged(bool yes) +{ + KConfig *config = kapp->config(); config->setGroup("Settings"); config->writeEntry("useMouse", yes); config->sync(); +} + +void Kolf::useAdvancedPuttingChanged(bool yes) +{ + KConfig *config = kapp->config(); config->setGroup("Settings"); config->writeEntry("useAdvancedPutting", yes); config->sync(); +} + +void Kolf::showInfoChanged(bool yes) +{ + KConfig *config = kapp->config(); config->setGroup("Settings"); config->writeEntry("showInfo", yes); config->sync(); +} + +void Kolf::showGuideLineChanged(bool yes) +{ + KConfig *config = kapp->config(); config->setGroup("Settings"); config->writeEntry("showGuideLine", yes); config->sync(); +} + +void Kolf::soundChanged(bool yes) +{ + KConfig *config = kapp->config(); config->setGroup("Settings"); config->writeEntry("sound", yes); config->sync(); +} + +void Kolf::initPlugins() +{ + //kdDebug(12007) << "initPlugins" << endl; + if (game) + game->pause(); + + obj->setAutoDelete(true); + obj->clear(); + plugins.setAutoDelete(false); + plugins.clear(); + + // add prefab objects + obj->append(new SlopeObj()); + obj->append(new PuddleObj()); + obj->append(new WallObj()); + obj->append(new CupObj()); + obj->append(new SandObj()); + obj->append(new WindmillObj()); + obj->append(new BlackHoleObj()); + obj->append(new FloaterObj()); + obj->append(new BridgeObj()); + obj->append(new SignObj()); + obj->append(new BumperObj()); + + ObjectList *other = PluginLoader::loadAll(); + Object *object = 0; + for (object = other->first(); object; object = other->next()) + { + obj->append(object); + plugins.append(object); + } + + if (game) + { + game->setObjects(obj); + game->unPause(); + } + + //kdDebug(12007) << "end of initPlugins" << endl; +} + +void Kolf::showPlugins() +{ + QString text = QString("

%1

    ").arg(i18n("Currently Loaded Plugins")); + Object *object = 0; + for (object = plugins.first(); object; object = plugins.next()) + { + text.append("
  1. "); + text.append(object->name()); + text.append(" - "); + text.append(i18n("by %1").arg(object->author())); + text.append("
  2. "); + } + text.append("
"); + KMessageBox::information(this, text, i18n("Plugins")); +} + +void Kolf::enableAllMessages() +{ + KMessageBox::enableAllMessages(); +} + +void Kolf::setCurrentHole(int hole) +{ + if (!holeAction) + return; + // Golf is 1-based, KListAction is 0-based + holeAction->setCurrentItem(hole - 1); +} + +#include "kolf.moc" diff --git a/kolf/kolf.desktop b/kolf/kolf.desktop new file mode 100644 index 00000000..454dcfda --- /dev/null +++ b/kolf/kolf.desktop @@ -0,0 +1,73 @@ +[Desktop Entry] +DocPath=kolf/index.html +Name=Kolf +Name[ar]=لعبة الجول٠(Kolf) +Name[be]=Гольф +Name[bn]=কলà§â€Œà¦« +Name[hi]=कोलà¥à¤« +Name[ne]=कोलà¥à¤« +Name[pa]=ਕੇ-ਗੋਲਫ਼ +Name[ta]=காலà¯à®ƒà®ªà¯ +Name[tg]=Колф + +Type=Application +Exec=kolf %U +Icon=kolf +MimeType=application/x-kourse;application/x-kolf; + +GenericName=Miniature Golf +GenericName[be]=Маленькі гольф +GenericName[bg]=Миниатюрен голф +GenericName[bn]=ছোটখাটো গলফ +GenericName[bs]=Minijaturni golf +GenericName[ca]=Golf en miniatura +GenericName[cs]=Miniaturní golf +GenericName[cy]=Golff Bach +GenericName[da]=Miniaturegolf +GenericName[de]=Minigolf +GenericName[el]=Μίνι γκολφ +GenericName[eo]=Eta golfludo +GenericName[es]=Golf en miniatura +GenericName[et]=Miniatuurne golf +GenericName[eu]=Golf txikia +GenericName[fi]=Pienoisgolf +GenericName[fr]=Golf miniature +GenericName[gl]=Golf en miniatura +GenericName[he]=מיני־גולף +GenericName[hi]=लघॠगोलà¥à¤« +GenericName[hr]=Mini-golf +GenericName[hu]=Minigolf +GenericName[is]=Minigolf +GenericName[it]=Minigolf +GenericName[ja]=ミニãƒãƒ¥ã‚¢ã‚´ãƒ«ãƒ• +GenericName[ko]=모형 골프 +GenericName[lt]=MiniatiÅ«rinis golfas +GenericName[lv]=MiniatÅ«rs golfs +GenericName[mk]=Минијатурен голф +GenericName[nb]=Minigolf +GenericName[nds]=Minigolf +GenericName[ne]=सानो गलà¥à¤« +GenericName[nl]=Miniatuurgolf +GenericName[nn]=Minigolf +GenericName[pa]=ਛੋਟੀ ਗੋਲਫ਼ ਭੇਜੋ +GenericName[pl]=Miniaturowy Golf +GenericName[pt]=Golfe em Miniatura +GenericName[pt_BR]=Mini-Golf +GenericName[ro]=Golf în miniatură +GenericName[ru]=Гольф +GenericName[se]=Minigolfa +GenericName[sk]=Miniatúrny golf +GenericName[sl]=Mini golf +GenericName[sr]=Минијатурни голф +GenericName[sr@Latn]=Minijaturni golf +GenericName[sv]=Minigolf +GenericName[ta]=சிறிய காலà¯à®ƒà®ªà¯ +GenericName[tg]=Колф дар шакли хурд +GenericName[tr]=Minyatür Golf +GenericName[uk]=Мініатюрний гольф +GenericName[wa]=Golf miniateure +GenericName[xh]=Igalufa encinci +GenericName[zh_CN]=微型高尔夫 +GenericName[zh_TW]=迷你高爾夫 +GenericName[zu]=Igalofu elincanyana +Categories=Qt;KDE;Game;ArcadeGame; diff --git a/kolf/kolf.h b/kolf/kolf.h new file mode 100644 index 00000000..8a2c6d78 --- /dev/null +++ b/kolf/kolf.h @@ -0,0 +1,147 @@ +#ifndef KOLF_H +#define KOLF_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include "game.h" + +class KolfGame; +class KToggleAction; +class KListAction; +class KAction; +class QGridLayout; +class ScoreBoard; +class QCloseEvent; +class QEvent; +class Player; +class QWidget; +class Editor; + +class KDE_EXPORT Kolf : public KMainWindow +{ + Q_OBJECT + +public: + Kolf(); + ~Kolf(); + + void openURL(KURL url); + +public slots: + void closeGame(); + void updateModified(bool); + +protected: + virtual bool queryClose(); + +protected slots: + void startNewGame(); + void loadGame(); + void tutorial(); + void newGame(); + void save(); + void saveAs(); + void saveGame(); + void saveGameAs(); + void print(); + void newPlayersTurn(Player *); + void gameOver(); + void editingStarted(); + void editingEnded(); + void checkEditing(); + void setHoleFocus() { game->setFocus(); } + void inPlayStart(); + void inPlayEnd(); + void maxStrokesReached(const QString &); + void updateHoleMenu(int); + void titleChanged(const QString &); + void newStatusText(const QString &); + void showInfoChanged(bool); + void useMouseChanged(bool); + void useAdvancedPuttingChanged(bool); + void showGuideLineChanged(bool); + void soundChanged(bool); + void initPlugins(); + void showPlugins(); + void showHighScores(); + void enableAllMessages(); + void createSpacer(); + + void emptySlot() {}; + + void setCurrentHole(int); + +private: + QWidget *dummy; + KolfGame *game; + Editor *editor; + KolfGame *spacer; + void initGUI(); + QString filename; + PlayerList players; + PlayerList spacerPlayers; + QGridLayout *layout; + ScoreBoard *scoreboard; + KToggleAction *editingAction; + KAction *newHoleAction; + KAction *resetHoleAction; + KAction *undoShotAction; + //KAction *replayShotAction; + KAction *clearHoleAction; + KAction *tutorialAction; + KAction *newAction; + KAction *endAction; + KAction *printAction; + KAction *saveAction; + KAction *saveAsAction; + KAction *saveGameAction; + KAction *saveGameAsAction; + KAction *loadGameAction; + KAction *aboutAction; + KListAction *holeAction; + KAction *highScoreAction; + KAction *nextAction; + KAction *prevAction; + KAction *firstAction; + KAction *lastAction; + KAction *randAction; + KToggleAction *showInfoAction; + KToggleAction *useMouseAction; + KToggleAction *useAdvancedPuttingAction; + KToggleAction *showGuideLineAction; + KToggleAction *soundAction; + void setHoleMovementEnabled(bool); + void setHoleOtherEnabled(bool); + inline void setEditingEnabled(bool); + bool competition; + + // contains everything + ObjectList *obj; + // contains subset of obj + ObjectList plugins; + + QString loadedGame; + + bool isTutorial; + bool courseModified; + QString title; + QString tempStatusBarText; +}; + +struct HighScore +{ + HighScore() {} + HighScore(const QString &name, int score) { this->name = name; this->score = score; } + QString name; + int score; +}; +typedef QValueList HighScoreList; + +#endif diff --git a/kolf/kolf.magic b/kolf/kolf.magic new file mode 100644 index 00000000..01bc5070 --- /dev/null +++ b/kolf/kolf.magic @@ -0,0 +1,2 @@ +0 string [0-course@-50,-50] application/x-kourse +0 string [0\ Saved\ Game] application/x-kolf diff --git a/kolf/kolfui.rc b/kolf/kolfui.rc new file mode 100644 index 00000000..b38d243d --- /dev/null +++ b/kolf/kolfui.rc @@ -0,0 +1,71 @@ + + + + + + + + + + + + Ho&le + + + + + + + + + + + + + + &Go + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kolf/kvolumecontrol.cpp b/kolf/kvolumecontrol.cpp new file mode 100644 index 00000000..11d0be15 --- /dev/null +++ b/kolf/kvolumecontrol.cpp @@ -0,0 +1,67 @@ +#include + +#include +#include + +#include "kvolumecontrol.h" + +KVolumeControl::KVolumeControl(Arts::SoundServerV2 server, KPlayObject *parent) + : QObject(parent) +{ + init(server); +} + +KVolumeControl::KVolumeControl(double vol, Arts::SoundServerV2 server, KPlayObject *parent) + : QObject(parent) +{ + init(server); + setVolume(vol); +} + +KVolumeControl::~KVolumeControl() +{ + manager.stop(); + volumeControl.stop(); +} + +void KVolumeControl::init(Arts::SoundServerV2 server) +{ + manager = Arts::DynamicCast(server.createObject("Arts::Synth_AMAN_PLAY")); + if (manager.isNull()) + { + kdError() << "Your OS is broken. Get an OS that installs KDE decently." << endl; + return; + } + manager.start(); + + volumeControl = Arts::DynamicCast(server.createObject("Arts::StereoVolumeControl")); + if (volumeControl.isNull()) + { + kdError() << "Your OS is broken. Get an OS that installs KDE decently." << endl; + return; + } + volumeControl.start(); + + Arts::connect((static_cast(parent()))->object(), "left", volumeControl, "inleft"); + Arts::connect((static_cast(parent()))->object(), "right", volumeControl, "inright"); + + Arts::connect(volumeControl, manager); +} + +void KVolumeControl::setVolume(double d) +{ + if (volumeControl.isNull()) + return; + + volumeControl.scaleFactor(d); +} + +double KVolumeControl::volume(void) +{ + if (volumeControl.isNull()) + return -1; + + return volumeControl.scaleFactor(); +} + +#include "kvolumecontrol.moc" diff --git a/kolf/kvolumecontrol.h b/kolf/kvolumecontrol.h new file mode 100644 index 00000000..3f0306a8 --- /dev/null +++ b/kolf/kvolumecontrol.h @@ -0,0 +1,28 @@ +#ifndef KVOLUMECONTROL_H +#define KVOLUMECONTROL_H + +#include +#include +#include +#include + +class KVolumeControl : public QObject +{ +Q_OBJECT + +public: + KVolumeControl(Arts::SoundServerV2 server, KPlayObject *parent); + KVolumeControl(double vol, Arts::SoundServerV2 server, KPlayObject *parent); + ~KVolumeControl(); + + void setVolume(double); + double volume(void); + + void init(Arts::SoundServerV2 server); + +private: + Arts::StereoVolumeControl volumeControl; + Arts::Synth_AMAN_PLAY manager; +}; + +#endif diff --git a/kolf/main.cpp b/kolf/main.cpp new file mode 100644 index 00000000..3a63c1f1 --- /dev/null +++ b/kolf/main.cpp @@ -0,0 +1,93 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "kolf.h" + +#include +#include +using namespace std; + +static const char description[] = +I18N_NOOP("KDE Minigolf Game"); + +static const char version[] = "1.1.1"; + +static KCmdLineOptions options[] = +{ + { "+file", I18N_NOOP("File"), 0 }, + { "course-info ", I18N_NOOP("Print course information and exit"), 0 }, + KCmdLineLastOption +}; + + +extern "C" KDE_EXPORT int kdemain(int argc, char **argv) +{ + KAboutData aboutData( "kolf", I18N_NOOP("Kolf"), version, description, KAboutData::License_GPL, "(c) 2002-2005, Jason Katz-Brown", 0, "http://www.katzbrown.com/kolf/"); + + aboutData.addAuthor("Jason Katz-Brown", I18N_NOOP("Main author"), "jason@katzbrown.com"); + aboutData.addAuthor("Niklas Knutsson", I18N_NOOP("Advanced putting mode"), 0); + aboutData.addAuthor("Rik Hemsley", I18N_NOOP("Border around course"), 0); + aboutData.addAuthor("Ryan Cumming", I18N_NOOP("Vector class"), 0); + aboutData.addAuthor("Daniel Matza-Brown", I18N_NOOP("Working wall-bouncing algorithm"), 0); + aboutData.addAuthor("Timo A. Hummel", I18N_NOOP("Some good sound effects"), "timo.hummel@gmx.net"); + + aboutData.addCredit("Rob Renaud", I18N_NOOP("Wall-bouncing help"), 0); + aboutData.addCredit("Aaron Seigo", I18N_NOOP("Suggestions, bug reports"), 0); + + KCmdLineArgs::init(argc, argv, &aboutData); + KCmdLineArgs::addCmdLineOptions(options); + + // I've actually added this for my web site uploaded courses display + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + if (args->isSet("course-info")) + { + KCmdLineArgs::enable_i18n(); + + QString filename(QFile::decodeName(args->getOption("course-info"))); + if (QFile::exists(filename)) + { + CourseInfo info; + KolfGame::courseInfo(info, filename); + + cout << info.name.latin1() + << " - " << i18n("By %1").arg(info.author).latin1() + << " - " << i18n("%1 holes").arg(info.holes).latin1() + << " - " << i18n("par %1").arg(info.par).latin1() + << endl; + + return 0; + } + else + { + KCmdLineArgs::usage(i18n("Course %1 does not exist.").arg(filename.latin1())); + } + } + + QApplication::setColorSpec(QApplication::ManyColor); + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + + Kolf *top = new Kolf; + + if (args->count() >= 1) + { + KURL url = args->url(args->count() - 1); + top->openURL(url); + args->clear(); + } + else + top->closeGame(); + + a.setMainWidget(top); + top->show(); + + return a.exec(); +} + diff --git a/kolf/newgame.cpp b/kolf/newgame.cpp new file mode 100644 index 00000000..d038185e --- /dev/null +++ b/kolf/newgame.cpp @@ -0,0 +1,347 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "newgame.h" +#include "game.h" + +NewGameDialog::NewGameDialog(bool enableCourses, QWidget *parent, const char *_name) + : KDialogBase(KDialogBase::TreeList, i18n("New Game"), Ok | Cancel, Ok, parent, _name) +{ + this->enableCourses = enableCourses; + + editors.setAutoDelete(true); + KConfig *config = kapp->config(); + + // lots o' colors :) + startColors << yellow << blue << red << lightGray << cyan << darkBlue << magenta << darkGray << darkMagenta << darkYellow; + + playerPage = addPage(i18n("Players")); + QVBoxLayout *bigLayout = new QVBoxLayout(playerPage, marginHint(), spacingHint()); + + addButton = new KPushButton(i18n("&New Player"), playerPage); + bigLayout->addWidget(addButton); + + connect(addButton, SIGNAL(clicked()), this, SLOT(addPlayer())); + + scroller = new QScrollView(playerPage); + bigLayout->addWidget(scroller); + layout = new QVBox(scroller->viewport()); + if (!QPixmapCache::find("grass", grass)) + { + grass.load(locate("appdata", "pics/grass.png")); + QPixmapCache::insert("grass", grass); + } + scroller->viewport()->setBackgroundPixmap(grass); + scroller->addChild(layout); + + QMap entries = config->entryMap("New Game Dialog"); + unsigned int i = 0; + for (QMap::Iterator it = entries.begin(); it != entries.end(); ++it) + { + if (i > startColors.count()) + return; + + addPlayer(); + editors.last()->setName(it.key().right(it.key().length() - 1)); + editors.last()->setColor(QColor(it.data())); + ++i; + } + + if (editors.isEmpty()) + { + addPlayer(); + addPlayer(); + } + + enableButtons(); + + if (enableCourses) + { + coursePage = addPage(i18n("Course"), i18n("Choose Course to Play")); + QVBoxLayout *coursePageLayout = new QVBoxLayout(coursePage, marginHint(), spacingHint()); + + KURLLabel *coursesLink = new KURLLabel("http://web.mit.edu/~jasonkb/www/kolf/", "http://web.mit.edu/~jasonkb/www/kolf/", coursePage); + connect(coursesLink, SIGNAL(leftClickedURL(const QString &)), kapp, SLOT(invokeBrowser(const QString &))); + coursePageLayout->addWidget(coursesLink); + + QHBoxLayout *hlayout = new QHBoxLayout(coursePageLayout, spacingHint()); + + // following use this group + config->setGroup("New Game Dialog Mode"); + + // find other courses + externCourses = config->readListEntry("extra"); + + /// course loading + QStringList items = externCourses + KGlobal::dirs()->findAllResources("appdata", "courses/*"); + QStringList nameList; + const QString lastCourse(config->readEntry("course", "")); + int curItem = 0; + i = 0; + for (QStringList::Iterator it = items.begin(); it != items.end(); ++it, ++i) + { + QString file = *it; + CourseInfo curinfo; + KolfGame::courseInfo(curinfo, file); + info[file] = curinfo; + names.append(file); + nameList.append(curinfo.name); + + if (lastCourse == file) + curItem = i; + } + + const QString newName(i18n("Create New")); + info[QString::null] = CourseInfo(newName, newName, i18n("You"), 0, 0); + names.append(QString::null); + nameList.append(newName); + + courseList = new KListBox(coursePage); + hlayout->addWidget(courseList); + courseList->insertStringList(nameList); + courseList->setCurrentItem(curItem); + connect(courseList, SIGNAL(highlighted(int)), this, SLOT(courseSelected(int))); + connect(courseList, SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); + + QVBoxLayout *detailLayout = new QVBoxLayout(hlayout, spacingHint()); + name = new QLabel(coursePage); + detailLayout->addWidget(name); + author = new QLabel(coursePage); + detailLayout->addWidget(author); + + QHBoxLayout *minorLayout = new QHBoxLayout(detailLayout, spacingHint()); + par = new QLabel(coursePage); + minorLayout->addWidget(par); + holes = new QLabel(coursePage); + minorLayout->addWidget(holes); + + detailLayout->addStretch(); + KPushButton *scores = new KPushButton(i18n("Highscores"), coursePage); + connect(scores, SIGNAL(clicked()), this, SLOT(showHighscores())); + detailLayout->addWidget(scores); + + detailLayout->addStretch(); + detailLayout->addWidget(new KSeparator(coursePage)); + + minorLayout = new QHBoxLayout(detailLayout, spacingHint()); + + KPushButton *addCourseButton = new KPushButton(i18n("Add..."), coursePage); + minorLayout->addWidget(addCourseButton); + connect(addCourseButton, SIGNAL(clicked()), this, SLOT(addCourse())); + + remove = new KPushButton(i18n("Remove"), coursePage); + minorLayout->addWidget(remove); + connect(remove, SIGNAL(clicked()), this, SLOT(removeCourse())); + + courseSelected(curItem); + selectionChanged(); + } + + // options page + optionsPage = addPage(i18n("Options"), i18n("Game Options")); + QVBoxLayout *vlayout = new QVBoxLayout(optionsPage, marginHint(), spacingHint()); + + mode = new QCheckBox(i18n("&Strict mode"), optionsPage); + vlayout->addWidget(mode); + mode->setChecked(config->readBoolEntry("competition", false)); + + QLabel *desc = new QLabel(i18n("In strict mode, undo, editing, and switching holes is not allowed. This is generally for competition. Only in strict mode are highscores kept."), optionsPage); + desc->setTextFormat(RichText); + vlayout->addWidget(desc); +} + +void NewGameDialog::slotOk() +{ + KConfig *config = kapp->config(); + + config->setGroup("New Game Dialog Mode"); + config->writeEntry("competition", mode->isChecked()); + if (enableCourses) + { + config->writeEntry("course", currentCourse); + config->writeEntry("extra", externCourses); + } + + config->deleteGroup("New Game Dialog"); + config->setGroup("New Game Dialog"); + + PlayerEditor *curEditor = 0; + int i = 0; + for (curEditor = editors.first(); curEditor; curEditor = editors.next(), ++i) + config->writeEntry(QString::number(i) + curEditor->name(), curEditor->color().name()); + + config->sync(); + + KDialogBase::slotOk(); +} + +void NewGameDialog::courseSelected(int index) +{ + currentCourse = *names.at(index); + + CourseInfo &curinfo = info[currentCourse]; + + name->setText(QString("%1").arg(curinfo.name)); + + author->setText(i18n("By %1").arg(curinfo.author)); + par->setText(i18n("Par %1").arg(curinfo.par)); + holes->setText(i18n("%1 Holes").arg(curinfo.holes)); +} + +void NewGameDialog::showHighscores() +{ + KScoreDialog *scoreDialog = new KScoreDialog(KScoreDialog::Name | KScoreDialog::Custom1 | KScoreDialog::Score, this); + scoreDialog->addField(KScoreDialog::Custom1, i18n("Par"), "Par"); + scoreDialog->setConfigGroup(info[currentCourse].untranslatedName + QString(" Highscores")); + scoreDialog->setComment(i18n("High Scores for %1").arg(info[currentCourse].name)); + scoreDialog->show(); +} + +void NewGameDialog::removeCourse() +{ + int curItem = courseList->currentItem(); + if (curItem < 0) + return; + + QString file = *names.at(curItem); + if (externCourses.contains(file) < 1) + return; + + names.remove(file); + externCourses.remove(file); + courseList->removeItem(curItem); + + selectionChanged(); +} + +void NewGameDialog::selectionChanged() +{ + const int curItem = courseList->currentItem(); + remove->setEnabled(!(curItem < 0 || externCourses.contains(*names.at(curItem)) < 1)); +} + +void NewGameDialog::addCourse() +{ + QStringList files = KFileDialog::getOpenFileNames(":kourses", QString::fromLatin1("application/x-kourse"), this, i18n("Pick Kolf Course")); + + bool hasDuplicates = false; + + for (QStringList::Iterator fileIt = files.begin(); fileIt != files.end(); ++fileIt) + { + if (names.contains(*fileIt) > 0) + { + hasDuplicates = true; + continue; + } + + CourseInfo curinfo; + KolfGame::courseInfo(curinfo, *fileIt); + info[*fileIt] = curinfo; + names.prepend(*fileIt); + externCourses.prepend(*fileIt); + + courseList->insertItem(curinfo.name, 0); + } + + if (hasDuplicates) + KMessageBox::information(this, i18n("Chosen course is already on course list.")); + + courseList->setCurrentItem(0); + courseSelected(0); + selectionChanged(); +} + +void NewGameDialog::addPlayer() +{ + if (editors.count() >= startColors.count()) + return; + + editors.append(new PlayerEditor(i18n("Player %1").arg(editors.count() + 1), *startColors.at(editors.count()), layout)); + editors.last()->show(); + connect(editors.last(), SIGNAL(deleteEditor(PlayerEditor *)), this, SLOT(deleteEditor(PlayerEditor *))); + + enableButtons(); +} + +void NewGameDialog::deleteEditor(PlayerEditor *editor) +{ + if (editors.count() < 2) + return; + + editors.removeRef(editor); + + enableButtons(); +} + +void NewGameDialog::enableButtons() +{ + addButton->setEnabled(!(editors.count() >= startColors.count())); +} + +///////////////////////// + +PlayerEditor::PlayerEditor(QString startName, QColor startColor, QWidget *parent, const char *_name) + : QWidget(parent, _name) +{ + QHBoxLayout *layout = new QHBoxLayout(this, KDialogBase::spacingHint()); + + if (!QPixmapCache::find("grass", grass)) + { + grass.load(locate("appdata", "pics/grass.png")); + QPixmapCache::insert("grass", grass); + } + setBackgroundPixmap(grass); + + editor = new KLineEdit(this); + layout->addWidget(editor); + editor->setFrame(false); + editor->setText(startName); + layout->addStretch(); + layout->addWidget(colorButton = new KColorButton(startColor, this)); + colorButton->setAutoMask(true); + colorButton->setBackgroundPixmap(grass); + + KPushButton *remove = new KPushButton(i18n("Remove"), this); + remove->setAutoMask(true); + layout->addWidget(remove); + remove->setBackgroundPixmap(grass); + connect(remove, SIGNAL(clicked()), this, SLOT(removeMe())); +} + +void PlayerEditor::removeMe() +{ + emit deleteEditor(this); +} + +#include "newgame.moc" diff --git a/kolf/newgame.h b/kolf/newgame.h new file mode 100644 index 00000000..b9770a80 --- /dev/null +++ b/kolf/newgame.h @@ -0,0 +1,106 @@ +#ifndef NEWGAME_H +#define NEWGAME_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "game.h" + +class KLineEdit; +class QFrame; +class QVBoxLayout; +class QVBox; +class QPainter; +class KListBox; +class QEvent; + +class PlayerEditor : public QWidget +{ + Q_OBJECT + +public: + PlayerEditor(QString name = QString::null, QColor = red, QWidget *parent = 0, const char *_name = 0); + QColor color() { return colorButton->color(); } + QString name() { return editor->text(); } + void setColor(QColor col) { colorButton->setColor(col); } + void setName(const QString &newname) { editor->setText(newname); } + +signals: + void deleteEditor(PlayerEditor *editor); + +private slots: + void removeMe(); + +private: + KLineEdit *editor; + KColorButton *colorButton; + QPixmap grass; +}; + +class NewGameDialog : public KDialogBase +{ + Q_OBJECT + +public: + NewGameDialog(bool enableCourses, QWidget *parent, const char *_name = 0); + QPtrList *players() { return &editors; } + bool competition() { return mode->isChecked(); } + QString course() { return currentCourse; } + +public slots: + void deleteEditor(PlayerEditor *); + +protected slots: + void slotOk(); + +private slots: + void addPlayer(); + void courseSelected(int); + void addCourse(); + void removeCourse(); + void selectionChanged(); + void showHighscores(); + +private: + QVBox *layout; + KPushButton *addButton; + QFrame *playerPage; + QScrollView *scroller; + QFrame *coursePage; + QFrame *optionsPage; + QValueList startColors; + QPtrList editors; + KPushButton *remove; + QCheckBox *mode; + + QPixmap grass; + + QStringList names; + QStringList externCourses; + QMap info; + + QStringList extraCourses; + + KListBox *courseList; + QLabel *name; + QLabel *author; + QLabel *par; + QLabel *holes; + + QString currentCourse; + + void enableButtons(); + bool enableCourses; +}; + +#endif diff --git a/kolf/object.cpp b/kolf/object.cpp new file mode 100644 index 00000000..aea33e01 --- /dev/null +++ b/kolf/object.cpp @@ -0,0 +1,2 @@ +#include "object.h" +#include "object.moc" diff --git a/kolf/object.h b/kolf/object.h new file mode 100644 index 00000000..a2bf2ef1 --- /dev/null +++ b/kolf/object.h @@ -0,0 +1,30 @@ +// it seems that OBJECT_H is used by something else + +#ifndef KOLF_OBJECT_H +#define KOLF_OBJECT_H + +#include +#include +#include + +class Object : public QObject +{ + Q_OBJECT + +public: + Object(QObject *parent = 0, const char *name = 0) : QObject(parent, name) { m_addOnNewHole = false; } + virtual QCanvasItem *newObject(QCanvas * /*canvas*/) { return 0; } + QString name() { return m_name; } + QString _name() { return m__name; } + QString author() { return m_author; } + bool addOnNewHole() { return m_addOnNewHole; } + +protected: + QString m_name; + QString m__name; + QString m_author; + bool m_addOnNewHole; +}; +typedef QPtrList ObjectList; + +#endif diff --git a/kolf/objects/Makefile.am b/kolf/objects/Makefile.am new file mode 100644 index 00000000..6410a006 --- /dev/null +++ b/kolf/objects/Makefile.am @@ -0,0 +1 @@ +# SUBDIRS = test poolball diff --git a/kolf/objects/poolball/Makefile.am b/kolf/objects/poolball/Makefile.am new file mode 100644 index 00000000..0388f4dd --- /dev/null +++ b/kolf/objects/poolball/Makefile.am @@ -0,0 +1,13 @@ +INCLUDES= $(all_includes) +lib_LTLIBRARIES = libkolfpoolball.la + +libkolfpoolball_la_SOURCES = poolball.cpp + +libkolfpoolball_la_LDFLAGS = $(all_libraries) $(LIB_KIO) -lkolf -module -avoid-version + +libkolfpoolball_la_METASOURCES = AUTO + +noinst_HEADERS = poolball.h + +kolf_DATA = poolball.plugin +kolfdir = $(kde_datadir)/kolf diff --git a/kolf/objects/poolball/poolball.cpp b/kolf/objects/poolball/poolball.cpp new file mode 100644 index 00000000..a5ca80ec --- /dev/null +++ b/kolf/objects/poolball/poolball.cpp @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "poolball.h" + +K_EXPORT_COMPONENT_FACTORY(libkolfpoolball, PoolBallFactory) +QObject *PoolBallFactory::createObject (QObject *, const char *, const char *, const QStringList &) { return new PoolBallObj; } + +PoolBall::PoolBall(QCanvas *canvas) + : Ball(canvas) +{ + setBrush(black); + m_number = 1; +} + +void PoolBall::save(KConfig *cfg) +{ + cfg->writeEntry("number", number()); +} + +void PoolBall::saveState(StateDB *db) +{ + db->setPoint(QPoint(x(), y())); +} + +void PoolBall::load(KConfig *cfg) +{ + setNumber(cfg->readNumEntry("number", 1)); +} + +void PoolBall::loadState(StateDB *db) +{ + move(db->point().x(), db->point().y()); + setVelocity(0, 0); + setState(Stopped); +} + +void PoolBall::draw(QPainter &p) +{ + // we should draw the number here + Ball::draw(p); +} + +PoolBallConfig::PoolBallConfig(PoolBall *poolBall, QWidget *parent) + : Config(parent), m_poolBall(poolBall) +{ + QVBoxLayout *layout = new QVBoxLayout(this, marginHint(), spacingHint()); + + layout->addStretch(); + + QLabel *num = new QLabel(i18n("Number:"), this); + layout->addWidget(num); + KIntNumInput *slider = new KIntNumInput(m_poolBall->number(), this); + slider->setRange(1, 15); + layout->addWidget(slider); + + layout->addStretch(); + + connect(slider, SIGNAL(valueChanged(int)), this, SLOT(numberChanged(int))); +} + +void PoolBallConfig::numberChanged(int newNumber) +{ + m_poolBall->setNumber(newNumber); + changed(); +} + +Config *PoolBall::config(QWidget *parent) +{ + return new PoolBallConfig(this, parent); +} + +#include "poolball.moc" diff --git a/kolf/objects/poolball/poolball.h b/kolf/objects/poolball/poolball.h new file mode 100644 index 00000000..eeb851b2 --- /dev/null +++ b/kolf/objects/poolball/poolball.h @@ -0,0 +1,63 @@ +#ifndef KOLFPOOLBALL_H +#define KOLFPOOLBALL_H + +#include +#include +#include + +#include + +#include +#include +#include +#include + +class StateDB; +class KConfig; + +class PoolBallFactory : KLibFactory { Q_OBJECT public: QObject *createObject(QObject *, const char *, const char *, const QStringList & = QStringList()); }; + +class PoolBall : public Ball +{ +public: + PoolBall(QCanvas *canvas); + + virtual bool deleteable() const { return true; } + + virtual Config *config(QWidget *parent); + virtual void saveState(StateDB *); + virtual void save(KConfig *cfg); + virtual void loadState(StateDB *); + virtual void load(KConfig *cfg); + virtual void draw(QPainter &); + virtual bool fastAdvance() const { return true; } + + int number() const { return m_number; } + void setNumber(int newNumber) { m_number = newNumber; update(); } + +private: + int m_number; +}; + +class PoolBallConfig : public Config +{ + Q_OBJECT + +public: + PoolBallConfig(PoolBall *poolBall, QWidget *parent); + +private slots: + void numberChanged(int); + +private: + PoolBall *m_poolBall; +}; + +class PoolBallObj : public Object +{ +public: + PoolBallObj() { m_name = i18n("Pool Ball"); m__name = "poolball"; m_author = "Jason Katz-Brown"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new PoolBall(canvas); } +}; + +#endif diff --git a/kolf/objects/poolball/poolball.plugin b/kolf/objects/poolball/poolball.plugin new file mode 100644 index 00000000..c305251f --- /dev/null +++ b/kolf/objects/poolball/poolball.plugin @@ -0,0 +1 @@ +Filename=libkolfpoolball diff --git a/kolf/objects/test/Makefile.am b/kolf/objects/test/Makefile.am new file mode 100644 index 00000000..4e9bb33f --- /dev/null +++ b/kolf/objects/test/Makefile.am @@ -0,0 +1,13 @@ +INCLUDES= $(all_includes) +lib_LTLIBRARIES = libkolftest.la + +libkolftest_la_SOURCES = test.cpp + +libkolftest_la_LDFLAGS = $(all_libraries) $(LIB_KIO) -lkolf -module -avoid-version + +libkolftest_la_METASOURCES = AUTO + +noinst_HEADERS = test.h + +kolf_DATA = test.plugin +kolfdir = $(kde_datadir)/kolf diff --git a/kolf/objects/test/test.cpp b/kolf/objects/test/test.cpp new file mode 100644 index 00000000..2c3d564f --- /dev/null +++ b/kolf/objects/test/test.cpp @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "test.h" + +K_EXPORT_COMPONENT_FACTORY(libkolftest, TestFactory) +QObject *TestFactory::createObject (QObject * /*parent*/, const char * /*name*/, const char * /*classname*/, const QStringList & /*args*/) +{ return new TestObj; } + +Test::Test(QCanvas *canvas) + : QCanvasEllipse(60, 40, canvas), count(0), m_switchEvery(20) +{ + // force to the bottom of other objects + setZ(-100000); + + // we want calls to advance() even though we have no velocity + setAnimated(true); +} + +void Test::advance(int phase) +{ + QCanvasEllipse::advance(phase); + + // phase is either 0 or 1, only calls with 1 should be handled + if (phase == 1) + { + // this makes it so the body is called every + // m_switchEvery times + if (count % m_switchEvery == 0) + { + // random color + const QColor myColor((QRgb)(kapp->random() % 0x01000000)); + + // set the brush, so our shape is drawn + // with the random color + setBrush(QBrush(myColor)); + + count = 0; + } + + count++; + } +} + +void Test::save(KConfig *cfg) +{ + // save our option from the course + // (courses are represented as KConfig files) + cfg->writeEntry("switchEvery", switchEvery()); +} + +void Test::load(KConfig *cfg) +{ + // load our option + setSwitchEvery(cfg->readNumEntry("switchEvery", 50)); +} + +TestConfig::TestConfig(Test *test, QWidget *parent) + : Config(parent), m_test(test) +{ + QVBoxLayout *layout = new QVBoxLayout(this, marginHint(), spacingHint()); + + layout->addStretch(); + + layout->addWidget(new QLabel(i18n("Flash speed"), this)); + + QHBoxLayout *hlayout = new QHBoxLayout(layout, spacingHint()); + QLabel *slow = new QLabel(i18n("Slow"), this); + hlayout->addWidget(slow); + QSlider *slider = new QSlider(1, 100, 5, 101 - m_test->switchEvery(), Qt::Horizontal, this); + hlayout->addWidget(slider); + QLabel *fast = new QLabel(i18n("Fast"), this); + hlayout->addWidget(fast); + + layout->addStretch(); + + connect(slider, SIGNAL(valueChanged(int)), this, SLOT(switchEveryChanged(int))); +} + +void TestConfig::switchEveryChanged(int news) +{ + // update our object + m_test->setSwitchEvery((101 - news)); + + // tells Kolf the hole was modified + changed(); +} + +Config *Test::config(QWidget *parent) +{ + return new TestConfig(this, parent); +} + +#include "test.moc" diff --git a/kolf/objects/test/test.h b/kolf/objects/test/test.h new file mode 100644 index 00000000..3086a578 --- /dev/null +++ b/kolf/objects/test/test.h @@ -0,0 +1,56 @@ +#ifndef KOLFTEST_H +#define KOLFTEST_H + +#include +#include + +#include + +#include +#include + +class KConfig; + +class TestFactory : KLibFactory { Q_OBJECT public: QObject *createObject(QObject *, const char *, const char *, const QStringList & = QStringList()); }; + +class Test : public QCanvasEllipse, public CanvasItem +{ +public: + Test(QCanvas *canvas); + + virtual Config *config(QWidget *parent); + virtual void save(KConfig *cfg); + virtual void load(KConfig *cfg); + + virtual void advance(int phase); + + int switchEvery() const { return m_switchEvery / 2; } + void setSwitchEvery(int news) { m_switchEvery = news * 2; } + +private: + int count; + int m_switchEvery; +}; + +class TestConfig : public Config +{ + Q_OBJECT + +public: + TestConfig(Test *test, QWidget *parent); + +private slots: + void switchEveryChanged(int news); + +private: + Test *m_test; +}; + +class TestObj : public Object +{ +public: + TestObj() { m_name = i18n("Flash"); m__name = "flash"; m_author = "Jason Katz-Brown"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Test(canvas); } +}; + +#endif diff --git a/kolf/objects/test/test.plugin b/kolf/objects/test/test.plugin new file mode 100644 index 00000000..c58bec40 --- /dev/null +++ b/kolf/objects/test/test.plugin @@ -0,0 +1 @@ +Filename=libkolftest diff --git a/kolf/pics/Makefile.am b/kolf/pics/Makefile.am new file mode 100644 index 00000000..e5515a85 --- /dev/null +++ b/kolf/pics/Makefile.am @@ -0,0 +1 @@ +KDE_ICON = AUTO diff --git a/kolf/pics/hi128-app-kolf.png b/kolf/pics/hi128-app-kolf.png new file mode 100644 index 00000000..e11f930c Binary files /dev/null and b/kolf/pics/hi128-app-kolf.png differ diff --git a/kolf/pics/hi16-app-kolf.png b/kolf/pics/hi16-app-kolf.png new file mode 100644 index 00000000..3dfe8c86 Binary files /dev/null and b/kolf/pics/hi16-app-kolf.png differ diff --git a/kolf/pics/hi22-app-kolf.png b/kolf/pics/hi22-app-kolf.png new file mode 100644 index 00000000..0a06094c Binary files /dev/null and b/kolf/pics/hi22-app-kolf.png differ diff --git a/kolf/pics/hi32-app-kolf.png b/kolf/pics/hi32-app-kolf.png new file mode 100644 index 00000000..06bf8809 Binary files /dev/null and b/kolf/pics/hi32-app-kolf.png differ diff --git a/kolf/pics/hi48-app-kolf.png b/kolf/pics/hi48-app-kolf.png new file mode 100644 index 00000000..422a8139 Binary files /dev/null and b/kolf/pics/hi48-app-kolf.png differ diff --git a/kolf/pics/hi64-app-kolf.png b/kolf/pics/hi64-app-kolf.png new file mode 100644 index 00000000..e6e7bccb Binary files /dev/null and b/kolf/pics/hi64-app-kolf.png differ diff --git a/kolf/pluginloader.cpp b/kolf/pluginloader.cpp new file mode 100644 index 00000000..23d1a494 --- /dev/null +++ b/kolf/pluginloader.cpp @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "pluginloader.h" + +ObjectList *PluginLoader::loadAll() +{ + ObjectList *ret = new ObjectList; + + QStringList libs; + QStringList files = KGlobal::dirs()->findAllResources("appdata", "*.plugin", false, true); + + for (QStringList::Iterator it = files.begin(); it != files.end(); ++it) + { + KSimpleConfig cfg(*it); + QString filename(cfg.readEntry("Filename", "")); + + libs.append(filename); + } + + for (QStringList::Iterator it = libs.begin(); it != libs.end(); ++it) + { + Object *newObject = load(*it); + if (newObject) + ret->append(newObject); + } + + return ret; +} + +Object *PluginLoader::load(const QString &filename) +{ + KLibFactory *factory = KLibLoader::self()->factory(filename.latin1()); + + if (!factory) + { + kdWarning() << "no factory for " << filename << "!" << endl; + return 0; + } + + QObject *newObject = factory->create(0, "objectInstance", "Object"); + + if (!newObject) + { + kdWarning() << "no newObject for " << filename << "!" << endl; + return 0; + } + + Object *ret = dynamic_cast(newObject); + + if (!ret) + kdWarning() << "no ret for " << filename << "!" << endl; + + return ret; +} + diff --git a/kolf/pluginloader.h b/kolf/pluginloader.h new file mode 100644 index 00000000..8d43db66 --- /dev/null +++ b/kolf/pluginloader.h @@ -0,0 +1,13 @@ +#ifndef PLUGINLOADER_H +#define PLUGINLOADER_H + +#include +#include + +namespace PluginLoader +{ + ObjectList *loadAll(); + Object *load(const QString &); +} + +#endif diff --git a/kolf/pool.kolf b/kolf/pool.kolf new file mode 100644 index 00000000..bd63df93 --- /dev/null +++ b/kolf/pool.kolf @@ -0,0 +1,303 @@ +[0-course@-50,-50] +author=Jason Katz-Brown +name=Pool + +[1-ball@205,302] +dummykey=true + +[1-blackhole@103,191|25] +exit=67,195 +exitDeg=90 +maxspeed=3 +minspeed=3 + +[1-blackhole@110,336|22] +exit=66,395 +exitDeg=90 +maxspeed=4 +minspeed=4 + +[1-blackhole@115,54|21] +exit=129,25 +exitDeg=0 +maxspeed=2 +minspeed=2 + +[1-blackhole@223,26|55] +exit=121,97 +exitDeg=0 +maxspeed=2 +minspeed=2 + +[1-blackhole@332,335|23] +exit=382,393 +exitDeg=90 +maxspeed=4 +minspeed=4 + +[1-blackhole@335,53|20] +exit=329,19 +exitDeg=180 +maxspeed=2 +minspeed=2 + +[1-blackhole@348,189|24] +exit=381,170 +exitDeg=90 +maxspeed=3 +minspeed=3 + +[1-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=4 +par=3 + +[1-poolball@-215,24|33] +number=1 + +[1-poolball@225,138|31] +number=1 + +[1-poolball@229,133|65] +number=1 + +[1-poolball@230,143|28] +number=1 + +[1-poolball@233,149|35] +number=1 + +[1-poolball@234,128|64] +number=1 + +[1-poolball@235,138|3] +number=1 + +[1-poolball@238,144|30] +number=1 + +[1-poolball@239,133|34] +number=1 + +[1-poolball@243,139|29] +number=1 + +[1-poolball@851,-273|32] +number=1 + +[1-slope@-2,0|57] +grade=5 +gradient=Diagonal +height=120 +reversed=true +stuckOnGround=false +width=127 + +[1-slope@194,2|54] +grade=4 +gradient=Elliptic +height=50 +reversed=true +stuckOnGround=false +width=50 + +[1-slope@314,315|61] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=false +width=40 + +[1-slope@318,32|60] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=false +width=40 + +[1-slope@329,170|59] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=false +width=40 + +[1-slope@332,0|56] +grade=8 +gradient=Opposite Diagonal +height=66 +reversed=true +stuckOnGround=false +width=68 + +[1-slope@78,168|58] +grade=4 +gradient=Elliptic +height=44 +reversed=true +stuckOnGround=false +width=44 + +[1-slope@88,315|63] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=false +width=40 + +[1-slope@94,32|62] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=false +width=40 + +[1-wall@0,0|10] +endPoint=352,56 +startPoint=337,70 + +[1-wall@0,0|11] +endPoint=114,72 +startPoint=114,174 + +[1-wall@0,0|12] +endPoint=124,40 +startPoint=135,55 + +[1-wall@0,0|13] +endPoint=337,206 +startPoint=337,317 + +[1-wall@0,0|14] +endPoint=315,332 +startPoint=326,350 + +[1-wall@0,0|15] +endPoint=129,332 +startPoint=117,349 + +[1-wall@0,0|16] +endPoint=114,72 +startPoint=100,65 + +[1-wall@0,0|17] +endPoint=347,330 +startPoint=337,317 + +[1-wall@0,0|18] +endPoint=116,316 +startPoint=91,332 + +[1-wall@0,0|19] +endPoint=337,206 +startPoint=355,213 + +[1-wall@0,0|26] +endPoint=116,206 +startPoint=101,211 + +[1-wall@0,0|27] +endPoint=114,174 +startPoint=99,174 + +[1-wall@0,0|36] +endPoint=336,352 +startPoint=326,350 + +[1-wall@0,0|37] +endPoint=347,330 +startPoint=349,343 + +[1-wall@0,0|38] +endPoint=117,349 +startPoint=102,353 + +[1-wall@0,0|39] +endPoint=93,347 +startPoint=91,332 + +[1-wall@0,0|4] +endPoint=324,43 +startPoint=318,55 + +[1-wall@0,0|40] +endPoint=100,65 +startPoint=101,47 + +[1-wall@0,0|41] +endPoint=110,38 +startPoint=101,47 + +[1-wall@0,0|42] +endPoint=352,42 +startPoint=352,56 + +[1-wall@0,0|43] +endPoint=324,43 +startPoint=343,36 + +[1-wall@0,0|44] +endPoint=352,42 +startPoint=343,36 + +[1-wall@0,0|45] +endPoint=124,40 +startPoint=110,38 + +[1-wall@0,0|46] +endPoint=102,353 +startPoint=93,347 + +[1-wall@0,0|47] +endPoint=336,352 +startPoint=349,343 + +[1-wall@0,0|48] +endPoint=99,174 +startPoint=83,185 + +[1-wall@0,0|49] +endPoint=101,211 +startPoint=84,201 + +[1-wall@0,0|5] +endPoint=116,206 +startPoint=116,316 + +[1-wall@0,0|50] +endPoint=84,201 +startPoint=83,185 + +[1-wall@0,0|51] +endPoint=365,178 +startPoint=351,167 + +[1-wall@0,0|52] +endPoint=368,195 +startPoint=355,213 + +[1-wall@0,0|53] +endPoint=365,178 +startPoint=368,195 + +[1-wall@0,0|6] +endPoint=315,332 +startPoint=129,332 + +[1-wall@0,0|7] +endPoint=337,70 +startPoint=337,175 + +[1-wall@0,0|8] +endPoint=351,167 +startPoint=337,175 + +[1-wall@0,0|9] +endPoint=318,55 +startPoint=135,55 diff --git a/kolf/printdialogpage.cpp b/kolf/printdialogpage.cpp new file mode 100644 index 00000000..1fc6ad37 --- /dev/null +++ b/kolf/printdialogpage.cpp @@ -0,0 +1,35 @@ +#include +#include +#include + +#include +#include +#include + +#include "printdialogpage.h" + +PrintDialogPage::PrintDialogPage(QWidget *parent, const char *name) + : KPrintDialogPage( parent, name ) +{ + setTitle(i18n("Kolf Options")); + + QVBoxLayout *layout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint()); + + titleCheck = new QCheckBox(i18n("Draw title text"), this); + titleCheck->setChecked(true); + layout->addWidget(titleCheck); +} + +void PrintDialogPage::getOptions(QMap &opts, bool /*incldef*/) +{ + opts["kde-kolf-title"] = titleCheck->isChecked()? "true" : "false"; +} + +void PrintDialogPage::setOptions(const QMap &opts) +{ + QString setting = opts["kde-kolf-title"]; + if (!!setting) + titleCheck->setChecked(setting == "true"); +} + +#include "printdialogpage.moc" diff --git a/kolf/printdialogpage.h b/kolf/printdialogpage.h new file mode 100644 index 00000000..047454a2 --- /dev/null +++ b/kolf/printdialogpage.h @@ -0,0 +1,27 @@ +#ifndef PRINTDIALOGPAGE_H +#define PRINTDIALOGPAGE_H + +#include +#include +#include + +class QCheckBox; +class QWidget; + +class PrintDialogPage : public KPrintDialogPage +{ + Q_OBJECT + + public: + PrintDialogPage(QWidget *parent = 0, const char *name = 0); + + //reimplement virtual functions + void getOptions(QMap &opts, bool incldef = false); + void setOptions(const QMap &opts); + + private: + QCheckBox *bgCheck; + QCheckBox *titleCheck; +}; + +#endif diff --git a/kolf/rtti.h b/kolf/rtti.h new file mode 100644 index 00000000..00f58536 --- /dev/null +++ b/kolf/rtti.h @@ -0,0 +1,6 @@ +#ifndef KOLF_RTTI_H +#define KOLF_RTTI_H + +enum RttiCodes { Rtti_NoCollision = 1001, Rtti_DontPlaceOn = 1002, Rtti_Ball = 1003, Rtti_Putter = 1004, Rtti_WallPoint = 1005, Rtti_Wall = 1006 }; + +#endif diff --git a/kolf/scoreboard.cpp b/kolf/scoreboard.cpp new file mode 100644 index 00000000..a750156f --- /dev/null +++ b/kolf/scoreboard.cpp @@ -0,0 +1,94 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "scoreboard.h" + +ScoreBoard::ScoreBoard(QWidget *parent, const char *name) + : QTable(1, 1, parent, name) +{ + vh = verticalHeader(); + hh = horizontalHeader(); + vh->setLabel(numRows() - 1, i18n("Par")); + hh->setLabel(numCols() - 1, i18n("Total")); + + setFocusPolicy(QWidget::NoFocus); + setRowReadOnly(0, true); + setRowReadOnly(1, true); +} + +void ScoreBoard::newHole(int par) +{ + int _numCols = numCols(); + insertColumns(_numCols - 1); + hh->setLabel(numCols() - 2, QString::number(numCols() - 1)); + setText(numRows() - 1, numCols() - 2, QString::number(par)); + setColumnWidth(numCols() - 2, 40); + + // update total + int tot = 0; + for (int i = 0; i < numCols() - 1; ++i) + tot += text(numRows() - 1, i).toInt(); + setText(numRows() - 1, numCols() - 1, QString::number(tot)); + + // shrink cell... + setColumnWidth(numCols() - 2, 3); + // and make it big enough for the numbers + adjustColumn(numCols() - 2); +} + +void ScoreBoard::newPlayer(const QString &name) +{ + //kdDebug(12007) << "name of new player is " << name << endl; + insertRows(numRows() - 1); + vh->setLabel(numRows() - 2, name); + setRowReadOnly(numRows() - 2, true); +} + +void ScoreBoard::setScore(int id, int hole, int score) +{ + //kdDebug(12007) << "set score\n"; + setText(id - 1, hole - 1, score > 0? QString::number(score) : QString("")); + + QString name; + setText(id - 1, numCols() - 1, QString::number(total(id, name))); + if (hole >= numCols() - 2) + ensureCellVisible(id - 1, numCols() - 1); + else + ensureCellVisible(id - 1, hole - 1); + + // shrink cell... + setColumnWidth(hole - 1, 3); + // and make it big enough for the numbers + adjustColumn(hole - 1); + + setCurrentCell(id - 1, hole - 1); +} + +void ScoreBoard::parChanged(int hole, int par) +{ + setText(numRows() - 1, hole - 1, QString::number(par)); + + // update total + int tot = 0; + for (int i = 0; i < numCols() - 1; ++i) + tot += text(numRows() - 1, i).toInt(); + setText(numRows() - 1, numCols() - 1, QString::number(tot)); +} + +int ScoreBoard::total(int id, QString &name) +{ + int tot = 0; + for (int i = 0; i < numCols() - 1; i++) + tot += text(id - 1, i).toInt(); + name = vh->label(id - 1); + //kdDebug(12007) << "tot is " << tot << endl; + return tot; +} + +#include "scoreboard.moc" diff --git a/kolf/scoreboard.h b/kolf/scoreboard.h new file mode 100644 index 00000000..b9dc78f4 --- /dev/null +++ b/kolf/scoreboard.h @@ -0,0 +1,29 @@ +#ifndef SCOREBOARD_H +#define SCOREBOARD_H + +#include + +class QWidget; +class QHeader; + +class ScoreBoard : public QTable +{ + Q_OBJECT + +public: + ScoreBoard(QWidget *parent = 0, const char *name = 0); + int total(int id, QString &name); + +public slots: + void newHole(int); + void newPlayer(const QString &name); + void setScore(int id, int hole, int score); + void parChanged(int hole, int par); + +private: + QTable *table; + QHeader *vh; + QHeader *hh; +}; + +#endif diff --git a/kolf/slope.cpp b/kolf/slope.cpp new file mode 100644 index 00000000..e2becea1 --- /dev/null +++ b/kolf/slope.cpp @@ -0,0 +1,585 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "slope.h" + +Slope::Slope(QRect rect, QCanvas *canvas) + : QCanvasRectangle(rect, canvas), type(KImageEffect::VerticalGradient), grade(4), reversed(false), color(QColor("#327501")) +{ + stuckOnGround = false; + showingInfo = false; + + gradientKeys[KImageEffect::VerticalGradient] = "Vertical"; + gradientKeys[KImageEffect::HorizontalGradient] = "Horizontal"; + gradientKeys[KImageEffect::DiagonalGradient] = "Diagonal"; + gradientKeys[KImageEffect::CrossDiagonalGradient] = "Opposite Diagonal"; + gradientKeys[KImageEffect::EllipticGradient] = "Elliptic"; + + gradientI18nKeys[KImageEffect::VerticalGradient] = i18n("Vertical"); + gradientI18nKeys[KImageEffect::HorizontalGradient] = i18n("Horizontal"); + gradientI18nKeys[KImageEffect::DiagonalGradient] = i18n("Diagonal"); + gradientI18nKeys[KImageEffect::CrossDiagonalGradient] = i18n("Opposite Diagonal"); + gradientI18nKeys[KImageEffect::EllipticGradient] = i18n("Circular"); + + setZ(-50); + + if (!QPixmapCache::find("grass", grass)) + { + grass.load(locate("appdata", "pics/grass.png")); + QPixmapCache::insert("grass", grass); + } + + point = new RectPoint(color.light(), this, canvas); + + QFont font(kapp->font()); + font.setPixelSize(18); + text = new QCanvasText(canvas); + text->setZ(99999.99); + text->setFont(font); + text->setColor(white); + + editModeChanged(false); + hideInfo(); + + // this does updatePixmap + setGradient("Vertical"); +} + +bool Slope::terrainCollisions() const +{ + // we are a terrain collision + return true; +} + +void Slope::showInfo() +{ + showingInfo = true; + Arrow *arrow = 0; + for (arrow = arrows.first(); arrow; arrow = arrows.next()) + { + arrow->setZ(z() + .01); + arrow->setVisible(true); + } + text->setVisible(true); +} + +void Slope::hideInfo() +{ + showingInfo = false; + Arrow *arrow = 0; + for (arrow = arrows.first(); arrow; arrow = arrows.next()) + arrow->setVisible(false); + text->setVisible(false); +} + +void Slope::aboutToDie() +{ + delete point; + clearArrows(); + delete text; +} + +void Slope::clearArrows() +{ + Arrow *arrow = 0; + for (arrow = arrows.first(); arrow; arrow = arrows.next()) + { + arrow->setVisible(false); + arrow->aboutToDie(); + } + arrows.setAutoDelete(true); + arrows.clear(); + arrows.setAutoDelete(false); +} + +QPtrList Slope::moveableItems() const +{ + QPtrList ret; + ret.append(point); + return ret; +} + +void Slope::setGrade(double newGrade) +{ + if (newGrade >= 0 && newGrade < 11) + { + grade = newGrade; + updatePixmap(); + } +} + +void Slope::setSize(int width, int height) +{ + newSize(width, height); +} + +void Slope::newSize(int width, int height) +{ + if (type == KImageEffect::EllipticGradient) + { + QCanvasRectangle::setSize(width, width); + // move point back to good spot + moveBy(0, 0); + + if (game && game->isEditing()) + game->updateHighlighter(); + } + else + QCanvasRectangle::setSize(width, height); + + updatePixmap(); + updateZ(); +} + +void Slope::moveBy(double dx, double dy) +{ + QCanvasRectangle::moveBy(dx, dy); + + point->dontMove(); + point->move(x() + width(), y() + height()); + + moveArrow(); + updateZ(); +} + +void Slope::moveArrow() +{ + int xavg = 0, yavg = 0; + QPointArray r = areaPoints(); + for (unsigned int i = 0; i < r.size(); ++i) + { + xavg += r[i].x(); + yavg += r[i].y(); + } + xavg /= r.size(); + yavg /= r.size(); + + Arrow *arrow = 0; + for (arrow = arrows.first(); arrow; arrow = arrows.next()) + arrow->move((double)xavg, (double)yavg); + + if (showingInfo) + showInfo(); + else + hideInfo(); + + text->move((double)xavg - text->boundingRect().width() / 2, (double)yavg - text->boundingRect().height() / 2); +} + +void Slope::editModeChanged(bool changed) +{ + point->setVisible(changed); + moveBy(0, 0); +} + +void Slope::updateZ(QCanvasRectangle *vStrut) +{ + const int area = (height() * width()); + const int defaultz = -50; + + double newZ = 0; + + QCanvasRectangle *rect = 0; + if (!stuckOnGround) + rect = vStrut? vStrut : onVStrut(); + + if (rect) + { + if (area > (rect->width() * rect->height())) + newZ = defaultz; + else + newZ = rect->z(); + } + else + newZ = defaultz; + + setZ(((double)1 / (area == 0? 1 : area)) + newZ); +} + +void Slope::load(KConfig *cfg) +{ + stuckOnGround = cfg->readBoolEntry("stuckOnGround", stuckOnGround); + grade = cfg->readDoubleNumEntry("grade", grade); + reversed = cfg->readBoolEntry("reversed", reversed); + + // bypass updatePixmap which newSize normally does + QCanvasRectangle::setSize(cfg->readNumEntry("width", width()), cfg->readNumEntry("height", height())); + updateZ(); + + QString gradientType = cfg->readEntry("gradient", gradientKeys[type]); + setGradient(gradientType); +} + +void Slope::save(KConfig *cfg) +{ + cfg->writeEntry("reversed", reversed); + cfg->writeEntry("width", width()); + cfg->writeEntry("height", height()); + cfg->writeEntry("gradient", gradientKeys[type]); + cfg->writeEntry("grade", grade); + cfg->writeEntry("stuckOnGround", stuckOnGround); +} + +void Slope::draw(QPainter &painter) +{ + painter.drawPixmap(x(), y(), pixmap); +} + +QPointArray Slope::areaPoints() const +{ + switch (type) + { + case KImageEffect::CrossDiagonalGradient: + { + QPointArray ret(3); + ret[0] = QPoint((int)x(), (int)y()); + ret[1] = QPoint((int)x() + width(), (int)y() + height()); + ret[2] = reversed? QPoint((int)x() + width(), y()) : QPoint((int)x(), (int)y() + height()); + + return ret; + } + + case KImageEffect::DiagonalGradient: + { + QPointArray ret(3); + ret[0] = QPoint((int)x() + width(), (int)y()); + ret[1] = QPoint((int)x(), (int)y() + height()); + ret[2] = !reversed? QPoint((int)x() + width(), y() + height()) : QPoint((int)x(), (int)y()); + + return ret; + } + + case KImageEffect::EllipticGradient: + { + QPointArray ret; + ret.makeEllipse((int)x(), (int)y(), width(), height()); + return ret; + } + + default: + return QCanvasRectangle::areaPoints(); + } +} + +bool Slope::collision(Ball *ball, long int /*id*/) +{ + if (grade <= 0) + return false; + + double vx = ball->xVelocity(); + double vy = ball->yVelocity(); + double addto = 0.013 * grade; + + const bool diag = type == KImageEffect::DiagonalGradient || type == KImageEffect::CrossDiagonalGradient; + const bool circle = type == KImageEffect::EllipticGradient; + + double slopeAngle = 0; + + if (diag) + slopeAngle = atan((double)width() / (double)height()); + else if (circle) + { + const QPoint start(x() + (int)width() / 2.0, y() + (int)height() / 2.0); + const QPoint end((int)ball->x(), (int)ball->y()); + + Vector betweenVector(start, end); + const double factor = betweenVector.magnitude() / ((double)width() / 2.0); + slopeAngle = betweenVector.direction(); + + // this little bit by Daniel + addto *= factor * M_PI / 2; + addto = sin(addto); + } + + switch (type) + { + case KImageEffect::HorizontalGradient: + reversed? vx += addto : vx -= addto; + break; + + case KImageEffect::VerticalGradient: + reversed? vy += addto : vy -= addto; + break; + + case KImageEffect::DiagonalGradient: + case KImageEffect::EllipticGradient: + reversed? vx += cos(slopeAngle) * addto : vx -= cos(slopeAngle) * addto; + reversed? vy += sin(slopeAngle) * addto : vy -= sin(slopeAngle) * addto; + break; + + case KImageEffect::CrossDiagonalGradient: + reversed? vx -= cos(slopeAngle) * addto : vx += cos(slopeAngle) * addto; + reversed? vy += sin(slopeAngle) * addto : vy -= sin(slopeAngle) * addto; + break; + + default: + break; + } + + ball->setVelocity(vx, vy); + // check if the ball is at the center of a pit or mound + // or has otherwise stopped. + if (vx == 0 && vy ==0) + ball->setState(Stopped); + else + ball->setState(Rolling); + + // do NOT do terrain collisions + return false; +} + +void Slope::setGradient(QString text) +{ + for (QMap::Iterator it = gradientKeys.begin(); it != gradientKeys.end(); ++it) + { + if (it.data() == text) + { + setType(it.key()); + return; + } + } + + // extra forgiveness ;-) (note it's i18n keys) + for (QMap::Iterator it = gradientI18nKeys.begin(); it != gradientI18nKeys.end(); ++it) + { + if (it.data() == text) + { + setType(it.key()); + return; + } + } +} + +void Slope::setType(KImageEffect::GradientType type) +{ + this->type = type; + + if (type == KImageEffect::EllipticGradient) + { + // calls updatePixmap + newSize(width(), height()); + } + else + updatePixmap(); +} + +void Slope::updatePixmap() +{ + // make a gradient, make grass that's bright or dim + // merge into this->pixmap. This is drawn in draw() + + // we update the arrows in this function + clearArrows(); + + const bool diag = type == KImageEffect::DiagonalGradient || type == KImageEffect::CrossDiagonalGradient; + const bool circle = type == KImageEffect::EllipticGradient; + + const QColor darkColor = color.dark(100 + grade * (circle? 20 : 10)); + const QColor lightColor = diag || circle? color.light(110 + (diag? 5 : .5) * grade) : color; + // hack only for circles + const bool _reversed = circle? !reversed : reversed; + QImage gradientImage = KImageEffect::gradient(QSize(width(), height()), _reversed? darkColor : lightColor, _reversed? lightColor : darkColor, type); + + QPixmap qpixmap(width(), height()); + QPainter p(&qpixmap); + p.drawTiledPixmap(QRect(0, 0, width(), height()), grass); + p.end(); + + const double length = sqrt(width() * width() + height() * height()) / 4; + + if (circle) + { + const QColor otherLightColor = color.light(110 + 15 * grade); + const QColor otherDarkColor = darkColor.dark(110 + 20 * grade); + QImage otherGradientImage = KImageEffect::gradient(QSize(width(), height()), reversed? otherDarkColor : otherLightColor, reversed? otherLightColor : otherDarkColor, KImageEffect::DiagonalGradient); + + QImage grassImage(qpixmap.convertToImage()); + + QImage finalGradientImage = KImageEffect::blend(otherGradientImage, gradientImage, .60); + pixmap.convertFromImage(KImageEffect::blend(grassImage, finalGradientImage, .40)); + + // make arrows + double angle = 0; + for (int i = 0; i < 4; ++i) + { + angle += M_PI / 2; + Arrow *arrow = new Arrow(canvas()); + arrow->setLength(length); + arrow->setAngle(angle); + arrow->setReversed(reversed); + arrow->updateSelf(); + arrows.append(arrow); + } + } + else + { + Arrow *arrow = new Arrow(canvas()); + + float ratio = 0; + float factor = 1; + + double angle = 0; + + switch (type) + { + case KImageEffect::HorizontalGradient: + angle = 0; + factor = .32; + break; + + case KImageEffect::VerticalGradient: + angle = M_PI / 2; + factor = .32; + break; + + case KImageEffect::DiagonalGradient: + angle = atan((double)width() / (double)height()); + + factor = .45; + break; + + case KImageEffect::CrossDiagonalGradient: + angle = atan((double)width() / (double)height()); + angle = M_PI - angle; + + factor = .05; + break; + + default: + break; + } + + float factorPart = factor * 2; + // gradePart is out of 1 + float gradePart = grade / 8.0; + + ratio = factorPart * gradePart; + + // reverse the reversed ones + if (reversed) + ratio *= -1; + else + angle += M_PI; + + KPixmap kpixmap = qpixmap; + (void) KPixmapEffect::intensity(kpixmap, ratio); + + QImage grassImage(kpixmap.convertToImage()); + + // okay, now we have a grass image that's + // appropriately lit, and a gradient; + // lets blend.. + pixmap.convertFromImage(KImageEffect::blend(gradientImage, grassImage, .42)); + arrow->setAngle(angle); + arrow->setLength(length); + arrow->updateSelf(); + + arrows.append(arrow); + } + + text->setText(QString::number(grade)); + + if (diag || circle) + { + // make cleared bitmap + QBitmap bitmap(pixmap.width(), pixmap.height(), true); + QPainter bpainter(&bitmap); + bpainter.setBrush(color1); + QPointArray r = areaPoints(); + + // shift all the points + for (unsigned int i = 0; i < r.count(); ++i) + { + QPoint &p = r[i]; + p.setX(p.x() - x()); + p.setY(p.y() - y()); + } + bpainter.drawPolygon(r); + + // mask is drawn + pixmap.setMask(bitmap); + } + + moveArrow(); + update(); +} + +///////////////////////// + +SlopeConfig::SlopeConfig(Slope *slope, QWidget *parent) + : Config(parent) +{ + this->slope = slope; + QVBoxLayout *layout = new QVBoxLayout(this, marginHint(), spacingHint()); + KComboBox *gradient = new KComboBox(this); + QStringList items; + QString curText; + for (QMap::Iterator it = slope->gradientI18nKeys.begin(); it != slope->gradientI18nKeys.end(); ++it) + { + if (it.key() == slope->curType()) + curText = it.data(); + items.append(it.data()); + } + gradient->insertStringList(items); + gradient->setCurrentText(curText); + layout->addWidget(gradient); + connect(gradient, SIGNAL(activated(const QString &)), this, SLOT(setGradient(const QString &))); + + layout->addStretch(); + + QCheckBox *reversed = new QCheckBox(i18n("Reverse direction"), this); + reversed->setChecked(slope->isReversed()); + layout->addWidget(reversed); + connect(reversed, SIGNAL(toggled(bool)), this, SLOT(setReversed(bool))); + + QHBoxLayout *hlayout = new QHBoxLayout(layout, spacingHint()); + hlayout->addWidget(new QLabel(i18n("Grade:"), this)); + KDoubleNumInput *grade = new KDoubleNumInput(this); + grade->setRange(0, 8, 1, true); + grade->setValue(slope->curGrade()); + hlayout->addWidget(grade); + connect(grade, SIGNAL(valueChanged(double)), this, SLOT(gradeChanged(double))); + + QCheckBox *stuck = new QCheckBox(i18n("Unmovable"), this); + QWhatsThis::add(stuck, i18n("Whether or not this slope can be moved by other objects, like floaters.")); + stuck->setChecked(slope->isStuckOnGround()); + layout->addWidget(stuck); + connect(stuck, SIGNAL(toggled(bool)), this, SLOT(setStuckOnGround(bool))); +} + +void SlopeConfig::setGradient(const QString &text) +{ + slope->setGradient(text); + changed(); +} + +void SlopeConfig::setReversed(bool yes) +{ + slope->setReversed(yes); + changed(); +} + +void SlopeConfig::setStuckOnGround(bool yes) +{ + slope->setStuckOnGround(yes); + changed(); +} + +void SlopeConfig::gradeChanged(double newgrade) +{ + slope->setGrade(newgrade); + changed(); +} + +#include "slope.moc" diff --git a/kolf/slope.h b/kolf/slope.h new file mode 100644 index 00000000..d638354f --- /dev/null +++ b/kolf/slope.h @@ -0,0 +1,98 @@ +#ifndef SLOPE_H +#define SLOPE_H + +#include + +#include "game.h" + +class Slope; +class SlopeConfig : public Config +{ + Q_OBJECT + +public: + SlopeConfig(Slope *slope, QWidget *parent); + +private slots: + void setGradient(const QString &text); + void setReversed(bool); + void setStuckOnGround(bool); + void gradeChanged(double); + +private: + Slope *slope; +}; + +class Slope : public QCanvasRectangle, public CanvasItem, public RectItem +{ +public: + Slope(QRect rect, QCanvas *canvas); + virtual void aboutToDie(); + virtual int rtti() const { return 1031; } + + virtual void showInfo(); + virtual void hideInfo(); + virtual void editModeChanged(bool changed); + virtual bool canBeMovedByOthers() const { return !stuckOnGround; } + virtual QPtrList moveableItems() const; + virtual Config *config(QWidget *parent) { return new SlopeConfig(this, parent); } + void setSize(int, int); + virtual void newSize(int width, int height); + + virtual void moveBy(double dx, double dy); + + virtual void draw(QPainter &painter); + virtual QPointArray areaPoints() const; + + void setGradient(QString text); + KImageEffect::GradientType curType() const { return type; } + void setGrade(double grade); + + double curGrade() const { return grade; } + void setColor(QColor color) { this->color = color; updatePixmap(); } + void setReversed(bool reversed) { this->reversed = reversed; updatePixmap(); } + bool isReversed() const { return reversed; } + + bool isStuckOnGround() const { return stuckOnGround; } + void setStuckOnGround(bool yes) { stuckOnGround = yes; updateZ(); } + + virtual void load(KConfig *cfg); + virtual void save(KConfig *cfg); + + virtual bool collision(Ball *ball, long int id); + virtual bool terrainCollisions() const; + + QMap gradientI18nKeys; + QMap gradientKeys; + + virtual void updateZ(QCanvasRectangle *vStrut = 0); + + void moveArrow(); + +private: + KImageEffect::GradientType type; + inline void setType(KImageEffect::GradientType type); + bool showingInfo; + double grade; + bool reversed; + QColor color; + QPixmap pixmap; + void updatePixmap(); + bool stuckOnGround; + QPixmap grass; + + void clearArrows(); + + QPtrList arrows; + QCanvasText *text; + RectPoint *point; +}; + +class SlopeObj : public Object +{ +public: + SlopeObj() { m_name = i18n("Slope"); m__name = "slope"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Slope(QRect(0, 0, 40, 40), canvas); } +}; + +#endif diff --git a/kolf/sounds/FROM b/kolf/sounds/FROM new file mode 100644 index 00000000..6c472d5b --- /dev/null +++ b/kolf/sounds/FROM @@ -0,0 +1,11 @@ +blackhole.wav, blackholeputin.wav, blackholeeject.wav, and hit.wav: + Timo A. Hummel + +holed.wav + Jason Katz-Brown (drop ball into mug :) + +holeinone.wav + KReversi (kdegames/kreversi/sounds/reversi-won.wav) + +puddle.wav, wall.wav + Free sounds sites diff --git a/kolf/sounds/Makefile.am b/kolf/sounds/Makefile.am new file mode 100644 index 00000000..48609f33 --- /dev/null +++ b/kolf/sounds/Makefile.am @@ -0,0 +1,4 @@ +sounddir = $(kde_datadir)/kolf/sounds +sound_DATA = wall.wav puddle.wav holeinone.wav holed.wav blackhole.wav blackholeputin.wav blackholeeject.wav hit.wav + +EXTRA_DIST=$(sound_DATA) diff --git a/kolf/sounds/blackhole.wav b/kolf/sounds/blackhole.wav new file mode 100644 index 00000000..30997110 Binary files /dev/null and b/kolf/sounds/blackhole.wav differ diff --git a/kolf/sounds/blackholeeject.wav b/kolf/sounds/blackholeeject.wav new file mode 100644 index 00000000..1c397dc2 Binary files /dev/null and b/kolf/sounds/blackholeeject.wav differ diff --git a/kolf/sounds/blackholeputin.wav b/kolf/sounds/blackholeputin.wav new file mode 100644 index 00000000..3b3bcc4d Binary files /dev/null and b/kolf/sounds/blackholeputin.wav differ diff --git a/kolf/sounds/hit.wav b/kolf/sounds/hit.wav new file mode 100644 index 00000000..dd8bd048 Binary files /dev/null and b/kolf/sounds/hit.wav differ diff --git a/kolf/sounds/holed.wav b/kolf/sounds/holed.wav new file mode 100644 index 00000000..d8ccd4af Binary files /dev/null and b/kolf/sounds/holed.wav differ diff --git a/kolf/sounds/holeinone.wav b/kolf/sounds/holeinone.wav new file mode 100644 index 00000000..8f5c0703 Binary files /dev/null and b/kolf/sounds/holeinone.wav differ diff --git a/kolf/sounds/puddle.wav b/kolf/sounds/puddle.wav new file mode 100644 index 00000000..d9c9d4ac Binary files /dev/null and b/kolf/sounds/puddle.wav differ diff --git a/kolf/sounds/wall.wav b/kolf/sounds/wall.wav new file mode 100644 index 00000000..65591bb4 Binary files /dev/null and b/kolf/sounds/wall.wav differ diff --git a/kolf/statedb.h b/kolf/statedb.h new file mode 100644 index 00000000..522d147f --- /dev/null +++ b/kolf/statedb.h @@ -0,0 +1,23 @@ +#ifndef KOLF_STATEDB_H +#define KOLF_STATEDB_H + +#include +#include +#include + +// items can save their per-game states here +// most don't have to do anything +class StateDB +{ +public: + void setPoint(const QPoint &point) { points[curName] = point; } + QPoint point() { return points[curName]; } + void setName(const QString &name) { curName = name; } + void clear() { points.clear(); } + +private: + QMap points; + QString curName; +}; + +#endif diff --git a/kolf/tutorial.kolf b/kolf/tutorial.kolf new file mode 100644 index 00000000..cfc5f5af --- /dev/null +++ b/kolf/tutorial.kolf @@ -0,0 +1,1245 @@ +[0-course@-50,-50] +Name=Tutorial Course +Name[af]=Tutoriaal Natuurlik +Name[bg]=Обучение +Name[bn]=টিউটোরিয়াল কোরà§à¦¸ +Name[bs]=Teren za obuku +Name[ca]=Tutorial de camp +Name[cs]=Výukový kurz +Name[da]=Øvebane +Name[de]=Einstieg +Name[el]=Πίστα εκμάθησης +Name[es]=Campo de tutorial +Name[et]=Õppeväljak +Name[fi]=Harjoituskenttä +Name[fr]=Parcours didacticiel +Name[gl]=Campo de adestramento +Name[he]=מסלול לימוד +Name[hi]=शिकà¥à¤·à¤£ कोरà¥à¤¸ +Name[hu]=Gyakorlópálya +Name[is]=Kennslubraut +Name[it]=Percorso scuola +Name[ja]=ãƒãƒ¥ãƒ¼ãƒˆãƒªã‚¢ãƒ«ã‚³ãƒ¼ã‚¹ +Name[lv]=ApmÄcÄ«bas kurss +Name[mk]=Терен за учење +Name[nb]=Øvingsbane +Name[nl]=Lesparcours +Name[nn]=Øvingsbane +Name[nso]=Course ya Tutorial +Name[pl]=Tor ćwiczebny +Name[pt]=Percurso de Aprendizagem +Name[pt_BR]=Curso Tutorial +Name[ru]=Учебный ÐºÑƒÑ€Ñ +Name[sk]=VýuÄba +Name[sl]=ZaÄetniÅ¡ko igriÅ¡Äe +Name[sr]=Терен за упознавање +Name[sr@Latn]=Teren za upoznavanje +Name[sv]=Övningsbana +Name[ta]=போதக மாரà¯à®•à¯à®•à®®à¯ +Name[tg]=КурÑи Омӯзиш +Name[tr]=Öğretici Pist +Name[uk]=ÐšÑƒÑ€Ñ Ð½Ð°Ð²Ñ‡Ð°Ð½Ð½Ñ +Name[ven]=Thero dza Ngudo +Name[xh]=Imbuyekezo yokufundisiweyo +Name[xx]=xxTutorial Coursexx +Name[zh_CN]=教学路线 +Name[zh_TW]=教學的路線 +Name[zu]=Isifundo sokubuyekeza +author=Jason Katz-Brown + +[1-ball@34,267] +dummykey=true + +[1-cup@52,159|6] +dummykey=true + +[1-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[1-sign@2,2|3] +Comment=

Welcome

to the tutorial course for Kolf! +Comment[bg]=

Добре дошли

на обучаващите нива по голф! +Comment[bn]=কলà§â€Œà¦« à¦à¦° টিউটোরিয়াল কোরà§à¦¸à§‡

সà§à¦¬à¦¾à¦—তম

! +Comment[bs]=

Dobrodošli

u Kolf teren za obuku! +Comment[ca]=

Benvingut

al tutorial de camp Kolf! +Comment[da]=

Velkommen

til Kolfs øvebane! +Comment[de]=

Willkommen

zur Einstiegsbahn in Kolf +Comment[el]=

Καλώς ήÏθατε

στην πίστα εκμάθησης για το Kolf! +Comment[es]=¡

Bienvenido

al curso de Kolf! +Comment[et]=

Tere tulemast

Kolfi õppeväljakule! +Comment[fi]=

Tervetuloa

Kolfin harjoituskierrokselle! +Comment[fr]=

Bienvenue

dans le didacticiel de Kolf ! +Comment[gl]=

Benvido

ao campo de adestramento de Kolf! +Comment[he]=

×‘×¨×•×›×™× ×”×‘××™×

למסלול הלימוד של Kolf! +Comment[hi]=

सà¥à¤µà¤¾à¤—त

कोलà¥à¤« के शिकà¥à¤·à¤£ कोरà¥à¤¸ में! +Comment[hu]=

Üdvözöljük

a Kolf gyakorlópályáján! +Comment[is]=

Velkomin(n)

á kennslubraut Kolf! +Comment[it]=

Benvenuto

nel percorso-scuola per Kolf! +Comment[ja]=

よã†ã“ã

Kolf ãƒãƒ¥ãƒ¼ãƒˆãƒªã‚¢ãƒ«ã‚³ãƒ¼ã‚¹ã¸! +Comment[lv]=

Laipni lūgti

ieks Kolf apmÄcÄ«bas kursa! +Comment[mk]=

Добредојдовте

во теренот за вежбање на Kolf! +Comment[nb]=

Velkommen

til øvingsbanen i Kolf! +Comment[nl]=

Welkom

bij het lesparcours voor Kolf! +Comment[nn]=

Velkomen

til øvingsbanen i Kolf! +Comment[pl]=

Witamy

na torze ćwiczebnym Kolfa! +Comment[pt]=

Bem-vindo

ao percurso de aprendizagem do Kolf! +Comment[pt_BR]=

Bem-vindo(a)

ao curso tutorial do Kolf! +Comment[ru]=

Добро пожаловать

на учебный ÐºÑƒÑ€Ñ Ð³Ð¾Ð»ÑŒÑ„Ð°! +Comment[sk]=

Víta vás

výuÄba v hre Kolf! +Comment[sl]=

Dobrodošli

na zaÄetniÅ¡ko stezo za Kolf! +Comment[sr]=

Добродошли

у терен за упознавање Kolf-а! +Comment[sr@Latn]=

Dobrodošli

u teren za upoznavanje Kolf-a! +Comment[sv]=

Välkommen

till Kolfs övningsbana +Comment[ta]=கோலà¯à®ƒà®ªà¯ போதக மாரà¯à®•à¯à®•à®¤à¯à®¤à®¿à®±à¯à®•à¯

வரவேறà¯à®ªà¯

! +Comment[tg]=

Марҳамат намоед

ба курÑи омӯзиши Колф! +Comment[tr]=

HoÅŸgeldiniz

Kolf'ün deneme pistine hoşgeldiniz! +Comment[uk]=

ЛаÑкаво проÑимо

до курÑу Ð½Ð°Ð²Ñ‡Ð°Ð½Ð½Ñ Kolf! +Comment[ven]=

Vhotanganedzwa

kha thero dza ngudo ya Kolf! +Comment[xh]=

Wamkelekile

kwimbuyiselo yokufundiweyo ye Kolf! +Comment[xx]=xx

Welcome

to the tutorial course for Kolf!xx +Comment[zh_CN]=

欢迎

加入 Kolf æ•™å­¦è·¯çº¿ï¼ +Comment[zh_TW]=

æ­¡è¿Ž

來到 Kolf çš„æ•™å­¸è·¯ç·šï¼ +Comment[zu]=

Wamukelekile

esifundweni sokubuyekeza se-Kolf! +botWallVisible=true +height=74 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=319 + +[1-sign@73,81|4] +Comment=To hit the ball, press and hold the Down Arrow or left mouse button. How long you hold down the mouse button or key determines strength of the shot. +Comment[af]=Na getref die bal, druk en hou die Ondertoe Pyl van links muis knoppie. Hoe lang jy hou Ondertoe die muis knoppie van sleutel bepaal sterkte van die geskiet. +Comment[bg]=ÐатиÑнете и задръжте клавиша "долна Ñтрелка" или Ð»ÐµÐ²Ð¸Ñ Ð±ÑƒÑ‚Ð¾Ð½ на мишката, за да ударите топката. Силата на удара завиÑи от продължителноÑтта на задържане на бутона на мишката или клавиатурата. +Comment[bn]=বলে আঘাত করার জনà§à¦¯ ডাউন অà§à¦¯à¦¾à¦°à§‹ অথবা মাউসের বাম বাটন চেপে ধরে রাখà§à¦¨à¥¤ আঘাতের ফলে বল কী পরিমাণ দূরতà§à¦¬ অতিকà§à¦°à¦® করবে তা নিরà§à¦­à¦° করে আপনি কতকà§à¦·à¦£ যাবতà§â€ কীবোরà§à¦¡ বা মাউসের বাটন চেপে ধরে রাখছেন তার ওপর। +Comment[bs]=Da biste udarili lopticu, pritisnite i držite dugme Strelica dole ili lijevo dugme miÅ¡a. Snagu udarca odreÄ‘ujete dužinom pritiska na tipku odnosno dugme miÅ¡a. +Comment[ca]=Per a picar la bola, prémer i mantindre la tecla de la fletxa cap avall o el botó esquerra del ratolí. El temps que el mantingueu premut determinarà la força del tir. +Comment[da]=For at slÃ¥ til bolden, tryk og hold ned-pilen eller venstre museknap nede. Slagets kraft afhænger af hvor lang tid du holder knappen i bund. +Comment[de]=Halten Sie die linke Maustaste oder die Pfeiltaste abwärts gedrückt, um den Ball zu schlagen. Die Dauer des Tastendrucks entscheidet über die Wucht. +Comment[el]=Για να χτυπήσετε τη μπάλα κÏατήστε πατημένο το πλήκτÏο του κάτω βέλους ή το αÏιστεÏÏŒ πλήκτÏο του ποντικιοÏ. Το πόσο κÏατάτε πατημένο το πλήκτÏο καθοÏίζει τη δÏναμη της βολής. +Comment[es]=Para golpear la bola, pulsar y mantener la tecla de dirección abajo o el botón izquierdo del ratón. El tiempo que mantiene pulsado el botón del ratón o la tecla determinan la intensidad del disparo. +Comment[et]=Löömiseks vajuta ja hoia all klahvi "Nool alla" või vasakut hiirenuppu. Mida kauem nuppu all hoiad, seda tugevam löök. +Comment[fi]=Lyödäksesi palloa, paina ja pidä alhaalla nuolta alas tai hiiren vasenta nappia. Painalluksen pituus määrittelee lyönnin voiman. +Comment[fr]=Pour frapper la balle, actionnez la flèche Bas ou le bouton gauche de la souris. Le temps pendant lequel vous maintenez le bouton ou la touche enfoncée détermine la force de la frappe. +Comment[gl]=Para golpear a bola, manteña premida a tecla 'Abaixo' ou o botón esquerdo do rato . O tempo que a manteña premida determinará o forza do golpe. +Comment[he]=כדי לחבוט בכדור, לחץ והחזק ×ת מקש ×”×—×¥ למטה ×ו ×ת הלחצן השמ×לי של העכבר. משך הלחיצה על לחצן העכבר ×ו המקש קובע ×ת עוצמת החבטה. +Comment[hi]=गेंद पर पà¥à¤°à¤¹à¤¾à¤° करने के लिठनीचे तीर कà¥à¤‚जी या बायाठमाउस बटन दबाकर रखें. कितनी देर आप बटन या कà¥à¤‚जी दबाकर रखे रहते हैं, यह आपके पà¥à¤°à¤¹à¤¾à¤° की शकà¥à¤¤à¤¿ निरà¥à¤§à¤¾à¤°à¤¿à¤¤ करता है. +Comment[hu]=A labda elütéséhez tartsa benyomva a lefelé nyilat vagy a bal egérgombot. Annál erÅ‘sebb lesz az ütés, minél tovább tartja lenyomva a billentyűt vagy az egérgombot. +Comment[is]=Tila að slá boltann, ýttu og haltu niðri 'ör-niður' eða vinstri músahnappi. Hversu lengi þú heldur hnappnum niðri ákvarðar höggkraftinn. +Comment[it]=Per colpire la pallina, premi a lungo la "freccia destra" o il bottone sinistro del mouse. Quanto più a lungo li tieni premuti, tanto più forte sarà il colpo. +Comment[ja]=ボールを打ã¤ã«ã¯ã€ä¸‹ã‚­ãƒ¼ã‹å·¦ã®ãƒžã‚¦ã‚¹ãƒœã‚¿ãƒ³ã‚’押ã—続ã‘ã¾ã™ã€‚押ã™æ™‚é–“ãŒé•·ã„ã¨ã€ã‚·ãƒ§ãƒƒãƒˆãŒå¼·ããªã‚Šã¾ã™ã€‚ +Comment[lv]=Lai sistu pa bumbiņu, nospiediet un turiet nospiestu bulti'na uz leju taustiņu vai kreiso peles taustiņu. Taustiņa nospieÅ¡anas ilgums noteiks sitiena spÄ“ku. +Comment[mk]=За да ја удрите топката притиÑнете ја и држете ја долната Ñтрелка, или левото копче од глушецот. Јачината на ударот завиÑи од тоа колку долго го држите копчето на глушецот или Ñтрелката. +Comment[nb]=Trykk og hold nedoverpila eller venstre museknapp for Ã¥ slÃ¥ ballen. Styrken pÃ¥ slaget bestemmes av hvor lenge du holder knappen inne. +Comment[nl]=Om een bal te raken, houdt u de pijltoets naar beneden of de linkermuisknop ingedrukt. De tijd dat u de muisknop of toets ingedrukt houdt bepaalt de kracht van de slag. +Comment[nn]=Trykk og hald nede pil ned eller den venstre museknappen for Ã¥ slÃ¥. Styrken pÃ¥ slaget vert avgjort av kor lenge du held nede knappen. +Comment[pl]=Aby uderzyć piÅ‚eczkÄ™ naciÅ›nij i przytrzymaj klawisz StrzaÅ‚ki w dół lub lewy przycisk myszy. DÅ‚ugość czasu przytrzymania przycisku myszy lub klawisza okreÅ›la siÅ‚Ä™ strzaÅ‚u. +Comment[pt]=Para acertar na bola, carregue e mantenha a tecla Baixo ou no botão esquerdo do rato. O tempo que mantiver carregado o botão do rato ou a tecla determina a força da tacada. +Comment[pt_BR]=Para arremessar a bola, pressione e mantenha pressionada a seta para baixo ou o botão esquerdo do mouse. A quantidade de tempo que você segura este botão determina a força do arremesso. +Comment[ru]=Чтобы ударить по мÑчу, нажмите Ñтрелку вниз или левую кнопку мыши. Сила удара будет завиÑеть от того, как долго вы удерживаете нажатие. +Comment[sk]=Úder do loptiÄky sa ovláda stlaÄením a podržaním Å¡ipky dolu alebo ľavého tlaÄidla myÅ¡i. Podľa toho, ako dlho ho držíte, urÄíte silu úderu. +Comment[sl]=Da udarite žogico, pritisnite in držite spodnjo puÅ¡Äico ali levi miÅ¡kin gumb. MoÄ udarca je doloÄena z dolžino trajanja pritiska miÅ¡kinega gumba ali tipke. +Comment[sr]=Да биÑте ударили лоптицу притиÑните и држите Ñтрелицу доле или лево дугме миша. Јачину ударца одређује колико дуго држите дугме миша или таÑтер притиÑнутим. +Comment[sr@Latn]=Da biste udarili lopticu pritisnite i držite strelicu dole ili levo dugme miÅ¡a. JaÄinu udarca odreÄ‘uje koliko dugo držite dugme miÅ¡a ili taster pritisnutim. +Comment[sv]=För att slÃ¥ bollen, hÃ¥ll nere och släpp nerÃ¥tpilen eller vänster musknapp. Hur länge du hÃ¥ller nere musknappen eller tangenten bestämmer hur hÃ¥rt slaget blir. +Comment[ta]=பநà¯à®¤à¯ˆ அடிகà¯à®•, கீழà¯à®¨à¯‹à®•à¯à®• à®…à®®à¯à®ªà¯ˆà®¯à¯‹ அலà¯à®²à®¤à¯ சà¯à®Ÿà¯à®Ÿà®¿à®¯à®¿à®©à¯ இடத௠பொதà¯à®¤à®¾à®©à¯ˆ à®…à®´à¯à®¤à¯à®¤à®¿ கொணà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®µà¯à®®à¯.எவà¯à®µà®³à®µà¯ நேரம௠அழà¯à®¤à¯à®¤à®¿ கொணà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®¿à®±à¯€à®°à¯à®•à®³à¯‹ அவà¯à®µà®³à®µà¯ பலம௠உஙà¯à®•à®³à¯ அடிகà¯à®•à¯ கிடைகà¯à®•à¯à®®à¯. +Comment[tg]=Барои задан ба тӯб, тирчаи поёнро Ñ‘ тугмаи чапи мушро пахш кунед, Қувваи Задан аз он вобаÑта мешавад, ки шумо чӣ қадар тугмаи муш Ñ‘ калидро паахш кунед. +Comment[tr]=Topa vurmak için aÅŸağı ok ya da sol fare tuÅŸuna basılı tutun. Basılı tutma süresi, topa vuruÅŸ hızınızı belirler. +Comment[uk]=Щоб вдарити м'Ñчик, натиÑніть Ñ– тримайте Ñтрілку вниз або ліву кнопку мишки. Сила удару буде залежати від того, наÑкільки довго ви будете тримати клавішу Ñтрілки або кнопку мишки. +Comment[xh]= Ukubetha ibhola cofa ubambe umkhonto ojonge phantsi okanye icala lasekhohlo lemouse. Ixesha olithathayo xa ucinezela iqhosha lemouse okanye iqhosa lekeyboard lithetha amandla obethe ngawo. +Comment[xx]=xxTo hit the ball, press and hold the Down Arrow or left mouse button. How long you hold down the mouse button or key determines strength of the shot.xx +Comment[zh_CN]=è¦å‡»çƒï¼Œè¯·æŒ‰ä½ä¸‹æ–¹å‘键或鼠标左键。您按ä½å¤šä¹…决定了击çƒçš„力é‡ã€‚ +Comment[zh_TW]=è¦æ“Šçƒï¼ŒæŒçºŒæŒ‰è‘— 下 æ–¹å‘éµæˆ–滑鼠左éµã€‚ 您按著滑鼠或éµç›¤æŒ‰éµçš„時間長短決定擊出的強度。 +Comment[zu]=Ukushaya ibhola, bamba umcibisholo obheke phansi noma inkinobho ye-mouse engakwesokunxele. Ubude besikhathi osithathayo ucindezele inkinobho ye-mouse noma ukhiye isho amandla okushaya ibhola. +botWallVisible=true +height=163 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=319 + +[1-sign@74,251|7] +Comment=To aim the putter, press the Left (rotate counterclockwise) and Right (rotate clockwise) Arrow keys or use the mouse. +Comment[bg]=За да наÑочите Ñтика, натиÑнете клавиш "лÑва Ñтрелка" (за завъртане обратно на чаÑовниковата Ñтрелка) и "дÑÑна Ñтрелка" (по чаÑовниковата Ñтрелка) или използвайте мишката. +Comment[bn]=পà§à¦Ÿà¦¾à¦°à§‡à¦° দিকে লকà§à¦·à§à¦¯à¦¸à§à¦¥à¦¿à¦° করার জনà§à¦¯ কীবোরà§à¦¡à§‡à¦° বাম ( ঘড়ির কাটার বিপরীতে) à¦à¦¬à¦‚ ডান ( ঘড়ির কাটার দিকে ) অà§à¦¯à¦¾à¦°à§‹ কী (Key) অথবা মাউস বà§à¦¯à¦¬à¦¹à¦¾à¦° করà§à¦¨à¥¤ +Comment[bs]=Da naniÅ¡anite palicom, pritisnite strelicu lijevo (rotacija suprotno kazaljci sata) ili desno (rotacija u smijeru kazaljke sata) ili koristite miÅ¡. +Comment[ca]=Per a dirigir el disparador, premeu les tecles de la fletxa esquerra (gira en el sentit contrari al rellotge) i dreta (gira en el sentit horari) o useu el ratolí. +Comment[da]=For at sigte mod hullet brug venstrepil (rotér mod uret) og højrepil (rotér med uret), eller brug musen. +Comment[de]=Um den Putter auszurichten, verwendet man entweder die linke bzw. rechte Pfeiltaste oder die Maus. +Comment[es]=Para dirigir el empujador, pulse las teclas de dirección izquierda (rotar en sentido antihorario) y derecha (sentido horario) o use el ratón. +Comment[et]=Putteri suunamiseks kasuta klahve "Nool vasakule" (pöörab vastupäeva) või "Nool paremale" (päripäeva) või hiirt. +Comment[fi]=Suunnataksesi putteria paina vasenta (kierrä myötäpäivään) ja oikeaa (kierrä vastapäivään) kursorinäppäintä tai käytä hiirtä. +Comment[fr]=Pour viser le trou, actionnez la touche Gauche (rotation anti-horaire) ou droite (rotation horaire) ou utilisez la souris. +Comment[gl]=Para dirixir o 'empuxador' prema as teclas das frechas esquerda (sentido antihorario) e dereita (sentido horario) ou ben use o rato. +Comment[he]=כדי לכוון ×ת המחבט, הקש על מקש ×”×—×¥ הימני (סיבוב ×¢× ×›×™×•×•×Ÿ השעון) והשמ×לי (סיבוב נגד כיוון השעון) ×ו השתמש בעכבר. +Comment[hi]=पà¥à¤Ÿà¤° पर निशाना साधने के लिठबायाठऔर दायाठतीर कà¥à¤‚जी का पà¥à¤°à¤¯à¥‹à¤— करें या माउस को घड़ी की दिशा में या उलट दिशा में घà¥à¤®à¤¾à¤à¤ +Comment[hu]=Célzáshoz a balra (balra forgatás) és jobbra (jobbra forgatás) nyilak ill. az egér használható. +Comment[is]=Til að breyta stefnu púttersins, ýttu á vinstri (Vinstri snúningur) og Hægri (Hægri snúningur) örvunum eða notaðu músina. +Comment[it]=Per dirigere il putter, premi i tasti delle frecce sinistra (rotazione antioraria) e destra (rotazione oraria), oppure usa il mouse. +Comment[ja]=å·¦ã®ã‚­ãƒ¼ã‚’押ã™ã¨ã€ãƒ‘ターãŒå·¦(å時計回り)ã«å›žã‚Šã€å³ã®ã‚­ãƒ¼ã‚’押ã™ã¨ パターãŒå³(時計回り)ã«å›žã‚Šã¾ã™ã€‚マウスも使ãˆã¾ã™ã€‚ +Comment[mk]=За да ја намеÑтите палката, притиÑнете ги Ñтрелките за лево (вртење обратно од Ñтрелката на чаÑовникот) и деÑно (вртење во правец на Ñтрелката на чаÑовникот) или употребете го глушецот. +Comment[nb]=Trykk venstrepil (med klokka), høyrepil (mot klokka) eller bruk musa for Ã¥ treffe hullet. +Comment[nl]=Om op de putter te richten drukt u op de linker pijltjestoets (tegen de klok draaien), of op de rechter pijltjestoets (met de klok mee draaien), of gebruikt u de muis. +Comment[nn]=Bruk musa eller venstre og høgre piltast for Ã¥ sikta. +Comment[pl]=Aby wycelować kij, wciÅ›nij klawisz z lewÄ… lub prawÄ… strzaÅ‚kÄ… (aby przekrÄ™cić odpowiednio przeciwnie lub zgodnie z ruchem wskazówek zegara) lub użyj myszy. +Comment[pt]=Para apontar o taco, carregue nas teclas Esquerda (rodar no sentido contrário aos ponteiros do relógio) e Direita (rodar nos ponteiros do relógio) ou use o rato. +Comment[pt_BR]=Para direcionar a mira, pressione a seta para Esquerda (rotação no sentido anti-horário) e Direita (sentido horário) ou use o mouse. +Comment[ru]=Чтобы прицелитьÑÑ, нажмите Ñтрелку влево (поворот против чаÑовой Ñтрелки) или вправо (по чаÑовой) или иÑпользуйте мышь. +Comment[sk]=Smer urÄíte pomocou Å¡ipky vľavo (otoÄenie proti smeru hodinových ruÄiÄiek) a vpravo (otoÄenie po smere) alebo použite myÅ¡. +Comment[sl]=Da namerite palico, pritisnite puÅ¡Äici levo (zasuk v nasprotni smeri urinega kazalca) ali desno (zasuk v smeri urinega kazalca) ali uporabite miÅ¡ko. +Comment[sr]=Да биÑте нациљали путер притиÑните леву Ñтрелицу (окреће Ñупротно Ñмеру казаљке на чаÑовнику) или деÑну (у Ñмеру казаљке на чаÑовнику), или кориÑтите миша. +Comment[sr@Latn]=Da biste naciljali puter pritisnite levu strelicu (okreće suprotno smeru kazaljke na Äasovniku) ili desnu (u smeru kazaljke na Äasovniku), ili koristite miÅ¡a. +Comment[sv]=För att sikta med puttern, tryck pÃ¥ vänster (rotera moturs) eller höger (rotera medurs) piltangent eller använd musen. +Comment[ta]=எறிபவரைக௠கà¯à®±à®¿ வைகà¯à®•, இடதà¯(கடிகாரசà¯à®´à®±à¯à®šà®¿à®•à¯à®•à¯ எதிராக சà¯à®±à¯à®±à¯)மறà¯à®±à¯à®®à¯ வலதà¯(கடிகாரசà¯à®´à®±à¯à®šà®¿à®¯à®¾à®• சà¯à®±à¯à®±à¯) à®…à®®à¯à®ªà¯ விசைகள௠அலà¯à®²à®¤à¯ சà¯à®Ÿà¯à®Ÿà®¿à®¯à¯ˆ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®µà¯à®®à¯. +Comment[tg]=Барои нишон гирифтан тирчаҳои Чап (гардиш ба муқобили ақрабаки Ñоат) ва РоÑÑ‚ (гардиш ба Ñамти ақрабаки Ñоат) Ñ‘ мушро иÑтифода баред. +Comment[tr]=Mekanizmayı ayarlamak için Sol ve SaÄŸ klavye yön tuÅŸlarını veya fareyi kullanın +Comment[uk]=щоб прицілитиÑÑŒ, натиÑніть клавішу лівої Ñтрілки (повернути проти годинникової Ñтрілки) або правої Ñтрілки (повернути за годинниковою Ñтрілкою), або ÑкориÑтайтеÑÑŒ мишкою. +Comment[xh]=Jolisa induku ye golf, ncinizela Ekohlo (ukujikelezisa ngokwe kloko)ockwise) Nasekunene (jikelezisa ikloko) Itolo lezitshixo zebhodi okanye usebenzise i mouse. +Comment[xx]=xxTo aim the putter, press the Left (rotate counterclockwise) and Right (rotate clockwise) Arrow keys or use the mouse.xx +Comment[zh_CN]=è¦çž„准çƒæŽ¨ï¼Œè¯·æŒ‰ä½å·¦æ–¹å‘é”®(逆时针)或å³æ–¹å‘é”®(顺时针)或使用鼠标。 +Comment[zh_TW]=想è¦æŽ¨çƒå…¥æ´žï¼ŒæŒ‰å·¦ï¼ˆé€†æ™‚é‡æ—‹è½‰ï¼‰èˆ‡ å³ï¼ˆé †æ™‚é‡æ—‹è½‰ï¼‰æ–¹å‘éµæˆ–使用滑鼠。 +Comment[zu]=Ukuqondisa induku yokushaya, cindezela umcibisholo wangakwesokunxele (jikelezisa ngokuphambene newashi) kanye nowangakwesokudla (jikelezisa ngokwewashi) noma usebenzise i-mouse. +botWallVisible=true +height=142 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=320 + +[10-ball@27,177] +dummykey=true + +[10-bridge@280,206|20] +botWallVisible=true +height=109 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=84 + +[10-bridge@30,193|3] +botWallVisible=false +height=58 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=62 + +[10-bridge@40,227|10] +botWallVisible=false +height=55 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=58 + +[10-cup@331,253|6] +dummykey=true + +[10-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[10-puddle@-6,246|18] +changeEnabled=false +changeEvery=50 +height=56 +width=242 + +[10-puddle@324,386|47] +changeEnabled=false +changeEvery=50 +height=296 +width=206 + +[10-puddle@95,137|12] +changeEnabled=false +changeEvery=50 +height=238 +width=112 + +[10-sign@7,9|4] +Comment=

Bridges

Bridges can have walls on the top, bottom, left, or right. +Comment[bg]=

МоÑтове

МоÑтовете могат да имат Ñтени отгоре, отдолу, отлÑво или отдÑÑно. +Comment[bn]=

বà§à¦°à§€à¦œ

বà§à¦°à§€à¦œà§‡à¦° উপরে, নীচে, ডানে, অথবা বামে দেয়াল থাকতে পারে। +Comment[bs]=

Mostovi

Mostovi mogu imati zidove iznad, ispod, lijevo ili desno. +Comment[ca]=

Ponts

Els ponts poden tindre baranes a dalt, a baix, a l'esquerra o a la dreta. +Comment[da]=

Broer

Broer kan have vægge øverst, nederst, til venstre og til højre. +Comment[de]=

Brücken

Brücken können oben, links, rechts oder unten Wände haben. +Comment[el]=

ΓέφυÏες

Οι γέφυÏες μποÏεί να έχουν τοίχους πάνω, κάτω, δεξιά ή αÏιστεÏά. +Comment[es]=

Puentes

Los puentes pueden tener paredes, en la parte superior, fondo, izquierdo o derecho. +Comment[et]=

Sillad

Sildadel võivad olla seinad üleval, all, vasakul või paremal. +Comment[fi]=

Sillat

Silloissa voi olla seinät ylhäällä, alhaalla, vasemmalla tai oikealla. +Comment[fr]=

Ponts

Les ponts peuvent avoir des murs en haut, en bas, à droite ou à gauche. +Comment[gl]=

Pontes

As pontes poden ter valados na parte superior, inferior,esquerda ou dereita. +Comment[he]=

גשרי×

×œ×’×©×¨×™× ×™×›×•×œ×™× ×œ×”×™×•×ª קירות מלמעלה, מלמטה, משמ×ל ×ו מימין. +Comment[hi]=

पà¥à¤²

पà¥à¤²à¥‹à¤‚ के ऊपर, नीचे, बाà¤à¤ और दाà¤à¤ दीवार हो सकते हैं. +Comment[hu]=

Hidak

A hidakhoz fal tartozhat felül, alul, jobbról vagy balról. +Comment[is]=

Brýr

Brýr geta haft veggi að ofan,að neðan, til vinstri eða hægri, +Comment[it]=

Ponti

. I ponti possono avere muri in alto, in basso. a destra o a sinistra. +Comment[ja]=

æ©‹

å£ã¯æ©‹ã®ä¸Šã¨ä¸‹ã¨å·¦ã¨å³ç«¯ã«å­˜åœ¨ã™ã‚‹ã“ã¨ãŒå‡ºæ¥ã¾ã™ã€‚ +Comment[mk]=

МоÑтови

МоÑтовите може да имаат ѕидови одгоре, оддолу, одлево или оддеÑно. +Comment[nb]=

Broer

Broer kan ha vegger på toppen, bunne, venstre, eller høyre. +Comment[nl]=

Bruggen

Bruggen kunnen boven, links, rechts of onder muren hebben. +Comment[nn]=

Bruer

Bruer kan ha rekkverk oppe, nede, til venstre eller til høgre. +Comment[pl]=

Mosty

Mosty mogą mieć ściany na górze, na dole, po lewej lub po prawej. +Comment[pt]=

Pontes

As pontes podem ter paredes no topo, no fundo, na esquerda ou na direita. +Comment[pt_BR]=

Pontes

Pontes podem ter muros no topo, base, esquerda ou direita. +Comment[ru]=

МоÑÑ‚Ñ‹

МоÑÑ‚Ñ‹ могут иметь Ñтены Ñверху, Ñнизу, Ñлева или Ñправа. +Comment[sk]=

Mosty

Mosty môžu mať hore, dole, vľavo a vpravo múry. +Comment[sl]=

Mostovi

Mostovi imajo lahko zidove zgoraj, spodaj, levo ali desno. +Comment[sr]=

МоÑтови

МоÑтови могу да имају зидове на врху, дну, лево или деÑно. +Comment[sr@Latn]=

Mostovi

Mostovi mogu da imaju zidove na vrhu, dnu, levo ili desno. +Comment[sv]=

Broar

Broar kan ha väggar över, under, till vänster eller till höger. +Comment[ta]=

பாலஙà¯à®•à®³à¯

பாலஙà¯à®•à®³à¯ அதன௠மேலà¯,கீழà¯,இடபà¯à®±à®®à¯ அலà¯à®²à®¤à¯ வலபà¯à®±à®¤à¯à®¤à®¿à®²à¯ சà¯à®µà®°à¯à®•à®³à¯ˆ கொணà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯. +Comment[tg]=

Кӯпрукҳо

Кӯпрукҳо деворҳоро дар боло, поён, чап Ñ‘ роÑÑ‚ дошта метавонад. +Comment[tr]=

Köprüler

Köprülerin üst, alt, sol veya sağ taraflarında duvarlar olabilir. +Comment[uk]=

МоÑти

МоÑти можуть мати Ñтіни зверху, знизу, з правого Ñ– лівого боків. +Comment[ven]=

Maburoho

Maburoho a ngavha na dzi mbondo nga ntha kana nga fhasi kana nga matungo. +Comment[xh]=

Iibhulorho

Iibhulorho zingaba neendonga ngaphezulu, ngezantsi, ekhohlo okanye ekunene. +Comment[xx]=xx

Bridges

Bridges can have walls on the top, bottom, left, or right.xx +Comment[zh_CN]=

æ¡¥

æ¡¥å¯ä»¥åœ¨ä¸Šã€ä¸‹ã€å·¦ã€å³å¸ƒç½®çŸ­å¢™ã€‚ +Comment[zh_TW]=

æ©‹

æ©‹å¯ä»¥åœ¨é ‚端ã€åº•éƒ¨ã€å·¦æ–¹ã€å³æ–¹æœ‰ç‰†ã€‚ +Comment[zu]=

Amabhuloho

Amabhuloho angaba nezindonga phezulu, phansi, kwesokunxele, noma kwesokudla. +botWallVisible=true +height=115 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=387 + +[11-ball@41,51] +dummykey=true + +[11-blackhole@55,332|11] +exit=137,371 +exitDeg=10 +maxspeed=3 +minspeed=2 + +[11-bridge@287,158|16] +botWallVisible=false +height=34 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=37 + +[11-cup@305,173|17] +dummykey=true + +[11-floater@42,211|9] +botWallVisible=false +endPoint=42,211 +height=74 +leftWallVisible=true +rightWallVisible=true +speed=5 +startPoint=38,69 +topWallVisible=false +width=67 + +[11-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=5 + +[11-puddle@162,25|21] +changeEnabled=false +changeEvery=50 +height=280 +width=116 + +[11-puddle@189,186|19] +changeEnabled=false +changeEvery=50 +height=154 +width=72 + +[11-sand@155,72|24] +changeEnabled=false +changeEvery=50 +height=214 +width=108 + +[11-sand@294,384|35] +changeEnabled=false +changeEvery=50 +height=120 +width=112 + +[11-sand@295,399|34] +changeEnabled=false +changeEvery=50 +height=90 +width=172 + +[11-sign@145,4|3] +Comment=

Everything

Here's a hole that has it all. Have fun with Kolf!
-- Jason Katz-Brown
+Comment[bg]=

Ð’Ñичко накуп

Това е дупка, коÑто притежава вÑичко. ПриÑтна игра!
-- Jason Katz-Brown
+Comment[bn]=

সবকিছà§

à¦à¦‡ গরà§à¦¤à§‡ সবকিছà§à¦‡ আছে। কে-ওলফ খেলে মজা করà§à¦¨!
-- জেসন কাটà§â€Œà¦œ-বà§à¦°à¦¾à¦‰à¦¨
+Comment[bs]=

Svega pomalo

Ovo je rupa koja ima sve. Zabavite se s Kolfom!
-- Jason Katz-Brown
+Comment[ca]=

Tot

Aquest és un forat que ho té tot. Que el gaudiu amb kolf!
-- Jason Katz-Brown
+Comment[da]=

Det hele

Her er en bane der har det hele. God fornøjelse med Kolf!
-- Jason Katz-Brown
+Comment[de]=

Alles

Hier ist ein Loch, das hat alles in sich. Viel Vergnügen mit Kolf.
-- Jason Katz-Brown
+Comment[el]=

Τα πάντα

Εδώ είναι μια Ï„ÏÏπα που τα έχει όλαl. Îα πεÏάσετε καλά με το Kolf!
-- Jason Katz-Brown
+Comment[es]=

Todo

Aquí hay un agujero que lo tiene todo. Disfrute con Kolf
-- Jason Katz-Brown
+Comment[et]=

Täisvärk

See on kõikvõimalike takistustega rada. Naudi Kolfi!
-- Jason Katz-Brown
+Comment[fi]=

Kaikki

Tässä reiässä on kaikki mahdollinen. Pidä hauskaa Kolf pelin kanssa!
-- Jason Katz-Brown
+Comment[fr]=

Tout

Voici un trou qui a tout. Amusez-vous avec Kolf !
-- Jason Katz-Brown
+Comment[gl]=

Todo

Aquí hai un burato que o ten todo. Paseo ben con Kolf!
-- Jason Katz-Brown
+Comment[he]=

הכל

×”× ×” גומה שיש בה הכל. שיהיה לך ×›×™×£ ×¢× Kolf!
-- ×’'ייסון ק×טס־בר×ון
+Comment[hi]=

सबकà¥à¤›

यहाठà¤à¤• होल है जिसमें सब है. कोलà¥à¤« के साथ मजा करें!
-- जेसन कातà¥à¤œ-बà¥à¤°à¤¾à¤‰à¤¨
+Comment[hu]=

Minden

Itt egy mindennel ellátott lyuk. Kellemes szórakozást kívánunk a játékhoz!
-- Jason Katz-Brown
+Comment[is]=

Allt

Hér er hola sem hefur það allt. Skemmtu þér með Kolf!
-- Jason Katz-Brown
+Comment[it]=

Tutto

Ecco una buca che ha tutto. Divertiti con Kolf!
-- Jason Katz-Brown
+Comment[ja]=

全部

ã“ã¡ã‚‰ã¯å…¨ã¦ã®ã‚ªãƒ–ジェクトãŒã‚るホールã§ã™ã€‚Kolf を楽ã—ã‚“ã§ä¸‹ã•ã„ï¼
-- Jason Katz-Brown
+Comment[mk]=

СÑ

Еве дупка која што има СЀ. Забавувајте Ñе Ñо Колф!
-- Jason Katz-Brown
+Comment[nb]=

Alt

Her er et hull som har alt. Kos deg med Kolf!
-- Jason Katz-Brown
+Comment[nl]=

Alles

Hier is een hole dat alles in zich heeft. Veel plezier met Kolf!
-- Jason Katz-Brown
+Comment[nn]=

Alt saman

Dette holet har alt saman. Ha det kjekt med Kolf!
-- Jason Katz-Brown
+Comment[pl]=

Wszystko

Ten dołek ma wszystkie atrakcje. Wesołej zabawy z Kolf!
-- Jason Katz-Brown
+Comment[pt]=

Tudo

Existe um buraco que tem tudo. Divirta-se com o Kolf!
-- Jason Katz-Brown
+Comment[pt_BR]=

Tudo

Aqui está um buracoque tem de tudo. Divirta-se com o Kolf!
-- Jason Katz-Brown
+Comment[ru]=

Ð’Ñе

Вот лунка, где вÑе Ñто еÑÑ‚ÑŒ. Играйте в гольф!
-- Jason Katz-Brown
+Comment[sk]=

VÅ¡etko

Tu je jamka, kde je všetko. Užite si Kolf!
-- Jason Katz-Brown
+Comment[sl]=

Vse

Tukaj je luknja, ki ima vse. Uživajte v Kolfu!
--Jason Katz-Brown
+Comment[sr]=

Све

Ево рупе која има Ñвега. Забављајте Ñе Kolf-ом!
— ÐејÑон Кец-Браун (Jason Katz-Brown)
+Comment[sr@Latn]=

Sve

Evo rupe koja ima svega. Zabavljajte se Kolf-om!
— Džejson Kec-Braun (Jason Katz-Brown)
+Comment[sv]=

Alltihop

Här är ett hål som har alltihop. Hoppas du får roligt med Kolf!
-- Jason Katz-Brown
+Comment[ta]=

எலà¯à®²à®¾à®µà®±à¯à®±à®¿à®²à¯à®®à¯

இஙà¯à®•à¯ ஓடà¯à®Ÿà¯ˆ உளà¯à®³à®¤à¯. கோலà¯à®ƒà®ªà¯à®¯à¯ˆ வைதà¯à®¤à¯ வேடிகà¯à®•à¯ˆ செயà¯!
-- ஜேஸன௠கேடà¯à®¸à¯ பிரவà¯à®©à¯
+Comment[tg]=

Ҳама чиз

Ин Ñӯрохие, ки ҳамаи чизҳоро дар он ҷо дороÑÑ‚. Бо бозии Голф дилхушӣ кунед!
-- Jason Katz-Brown
+Comment[tr]=

HerÅŸey

Burada tümünü barındıran bir delik var. Kolf ile eğlenin
-- Jason Katz-Brown
+Comment[uk]=

Ð’Ñе

Це лунка, Ñка вÑе це має. Удачі вам з Kolf!
-- Jason Katz-Brown
+Comment[ven]=

Zwithu zwothe

Henefha buli li re nazwo zwothe.Diphineni nga Kolf!
-- Jason Katz-Brown
+Comment[xh]=

Yonke into

Nanku umngxunya oqulathe konke. Yiba nolonwabo ne Kolf!
-- Jason Katz-Brown
+Comment[xx]=xx

Everything

Here's a hole that has it all. Have fun with Kolf!
-- Jason Katz-Brown
xx +Comment[zh_CN]=

å„ç§éšœç¢

这是一个有å„ç§éšœç¢çš„çƒæ´žã€‚ç¥æ‚¨çŽ©å¾—愉快ï¼
-- Jason Katz-Brown
+Comment[zh_TW]=

æ¯æ¨£æ±è¥¿

這是個什麼都有的洞。希望您在 Kolf 玩得開心ï¼
-- Jason Katz-Brown
+Comment[zu]=

Yonke into

Nansi imbobo enako konke. Zijabulise nge-Kolf!
-- Jason Katz-Brown
+botWallVisible=true +height=132 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=253 + +[11-slope@13,101|10] +grade=7 +gradient=Horizontal +height=157 +reversed=true +stuckOnGround=true +width=125 + +[11-slope@231,141|15] +grade=3 +gradient=Vertical +height=122 +reversed=false +stuckOnGround=false +width=156 + +[11-slope@309,264|23] +grade=3 +gradient=Opposite Diagonal +height=124 +reversed=true +stuckOnGround=false +width=77 + +[11-wall@0,0|12] +endPoint=231,255 +startPoint=93,391 + +[11-wall@0,0|13] +endPoint=229,118 +startPoint=231,255 + +[11-wall@0,0|22] +endPoint=390,389 +startPoint=311,295 + +[11-windmill@231,255|14] +botWallVisible=false +bottom=true +height=40 +leftWallVisible=true +rightWallVisible=true +speed=2 +topWallVisible=false +width=80 + +[2-ball@38,373] +dummykey=true + +[2-cup@129,209|6] +dummykey=true + +[2-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[2-sign@1,5|3] +Comment=

Slopes

Slopes are slanted areas of ground that push the ball in the direction that they slope. This direction is shown when you choose Hole->Show Info. +Comment[bg]=

Склонове

Това Ñа наклонени земни учаÑтъци, които изтлаÑкват топката по поÑока на Ñ‚ÐµÑ…Ð½Ð¸Ñ Ð½Ð°ÐºÐ»Ð¾Ð½. За да видите поÑоката изберете от менюто Дупка/ИнформациÑ. +Comment[bn]=

ঢাল

ঢাল হল মাটির মধà§à¦¯à§‡ নিচৠসà§à¦¥à¦¾à¦¨ যা বলকে কোন নিরà§à¦¦à¦¿à¦·à§à¦Ÿ দিকে নিয়ে যায়। বলের à¦à¦‡ গতিপথ দেখার জনà§à¦¯ গরà§à¦¤->তথà§à¦¯ পà§à¦°à¦¦à¦°à§à¦¶à¦¨ চাপà§à¦¨à¥¤ +Comment[bs]=

Nagibi

Nagibi su nakošene oblasti tla koje guraju lopticu u smijeru u kojem padaju. Ovaj smjer se može vidjeti kada izaberete Rupa->Prikaži informacije. +Comment[ca]=

Baixada

Les baixades són àrees ratllades en el terra que empenyen la bola en la direcció de la baixada. Aquesta direcció se selecciona quan escolliu Forat->Mostra informació. +Comment[da]=

Skråninger

Skråninger på banen skubber bolden i den retning de hælder. Denne retning vises når du vælger Hul -> Vis info. +Comment[de]=

Hügel

Hügel sind kurvige Bodenstücke, die den Ball in die eine oder andere Richtung ablenken. Diese Richtung ist sichtbar, wenn man Loch->Informationen anzeigen auswählt. +Comment[es]=

Pendientes

Las pendientes son áreas rayadas de suelo que empujan la bola en la dirección de la pendiente. Esta dirección se muestra cuando se selecciona Hoyo->Mostrar información. +Comment[et]=

Kallakud

Kallakud on sellised maapinna osad, mis lasevad pallil veereda oma kalde suunas. Seda suunda võid näha, kui valid Rada->Näita infot. +Comment[fi]=

Kallistukset

Kallistukset ovat alueita, jotka painavat palloa mäen suuntaan. Tämä suunta näytetään, kun valitset Reikä->Näytä tiedot. +Comment[fr]=

Pentes

Les pentes sont des zones inclinées du sol qui poussent la balle dans la direction où elles penchent. Cette direction est montrée quand vous choisissez Trou / Afficher les informations. +Comment[gl]=

Pendentes

As pendentes son zonas raiadas do chan que empuxan a bola no sentido da pendente. Este sentido amósase cando se selecciona Buraco->Amosar Información. +Comment[he]=

מדרוני×

×ž×“×¨×•× ×™× ×”× ×ž×©×˜×—×™× ×ž×©×•×¤×¢×™× ×©×“×•×—×¤×™× ×ת הכדור לפי כיוון השיפוע שלה×. כיוון ×–×” מוצג ×›×שר ×תה בוחר בגומה->הצג מידע. +Comment[hi]=

ढलान

ढलान भूमि के à¤à¥à¤•à¥‡ हà¥à¤ कà¥à¤·à¥‡à¤¤à¥à¤° होते हैं जो गेंद को ढलान की ओर ढकेलते हैं. यह दिशा दिखाई जाती है जब आप चà¥à¤¨à¤¤à¥‡ हैंहोल->जानकारी दिखाà¤à¤. +Comment[hu]=

Lejtők

A lejtős területeken a golyók a lejtő irányában gurulnak tovább. Az irány megjelenítéséhez válassza a Lyuk->Információk menüpontot. +Comment[is]=

Hallar

Hallar eru hallandi svæði í brautinni sem ýta boltanum í þá átt sem hún hallar. Þessi stefna er sýnd þegar þú velur Hola->Sýna upplýsingar +Comment[it]=

Pendii

I pendii sono zone di prato in pendenza che fanno scendere la pallina lungo la discesa. La direzione di discesa viene mostrata scegliendo Buca->Mostra informazioni. +Comment[ja]=

æ–œé¢

æ–œé¢ã¯ãƒœãƒ¼ãƒ«ã‚’å‹•ã‹ã™å‚¾ã„ã¦ã„る地é¢ã§ã™ã€‚ホール->情報を表示 ã¨ã™ã‚‹ã¨ã€æ–œé¢ã®å‹¾é…ã®æ–¹å‘ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ +Comment[mk]=

Ðаклони

Ðаклоните Ñе закривени површини на земјата кои што ја туркаат топката во наÑоката во која Ñе Ñпуштаат. Оваа наÑока Ñе прикажува кога ќе изберете Дупка->Прикажи информации +Comment[nb]=

Skråninger

Skråninger er områder som får ballen til å rulle i den retningen de skråner. Du kan se retningen ved å velge Hull->Vis info. +Comment[nl]=

Heuvels

Heuvels zijn hellende stukken grond die de bal in een bepaalde richting afbuigen. De richting ervan wordt getoond als u kiest voor Hole->Info tonen. +Comment[nn]=

Bakkar

I ein bakke vil ballen rulla i ein viss retning. Du kan sjå retningen med Hol->Vis info. +Comment[pl]=

Zbocza

Zbocza to nachylone obszary spychające piłkę w kierunku w którym opadają. Kierunek opadania zostanie pokazany, gdy wybierzesz Dołek->Pokaż informacje. +Comment[pt]=

Rampas

As rampas são zonas inclinadas de terra que empurram a bola na direcção da inclinação. Esta direcção é mostrada quando escolhe o Buraco->Mostrar a Informação. +Comment[pt_BR]=

Ladeiras

Ladeiras são as áreas inclinadas de terra que empurram a bola na direção que inclinam. Esta direção é mostrada quando você escolhe Buraco-> Mostrar Informação. +Comment[ru]=

Горки

Ðа горке мÑч будет катитьÑÑ Ð¿Ð¾Ð´ уклон. Ðаправление будет показано, еÑли вы выберите Лунка->СведениÑ. +Comment[sk]=

Svahy

Svahy sú naklonené miesta, ktoré usmerňujú loptiÄku v smere, v ktorom sú sklonené. Smer sa dá zobraziÅ¥ výberom Jamka->ZobraziÅ¥ informácie. +Comment[sl]=

Strmine

Strmine so nagnjene površine, ki porinejo žogico v smer strmine. Ta smer je prikazana, ko izberete Luknja -> Pokaži informacije. +Comment[sr]=

Ðагиби

Ðагиби Ñу закошене облаÑти тла које гурају лоптицу у Ñмеру у коме Ñу нагнуте. Тај Ñмер Ñе приказује када изаберете Рупа->Прикажи информације. +Comment[sr@Latn]=

Nagibi

Nagibi su zakošene oblasti tla koje guraju lopticu u smeru u kome su nagnute. Taj smer se prikazuje kada izaberete Rupa->Prikaži informacije. +Comment[sv]=

Sluttningar

Sluttningar är lutande markområden som rullar bollen i riktningen de lutar. Riktningen visas när du väljer Hål->Visa information. +Comment[ta]=

சரிவà¯à®•à®³à¯

சரிவà¯à®•à®³à¯ ஒர௠நிலதà¯à®¤à®¿à®©à¯ சாயà¯à®¨à¯à®¤ பரபà¯à®ªà®³à®µà¯. சரிவின௠திசையில௠அத௠பநà¯à®¤à¯ˆà®¤à¯ தளà¯à®³à¯à®®à¯.நீஙà¯à®•à®³à¯ தகவலை காடà¯à®Ÿà¯ எனà¯à®ªà®¤à¯ˆ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à¯à®®à¯ போத௠இதன௠திசை Hole-> காடà¯à®Ÿà®ªà¯à®ªà®Ÿà¯à®®à¯. . +Comment[tg]=

Теппаҳо

Дар теппачаҳо тӯб ба хамӣ меғелад. Ин Ñамт нишон дода мешавад, агар шумо -Сӯрохӣ->Ðишондиҳии Маълумот-ро интихоб кунед. +Comment[tr]=

EÄŸimler

Eğimler yerin değişik eğimlerde bulunmasını sağlar böylece top eğimlerin yönünde hareket eder. Bu yön Delik->Bilgi Göster seçtiğinizde gösterilir. +Comment[uk]=

Схили

Схили - це похилі ділÑнки землі6 Ñкі підштовхують м'Ñч у напрÑмку нахилу. Цей напрÑмок видно, коли ви вибираєтеЛунка->Показати інформацію. +Comment[ven]=

Zwivhanga

Zwivhanga ndi vhethu ho tshingamaho hune ha sukumedza bola uri i yele kha siya le ha tshingamela hone.Siya heli li sumbedzwa musi ni tshi nanga Buli->Sumbedza zwidodombedzwa. +Comment[xh]=

Amathambeka

Amathambeka ngamacala lalele kwiindawo zebala alityhala ibhola kwicala athambekele kulo. Le ndlela iboniswe when you choose Hole->Show Info. +Comment[xx]=xx

Slopes

Slopes are slanted areas of ground that push the ball in the direction that they slope. This direction is shown when you choose Hole->Show Info.xx +Comment[zh_CN]=

æ–œå¡

æ–œå¡å°±æ˜¯åœºåœ°ä¸Šçš„倾斜区域,这会使çƒæœå®ƒä»¬å€¾æ–œçš„æ–¹å‘è¿åŠ¨ã€‚该方å‘会在您选择çƒæ´ž->显示信æ¯æ—¶æ˜¾ç¤ºå‡ºæ¥ã€‚ +Comment[zh_TW]=

æ–œå¡

æ–œå¡æ˜¯å ´åœ°ä¸­å‚¾æ–œçš„å€åŸŸï¼Œä¸¦ä¸”會將çƒæŽ¨å‘它們傾斜的方å‘。這個方å‘會在您é¸æ“‡æ´ž->顯示資訊時顯示。 +Comment[zu]=

Okusantaba

Okusantaba izindawo ezitshekile zomhlaba ezidudula ibhola liye endleleni lapho zitshekele ngakhona. Lendlela iyakhombiswa uma ukhetha Imbobo->Bonisa ulwazi. +botWallVisible=true +height=187 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=392 + +[2-sign@197,198|5] +Comment=Hit the ball onto the slope and let it roll into cup. +Comment[bg]=Ðко Ñте на Ñклон, ударете топката леко и Ñ Ð¾Ñтавете да Ñе Ð¸Ð·Ñ‚ÑŠÑ€ÐºÐ°Ð»Ñ Ð´Ð¾ дупката. +Comment[bn]=বলে আঘাত করে ঢালের মধà§à¦¯à§‡ ঠেলে দিন à¦à¦¬à¦‚ তা নিজে থেকেই গড়িয়ে গড়িয়ে কাপের দিকে চলে যাবে। +Comment[bs]=Å utnite loptu prema nagibu i pustite da se spusti u rupu. +Comment[ca]=Pica la pilota en la baixada i deixa'l·la arribar fins a la copa. +Comment[da]=SlÃ¥ bolden op pÃ¥ skrÃ¥ningen og lad den rulle ned i fordybningen. +Comment[de]=Spielen Sie den Ball aufs Grün und lassen Sie ihn ins Loch rollen +Comment[es]=Golpear la bola en la pendiente y dejarla rodar hasta la copa. +Comment[et]=Löö pall kallakule ja lase tal auku veereda. +Comment[fi]=Lyä pallo rinteeseen ja anna sen vieriä kuppiin. +Comment[fr]=Frappez la balle dans la pente et laissez-la rouler dans le trou. +Comment[gl]=Golpee a bola na pendente e déixea rodar ata a copa. +Comment[he]=חבוט בכדור כך שיטפס על המדרון, ותן לו להתגלגל לתוך הגומה. +Comment[hi]=गेंद पर पà¥à¤°à¤¹à¤¾à¤° करें ताकि ढलान पर लà¥à¤¢à¤¼à¤•à¤¤à¤¾ हूआ कप में चला जाà¤. +Comment[hu]=Ãœsse a golyót a lejtÅ‘ irányában és hagyja lefelé gurulni. +Comment[is]=Sláðu á boltann í brekkuna og láttu renna í bollann. +Comment[it]=Batti la pallina verso il pendio e lasciala rotolare in buca. +Comment[ja]=カップã«å…¥ã‚‹ã‚ˆã†ã«æ–œé¢ã«ãƒœãƒ¼ãƒ«ã‚’打ã£ã¦ä¸‹ã•ã„。 +Comment[lv]=Uzsitiet bumbiņu uz nogÄzes un ļaujiet tai ierpot caurumÄ. +Comment[mk]=Удрете ја топката во наклонот и оÑтавете ја да Ñе дотркала до дупката. +Comment[nb]=SlÃ¥ ballen ned skrÃ¥ningen og la den rulle ned i koppen. +Comment[nl]=Speel de bal op de heuvel en laat deze in het gat rollen. +Comment[nn]=SlÃ¥ ballen opp bakken og lat han trilla ned i holet. +Comment[pl]=Uderz piÅ‚kÄ™ na zbocze i pozwól jej stoczyć siÄ™ do doÅ‚ka. +Comment[pt]=Acerta na bola para a rampa e deixe-a rolar para o buraco. +Comment[pt_BR]=Bata na bola em direção à ladeira e deixe-a rolar para dentro do buraco. +Comment[ru]=ЗаброÑьте мÑч на Ñклон и дайте ему закатитьÑÑ Ð² лунку. +Comment[sk]=Trafte svah a nechajte loptiÄku dokotúľaÅ¥ do jamky. +Comment[sl]=Udarite žogico na strmino in jo spravite v luknjico. +Comment[sr]=Пошаљите лоптицу на нагиб и пуÑтите је да Ñе откотрља близу рупе. +Comment[sr@Latn]=PoÅ¡aljite lopticu na nagib i pustite je da se otkotrlja blizu rupe. +Comment[sv]=SlÃ¥ bollen över sluttningen och lÃ¥t den rulla i hÃ¥let. +Comment[ta]=சரிவில௠பநà¯à®¤à¯ˆ உதைதà¯à®¤à®¾à®²à¯ அத௠கோபà¯à®ªà¯ˆà®•à¯à®•à¯à®³à¯ உரà¯à®³à¯à®®à¯. +Comment[tg]=Тӯбро ба теппача партоед ва ба он имкониÑÑ‚ диҳед, ки ба Ñӯрохи дароÑд. +Comment[tr]=EÄŸimdeki topa vurun ve fincana düşmesini saÄŸlayın. +Comment[uk]=Вдарити м'Ñчик на Ñхил Ñ– дати йому закотитиÑÑŒ в лунку. +Comment[ven]=Rwisani bola na mutsiso ni i tendele i kunguwele nga ngomu bigirini. +Comment[xh]=Betha ibhola edongeni uphinde uyeke iqengqelekele ekhaphini. +Comment[xx]=xxHit the ball onto the slope and let it roll into cup.xx +Comment[zh_CN]=把çƒæ‰“到斜å¡ä¸Šï¼Œè®©å®ƒæ»šåˆ°æ¯å­é‡Œã€‚ +Comment[zh_TW]=å°‡çƒæ“Šåˆ°æ–œå¡ä¸Šä¸¦è®“它滾進çƒæ´žã€‚ +Comment[zu]=Shaya ibhola liye kokusantaba ebese uliyeka ligingqikele enkomishini. +botWallVisible=true +height=195 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=197 + +[2-slope@11,237|4] +grade=4 +gradient=Vertical +height=113 +reversed=false +stuckOnGround=false +width=181 + +[3-ball@86,353] +dummykey=true + +[3-cup@229,307|3] +dummykey=true + +[3-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[3-sign@4,4|4] +Comment=Try out these different types of slopes. +Comment[bg]=Тук може, да изпробвате различните видове наклони. +Comment[bn]=বিভিনà§à¦¨ ধরনের à¦à¦‡ ঢালগà§à¦²à§‹ বà§à¦¯à¦¬à¦¹à¦¾à¦° করে দেখà§à¦¨à¥¤ +Comment[bs]=Isprobajte ove razne vrste nagiba. +Comment[ca]=Intenta aquests diferents tipus de baixades. +Comment[da]=Afprøv disse forskellige typer skrÃ¥ninger. +Comment[de]=Probieren Sie diese verschiedenen Typen von Grüns aus +Comment[es]=Intente estos diferentes tipos de pendiente. +Comment[et]=Proovi erinevat tüüpi kallakuid. +Comment[fi]=Kokeile näitä eri kaltevuuksia +Comment[fr]=Essayez ces différents types de pente. +Comment[gl]=Probe estes distintos tipos de pendentes. +Comment[he]=נסה ×ת סוגי ×”×ž×“×¨×•× ×™× ×”×©×•× ×™×. +Comment[hi]=ये भिनà¥à¤¨ पà¥à¤°à¤•à¤¾à¤° के ढलान आजमा देखें. +Comment[hu]=Válogatni lehet a különféle lejtÅ‘k között. +Comment[is]=Reyndu þig við þessar mismunandi brekkur. +Comment[it]= Prova questi diversi tipi di pendio. +Comment[ja]=ã“ã®ã‚¿ã‚¤ãƒ—ã®é•ã†æ–œé¢ã«ãƒœãƒ¼ãƒ«ã‚’打ã£ã¦ã¿ã¦ä¸‹ã•ã„。 +Comment[lv]=IzmÄ“Ä£iniet Å¡Ä«s dažÄdÄs nogÄzes. +Comment[mk]=ИÑпробајте ги различните типови на наклони. +Comment[nb]=Prøv disse forskjellige skrÃ¥ningene. +Comment[nl]=Probeer de verschillende typen heuvels uit. +Comment[nn]=Prøv desse ulike bakketypane. +Comment[pl]=Wypróbuj różne typy zboczy. +Comment[pt]=Experimente estes diferentes tipos de inclinação. +Comment[pt_BR]=Experimente estes diferentes tipos de ladeiras. +Comment[ru]=Попробуйте разные типы Ñклонов. +Comment[sk]=Skúste tieto rôzne sklony. +Comment[sl]=Poskusite razliÄne vrste strmin. +Comment[sr]=ИÑпробајте ове различите типове нагиба. +Comment[sr@Latn]=Isprobajte ove razliÄite tipove nagiba. +Comment[sv]=Prova med de här olika typerna av sluttning. +Comment[ta]=இநà¯à®¤ பலவகைச௠சரிவà¯à®•à®³à¯ˆ à®®à¯à®¯à®±à¯à®šà®¿à®•à¯à®•à®µà¯à®®à¯. +Comment[tg]=Ðавъҳои гуногуни теппачаҳоро иÑтифода баред. +Comment[tr]=Farklı tür eÄŸimleri de denemelisiniz. +Comment[uk]=Спробуйте ці різні типи Ñхилів. +Comment[ven]=Lingedzani dzinwe tshaka dza mitsitso. +Comment[xh]=Zama ezintlobo zahlukeneyo zezlopi. +Comment[xx]=xxTry out these different types of slopes.xx +Comment[zh_CN]=试é这些å„ç§ä¸åŒç±»åž‹çš„æ–œå¡ã€‚ +Comment[zh_TW]=試驗這些ä¸åŒé¡žåž‹çš„æ–œå¡ã€‚ +Comment[zu]=Zama lezizinhlobo ezahlukene zokusantaba +botWallVisible=true +height=87 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=343 + +[3-slope@148,292|5] +grade=4 +gradient=Vertical +height=79 +reversed=true +stuckOnGround=false +width=66 + +[3-slope@15,264|9] +grade=4 +gradient=Diagonal +height=76 +reversed=true +stuckOnGround=false +width=93 + +[3-slope@190,147|6] +grade=4 +gradient=Horizontal +height=80 +reversed=false +stuckOnGround=false +width=71 + +[3-slope@209,239|11] +grade=4 +gradient=Opposite Diagonal +height=42 +reversed=false +stuckOnGround=false +width=46 + +[3-slope@231,315|10] +grade=4 +gradient=Opposite Diagonal +height=67 +reversed=true +stuckOnGround=false +width=92 + +[3-slope@266,240|7] +grade=4 +gradient=Horizontal +height=63 +reversed=true +stuckOnGround=false +width=74 + +[3-slope@285,144|13] +grade=4 +gradient=Elliptic +height=87 +reversed=true +stuckOnGround=false +width=87 + +[3-slope@32,129|8] +grade=4 +gradient=Diagonal +height=72 +reversed=false +stuckOnGround=false +width=90 + +[3-slope@99,204|12] +grade=4 +gradient=Elliptic +height=84 +reversed=false +stuckOnGround=false +width=84 + +[4-ball@34,271] +dummykey=true + +[4-cup@367,353|6] +dummykey=true + +[4-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[4-sign@6,3|3] +Comment=The steepness of a slope is shown when you choose Hole->Show Info. Steepness goes from 8 (steepest) to 1 (shallowest). +Comment[bg]=За да видите Ñтръмнината на Ñклона, изберете от менюто Дупка/ИнформациÑ. Ðаклонът Ñе Ð¸Ð·Ð¼ÐµÐ½Ñ Ð¾Ñ‚ 8 (най-Ñтръмен) до 1 (най-полегат). +Comment[bn]=ঢালের বকà§à¦°à¦¤à¦¾ দেখার জনà§à¦¯ গরà§à¦¤->তথà§à¦¯ পà§à¦°à¦¦à¦°à§à¦¶à¦¨ চাপà§à¦¨à¥¤ বকà§à¦°à¦¤à¦¾à¦° বà§à¦¯à¦¾à¦ªà§à¦¤à¦¿ ১ ( অগভীরতম ) থেকে ৮ ( বকà§à¦°à¦¤à¦® ) পরà§à¦¯à¦¨à§à¦¤à¥¤ +Comment[bs]=Stupanj nagiba možete dobiti ako izaberete Rupa->Prikaži informacije. Nagnutost se kreće od 8 (najveća) do 1 (najmanja). +Comment[ca]=La intensitat de la baixada es mostra quan escolliu Forat->Mostra informació. La força va des de 8 (màxim) a 1 (mínim). +Comment[da]=Du kan se hvor stejl en skrÃ¥ning er ved at vælge Hul -> Vis info. Stejlhed gÃ¥r fra 8 (stejlest) til 1 (fladest). +Comment[de]=Die Steigung eines Grüns bekommen Sie angezeigt, wenn Sie Loch->Informationen anzeigen auswählen. Steigungen gehen von 8 (sehr steil) bis 1 (sehr flach). +Comment[es]=La intensidad de la pendiente se muestra cuando se selecciona Hoyo->Mostrar información. La intensidad va de 8 (máximo) a 1 (mínimo). +Comment[et]=Kallaku kallet näeb, kui valid Rada->Näita infot. Kalle jääb 8 (kõige järsem) ja 1 (laugeim) vahele. +Comment[fi]=Mäen kaltevuus näytetään kun valitset Reikä->Näytä tiedot. Kaltevuus on välillä 8 (jyrkin) - 1 (loivin) +Comment[fr]=L'inclinaison d'une pente est montrée quand vous choisissez Trou / Afficher les informations. L'inclinaison va de 8 (le plus pentu) à 1 (le moins pentu). +Comment[gl]=A inclinación dunha pendente amósase cando se elixe Burato->Amosar Información. A inclinación vai dende 8 (máximo) ata 1 (mínimo). +Comment[he]=השיפוע של המדרון מוצג ×›×שר ×תה בוחר בגומה->הצג מידע. השיפוע × ×¢ בין 8 (התלול ביותר) ל־1 (המתון ביותר). +Comment[hi]=ढलान की ढाल दिखाई देगी जब आप चà¥à¤¨à¥‡à¤‚गेहोल->जानकारी दिखाà¤à¤. ढाल 8 (सबसे तेज ढाल) से 1 (सबसे कम ढाल) तक है. +Comment[hu]=A lejtÅ‘ meredeksége megtudható a Lyuk->Információ menüponttal. A meredekség értéke 8-tól (legmeredekebb) 1-ig (leglaposabb) terjedhet. +Comment[is]=Halli brekkunnar er sýndur þegar þú velur Hola->Sýna upplýsingar. Hallinn fer frá 8 (brattast) til 1 (flatast). +Comment[it]= Il grado di inclinazione di un pendio viene mostrato quando scegli Buca->Mostra Infornazioni nel menu. La ripidezza va da 8 (il più scosceso) a 1 (il più dolce). +Comment[ja]=ホール->情報を表示 ã¨ã™ã‚‹ã¨ã€ æ–œé¢ã®å‹¾é…ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚勾é…㯠8 (急) ã‹ã‚‰ 1 (ç·©ã‚„ã‹) ã¾ã§ã®ã‚¹ã‚±ãƒ¼ãƒ«ãŒã‚ã‚Šã¾ã™ã€‚ +Comment[lv]=NogÄzes slÄ«pumu var apskatÄ«ties, ja izvÄ“las Caurums->RÄdÄ«t informÄciju. SlÄ«pums ir no 8 (slÄ«pÄkais) lÄ«dz 1 (lÄ“zenÄkais). +Comment[mk]=СтрмноÑта на наклонот е прикажана кога избирате Дупка->Прикажи информација. СтрмноÑта Ñе движи од 8 (најÑтрмна) до 1 (најблага). +Comment[nb]=Brattheten pÃ¥ en skrÃ¥ning vises hvis du velger Hull->Vis info. Brattheten gÃ¥r fra 8 (brattest) til 1 (slakest). +Comment[nl]=De stijging van een een heuvel wordt getoond als u kiest voor Hole->info tonen. De stijging gaat van 8 (steilste) tot 1 (vlakste). +Comment[nn]=Du kan sjÃ¥ kor bratt bakken er med Hol->Vis info. Den brattaste bakken har verdien 8. +Comment[pl]=Gdy wybierzesz DoÅ‚ek->Pokaż informacje pokazany zostanie kÄ…t nachylenia zbocza. KÄ…t nachylenia może przybierać wartoÅ›ci od 8 (najbardziej stromy) do 1 (najbardziej pÅ‚aski). +Comment[pt]=A inclinação de uma rampa é mostrada quando escolhe o Buraco->Mostrar a Informação. A inclinação vai de 8 (a pique) até 1 (liso). +Comment[pt_BR]=A inclinação de uma ladeira é mostrada quando você escolhe Buraco-> Mostrar Informação. O valor da inclinação da ladeira vai de 8 (mais íngreme) para 1 (mais baixo/plano). +Comment[ru]=Крутизна Ñклона показываетÑÑ Ð¿Ñ€Ð¸ нажатии Лунка->СведениÑ. Она менÑетÑÑ Ð¾Ñ‚ 8 (крутые) до 1 (пологие). +Comment[sk]=Sklon svahu sa zobrazí po výbere Jamka->ZobraziÅ¥ informácie. Sklon môže byÅ¥ od 8 (najstrmÅ¡ie) až po 1 (najrovnejÅ¡ie). +Comment[sl]=Nagib strmine je prikazan, ko izberete Luknja -> Pokaži informacije. Nagib gre od 8 (najbolj strmo) do 1 (najmanj strmo). +Comment[sr]=Стрмина нагиба Ñе приказује када изаберете Рупа->Прикажи информације. Стрмина иде од 8 (најÑтрмије) до 1 (најплиће). +Comment[sr@Latn]=Strmina nagiba se prikazuje kada izaberete Rupa->Prikaži informacije. Strmina ide od 8 (najstrmije) do 1 (najpliće). +Comment[sv]=En sluttnings lutning visas när du väljer HÃ¥l->Visa information. Lutningen sträcker sig frÃ¥n 8 (brantast) till 1 (minst brant). +Comment[ta]=தகவலை காடà¯à®Ÿà¯ எனà¯à®ªà®¤à¯ˆ நீஙà¯à®•à®³à¯ தேரà¯à®µà¯ செயà¯à®¯à¯à®®à¯ பொழà¯à®¤à¯ சரிவின௠செஙà¯à®•à¯à®¤à¯à®¤à¯ காடà¯à®Ÿà®ªà¯à®ªà®Ÿà¯à®®à¯.. ஆழம௠8(அதிக அளவ௠செஙà¯à®•à¯à®¤à¯à®¤à¯)லிரà¯à®¨à¯à®¤à¯ to 1(கà¯à®±à¯ˆà®¨à¯à®¤ ஆழமà¯)வரை செலà¯à®•à®¿à®±à®¤à¯. +Comment[tg]=Мушкилоти теппачаҳо дар натиҷаи интихоби Сӯрохӣ->Ðишондиҳии Маълумот, нишон дода мешаванд. Он аз 8 (шитоб) то 1 (ҳамвор) иваз мешавад. +Comment[uk]=Ðахил Ñхилу видно, коли ви вибираєте Лунка->Показати інформацію. Ðахил йде від 8 (найкрутіший) до 1 (пологий). +Comment[xh]=Ukwenyukela kwethambeka kuboniswa xa ukhetha Umngxuma->Bonisa ulwazi. Ukwenyukela buqala kwisibhozo (eyona enyukeleyo) ukuya kwenye (Eyona enganzulwanga). +Comment[xx]=xxThe steepness of a slope is shown when you choose Hole->Show Info. Steepness goes from 8 (steepest) to 1 (shallowest).xx +Comment[zh_CN]=æ–œå¡çš„å¡åº¦ä¼šåœ¨æ‚¨é€‰æ‹©çƒæ´ž->显示信æ¯æ—¶æ˜¾ç¤ºå‡ºæ¥ã€‚å¡åº¦å€¼ä¸º 8 (陡峭) 到 1 (平缓)。 +Comment[zh_TW]=æ–œå¡çš„傾斜度會在您é¸æ“‡æ´ž->顯示資訊時顯示。傾斜度å¯ä»¥å¾ž 8 (最陡)到 1 (最緩)。 +Comment[zu]=Izinga lokutsheka kokusantaba kuyakhonjiswa uma ukhetha Imbobo-> Khombisa ulwazi. Izinga lokutsheka lisuka ku-8 (okutsheke kakhulu) kuya ku 1 (okutsheke kancane). +botWallVisible=true +height=158 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=384 + +[4-slope@214,271|5] +grade=6 +gradient=Vertical +height=106 +reversed=false +stuckOnGround=false +width=103 + +[4-slope@61,255|4] +grade=2 +gradient=Vertical +height=122 +reversed=false +stuckOnGround=false +width=111 + +[5-ball@200,200] +dummykey=true + +[5-cup@204,356|7] +dummykey=true + +[5-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[5-sign@3,5|3] +Comment=

Walls

Bounce the ball off of the red walls. +Comment[bg]=

Стени

Стените изтлаÑкват топката, когато Ñе удари в Ñ‚ÑÑ…. +Comment[bn]=

দেয়াল

লাল দেয়ালে আঘাত করে বলকে ফিরিয়ে আনà§à¦¨à¥¤ +Comment[bs]=

Zidovi

Lopticu možete odbijati od crvene zidove. +Comment[ca]=

Baranes

Rebota la bola fora de les baranes vermelles. +Comment[da]=

Vægge

Få bolden til at støde ind i de røde vægge og hoppe tilbage. +Comment[de]=

Wände

Schlagen Sie den Ball von den roten Wänden weg +Comment[es]=

Paredes

Rebota la pelota fuera de las paredes rojas. +Comment[et]=

Seinad

Pall põrkab punastelt seintelt tagasi. +Comment[fi]=

Seinät

Lyö kimmoke punaisista seinistä. +Comment[fr]=

Murs

La balle rebondit sur les murs rouges. +Comment[gl]=

Balados

.Rebote a bola fora dos valados vermellos. +Comment[he]=

קירות

×’×¨×•× ×œ×›×“×•×¨ לקפוץ חזרה מהקירות ×”×דומי×. +Comment[hi]=

दीवारें

गेंद को लाल दीवार पर से उछालें +Comment[hu]=

Falak

Pattintsa vissza a golyót a piros falakról. +Comment[is]=

Veggir

Boltarnir skoppa af rauðu veggjunum. +Comment[it]=

Muri

Fai rimbalzare la pallina contro i muri di mattoni. +Comment[ja]=

å£

赤ã„å£ã«ãƒœãƒ¼ãƒ«ã‚’ãƒã‚¦ãƒ³ã‚¹ã•ã›ã¦ä¸‹ã•ã„。 +Comment[lv]=

Sienas

Bumbiņa atlec no sarkanajÄm sienÄm. +Comment[mk]=

Ѕидови

Одбијте ја топката од црвените ѕидови. +Comment[nb]=

Vegger

Sprett ballen vekk fra de røde veggene. +Comment[nl]=

Muren

Leid de bal weg van de rode muren. +Comment[nn]=

Veggar

Ballen kan spretta mot dei raude veggane. +Comment[pl]=

Åšciany

Odbijają piłkę od czerwonych ścian. +Comment[pt]=

Paredes

Faça tabelas com a bola nas paredes vermelhas. +Comment[pt_BR]=

Paredes

Arremesse a bola fora das paredes vermelhas. +Comment[ru]=

Стены

Шарик отÑкакивает от краÑных Ñтен. +Comment[sk]=

Steny

Použite odraz loptiÄky od Äervených stien. +Comment[sl]=

Zidovi

Žogica se odbije od rdeÄih zidov. +Comment[sr]=

Зидови

Одбијте лоптицу од црвених зидова. +Comment[sr@Latn]=

Zidovi

Odbijte lopticu od crvenih zidova. +Comment[sv]=

Väggar

Studsa bollen från de röda väggarna. +Comment[ta]=

சà¯à®µà®°à¯à®•à®³à¯

சிவபà¯à®ªà¯ சà¯à®µà®°à®¿à®²à¯ பநà¯à®¤à¯ˆ தடà¯à®Ÿà®µà¯à®®à¯. +Comment[tg]=

Деворҳо

Тӯбча ба деворҳои Ñурх зада паÑмегардад. +Comment[tr]=

Duvarlar

Topları kırmızı duvardan sektirin. +Comment[uk]=

Стіни

Ðœ'Ñчик відÑкакує від червоних Ñтін. +Comment[ven]=

Mbondo

Bammbisani bola kha luvhondo lutswuku. +Comment[xh]=

Amadonga

Betha phantsi ibhola ngaphandle kwamadonga abomvu. +Comment[xx]=xx

Walls

Bounce the ball off of the red walls.xx +Comment[zh_CN]=

短墙

通过红墙将çƒå弹。 +Comment[zh_TW]=

牆

çƒç¢°åˆ°ç´…色的牆彈回。 +Comment[zu]=

Izindonga

Bampisa ibhola ezindongeni ezibomvu. +botWallVisible=true +height=117 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=371 + +[5-wall@0,0|4] +endPoint=104,201 +startPoint=55,329 + +[5-wall@0,0|5] +endPoint=293,388 +startPoint=345,252 + +[5-wall@0,0|6] +endPoint=278,266 +startPoint=139,283 + +[6-ball@65,361] +dummykey=true + +[6-cup@362,248|31] +dummykey=true + +[6-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=4 + +[6-puddle@122,326|3] +changeEnabled=false +changeEvery=50 +height=44 +width=138 + +[6-puddle@319,408|14] +changeEnabled=false +changeEvery=50 +height=120 +width=208 + +[6-puddle@369,394|12] +changeEnabled=false +changeEvery=50 +height=240 +width=198 + +[6-sand@202,221|33] +changeEnabled=false +changeEvery=50 +height=134 +width=74 + +[6-sand@213,260|11] +changeEnabled=false +changeEvery=50 +height=60 +width=78 + +[6-sign@3,152|37] +Comment=

Sand

Sand is yellow, and slows your ball down. +Comment[bg]=

ПÑÑъци

ПÑÑъците Ñа жълти и забавÑÑ‚ топката. +Comment[bn]=

বালি

বালির রং হলà§à¦¦, à¦à¦¬à¦‚ à¦à¦Ÿà¦¿ বলের গতি হà§à¦°à¦¾à¦¸ করে। +Comment[bs]=

Pijesak

Pijesak je žute boje i usporava vašu lopticu. +Comment[ca]=

Sorra

La sorra és grogra i frena a la pilota. +Comment[da]=

Sand

Sand er gult og tager farten af din bold. +Comment[de]=

Sand

Sand ist gelb, und verlangsamt Ihren Ball. +Comment[el]=

Άμμος

Η άμμος είναι κίτÏινη και επιβÏαδÏνει την μπάλα σας. +Comment[es]=

Arena

La arena es amarilla, y ralentiza su pelota. +Comment[et]=

Liiv

Liiv on kollane ja aeglustab palli liikumist. +Comment[fi]=

Hiekka

Hiekka on keltaista, ja hidastaa palloasi. +Comment[fr]=

Sable

Le sable est jaune et ralentit votre balle. +Comment[gl]=

Area

A area é amarela, e fai que a bola vaia máis a modo. +Comment[he]=

חול

חול, המוצג בצבע צהוב, מ×ט ×ת הכדור שלך. +Comment[hi]=

रेत

रेत पीली है, तथा आपके गेंद को धीमा करती है. +Comment[hu]=

Homok

A sárga színű homok lelassítja a golyó mozgását. +Comment[is]=

Sandur

Sandur er gulur og hægir á boltanum. +Comment[it]=

Bunker

il bunker di sabbia è giallo e frena la pallina. +Comment[ja]=

ç ‚

ç ‚ã¯é»„色ã§ã€ãƒœãƒ¼ãƒ«ã¨æŽ¥ã™ã‚‹ã¨é€Ÿåº¦ã‚’è½ã—ã¾ã™ã€‚ +Comment[lv]=

Smiltis

Smiltis ir dzeltenas un palēnina Jūsu bumbas ripojumu. +Comment[mk]=

ПеÑок

ПеÑокот е жолт и ја уÑпорува топката. +Comment[nb]=

Sand

Sand er gul, og sakker farten på ballen. +Comment[nl]=

Zand

Zand is geel, en vertraagt uw bal. +Comment[nn]=

Sand

Dei gule felta er sand som seinkar ballen. +Comment[pl]=

Piasek

Piasek jest żółty i spowalnia twoją piłkę. +Comment[pt]=

Areia

Areia é amarela e reduz a velocidades das bolas. +Comment[pt_BR]=

Areia

A areia é amarela, e faz sua bola andar mais devagar. +Comment[ru]=

ПеÑок

ПеÑок жёлтый, и шарик на нем тормозит. +Comment[sk]=

Piesok

Piesok je žltý a spomaľuje loptiÄku. +Comment[sl]=

Pesek

Pesek je rumen in upoÄasni vaÅ¡o žogico. +Comment[sr]=

ПеÑак

ПеÑак је жут и уÑпорава вашу лоптицу. +Comment[sr@Latn]=

Pesak

Pesak je žut i usporava vašu lopticu. +Comment[sv]=

Bunkrar

Bunkrar är gula, och saktar ner bollen. +Comment[ta]=

மணலà¯

மஞà¯à®šà®³à®¾à®© மணல௠உஙà¯à®•à®³à¯ பநà¯à®¤à®¿à®©à¯ வேகதà¯à®¤à¯ˆ கà¯à®±à¯ˆà®•à¯à®•à¯à®®à¯. +Comment[tg]=

Қум

Қум зард аÑÑ‚ ва тӯби шуморо ÑуÑÑ‚ мегардонад. +Comment[tr]=

Kum

Sarı renkli kumluk bölgeler topunuzu yavaşlatır. +Comment[uk]=

ПіÑок

ПіÑок жовтий; він ÑповільнÑÑ” рух м'Ñчика. +Comment[ven]=

Mutavha

Mutavha ndi yellow, fhungudzani luvhilo lwa bola yanu. +Comment[xh]=

Isanti

Isanti etyheli, ucothise ibhola phantsi. +Comment[xx]=xx

Sand

Sand is yellow, and slows your ball down.xx +Comment[zh_CN]=

沙地

沙地是黄色的,å¯ä»¥å‡æ…¢çƒé€Ÿã€‚ +Comment[zh_TW]=

æ²™

沙是黃色的,而且會減慢您çƒçš„速度。 +Comment[zu]=

Isihlabathi

Isihlabathi siqanda, kanye sihambisa ibhola kancane. +botWallVisible=true +height=147 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=138 + +[6-sign@3,5|4] +Comment=

Puddles (Water)

Hitting into a puddle (blue) adds a penalty stroke to your score, and your ball is placed outside the puddle. +Comment[bg]=

Локви

Падането на топката в локва, Ð¿Ñ€Ð¸Ð±Ð°Ð²Ñ Ð½Ð°ÐºÐ°Ð·Ð°Ñ‚ÐµÐ»ÐµÐ½ удар към резултата и топката Ñе изважда от локвата за ÑÐ»ÐµÐ´Ð²Ð°Ñ‰Ð¸Ñ ÑƒÐ´Ð°Ñ€. +Comment[bn]=

ডোবা

ডোবার (নীল রঙের) মধà§à¦¯à§‡ বল ফেললে আপনার সà§à¦•à§‹à¦° কমে যাবে, তবে বলটি ডোবা থেকে তà§à¦²à§‡ দেওয়া হবে। +Comment[bs]=

Bare (Voda)

Pogodak u baru (plava boja) dodaje kaznene bodove na vaš rezultat, a loptica se premješta izvan bare. +Comment[ca]=

Basses

Passar per una bassa (blau) afegirà una penalització a la vostra puntuació i la pilotarà serà emplaçada fora de la bassa. +Comment[da]=

Vandhuller

Rammer du et vandhul (blåt) får du strafpoint, og din bold placeres uden for vandhullet. +Comment[de]=

Pfützen (Wasser)

Wenn Sie in eine Pfütze hineinspielen (blau dargestellt), bekommen Sie einen zusätzlichen Strafpunkt, und Ihr Ball wird außerhalb der Pfütze auf dem Spielfeld platziert. +Comment[es]=

Charcos (agua)

Pisar un charco (azul) añade una penalización a su puntuación, y su pelota es situada fuera del charco. +Comment[et]=

Lombid (vesi)

Kui sul õnnestub pall vette (sinine) lüüa, karistatakse sind ühe löögi lisamisega ja pall paigutatakse uueks löögiks lombi äärde. +Comment[fi]=

Lammet (Vesi)

Lampeen pyöminen (sininen) lisää rangaistuslyönnin tulokseesi ja pallosi asetetaan lammen ulkopuolelle. +Comment[fr]=

Rivières

Tomber dans une mare (bleue) ajoute une pénalité à votre score, et votre balle est placée hors de la rivière. +Comment[gl]=

Pozas (Auga)

Caer nunha poza (azul) engade un golpe de penalización á súa puntuación, e a bola sitúase fora da poza. +Comment[he]=

שלוליות (מי×)

כניסה לשלולית (בכחול) מוסיפה חבטת עונשין לניקוד שלך. הכדור שלך יונח מחוץ לשלולית. +Comment[hi]=

डबरी (पानी)

डबरी(नीली) में पà¥à¤°à¤¹à¤¾à¤° करने पर आपके अंक में पेनालà¥à¤Ÿà¥€ सà¥à¤Ÿà¥à¤°à¥‹à¤• जोड़ता है तथा आपकी गेंद डबरी के बाहर रख दी जाती है. +Comment[hu]=

Pocsolyák

Ha a golyó pocsolyába esik (a víz kék színű), akkor pontlevonást kap, és a golyót a pocsolya mellé helyezik. +Comment[is]=

Pollar (vatn)

Ef þú hittir í pollinn (blár) þá færð þú refsistig, og boltinn verðu settur við hlið pollsins. +Comment[it]=

Laghetti (acqua)

Se mandi la pallina in un laghetto, ti viene aggiunto un colpo di penalità al punteggio e la tua pallina viene collocata fuori dal laghetto. +Comment[ja]=

æ±  (æ°´)

é’ã„æ± ã«å…¥ã‚‹ã¨ã€1ショットを罰ã—ã¦ã€ æ± ã®è¿‘ãã«ãƒœãƒ¼ãƒ«ãŒç½®ã‹ã‚Œã¾ã™ã€‚ +Comment[mk]=

Бари (Вода)

Удирањето во барата (Ñино) додава казнен удар на вашите поени, а вашата топка е поÑтавена надвор од барата. +Comment[nb]=

Pytter (Vann)

Hvis du treffer en pytt (blå) gir deg et straffeslag, og ballen din plasseres utenfor pytten. +Comment[nl]=

Poelen (water)

Als u de bal in een poel (blauw gebied) slaat, dan krijgt u er een strafpunt bij. Uw bal wordt buiten de poel op het speelveld geplaatst. +Comment[nn]=

Pyttar (vatn)

Dersom du treff ein pytt (blå), får du eit straffeslag og ballen vert plassert utanfor pytten. +Comment[pl]=

Oczka wodne

Trafienie w oczko wodne (niebieski kolor) dolicza karne uderzenie do twojego wyniku i powoduje umieszczenie piłki na brzegu oczka. +Comment[pt]=

Poças (Ãgua)

Se acertar numa poça (azul) adiciona uma tacada de penalização à sua pontuação, e a sua bola é posta fora da poça. +Comment[pt_BR]=

Poças (Ãgua)

Acertar a bola numa poça (azul) adiciona uma tacada de penalidade à sua pontuação, e sua bola é colocada fora da poça. +Comment[ru]=

Лужи (вода)

ЕÑли мÑч попадает в лужу (Ñинюю), вам начиÑлÑетÑÑ ÑˆÑ‚Ñ€Ð°Ñ„Ð½Ð¾Ð¹ удар, а мÑч помещаетÑÑ Ð² Ñтороне от лужи. +Comment[sk]=

Puddles (Voda)

Ak zasiahnete puddle (modrá), pripoÄíta sa vám penalizaÄný úder a vaÅ¡a loptiÄka sa objaví vedľa puddle. +Comment[sl]=

Ribniki (Voda)

ÄŒe zadenete ribnik (modro), dobite kazenski udarec k toÄkam, žogica pa je postavljena zunaj ribnika. +Comment[sr]=

Баре (вода)

Убацивањем лоптице у бару добијате казнени поен у резултату, а ваша лоптица Ñе поÑтавља изван баре. +Comment[sr@Latn]=

Bare (voda)

Ubacivanjem loptice u baru dobijate kazneni poen u rezultatu, a vaša loptica se postavlja izvan bare. +Comment[sv]=

Vattenhinder

Att slå i ett vattenhinder (blå) lägger till ett extra slag till poängen, och bollen placeras utanför hindret. +Comment[ta]=

சேறà¯à®±à¯à®®à®Ÿà¯(தணà¯à®£à¯€à®°à¯)

சேறà¯à®±à¯à®®à®Ÿà¯à®µà®¿à®²à¯ உதைபà¯à®ªà®¤à®¾à®²à¯ (நீலமà¯) உஙà¯à®•à®³à¯ மதிபà¯à®ªà¯†à®£à¯à®£à®¿à®²à¯ அபராத கà¯à®±à®¿ சேரà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯. உஙà¯à®•à®³à¯ பநà¯à®¤à¯ சேறà¯à®±à¯à®®à®Ÿà¯à®µà®¿à®©à¯ வெளிபà¯à®ªà¯à®±à®¤à¯à®¤à®¿à®²à¯ வைகà¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯. +Comment[tg]=

Кӯлмак (Об)

Ðгар тӯбча ба кӯлак афтад (кабуд) шумо Ñоҳиби задани ҷаримавӣ мегардед ва тӯбча дар канори кӯлмак ҷойгир карда мешавад. +Comment[tr]=

Birikinti (Su)

Bir birikintiye (mavi) çarpmak skorunuza bir penaltı puanı eklenmesine yol açar, ve topunuz birikinti dışına çıkartılır. +Comment[uk]=

Калюжі (Вода)

Якщо м'Ñчик попадає в калюжу (ÑинÑ), вам нараховуєтьÑÑ ÑˆÑ‚Ñ€Ð°Ñ„Ð½Ð¸Ð¹ удар Ñ– ваш м'Ñчик поміщаєтьÑÑ Ð¿Ð¾Ð·Ð° калюжею. +Comment[xh]=

Ichityana (Amanzi)

Bethela kwi chityana (bhlowu) yongeza umvumbo wesohlwayo kwinqaku lakho, nebhola yakho ibekwe ngaphandle kwechityana. +Comment[xx]=xx

Puddles (Water)

Hitting into a puddle (blue) adds a penalty stroke to your score, and your ball is placed outside the puddle.xx +Comment[zh_CN]=

æ°´å‘(æ°´)

打到水å‘里(è“色)会在您的æˆç»©ä¸­è®°ä¸€æ¬¡æƒ©ç½šï¼Œç„¶åŽæ‚¨çš„çƒä¼šè¢«æ”¾åœ¨æ°´å‘的外边。 +Comment[zh_TW]=

æ°´å‘(水)

å°‡çƒæ“Šå…¥æ°´å‘(è—色)會在您的分數中增加懲罰性的桿數,而您的çƒæœƒè¢«æ”¾åœ¨æ°´å‘外é¢ã€‚ +Comment[zu]=

Amachibi (amanzi)

Ukushayela echibini (liluhlaza) kufaka isigwebo emaphuzwini akho, ebese ibhola libekwa ngaphandle kwechibi. +botWallVisible=true +height=145 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=394 + +[6-wall@0,0|38] +endPoint=264,354 +startPoint=167,390 + +[7-ball@48,355] +dummykey=true + +[7-cup@104,54|5] +dummykey=true + +[7-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[7-sign@130,2|4] +Comment=

Windmills

Windmills (brown base with moving arm) have brown walls (or half walls). The windmill arm's speed may vary by hole. +Comment[bg]=

Ð’Ñтърни мелници

Ð’Ñтърните мелници имат кафÑви Ñтени Ñ Ð¿Ð¾Ð´Ð²Ð¸Ð¶Ð½Ð¸ чаÑти, между които Ñ‚Ñ€Ñбва да мине топката. СкороÑтта им на движение може да е различна за различните дупки. +Comment[bn]=

বায়à§à¦•à¦²

বায়à§à¦•à¦²à§‡à¦° ( বাদামী ভিতà§à¦¤à¦¿à¦° ওপর চলনà§à¦¤ পাখা ) দেয়ালের ( বা অরà§à¦§à§‡à¦• দেয়ালের ) রং বাদামী। গরà§à¦¤ ভেদে বায়à§à¦•à¦²à§‡à¦° পাখার গতি ভিনà§à¦¨ হতে পারে। +Comment[bs]=

VjetrenjaÄe

VjetrenjaÄa (smeÄ‘i stub sa pokretnom rukom) ima smeÄ‘e zidove (ili polu-zidove). Ruka vjetrenjaÄe može biti razliÄita za svaku rupu. +Comment[ca]=

Molins de vent

Els molins de vent (base marró amb braç mòbil) tenen parets marrons (o mitja paret). La velocitat de rotació del braç no és constant. +Comment[da]=

Vindmøller

Vindmøller (brun base med bevægende vinge) har brune vægge (eller halve vægge). Vindmøllevingens fart kan variere fra bane til bane. +Comment[de]=

Windmühlen

Windmühlen (braune Gebäude mit sich bewegenden "Armen") haben braune Wände (oder Halbwände). Die Geschwindigkeit der "Arme" kann sich von Loch zu Loch ändern. +Comment[es]=

Molinos de viento

Los molinos de viento (base marrón con brazo móvil) tienen paredes marrones (o medias paredes). La velocidad del brazo del molino puede variar completamente. +Comment[et]=

Tuuleveskid

Tuuleveskid (pruun korpus, keerlevad tiivikud) on pruunide seinte (või poolseintega). Tiiviku liikumise kiirus võib olla rajati erinev. +Comment[fi]=

Tuulimyllyt

Tuulimyllyissä (ruskea jalusta, pyörivät siivet) on ruskeat seinät (tai puoliseinät). Tuulimyllyn siiven vauhti voi vaihdella reikien välillä. +Comment[fr]=

Moulins à vent

Les moulins à vent (base marron avec des pales qui remuent) ont des murs marrons (ou des demi-murs). La vitesse des bras des moulins peut varier à chaque tour. +Comment[gl]=

Muíños de vento

Os muiños de vento (marróns con aspas en movemento) teñen valados marróns (ou medios valados). A velocidade das aspas pode variar dependendo do burato. +Comment[he]=

טחנות רוח

לטחנות רוח (בסיס ×—×•× ×¢× ×–×¨×•×¢ × ×¢×”) יש קירות (×ו חצ××™ קירות) בצבע חו×. המהירות של זרוע הטחנה עשויה להיות שונה מגומה לגומה. +Comment[hi]=

पवनचकà¥à¤•à¤¿à¤¯à¤¾à¤

पवनचकà¥à¤•à¤¿à¤¯à¤¾à¤ (भूरे आधार यà¥à¤•à¥à¤¤ घूमती भà¥à¤œà¤¾ लिठहà¥à¤) भूरे रंग की दीवार (या आधी दीवार) यà¥à¤•à¥à¤¤ हैं. होल के अनà¥à¤¸à¤¾à¤° पवनचकà¥à¤•à¤¿à¤¯à¥‹à¤‚ के भà¥à¤œà¤¾à¤“ं की गतियों में अनà¥à¤¤à¤° होगा. +Comment[hu]=

Szélmalmok

A szélmalmok (barna alap, mozgó karok) falai barna színűek (vagy felezve vannak). A szélmalmok lapátjainak sebessége lyukanként változhat. +Comment[is]=

Vindmyllur

Vindmyllur (Brúnnn flötur með breyfiarmi) hafa brúna veggi (eða hálf-veggi). Hraði arma vindmyllunnar getur verið breytilegur. +Comment[it]=

Mulini a vento

I mulini a vento (una base marrone con delle pale mobili) hanno murature marroni (o mezzi muri). La velocità dellepale del mulino può cambiare da una buca all'altra. +Comment[ja]=

風車

風車(茶色ã§ã€å‹•ã„ã¦ã„るアーム) ã¯ã€ 茶色ã®å£ãŒãƒœãƒ¼ãƒ€ãƒ¼ã«ã‚ã‚Šã¾ã™ã€‚ホールã«ã‚ˆã£ã¦ã‚¢ãƒ¼ãƒ ã¯é•ã†é€Ÿåº¦ã§ å‹•ãã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 +Comment[mk]=

Ветерници

Ветерниците (кафеава подлога Ñо подвижна преграда) имаат кафеави ѕидови (или половина ѕидови). Брзината на преградата на ветерницата завиÑи од дупка до дупка. +Comment[nb]=

Vindmøller

Vindmøller (brun base med bevegelig arm) har brune vegger (eller halv-vegger). Vindmøllas fart kan variere fra hull til hull. +Comment[nl]=

Windmolens

Windmolens (bruine basis met bewegende wieken) hebben bruine muren (of halve muren). De bewegingssnelheid van de wieken varieert van hole tot hole. +Comment[nn]=

Vindmøller

Vindmøller (brun, med arm som rører seg) har brune veggar (eller halvveggar). Farten på armen kan vera ulik frå hol til hol. +Comment[pl]=

Wiatraki

Wiatraki (brązowa podstawa z ruchomym ramieniem) mają brązowe ściany (lub pół-ściany). Szybkość ramienia wiatraka może być różna dla różnych dołków. +Comment[pt]=

Moinhos

Os moinhos (base castanha com um braço móvel) têm paredes (ou meias-paredes) castanhas. A velocidade do moinho pode variar de acordo com o buraco. +Comment[pt_BR]=

Moinhos

Os moinhos (base marrom com braço móvel) possuem paredes marrons (ou meias-paredes). A velocidade do braço do moinho pode variar de um buraco a outro. +Comment[ru]=

Мельницы

Мельницы (коричневое оÑнование и движущиеÑÑ Ð»Ð¾Ð¿Ð°Ñти) имеют коричневые Ñтены (или ворота). СкороÑÑ‚ÑŒ лопаÑтей может менÑÑ‚ÑŒÑÑ Ð¾Ñ‚ лунки к лунке. +Comment[sk]=

Veterné mlyny

Veterné mlyny (hnedá základňa s pohybujúcim sa ramenom) má hnedé steny (alebo polo-steny). Rýchlosť ramena môže byť rôzna. +Comment[sl]=

Mlini na veter

Mlini na veter (rjava osnova z gibajoÄo roko) imajo rjave zidove (ali polzidove). Hitrost roke mlina na veter se lahko spreminja. +Comment[sr]=

Ветрењаче

Ветрењаче (Ñмеђа база Ñа покретним краком) имају Ñмеђе зидове (или полузидове). Брзина крака ветрењаче може варирати од рупе до рупе. +Comment[sr@Latn]=

VetrenjaÄe

VetrenjaÄe (smeÄ‘a baza sa pokretnim krakom) imaju smeÄ‘e zidove (ili poluzidove). Brzina kraka vetrenjaÄe može varirati od rupe do rupe. +Comment[sv]=

Väderkvarnar

Väderkvarnar (brun bas med vinge som flyttas) har bruna väggar (eller halva väggar). Hastigheten på väderkvarnens vinge kan variera för olika hål. +Comment[ta]=

வினà¯à®Ÿà¯à®®à®¿à®²à¯à®¸à¯

வினà¯à®Ÿà¯à®®à®¿à®²à¯à®¸à¯(பிரவà¯à®©à¯ அடிதà¯à®¤à®³à®¤à¯à®¤à¯‹à®Ÿà¯ சà¯à®±à¯à®±à¯à®®à¯ கரஙà¯à®•à®³à¯)பிரவà¯à®©à¯ சà¯à®µà®°à¯à®•à®³à¯ உளà¯à®³à®©(அலà¯à®²à®¤à¯ பாதி சà¯à®µà®°à¯à®•à®³à¯) இநà¯à®¤ வினà¯à®Ÿà¯à®®à®¿à®²à¯à®¸à¯ கரஙà¯à®•à®³à®¿à®©à¯ வேகம௠ஒர௠ஓடà¯à®Ÿà¯ˆà®¯à®³à®µà¯ வேறà¯à®ªà®Ÿà¯à®®à¯. +Comment[tg]=

ОÑиёҳо

ОÑиёҳо (аÑоÑи ҷигарранг ва даÑтаҳои ҳаракаткунанда) деворҳои ҷигарранг доранд (Ñ‘ ниÑфи деворҳо). Ðз Ñӯрох то ба Ñӯрох Ñуръати даÑтаҳо тағир ёфта метавонанд. +Comment[tr]=

Yel DeÄŸirmenleri

Yel değirmenleri (hareketli kolu olan kahverengi cisimler) kahverengi duvarlara (veya yarım duvarlara) sahiptir. Yel değirmeninin kolu topun hızını artırır. +Comment[uk]=

Млини

Млини (коричневі оÑнови з вітрÑками) мають коричневі Ñтіни (або напів-Ñтіни). ШвидкіÑÑ‚ÑŒ вітрÑка млина може бути різною Ð´Ð»Ñ Ñ€Ñ–Ð·Ð½Ð¸Ñ… лунок. +Comment[xh]=

Amaphiko omoya

Amaphiko omoya (Isiseko esimdaka nebala kunye nengalo ehambayo) Yiba namadonga amdaka ngebala (Okanye isiqingatha samadonga). amendu engalo yephikomoya angahluka ngomngxunya. +Comment[xx]=xx

Windmills

Windmills (brown base with moving arm) have brown walls (or half walls). The windmill arm's speed may vary by hole.xx +Comment[zh_CN]=

风车

风车(棕色的底座和è¿åŠ¨çš„转臂)有棕色的墙(或åŠå¢™)。ä¸åŒçƒæ´žé£Žè½¦è½¬è‡‚的速度å¯ä»¥ä¸åŒã€‚ +Comment[zh_TW]=

風車

風車(è¤è‰²åŸºç¤Žä¸¦æœ‰ç§»å‹•çš„風扇)有è¤è‰²çš„牆(或一åŠçš„牆)。風車的風扇速度å¯èƒ½éš¨è‘—洞而改變。 +Comment[zu]=

Iphiko lomoya

Iphiko lomoya (Isiqu esinsundu sinengalo enyakazayo) inezindonga ezinsundu (noma izindonga ezinguhhafu). Ijubane lengalo yephiko lomoya lingashiyana ngezimbobo. +botWallVisible=true +height=206 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=267 + +[7-windmill@15,145|3] +botWallVisible=false +bottom=true +height=71 +leftWallVisible=true +rightWallVisible=true +speed=5 +topWallVisible=false +width=107 + +[8-ball@39,234] +dummykey=true + +[8-blackhole@60,343|4] +exit=195,287 +exitDeg=0 +maxspeed=3 +minspeed=1 + +[8-cup@333,287|5] +dummykey=true + +[8-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[8-sign@5,5|3] +Comment=

Black Holes

Black Holes transport the ball to their exit, and eject it at a speed directly relational to the speed your ball was going. Choose Hole->Show Info to see which Black Hole goes to which exit and the direction the ball will come out at. +Comment[af]=

Swart Gate

Swart Gate vervoerder die bal na hulle beïendig, en uitskiet dit na 'n spoed direk verwante na die spoed jou bal was gaan. Kies Gat->Show Inligting na sien wat Swart Gat gaan na wat beïendig en die rigting die bal sal kom uit na. +Comment[bg]=

Черни дупки

Черните дупки пренаÑÑÑ‚ топката до даден изхода и Ñ Ð¸Ð·Ñ…Ð²ÑŠÑ€Ð»ÑÑ‚ ÑÑŠÑ ÑкороÑÑ‚, пропорционална на ÑкороÑтта на движение на топката. Изберете от менюто Дупка/ИнформациÑ, за да видите ÐºÐ¾Ñ Ñ‡ÐµÑ€Ð½Ð° дупка до кой изход води и поÑоката, от коÑто ще Ñе поÑви топката. +Comment[bn]=

কালো গরà§à¦¤

কালো গরà§à¦¤ দিয়ে বল তার বহিরà§à¦—মনের দিকে চলে যায়; কী গতিতে বহিরà§à¦—মন দিয়ে বল বের হবে তা বলের গতির সাথে সরাসরি সমà§à¦ªà¦°à§à¦•à¦¿à¦¤à¥¤ কোন কালো গরà§à¦¤ কোন বহিরà§à¦—মনের সাথে যà§à¦•à§à¦¤ à¦à¦¬à¦‚ কোন দিক দিয়ে বল বের হয়ে আসবে তা জানার জনà§à¦¯ গরà§à¦¤-তথà§à¦¯ পà§à¦°à¦¦à¦°à§à¦¶à¦¨ চাপà§à¦¨à¥¤ +Comment[bs]=

Crne rupe

Crne rupe transportuju lopticu do svog izlaza, te je izbacuju brzinom proporcionalnom brzini kojom je loptica ranije iÅ¡la. Izaberite Rupa->Pokaži informacije da vidite koja Crna rupa odgovara kojem izlazu kao i smjer kojim će loptica biti izbaÄena. +Comment[ca]=

Forats negres

Els forats negres transporten la pilota cap a la sortida i la disparen a una velocitat directament proporcional a la que ja tenien. Seleccioneu Forat->Mostra informació per a veure quin forat negre porta a cada sortida i al direcció en la sortirà la bola. +Comment[da]=

Sorte huller

Sorte huller transporterer bolden til deres udgang og affyrer bolden med en fart i direkte relation til boldens oprindelige fart. VælgHul -> Vis info for at se hvilket sort hul der fører til hvilken udgang og den retning bolden vil komme ud i. +Comment[de]=

Schwarze Löcher

Schwarze Löcher teleportieren den Ball zu ihrem Ausgang, und werfen den Ball mit einer Geschwindigkeit, die zu seiner ursprünglichen Geschwindigkeit direkt im Verhältnis steht, wieder auf das Spielfeld. Wählen Sie Loch->Info anzeigen um zu sehen, welches Schwarze Loch zu welchem Ausgang gehört, und in welcher Richtung der Ball herauskommt. +Comment[es]=

Agujeros negros

Los agujeros negros transportan la pelota a la salida y la disparan a una velocidad directamente proporcional a la que llevaban. Seleccione Hoyo->Mostrar información para ver qué agujero negro lleva a cada salida y la dirección en la que saldrá la bola. +Comment[et]=

Mustad augud

Mustad augud toimetavad palli väljumisavani ning paiskavad selle sealt välja sama kiirusega, millega pall musta auku löödi. Vali Rada->Näita infot, mis näitab, kus asub musta augu väljumisava ja millises suunas pall sealt väljub. +Comment[fi]=

Mustat aukot

Mustat aukot kuljettavat pallon ulostuloonsa, ja ampuvat pallon nopeudella, joka on verranollinen pallon entiseen nopeuteen. Valitse Reikä->Näytä tiedot nähdäksesi mikä musta aukko on yhteydessä mihinkin ulostuloon ja mihin suuntaan pallo lähtee. +Comment[fr]=

Trous noirs

Les trous noirs transportent la balle vers leur sortie et l'éjectent à une vitesse directement en relation avec la vitesse à laquelle votre balle est entrée. Choisissez Trou / Afficher les informations pour voir quel trou noir va à quelle sortie et la direction dans laquelle la balle sortira. +Comment[gl]=

Buracos negros

Os buracos negros levan a bola ata a súa saída, e expúlsana a unha velocidade directamente proporcional á que tiña a bola. Elixe Burato->Mostrar Información para ver cara onde vai saír un burato negro e a dirección na que sairá a bola. +Comment[he]=

×—×•×¨×™× ×©×—×•×¨×™×

×—×•×¨×™× ×©×—×•×¨×™× ×ž×¢×‘×™×¨×™× ×ת הכדור ליצי××” שלה×, ×•×¤×•×œ×˜×™× ××•×ª× ×‘×ž×”×™×¨×•×ª העומדת ביחס ישר למהירות שבה הכדור שלך × ×¢ ×›×שר ×”×•× × ×›× ×¡ לחור השחור. בחר בגומה->הצג מידע כדי לר×ות ל×יזו יצי××” מוביל כל חור שחור וב××™×–×” כיוון ×™×™×¦× ×”×›×“×•×¨. +Comment[hi]=

बà¥à¤²à¥‡à¤• होलà¥à¤¸

बà¥à¤²à¥‡à¤• होलà¥à¤¸ गेंद को उनके निरà¥à¤—म पर ले जाती हैं, तथा आपकी गेंद की गति के सीधे अनà¥à¤ªà¤¾à¤¤ की गति से निकाल बाहर फेंकती है. चà¥à¤¨à¥‡à¤‚ होल->जानकारी दिखाà¤à¤ यह देखने के लिठकि कौन सा बà¥à¤²à¥‡à¤• होल किस निरà¥à¤—म पर जाता है तथा गेंद किस दिशा से बाहर आà¤à¤—ा. +Comment[hu]=

Fekete lyukak

A fekete lyukba esett golyó a lyuk kijáratához kerül, a kilépési sebesség arányos lesz a belépésivel. Válassza a Lyuk->Információ menüpontot, ha tudni szeretné, melyik fekete lyuk hová vezet és milyen a kilépési irány. +Comment[is]=

Svarthol

Svartar holur flytja boltann um ormagöng að úttaki, og spýta boltanum út í réttu hlutfalli við hraðann sem þú sendir á inn í svartholið. Veldu Hola->Sýna upplýsingar til að sjá ormagöngin og þá hvar boltinn mun koma út. +Comment[it]=

Buchi neri

I buchi neri trasportano la pallina alla loro uscita e la espellono con una velocità proporzionale a quella con cui è entrata. Scegli Buca->Mostra informazioni per vedere la corrispondenza tra buchi neri e uscite e la direzione con cui vengono espulse le palline. +Comment[ja]=

ブラックホール

ブラックホールã¯ãƒœãƒ¼ãƒ«ã‚’ å…¥ã£ãŸæ™‚ã®é€Ÿåº¦ã¨åŒã˜é€Ÿåº¦ã§å‡ºå£ã«é‹ã‚“ã§å‡ºã—ã¾ã™ã€‚ ホール->情報を表示 ã¨ã™ã‚‹ã¨ã€ãƒ–ラックホールã¨å‡ºå£ã®é€£ä¿‚㨠出å£ã®æ–¹å‘ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ +Comment[mk]=

Црни дупки

Црните дупки ги пренеÑуваат топките до нивниот излез и ги иÑфрлаат Ñо брзина која е директно во релација Ñо брзината Ñо која вашата топка предходно Ñе движела. Изберете Дупка->Прикажи информација за да видите која црна дупка оди Ñо кој излез и наÑоката во која топката ќе излезе. +Comment[nb]=

Svarte hull

Svarte hull sender ballen til utgangen, og kaster den ut i samme fart som den kom inn. Velg Hull->Vis info for å se hvilket svart hull som går til hvilken utgang og retningen ballen vil komme ut av hullet. +Comment[nl]=

Zwarte gaten

Zwarte gaten teleporteren de bal naar hun uitgang, en werpen de bal met een snelheid die overeenkomt met de oorspronkelijke snelheid terug op het speelveld. Kies Hole->Info tonen om te zien welk zwart gat naar welke uitgang leidt, en in welke richting de bal de uitgang zal verlaten. +Comment[nn]=

Svarte hol

Svarte hol flyttar ballen til utgangen, og kastar han ut med ein fart som svarar til farten han hadde. Vel Hol->Vis info for å sjå kva for svarte hol som går til kva for utgang, og kva for retning ballen vil få. +Comment[pl]=

Czarne dziury

Czarne dziury przenoszą piłkę do swojego wyjścia i wyrzucają ją z prędkością proporcjonalną do prędkości z jaką leciała. Wybierz Dołek->Pokaż informacje, aby zobaczyć które wyjście odpowiada której Czarnej Dziurze i w jakim kierunku piłka zostanie wyrzucona. +Comment[pt]=

Buracos Negros

Os buracos negros transportam a bola para a sua saída e expulsam-na a uma velocidade proporcional à velocidade com que a sua bola estava a ir. Escolha o Buraco->Mostrar a Informação para ver para onde vai um determinado buraco negro e a direcção com que a bola irá sair. +Comment[pt_BR]=

Buracos negros

Os buracos negros transportam a bola para sua saída, e ejetam-na com uma velocidade diretamente relacionada com a velocidade em que sua bola estava entrou. Escolha Buraco-> Mostrar Informação para ver qual Buraco Negro dá em qual saída e qual a direção da bola ao sair dele. +Comment[ru]=

Чёрные дыры

Чёрные дыры ведут мÑч к выходу и выбраÑывают его Ñо ÑкороÑтью, прÑмо завиÑÑщей от его ÑобÑтвенной ÑкороÑти. См. Лунка->СведениÑ, чтобы узнать, к какому выходу ведёт Ñ‡Ñ‘Ñ€Ð½Ð°Ñ Ð´Ñ‹Ñ€Ð° и в каком направлении вылетит мÑч. +Comment[sk]=

ÄŒierne diery

ÄŒierne diery presúvajú loptiÄku na svoj druhý koniec a tam ju vystrelia rovnakou rýchlosÅ¥ou, ktorou do diery vletela. Výberom Jamka-ZobraziÅ¥ informácie môžete zistiÅ¥, kde je druhý koniec a ktorým smerom loptiÄka vyletí. +Comment[sl]=

ÄŒrne luknje

ÄŒrne luknje prestavijo žogico do svojega izhoda in jo izvržejo s hitrostjo, ki je odvisna od prvotne hitrosti žogice. Izberite Luknja -> Pokaži informacije, da vidite, katera Ärna luknja vodi do katerega izhoda in smer izvržene žogice. +Comment[sr]=

Црне рупе

Црне рупе транÑпортују лоптицу до Ñвог излаза и избацују је брзином директно пропорционалном брзини којом је лоптица претходно ишла. Изаберите Рупа->Прикажи информације да биÑте видели која црна рупа има који излаз и Ñмер који ће лоптица имати када изађе. +Comment[sr@Latn]=

Crne rupe

Crne rupe transportuju lopticu do svog izlaza i izbacuju je brzinom direktno proporcionalnom brzini kojom je loptica prethodno išla. Izaberite Rupa->Prikaži informacije da biste videli koja crna rupa ima koji izlaz i smer koji će loptica imati kada izađe. +Comment[sv]=

Svarta hål

Svarta hål transporterar bollen till sina utgångar, och skickar ut den med en hastighet som är direkt proportionell mot hastigheten som bollen hade. Välj Hål->Visa information för att se vilket svart hål som går till vilken utgång, och i vilken riktning bollen kommer ut. +Comment[ta]=

கரà¯à®ªà¯à®ªà¯ ஓடà¯à®Ÿà¯ˆà®•à®³à¯

கரà¯à®ªà¯à®ªà¯ ஓடà¯à®Ÿà¯ˆà®•à®³à¯ பநà¯à®¤à¯ˆ வெளியேறà¯à®±à®¤à¯à®¤à®¿à®²à¯ நிறà¯à®¤à¯à®¤à®¿ பநà¯à®¤à¯ எநà¯à®¤ வேகதà¯à®¤à®¿à®²à¯ வரà¯à®•à®¿à®±à®¤à¯‹ அதே வேகதà¯à®¤à®¿à®²à¯ வெளியேறà¯à®±à¯à®®à¯. தேரà¯à®µà¯ ஓடà¯à®Ÿà¯ˆ-> எநà¯à®¤ கரà¯à®ªà¯à®ªà¯ ஓடà¯à®Ÿà¯ˆ எநà¯à®¤ வழியாக வெளியேறà¯à®•à®¿à®±à®¤à¯ எனà¯à®ªà®¤à¯ˆà®¯à¯à®®à¯ பநà¯à®¤à¯ வெளியேறà¯à®®à¯ திசையையà¯à®®à¯ பாரà¯à®ªà¯à®ªà®¤à®±à¯à®•à¯ தகவலை காடà¯à®Ÿà®µà¯à®®à¯. +Comment[tr]=

Karadelikler

Kara delikler topu çıkışlarına taşırlar ve topuzun hızı ile orantılı bir hız ile dışarı atarlar. Delik->Bilgi Göster seçerek hangi karadeliğin hangi çıkışa sahip olduğunu ve topun çıkış yönünü öğrenebilirsiniz. +Comment[uk]=

Чорні діри

Чорні діри переноÑÑÑ‚ÑŒ м'Ñчик до Ñ—Ñ… виходу Ñ– викидають його із швидкіÑÑ‚ÑŒ прÑмо залежною від його влаÑної. Виберіть Лунка->Показати інформацію, щоб подивитиÑÑŒ Ñка чорна діра веде до Ñкого виходу Ñ– в Ñкому напрÑмку вилетить м'Ñчик. +Comment[xh]=

Imingxuma Emnyama

Imingxuma Emnyama ihambisa ibhola iyokutsho kwindawo yazo yokuphuma, ize iyikhuphe ngqo ngesantya esihlobene kwisantya ibihamba ngaso ibhola yakho. Khetha Umngxuma->Bonisa Ulwazi ze bone ukuba ngowuphi Umngxuma Omnyama ohamba ayokutsho kweliphi icala lendlela nakweyiphi indawo yokuphuma eza kuza kulo ibhola yakho. +Comment[xx]=xx

Black Holes

Black Holes transport the ball to their exit, and eject it at a speed directly relational to the speed your ball was going. Choose Hole->Show Info to see which Black Hole goes to which exit and the direction the ball will come out at.xx +Comment[zh_CN]=

黑洞

黑洞将çƒè¿é€åˆ°å‡ºå£å¤„,并根æ®æ‚¨çš„çƒé€ŸæŒ‰æˆæ¯”例的速度将其弹出。选择çƒæ´ž->显示信æ¯å¯æŸ¥çœ‹å“ªä¸ªé»‘洞对应哪个出å£ï¼Œä»¥åŠçƒä»Žå‡ºå£å¼¹å‡ºçš„æ–¹å‘。 +Comment[zh_TW]=

黑洞

黑洞會將çƒå‚³é€åˆ°å®ƒå€‘的出å£ï¼Œè€Œå°‡å®ƒå€‘射出的速度與您的çƒé€²å…¥çš„速度有直接關係。é¸æ“‡æ´ž->顯示資訊來看看從哪個黑洞進入會從從哪裡出來以åŠçƒå‡ºç¾çš„æ–¹å‘。 +Comment[zu]=

Izimbobo ezimnyama

Izimbobo ezimnyama zithatha ibhola ziliyise endaweni yazo yokuphuma, zililahle ngejubane elihambisana nejubane lebhola lakho. KhethaImbobo->Khombisa ulwaziukubona ukuthi yiphi imobo emnyama iya kuyiphi indawo yokuphuma nendlela ibhola elizophuma kuyo. +botWallVisible=true +height=206 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=390 + +[8-wall@0,0|6] +endPoint=145,132 +startPoint=147,317 + +[8-wall@0,0|7] +endPoint=147,317 +startPoint=69,390 + +[9-ball@57,307] +dummykey=true + +[9-cup@360,255|6] +dummykey=true + +[9-floater@236,298|4] +botWallVisible=false +endPoint=236,298 +height=40 +leftWallVisible=false +rightWallVisible=false +speed=3 +startPoint=105,248 +topWallVisible=false +width=80 + +[9-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=2 + +[9-sign@5,6|3] +Comment=

Floaters

Floaters are moving platforms that carry a ball that lands on it. Floaters' speeds vary. +Comment[bg]=

Хвърчащи килимчета

Хвърчащите килимчета Ñа подвижни платформи, които ноÑÑÑ‚ падналите върху Ñ‚ÑÑ… топки. СкороÑтта им може да варира. +Comment[bn]=

ফà§à¦²à§‹à¦Ÿà¦¾à¦°

ফà§à¦²à§‹à¦Ÿà¦¾à¦° হল à¦à¦•à¦Ÿà¦¿ ভাসমান পà§à¦²à¦¾à¦Ÿà¦«à¦°à§à¦® যার ওপর বল পড়লে সে তা নিয়ে ভেসে বেড়ায়। বিভিনà§à¦¨ ফà§à¦²à§‹à¦Ÿà¦¾à¦°à§‡à¦° গতি বিভিনà§à¦¨ রকম। +Comment[bs]=

PlutaÄi

PlutaÄi su pokretne platforme koje prenose lopticu koja padne na njih. Brzina plutaÄa je promjenljiva. +Comment[ca]=

Flotadors

Els flotadors són plataformes mòbils que transporten a una pilota que aterra a sobre seu. La velocitat dels flotadors no és constant. +Comment[da]=

Svævere

Svævere er bevægende platforme som flytter en bold der lander på dem. Svæveres fart varierer. +Comment[de]=

Floß

Flöße sind bewegliche Plattformen, die einen Ball weitertragen, wenn er auf ihnen landet. Es gibt Flöße mit unterschiedlichen Geschwindigkeiten. +Comment[es]=

Flotadores

Los flotadores son plataformas móviles que transportan una pelota que aterrice en ellas. La velocidad de los flotadores es variable. +Comment[et]=

Hõljukid

Hõljukid on liikuvad platvormid, mis suudavad neil peatunud palli liigutada. Hõljukite kiirused erinevad. +Comment[fi]=

Kellujat

Kellujat ovat tasoja, jotka siirtävät palloa joka on tasolla. Tasojen vauhti vaihtelee. +Comment[fr]=

Flotteurs

Les flotteurs sont des plates-formes qui portent une balle qui atterrit dessus. La vitesse des flotteurs varie. +Comment[gl]=

Flotadores

Os Flotadores son plataformas móbiles que transportan á pelota que aterrou nelas. A velocidade dos flotadores é variable. +Comment[he]=

×ž×©×˜×—×™× ×¦×¤×™×

×לה ×ž×©×˜×—×™× × ×¢×™× ×©× ×•×©××™× ××™×ª× ×›×“×•×¨ שנוחת עליה×. ×ž×”×™×¨×•×ª× ×¢×©×•×™×” להשתנות ממשטח למשטח. +Comment[hi]=

तैराक

तैराक चलते हà¥à¤ चबूतरे होते हैं जो अपने ऊपर अवतरित गेंद को लिठहà¥à¤ होते हैं. तैराक की गति में अनà¥à¤¤à¤° होते रहता है. +Comment[hu]=

Lebegők

Ezek a vízen úszó platformok elbírják a rájuk eső golyókat. Sebességük különböző lehet. +Comment[is]=

Prammar

Prammar geta flutt bolta sem lenda á honum. hraði er breytilegur. +Comment[it]=

Floater

I floaters sono piattaforme mobili che trasportano la pallina che atterra su di essi. La velocità dei floater è variabile. +Comment[ja]=

フローター

フローターã¯ãƒœãƒ¼ãƒ«ã‚’é‹ã¶ã€ å‹•ãプラットフォームã§ã™ã€‚ホールã«ã‚ˆã£ã¦é•ã†é€Ÿåº¦ã§å‹•ãã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 +Comment[lv]=

SlÄ«doÅ¡Äs platformas

SlÄ«doÅ¡Äs platformas pÄrvietojas un pÄrvieto bumbiņu, kas atordas uz tÄs. Platformu Ätrums variÄ“. +Comment[mk]=

Лебдачи

Лебдачите Ñе подвижни платформи кои ја ноÑат топката која ќе Ñлета на нив. Брзините на лебдачите Ñе различни. +Comment[nb]=

Fløterne

Fløterne er bevegelige plattformer som kan bære en ball som lander på den. Farten kan variere. +Comment[nl]=

Drijvers

Drijvers zijn bewegende platforms die een bal meenemen als deze er op landt. De snelheid van drijvers kan vari�en. +Comment[nn]=

Plattformar

Plattformar flyttar seg og tek med seg ballar som landar på dei. Farten til plattformane kan variera frå hol til hol. +Comment[pl]=

Promy

Promy to poruszające się platformy, które przewożą piłkę, która na nich wyląduje. Prędkość promów może być różna. +Comment[pt]=

Bóias

As bóias são plataformas móveis que carregam uma bola que pare em cima delas. As velocidades das bóias variam. +Comment[pt_BR]=

Bóias

As bóias são plataformas em movimento que têm uma bola sobre ela. A velocidade das bóias pode variar. +Comment[ru]=

Ползуны

Ползуны движутÑÑ Ð¸ увлекают мÑч, который приземлилÑÑ Ð½Ð° них. СкороÑÑ‚ÑŒ ползунов может быть разной. +Comment[sk]=

Floaters

Floaters sú presúvajúce sa plochy, ktoré odnášajú loptiÄky. Ich rýchlosÅ¥ sa mení. +Comment[sl]=

Plovci

Plovci so premikajoÄe se ploÅ¡Äadi, ki bodo nosili žogico, ki pristane na njej. Hitrost plovcev se lahko spreminja. +Comment[sr]=

Плутачи

Плутачи Ñу покретне платформе које ноÑе лоптицу која падне на њих. Брзина плутача може варирати. +Comment[sr@Latn]=

PlutaÄi

PlutaÄi su pokretne platforme koje nose lopticu koja padne na njih. Brzina plutaÄa može varirati. +Comment[sv]=

Flottar

Flottar är flyttbara plattformar som kan bära en boll som landar på dem. Flottarnas hastighet varierar. +Comment[ta]=

மிதபà¯à®ªà®µà®°à¯à®•à®³à¯

மிதபà¯à®ªà®µà®°à¯à®•à®³à¯ எலà¯à®²à¯‹à®°à¯à®®à¯ நகரà¯à®®à¯ தளமேடை. அத௠தன௠மேலிரà¯à®•à¯à®•à¯à®®à¯ பநà¯à®¤à¯à®•à®³à¯ˆ எடà¯à®¤à¯à®¤à¯à®šà¯ செலà¯à®²à¯à®®à¯. மிதபà¯à®ªà®µà®°à¯à®•à®³à®¿à®©à¯ வேகம௠மாறà¯à®ªà®Ÿà¯à®®à¯. +Comment[tg]=

Хазандаҳо

Хазандаҳо тӯберо, ки ба онҳо афтидааÑÑ‚, гирифта мебаранд. Суръати Хазандаҳо гуногун буда метавонад. +Comment[tr]=

Yüzücüler

Yüzücüler üstüne konan topları taşıyan platformlardır. Yüzücülerin değişik hızları vardır. +Comment[uk]=

Плавуни

Плавуни - це рухомі платформи, Ñкі неÑуть м'Ñчик, котрий на них попадає. ШвидкіÑÑ‚ÑŒ плавунів може бути різною. +Comment[xh]=

Izinto ezidadayo

Floaters are moving platforms that carry a ball that lands on it. Floaters' speeds vary. +Comment[xx]=xx

Floaters

Floaters are moving platforms that carry a ball that lands on it. Floaters' speeds vary.xx +Comment[zh_CN]=

æµ®å°

æµ®å°æ˜¯å¯ä»¥è½½ç€çƒç§»åŠ¨çš„å¹³å°ã€‚æµ®å°çš„速度是å¯å˜çš„。 +Comment[zh_TW]=

浮標

浮標是會移動的平å°ï¼Œå¯ä»¥æ‰¿è¼‰è½åœ¨å®ƒä¸Šé¢çš„çƒã€‚浮標的速度是多變的。 +Comment[zu]=

Okuntantayo

Okuntantayo amabala anyakazayo aphatha ibhola elihlala kuwo.Okuntantayo kunamajubane ashiyanayo. +botWallVisible=true +height=127 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=391 + +[9-wall@0,0|5] +endPoint=177,126 +startPoint=171,391 diff --git a/kolf/tutorial.kolfgame b/kolf/tutorial.kolfgame new file mode 100644 index 00000000..30736700 --- /dev/null +++ b/kolf/tutorial.kolfgame @@ -0,0 +1,10 @@ +[0 Saved Game] +Competition=false +Course=/opt/kde3/share/apps/kolf/tutorial.kolf +Current Hole=1 +Players=1 + +[1] +Color=#bbffd4 +Name=Player 1 +Scores=0 diff --git a/kolf/vector.cpp b/kolf/vector.cpp new file mode 100644 index 00000000..f4c05262 --- /dev/null +++ b/kolf/vector.cpp @@ -0,0 +1,106 @@ +#include + +#include "vector.h" + +// this and vector.h by Ryan Cummings + +// Creates a vector with between two points +Vector::Vector(const QPoint &source, const QPoint &dest) { + _magnitude = sqrt(pow(source.x() - dest.x(), 2) + pow(source.y() - dest.y(), 2)); + _direction = atan2(source.y() - dest.y(), source.x() - dest.x()); +} + +// Creates a vector with between two points +Vector::Vector(const Point &source, const Point &dest) { + _magnitude = sqrt(pow(source.x - dest.x, 2) + pow(source.y - dest.y, 2)); + _direction = atan2(source.y - dest.y, source.x - dest.x); +} + +// Creates an empty Vector +Vector::Vector() { + _magnitude = 0.0; + _direction = 0.0; +} + +// Copy another Vector object +Vector::Vector(const Vector& v) { + _magnitude = v._magnitude; + _direction = v._direction; +} + +// Set the X component +void Vector::setComponentX(double x) { + setComponents(x, componentY()); +} + +// Set the Y component +void Vector::setComponentY(double y) { + setComponents(componentX(), y); +} + +// Operations with another Vector performs vector math +Vector Vector::operator+(const Vector& v) { + double x = componentX() + v.componentX(); + double y = componentY() + v.componentY(); + + return Vector(sqrt((x * x) + (y * y)), atan2(y, x)); +} + +Vector Vector::operator-(const Vector& v) { + double x = componentX() - v.componentX(); + double y = componentY() - v.componentY(); + + return Vector(sqrt((x * x) + (y * y)), atan2(y, x)); +} + +Vector& Vector::operator+=(const Vector& v) { + setComponents(componentX() + v.componentX(), componentY() + v.componentY()); + return *this; +} + +Vector& Vector::operator-=(const Vector& v) { + setComponents(componentX() - v.componentX(), componentY() - v.componentY()); + return *this; +} + +double Vector::operator*(const Vector& v) { + return ((componentX() * v.componentX()) + (componentY() * v.componentY())); +} + +// Operations with a single double value affects the magnitude +Vector& Vector::operator+= (double m) { + _magnitude += m; + return *this; +} + +Vector& Vector::operator-= (double m) { + _magnitude -= m; + return *this; +} + +Vector& Vector::operator*= (double m) { + _magnitude *= m; + return *this; +} + +Vector& Vector::operator/= (double m) { + _magnitude /= m; + return *this; +} + +// Sets both components at once (the only way to do it efficiently) +void Vector::setComponents(double x, double y) { + _direction = atan2(y, x); + _magnitude = sqrt((x * x) + (y * y)); +} + +void debugPoint(const QString &text, const Point &p) +{ + kdDebug(12007) << text << " (" << p.x << ", " << p.y << ")" << endl; +} + +void debugVector(const QString &text, const Vector &p) +{ + // debug degrees + kdDebug(12007) << text << " (magnitude: " << p.magnitude() << ", direction: " << p.direction() << ", direction (deg): " << (360L / (2L * M_PI)) * p.direction() << ")" << endl; +} diff --git a/kolf/vector.h b/kolf/vector.h new file mode 100644 index 00000000..1da7328b --- /dev/null +++ b/kolf/vector.h @@ -0,0 +1,92 @@ +#ifndef KOLF_VECTOR_H +#define KOLF_VECTOR_H + +#include + +#include + +class Point +{ +public: + Point(double _x, double _y) + { + x = _x; + y = _y; + } + + Point() + { + x = 0; + y = 0; + } + + double x; + double y; +}; + +void debugPoint(const QString &, const Point &); + +// This and vector.cpp by Ryan Cummings + +// Implements a vector in 2D +class Vector { + public: + // Normal constructors + Vector(double magnitude, double direction) { _magnitude = magnitude; _direction = direction; } + Vector(const QPoint& source, const QPoint& dest); + Vector(const Point& source, const Point& dest); + Vector(); + + // Copy constructor + Vector(const Vector&); + + // Accessors, sorta + double componentX() const { return (_magnitude * cos(_direction)); }; + double componentY() const { return (_magnitude * sin(_direction)); }; + + // Sets individual components + // Wrappers around setComponents(double, double) - below + void setComponentX(double x); + void setComponentY(double y); + + // Sets both components at once + void setComponents(double x, double y); + + // Accessors + double magnitude() const { return _magnitude; } + double direction() const { return _direction; } + void setMagnitude(double m) { _magnitude = m; } + void setDirection(double d) { _direction = d; } + + // Vector math + Vector operator+(const Vector&); + Vector operator-(const Vector&); + + Vector& operator+=(const Vector&); + Vector& operator-=(const Vector&); + + // Dot product + double operator*(const Vector&); + + // Magnitude math + Vector operator+(double m) { return Vector(_magnitude + m, _direction); } + Vector operator-(double m) { return Vector(_magnitude - m, _direction); } + Vector operator*(double m) { return Vector(_magnitude * m, _direction); } + Vector operator/(double m) { return Vector(_magnitude / m, _direction); } + + Vector& operator+=(double m); + Vector& operator-=(double m); + Vector& operator*=(double m); + Vector& operator/=(double m); + + // Return the vector's equalivent on the unit circle + Vector unit() const { return Vector(1.0, _direction); } + + protected: + double _magnitude; + double _direction; +}; + +void debugVector(const QString &, const Vector &); + +#endif diff --git a/kolf/x-kolf.desktop b/kolf/x-kolf.desktop new file mode 100644 index 00000000..e2c622e1 --- /dev/null +++ b/kolf/x-kolf.desktop @@ -0,0 +1,60 @@ +# KDE Config File +[Desktop Entry] +MimeType=application/x-kolf +Type=MimeType +Comment=Kolf Saved Game +Comment[ar]=لعبة Kolf محÙوظة +Comment[be]=Ð—Ð°Ñ…Ð°Ð²Ð°Ð½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ Ñž гольф +Comment[bg]=ЗапиÑана игра на Kolf +Comment[bn]=কলà§â€Œà¦« খেলা সংরকà§à¦·à¦£ করেছে +Comment[br]=C'hoari enrollet Kolf +Comment[bs]=Kolf snimljena igra +Comment[ca]=Partida de Kolf desada +Comment[cs]=Uložená hra Kolfu +Comment[cy]=Gêm Kolf wedi'i Gadw +Comment[da]=Gemt Kolf-spil +Comment[de]=Gespeichertes Kolf-Spiel +Comment[el]=Αποθηκευμένο παιχνίδι Kolf +Comment[eo]=Kolf konservita ludo +Comment[es]=Juego de Kolf guardado +Comment[et]=Kolfi salvestatud mäng +Comment[eu]=Kolf-en gordetako jokoa +Comment[fa]=بازی ذخیره‌شدۀ Kolf +Comment[fi]=Kolfi-pelitallennus +Comment[fr]=Partie enregistrée de Kolf +Comment[gl]=Xogo de Kolf gravado +Comment[he]=משחק שמור של Kolf +Comment[hi]=सहेजा गया कोलà¥à¤« खेल +Comment[hr]=Spremljena Kolf igra +Comment[hu]=Kolf elmentett játék +Comment[is]=Vistaður Kolf leikur +Comment[it]=Partita di Kolf salvata +Comment[ja]=Kolf ã®ä¿å­˜ã•ã‚ŒãŸã‚²ãƒ¼ãƒ  +Comment[km]=ល្បែង Kolf ដែល​បាន​រក្សាទុក +Comment[lt]=Kolf iÅ¡saugotas žaidimas +Comment[lv]=Kolf saglabÄta spÄ“le +Comment[mk]=Зачувана игра од Kolf +Comment[nb]=Kolf lagret spill +Comment[nds]=Sekert Kolf-Speel +Comment[ne]=कोलà¥à¤«à¤²à¥‡ खेल बचत गरà¥à¤¯à¥‹ +Comment[nl]=Opgeslagen Kolf-spel +Comment[nn]=Kolf lagra spel +Comment[pa]=ਗੋਲਫ਼ ਸੰਭਾਲੀ ਖੇਡ +Comment[pl]=Zapisana gra w Kolfa +Comment[pt]=Jogo Gravado do Kolf +Comment[pt_BR]=Jogo Kolf Salvo +Comment[ru]=Ð¡Ð¾Ñ…Ñ€Ð°Ð½Ñ‘Ð½Ð½Ð°Ñ Ð¸Ð³Ñ€Ð° в гольф +Comment[se]=Kolf vurkii spealu +Comment[sk]=Uložená hra Kolf +Comment[sl]=Shranjena igra Kolf +Comment[sr]=Снимљена игра Kolf-а +Comment[sr@Latn]=Snimljena igra Kolf-a +Comment[sv]=Sparat Kolfspel +Comment[ta]=கோலà¯à®ƒà®ªà¯ சேமிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿ விளையாடà¯à®Ÿà¯ +Comment[tg]=Бозии Захиракардаи Голф +Comment[tr]=Kayıtlı Kolf Oyunu +Comment[uk]=Збережена гра в гольф +Comment[zh_CN]=Kolf ä¿å­˜çš„æ¸¸æˆ +Comment[zh_TW]=Kolf 儲存的éŠæˆ² +Icon=kolf +Patterns=*.kolfgame diff --git a/kolf/x-kourse.desktop b/kolf/x-kourse.desktop new file mode 100644 index 00000000..19951038 --- /dev/null +++ b/kolf/x-kourse.desktop @@ -0,0 +1,53 @@ +# KDE Config File +[Desktop Entry] +MimeType=application/x-kourse +Comment=Kolf Course +Comment[be]=ПлÑцоўка Ð´Ð»Ñ Ð³Ð¾Ð»ÑŒÑ„Ð° +Comment[bg]=ÐšÑƒÑ€Ñ Ð½Ð° Kolf +Comment[bn]=কলà§â€Œà¦« কোরà§à¦¸ +Comment[bs]=Kolf teren +Comment[ca]=Camp de Kolf +Comment[cs]=Kurs Kolfu +Comment[cy]=Cwrs Kolf +Comment[da]=Kolf-bane +Comment[de]=Kolf-Platz +Comment[el]=Πίστα kolf +Comment[eo]=Golftereno +Comment[es]=Partida de Kolf +Comment[et]=Kolfi väljak +Comment[eu]=Kolf-en zelaia +Comment[fa]=مسابقۀ Kolf +Comment[fi]=Golf-rata +Comment[fr]=Parcours de Kolf +Comment[he]=מסלול Kolf +Comment[hr]=Kolf teren +Comment[hu]=Kolf-pálya +Comment[is]=Kolf Braut +Comment[it]=Percorso di Kolf +Comment[ja]=Kolf コース +Comment[km]=វគ្គ Kolf +Comment[lt]=Kolf trasa +Comment[lv]=Kolf kurss +Comment[mk]=Терен на Kolf +Comment[nb]=Kolf-bane +Comment[nds]=Kolf-Platz +Comment[ne]=कोलà¥à¤« सà¥à¤°à¥‹à¤¤ +Comment[nl]=Kolf-baan +Comment[nn]=Kolf-bane +Comment[pa]=ਗà©à¨²à¨«à¨¼ ਕੋਰਸ +Comment[pl]=Kurs Kolfa +Comment[pt]=Percurso do Kolf +Comment[pt_BR]=Curso do Kolf +Comment[ru]=Площадка Ð´Ð»Ñ Ð³Ð¾Ð»ÑŒÑ„Ð° +Comment[se]=Kolf-bána +Comment[sk]=Kolf kurz +Comment[sl]=IgriÅ¡Äe za Kolf +Comment[sr]=Kolf-ов терен +Comment[sr@Latn]=Kolf-ov teren +Comment[sv]=Kolfbana +Comment[ta]=காலà¯à®ƒà®ªà¯ கோரà¯à®¸à¯ +Comment[uk]=Майданчик Ð´Ð»Ñ Ð³Ð¾Ð»ÑŒÑ„Ð° +Comment[zh_TW]=Kolf 路線 +Icon=kolf +Type=MimeType +Patterns=*.kolf;*.course;*.kourse; diff --git a/konquest/AUTHORS b/konquest/AUTHORS new file mode 100644 index 00000000..6d46c5fd --- /dev/null +++ b/konquest/AUTHORS @@ -0,0 +1,12 @@ +Galactic Konquest +----------------- + +Contributers are: + +- Russell Steffen + + +- Computer/AI Player +Stephan Zehetner + + diff --git a/konquest/ChangeLog b/konquest/ChangeLog new file mode 100644 index 00000000..0601252e --- /dev/null +++ b/konquest/ChangeLog @@ -0,0 +1,26 @@ +2005-02-18 Inge Wallin + + * version.h (KONQUEST_VERSION): Bumped version number to 1.1 + because of the upcoming release of KDE 3.4. + +2004-08-22 Inge Wallin + + * gameboard.cc (nextTurn): Fixed a grammatical error. + +0.99.4 -- + Brought the KDE CVS tree in sync with my own. Changes from previous version + include: + -- removed dependence on libg++ random integer classes + -- fixed a couple of problems with the game state machine. + +0.0.4 -- + Fixed (hopefully) a compiler error. gamecore.cc would compile fine with + debug (-g), but not with the optimizer (-O2). Added a destructor + and things seem okay. + + Added the distance measuring function of the original game. It appears as a + in the toolbar. + +0.0.3 -- + wasn't keeping a changelog prior to 0.0.4 + diff --git a/konquest/Konquest.cc b/konquest/Konquest.cc new file mode 100644 index 00000000..90a6b6c6 --- /dev/null +++ b/konquest/Konquest.cc @@ -0,0 +1,34 @@ +#include +#include +#include +#include + +#include "version.h" +#include "mainwin.h" +#include "map_widget.h" + +static const char description[] = I18N_NOOP("Galactic Strategy KDE Game"); + +int +main(int argc, char **argv) +{ + KAboutData aboutData( "konquest", I18N_NOOP("Konquest"), + KONQUEST_VERSION, description, KAboutData::License_GPL, + I18N_NOOP("Copyright (c) 1999-2001, Developers")); + aboutData.addAuthor("Russ Steffen",0, "rsteffen@bayarea.net"); + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication a; + QApplication::setGlobalMouseTracking( true ); + KGlobal::locale()->insertCatalogue("libkdegames"); + + if (a.isRestored()) + RESTORE(MainWindow) + else { + MainWindow *w = new MainWindow; + a.setMainWidget(w); + w->show(); + } + return a.exec(); +} + diff --git a/konquest/Makefile.am b/konquest/Makefile.am new file mode 100644 index 00000000..e6b50150 --- /dev/null +++ b/konquest/Makefile.am @@ -0,0 +1,23 @@ +SUBDIRS = pics + +INCLUDES= -I$(top_srcdir)/libkdegames $(all_includes) +KDE_ICON = konquest + +bin_PROGRAMS = konquest +konquest_SOURCES = Konquest.cc gameboard.cc gamecore.cc int_validator.cc \ + mainwin.cc map_widget.cc minimap.cc newgamedlg.cc planet_info.cc \ + gameenddlg.cc scoredlg.cc fleetdlg.cc newGameDlg_ui.ui +konquest_LDFLAGS = $(all_libraries) $(KDE_RPATH) +konquest_LDADD = $(LIB_KDEGAMES) +konquest_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + + +METASOURCES = AUTO + +xdg_apps_DATA = konquest.desktop + +rcdir = $(kde_datadir)/konquest +rc_DATA = konquestui.rc + +messages: rc.cpp + $(XGETTEXT) rc.cpp *.cc -o $(podir)/konquest.pot diff --git a/konquest/README b/konquest/README new file mode 100644 index 00000000..b8950f43 --- /dev/null +++ b/konquest/README @@ -0,0 +1,10 @@ +Konquest +---------------------------------------- + +Konquest is a multi-player strategy game. The goal of +the game is to expand your interstellar empire across the + galaxy and, of course, crush your rivals in the process. + +Please see the konquest help page for more information. + + diff --git a/konquest/TODO b/konquest/TODO new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/konquest/TODO @@ -0,0 +1 @@ + diff --git a/konquest/fleetdlg.cc b/konquest/fleetdlg.cc new file mode 100644 index 00000000..0117f74d --- /dev/null +++ b/konquest/fleetdlg.cc @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "fleetdlg.h" + +FleetDlgListViewItem::FleetDlgListViewItem(QListView *parent, QString s1, QString s2, QString s3, QString s4, QString s5) : QListViewItem(parent, s1, s2, s3, s4, s5) +{ +} + +int FleetDlgListViewItem::compare(QListViewItem *i, int col, bool) const +{ + if (col == 1) + { + if (text(col) > i -> text(col)) return 1; + else if (text(col) < i -> text(col)) return -1; + else return compare(i, 0, true); + } + else if (col == 3) + { + if (text(col).toDouble() > i -> text(col).toDouble()) return 1; + else if (text(col).toDouble() < i -> text(col).toDouble()) return -1; + else return compare(i, 0, true); + } + else + { + if (text(col).toInt() > i -> text(col).toInt()) return 1; + else if (text(col).toInt() < i -> text(col).toInt()) return -1; + else return compare(i, 0, true); + } +} + + +FleetDlg::FleetDlg( QWidget *parent, AttackFleetList *fleets ) + : QDialog(parent, "FleetDlg", true ), fleetList(fleets) +{ + setCaption( kapp->makeStdCaption(i18n("Fleet Overview")) ); + + fleetTable = new KListView( this, 0 ); + fleetTable->addColumn(i18n("Fleet No.")); + fleetTable->addColumn(i18n("Destination")); + fleetTable->addColumn(i18n("Ships")); + fleetTable->addColumn(i18n("Kill Percentage")); + fleetTable->addColumn(i18n("Arrival Turn")); + fleetTable->setMinimumSize( fleetTable->sizeHint() ); + + KPushButton *okButton = new KPushButton( KStdGuiItem::ok(), this ); + okButton->setMinimumSize( okButton->sizeHint() ); + okButton->setDefault(true); + + QVBoxLayout *layout1 = new QVBoxLayout( this ); + QHBoxLayout *layout2 = new QHBoxLayout; + + layout1->addWidget( fleetTable, 1 ); + layout1->addLayout( layout2 ); + + layout2->addStretch( 2 ); + layout2->addWidget( okButton ); + layout2->addStretch( 2 ); + + connect( okButton, SIGNAL(clicked()), this, SLOT(accept()) ); + + init(); + + resize( 580, 140 ); +} + +void +FleetDlg::init() +{ + AttackFleet *curFleet; + AttackFleetListIterator nextFleet( *fleetList ); + int fleetNumber = 0; + + while( (curFleet = nextFleet())) { + fleetNumber++; + new FleetDlgListViewItem(fleetTable, + QString("%1").arg(fleetNumber), + curFleet->destination->getName(), + QString("%1").arg(curFleet->getShipCount()), + QString("%1").arg(KGlobal::locale()->formatNumber(curFleet->killPercentage, 3)), + QString("%1").arg((int)ceil(curFleet->arrivalTurn))); + } +} diff --git a/konquest/fleetdlg.h b/konquest/fleetdlg.h new file mode 100644 index 00000000..abab1957 --- /dev/null +++ b/konquest/fleetdlg.h @@ -0,0 +1,29 @@ +#ifndef FLEETDLG_H +#define FLEETDLG_H + +#include + +#include + +#include "gamecore.h" + +class FleetDlgListViewItem : public QListViewItem +{ + public: + FleetDlgListViewItem(QListView *parent, QString s1, QString s2, QString s3, QString s4, QString s5); + int compare(QListViewItem *i, int col, bool) const; +}; + +class FleetDlg : public QDialog { + +public: + FleetDlg( QWidget *parent, AttackFleetList *fleets ); + +private: + void init(); + + AttackFleetList *fleetList; + QListView *fleetTable; +}; + +#endif diff --git a/konquest/gameboard.cc b/konquest/gameboard.cc new file mode 100644 index 00000000..75e440d7 --- /dev/null +++ b/konquest/gameboard.cc @@ -0,0 +1,991 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "gamecore.h" + +#include "int_validator.h" +#include "newgamedlg.h" +#include "gameenddlg.h" +#include "scoredlg.h" +#include "fleetdlg.h" +#include "gameboard.h" +#include "gameboard.moc" + + +/********************************************************************* + Game Board +*********************************************************************/ +GameBoard::GameBoard( QWidget *parent ) + : QWidget( parent ), gameInProgress( false ), gameState( NONE ) +{ + QColorGroup cg( white, black, green.light(), green.dark(), green, green.dark(75), green.dark() ); + QPalette palette( cg, cg, cg ); + + neutralPlayer = Player::createNeutralPlayer(); + map = new Map; + + planets.setAutoDelete(true); + players.setAutoDelete(true); + + //******************************************************************** + // Create the widgets in the main window + //******************************************************************** + mapWidget = new ConquestMap( map, this ); + msgWidget = new QTextEdit( this ); + msgWidget->setTextFormat(LogText); + msgWidget->setMinimumHeight(100); + msgWidget->setHScrollBarMode(QScrollView::AlwaysOff); + msgWidget->setPaper(QBrush(Qt::black)); + planetInfo = new PlanetInfo( this, palette ); + gameMessage = new QLabel( this ); + gameMessage->setPalette( palette ); + turnCounter = new QLabel( this ); + turnCounter->setPalette( palette ); + turnCounter->setText( "Turn" ); + turnCounter->setMaximumHeight( turnCounter->sizeHint().height() ); + + endTurn = new QPushButton( i18n("End Turn"), this ); + endTurn->setFixedSize( endTurn->sizeHint() ); + endTurn->setPalette( palette ); + + shipCountEdit = new QLineEdit( this ); + IntValidator *v = new IntValidator( 1, 32767, this ); + shipCountEdit->setValidator( v ); + shipCountEdit->setMinimumSize( 40, 0 ); + shipCountEdit->setMaximumSize( 32767, 40 ); + shipCountEdit->setEnabled(false); + shipCountEdit->setPalette( palette ); + shipCountEdit->setEchoMode( QLineEdit::Password ); + + splashScreen = new QLabel( this ); + splashScreen->setPixmap(QPixmap(IMAGE_SPLASH)); + splashScreen->setGeometry( 0, 0, 600, 550 ); + + setMinimumSize( 600, 600 ); + + setMouseTracking( true ); + setFocusPolicy( StrongFocus ); + setFocus(); + + //******************************************************************** + // Layout the main window + //******************************************************************** + QHBoxLayout *layout1 = new QHBoxLayout( this ); + QVBoxLayout *layout2 = new QVBoxLayout; + QHBoxLayout *layout3 = new QHBoxLayout; + QVBoxLayout *layout4 = new QVBoxLayout; + + layout1->addLayout( layout2 ); + layout2->addLayout( layout3 ); + + layout3->addSpacing( 5 ); + layout3->addWidget( gameMessage, 10 ); + layout3->addWidget( shipCountEdit, 1 ); + layout3->addWidget( endTurn, 1 ); + + layout2->addSpacing( 5 ); + layout2->addWidget( mapWidget, 0, AlignTop ); + layout2->addWidget( msgWidget ); + layout2->addStretch( 1 ); + + layout1->addSpacing( 5 ); + layout1->addLayout( layout4, 10 ); + + layout4->addWidget( planetInfo, 1 ); + layout4->addSpacing( 10 ); + layout4->addWidget( turnCounter, 1 ); + layout4->addStretch( 1 ); + + layout1->addStretch( 1 ); + + //********************************************************************** + // Set up signal/slot connections + //********************************************************************** + connect( mapWidget, SIGNAL( planetSelected(Planet *) ), this, SLOT(planetSelected(Planet *)) ); + connect( shipCountEdit, SIGNAL(returnPressed()), this, SLOT(newShipCount()) ); + connect( endTurn, SIGNAL( clicked() ), this, SLOT( nextPlayer() ) ); + connect( mapWidget, SIGNAL( planetHighlighted(Planet *)), planetInfo, SLOT(showPlanet(Planet *)) ); + + changeGameBoard( false ); +} + + +//********************************************************************** +// Destructor +//********************************************************************** +GameBoard::~GameBoard() +{ + // Nothing much to do yet +} + +#if 0 +QSize GameBoard::sizeHint() const +{ + return QSize( 600, 550 ); +} +#endif + +//************************************************************************ +// Keyboard Event handlers +//************************************************************************ +void +GameBoard::keyPressEvent( QKeyEvent *e ) +{ + // Check for the escape key + if( e->key() == Key_Escape ) { + switch( gameState ) { + case DEST_PLANET: + case SHIP_COUNT: + case RULER_SOURCE: + case RULER_DEST: + gameState = SOURCE_PLANET; + haveSourcePlanet = false; + haveDestPlanet = false; + turn(); + break; + default: + break; + } + return; + } + + if( !isgraph( e->ascii() ) ) { + e->ignore(); + return; + } + + PlanetListIterator planetSearch( planets ); + QString planetName; + + planetName += toupper( e->ascii() ); + + for(Planet *p = planetSearch.toFirst(); + p != NULL; + p = ++planetSearch ) { + + if( p->getName() == planetName ) + planetSelected( p ); + } + +} + +QString +GameBoard::playerString(Player *player) +{ + if (!player) + player = currentPlayer->current(); + return player->getColoredName(); +} + +//************************************************************************ +// Game engine/state machine +//************************************************************************ +void +GameBoard::turn() +{ + PlanetListIterator planetAi( planets ); + PlanetListIterator planetAttack( planets ); + Planet *target = 0; + + switch( gameState ) { + case NONE : + // stuff for none + gameState = SOURCE_PLANET; + haveSourcePlanet = false; + haveDestPlanet = false; + haveShipCount = false; + shipCount = 0; + mapWidget->unselectPlanet(); + + + turn(); + setFocus(); + break; + + case SOURCE_PLANET : + + if( haveSourcePlanet ) { + gameState = DEST_PLANET; + + sourcePlanet->select(); + turn(); + + } else { + shipCountEdit->hide(); + endTurn->setEnabled( true ); + mapWidget->unselectPlanet(); + + gameMessage->setText( "" + playerString() + ": " + + i18n("Select source planet...") + "" ); + setFocus(); + } + + break; + + case DEST_PLANET : + + if( haveDestPlanet ) { + mapWidget->unselectPlanet(); + gameState = SHIP_COUNT; + turn(); + + } else { + shipCountEdit->hide(); + endTurn->setEnabled( false ); + sourcePlanet->select(); + gameMessage->setText( "" + playerString() + ": " + + i18n("Select destination planet...") + "" ); + setFocus(); + } + + break; + + case SHIP_COUNT: + + if( haveShipCount ) { + // We now have a complete fleet to send, so send it + sendAttackFleet( sourcePlanet, destPlanet, shipCount); + + shipCountEdit->hide(); + endTurn->setEnabled( true ); + + gameState = NONE; + turn(); + + endTurn->setFocus(); + + } else { + gameMessage->setText( currentPlayer->current()->getName() + + i18n(": How many ships?") ); + + shipCountEdit->setText( "" ); + shipCountEdit->show(); + shipCountEdit->setEnabled(true); + shipCountEdit->setFocus(); + + endTurn->setEnabled( false ); + + mapWidget->unselectPlanet(); + } + + break; + + case RULER_SOURCE: + if( haveSourcePlanet ) { + gameState = RULER_DEST; + sourcePlanet->select(); + turn(); + } else { + shipCountEdit->hide(); + endTurn->setEnabled( true ); + mapWidget->unselectPlanet(); + + gameMessage->setText( i18n("Ruler: Select starting planet.") ); + setFocus(); + } + + break; + + case RULER_DEST: + if( haveDestPlanet ) { + mapWidget->unselectPlanet(); + + // Display the distance between the two planets + CoreLogic cl; + double dist = cl.distance( sourcePlanet, destPlanet ); + + QString msg; + msg = i18n("The distance from Planet %1 to Planet %2 is %3 light years.\n" + "A ship leaving this turn will arrive on turn %4") + .arg(sourcePlanet->getName()) + .arg(destPlanet->getName()) + .arg(KGlobal::locale()->formatNumber( dist, 2 )) + .arg(KGlobal::locale()->formatNumber( turnNumber + (int)dist, 0 )); + KMessageBox::information( this, msg, i18n("Distance")); + + gameState = NONE; + turn(); + } else { + gameMessage->setText( i18n("Ruler: Select ending planet.") ); + shipCountEdit->hide(); + endTurn->setEnabled( false ); + sourcePlanet->select(); + + setFocus(); + } + + break; + + case AI_PLAYER: + endTurn->setEnabled( false ); + gameMessage->setText( i18n("Computer Player thinking...") ); + + Planet *home; + + int ships; + planetAi.toFirst(); + + while ((home = planetAi())) { + if (home->getPlayer() == currentPlayer->current()) { + + bool hasAttack = false; + ships = (int)floor(home->getFleet().getShipCount() * 0.7 ); + + if (ships >= 20) { + + Planet *attack; + double minDistance = 100; + planetAttack.toFirst(); + while ((attack = planetAttack())) { + bool skip = false; + + CoreLogic cl; + double dist = cl.distance( home, attack ); + + if ((dist < minDistance) && (attack->getPlayer() != currentPlayer->current()) && + (attack->getFleet().getShipCount() < ships )) { + AttackFleetListIterator FleetsinFlight( currentPlayer->current()->getAttackList() ); + AttackFleet *curFleet; + + while ( (curFleet = FleetsinFlight())) { + if (curFleet->destination == attack) { + skip = true; + } + } + if (skip) continue; + + target = attack; + hasAttack = true; + minDistance = dist; + } + } + + if (hasAttack) { + sendAttackFleet( home, target, ships ); + } + else { + planetAttack.toFirst(); + minDistance = 100; + int shipsToSend = 0; + bool hasDestination = false; + + while ((attack = planetAttack())) { + bool skip = false; + CoreLogic cl; + double dist = cl.distance( home, attack ); + int homeships = (int)floor(home->getFleet().getShipCount() * 0.5 ); + + if ((dist < minDistance) && (attack->getPlayer() == currentPlayer->current()) && + (attack->getFleet().getShipCount() < homeships )) { + AttackFleetListIterator FleetsinFlight( currentPlayer->current()->getAttackList() ); + AttackFleet *curFleet; + + while ( (curFleet = FleetsinFlight())) { + if (curFleet->destination == attack) { + skip = true; + } + } + if (skip) continue; + + shipsToSend = (int)floor((home->getFleet().getShipCount() - attack->getFleet().getShipCount()) / 2) ; + + target = attack; + hasDestination = true; + minDistance = dist; + } + } + + if (hasDestination) { + sendAttackFleet( home, target, shipsToSend ); + } + } + } + } + } + + endTurn->setEnabled( true ); + nextPlayer(); + + break; + + default: + break; + } + + QString turnStr; + turnStr = i18n("Turn #: %1 of %2").arg(turnNumber).arg(lastTurn); + + turnCounter->setText( turnStr ); + + emit newGameState( gameState ); +} +//************************************************************************ +// To the end turn processing (resolve combat, etc.) +//************************************************************************ +void +GameBoard::nextTurn() +{ + resolveShipsInFlight(); + + scanForSurvivors(); + + // advance to first living player + while( currentPlayer->current() && !currentPlayer->current()->isInPlay() ) { + ++(*currentPlayer); + }; + + // advance turn counter + turnNumber++; + + // update the planets + PlanetListIterator nextPlanet( planets ); + Planet *planet; + + while( (planet = nextPlanet()) ) + { + planet->turn(); + } + + // Tell the status widget to update itself + planetInfo->rescanPlanets(); + + Player *winner = findWinner(); + if (winner) + { + mapWidget->repaint(true); + KMessageBox::information(this, + i18n("The mighty %1 has conquered the galaxy!").arg(winner->getName()), + i18n("Game Over")); + } + + if( (turnNumber == lastTurn) && !winner ) + { + mapWidget->repaint(true); + GameEndDlg *dlg = new GameEndDlg( this ); + + if( dlg->exec() == KDialogBase::Yes ) { + lastTurn += dlg->extraTurns(); + } + + delete dlg; + } + + if( winner || (turnNumber >= lastTurn) ) + { + // Game over, man! Game over. + + mapWidget->repaint(true); + + gameOver(); + }; +} + +//************************************************************************ +// determine the fate of the ships in transit +//************************************************************************ +void +GameBoard::resolveShipsInFlight() +{ + AttackFleetList arrivingShips; + PlayerListIterator nextPlayer( players ); + Player *plr; + + while( (plr = nextPlayer()) ) { + AttackFleetListIterator nextFleet( plr->getAttackList() ); + + AttackFleet *fleet; + + while( (fleet = nextFleet()) ) { + double fleetArrivalTurn = floor(fleet->arrivalTurn); + + if( turnNumber == int (fleetArrivalTurn) ) { + doFleetArrival( fleet ); + plr->getAttackList().removeRef( fleet ); + delete fleet; + } + } + } + +} + +Player * +GameBoard::findWinner() +{ + Player *winner = 0; + int activePlayers = 0; + + PlayerListIterator nextPlayer( players ); + Player *plr; + + while( (plr = nextPlayer()) ) { + if (plr->isInPlay()) + { + winner = plr; + activePlayers++; + } + else if (plr->getAttackList().count() != 0) + { + activePlayers++; + } + } + if (activePlayers == 1) + return winner; + + return 0; +} + +void +GameBoard::gameMsg(const QString &msg, Player *player, Planet *planet, Player *planetPlayer) +{ + bool isHumanInvolved = false; + + QString color = "white"; + QString colorMsg = msg; + QString plainMsg = msg; + + if (player) + { + if (!player->isAiPlayer()) + isHumanInvolved = true; + colorMsg = colorMsg.arg(playerString(player)); + plainMsg = plainMsg.arg(player->getName()); + } + + if (planet) + { + if (!planetPlayer) + planetPlayer = planet->getPlayer(); + if (!planetPlayer->isAiPlayer() && !planetPlayer->isNeutral()) + isHumanInvolved = true; + + QString color = planetPlayer->getColor().name(); + colorMsg = colorMsg.arg(QString("%2").arg(color, planet->getName())); + plainMsg = plainMsg.arg(planet->getName()); + } + msgWidget->append(("Turn %1: ").arg(turnNumber)+colorMsg+""); + msgWidget->scrollToBottom(); + + if (isHumanInvolved) + { + mapWidget->repaint(true); + KMessageBox::information(this, plainMsg); + } +} + +//************************************************************************ +// check to see any players have been eliminated +//************************************************************************ +void +GameBoard::scanForSurvivors() +{ + PlayerListIterator nextPlayer( players ); + PlayerList activePlayers; + PlayerList inactivePlayers; + + // insert all of the active players into a special + // list, the deactivate them + Player *plr; + while( (plr = nextPlayer()) ) { + if( plr->isInPlay() ) { + activePlayers.append( plr ); + plr->setInPlay( false ); + } else { + inactivePlayers.append( plr ); + } + } + + + // iterate through the list of planets and + // mark their owners in play + PlanetListIterator nextPlanet( planets ); + + Planet *planet; + while( (planet = nextPlanet()) ) { + planet->getPlayer()->setInPlay( true ); + } + + + PlayerListIterator nextActivePlayer( activePlayers ); + while( (plr = nextActivePlayer()) ) { + if( !plr->isInPlay() ) { + // Player has bitten the dust + QString msg; + msg = i18n("The once mighty empire of %1 has fallen in ruins."); + gameMsg(msg, plr); + } + } + + PlayerListIterator nextInactivePlayer( inactivePlayers ); + while( (plr = nextInactivePlayer()) ) { + if( plr->isInPlay() ) { + // Player has bitten the dust + QString msg; + msg = i18n("The fallen empire of %1 has staggered back to life."); + gameMsg(msg, plr); + } + } +} + +//************************************************************************ +// handle the arrival of a fleet at a planet +//************************************************************************ +void +GameBoard::doFleetArrival( AttackFleet *arrivingFleet ) +{ + // Check to see of (fleet owner) == (planet owner) + // if the planet and fleet owner are the same, then merge the fleets + // otherwise attack. + + if( (*arrivingFleet->owner) == (*arrivingFleet->destination->getPlayer())) { + if (!arrivingFleet->owner->isAiPlayer()) { + arrivingFleet->destination->getFleet().absorb(arrivingFleet); + + QString msg; + msg = i18n("Reinforcements (%1 ships) have arrived for planet %2.") + .arg(arrivingFleet->getShipCount()); + gameMsg(msg, 0, arrivingFleet->destination); + } + } else { + + // let's get ready to rumble... + + CoreLogic cl; + AttackFleet &attacker = *arrivingFleet; + DefenseFleet &defender = arrivingFleet->destination->getFleet(); + Planet &prizePlanet = *(arrivingFleet->destination); + + bool haveVictor = false; + bool planetHolds = true; + + while( !haveVictor ) { + double attackerRoll = cl.roll(); + double defenderRoll = cl.roll(); + + if( defenderRoll < prizePlanet.getKillPercentage() ) { + attacker.removeShips( 1 ); + } + + if( attacker.getShipCount() <= 0 ) { + haveVictor = true; + planetHolds = true; + continue; + } + + if( attackerRoll < attacker.killPercentage ) { + defender.removeShips( 1 ); + attacker.owner->statEnemyShipsDestroyed( 1 ); + } + + if( defender.getShipCount() <= 0 ) { + haveVictor = true; + planetHolds = false; + } + } + + if( planetHolds ) { + prizePlanet.getPlayer()->statEnemyFleetsDestroyed(1); + QString msg; + msg = i18n("Planet %2 has held against an attack from %1."); + gameMsg(msg, attacker.owner, &prizePlanet); + } else { + Player *defender = prizePlanet.getPlayer(); + attacker.owner->statEnemyFleetsDestroyed( 1 ); + + arrivingFleet->destination->conquer( arrivingFleet ); + + QString msg; + msg = i18n("Planet %2 has fallen to %1."); + gameMsg(msg, attacker.owner, &prizePlanet, defender); + } + } + + mapWidget->repaint(true); +} + +//************************************************************************ +// Set up the game board for a new game +//************************************************************************ +void +GameBoard::startNewGame() +{ + shutdownGame(); + + if( gameInProgress ) + return; + + NewGameDlg *newGame = new NewGameDlg( this, map, &players, neutralPlayer, &planets ); + + if( !newGame->exec() ) + { + delete newGame; + return; + } + newGame->save(); // Save settings for next time + + msgWidget->clear(); + + changeGameBoard( true ); + + planetInfo->setPlanetList(planets); + + shipCountEdit->hide(); + endTurn->setEnabled( true ); + + currentPlayer = new PlayerListIterator( players ); + currentPlayer->toFirst(); + + endTurn->show(); + gameMessage->show(); + + lastTurn = newGame->turns(); + + turnNumber = 1; + turn(); + + delete newGame; +} + +//************************************************************************ +// Shut down the current game +//************************************************************************ +void +GameBoard::shutdownGame() +{ + if( !gameInProgress ) + return; + + int choice = KMessageBox::warningContinueCancel + ( this, + i18n("Do you wish to retire this game?"), + i18n("End Game"), + KStdGuiItem::ok() ); + + if( choice == KMessageBox::Cancel ) + return; + + gameOver(); +} + +void +GameBoard::gameOver() +{ + ScoreDlg *scoreDlg = new ScoreDlg( this, i18n("Final Standings"), &players ); + scoreDlg->exec(); + + cleanupGame(); +} + +void +GameBoard::cleanupGame() +{ + map->clearMap(); + + planets.clear(); + players.clear(); + + delete currentPlayer; + currentPlayer = NULL; + + shipCountEdit->hide(); + endTurn->setEnabled( false ); + + gameMessage->hide(); + endTurn->hide(); + + changeGameBoard( false ); + gameState = NONE; + emit newGameState(gameState); +} + + +//************************************************************************ +// Player selected a planet +//************************************************************************ +void +GameBoard::planetSelected( Planet *planet ) +{ + switch( gameState ) { + case SOURCE_PLANET: + if( (*planet->getPlayer()) == (*currentPlayer->current()) ) { + // got a match + haveSourcePlanet = true; + sourcePlanet = planet; + + turn(); + } + + break; + + case RULER_SOURCE: + haveSourcePlanet = true; + sourcePlanet = planet; + turn(); + break; + + case DEST_PLANET: + case RULER_DEST: + if( planet != sourcePlanet ) { + // got a match + haveDestPlanet = true; + destPlanet = planet; + + turn(); + } + + break; + + default: + case NONE : + break; + } + +} + +//************************************************************************ +// Player hit return in the ship count edit box +//************************************************************************ +void +GameBoard::newShipCount() +{ + QString temp( shipCountEdit->text() ); + bool ok; + + switch( gameState ) { + case SHIP_COUNT: + shipCount = temp.toInt(&ok); + + if( ok ) + haveShipCount = true; + + shipCountEdit->setText( "" ); + + turn(); + break; + + default: + break; + }; + +} + +//********************************************************************** +// transition board from play to non-play +//********************************************************************** +void +GameBoard::changeGameBoard( bool inPlay ) +{ + gameInProgress = inPlay; + + if( gameInProgress ) { + mapWidget->show(); + planetInfo->show(); + gameMessage->show(); + endTurn->show(); + shipCountEdit->show(); + splashScreen->hide(); + setBackgroundColor( black ); + } else { + mapWidget->hide(); + planetInfo->hide(); + gameMessage->hide(); + endTurn->hide(); + shipCountEdit->hide(); + splashScreen->show(); + setBackgroundColor( black ); + } + +} + +//************************************************************************ +// Player clicked the 'End Turn' button +//************************************************************************ +void +GameBoard::nextPlayer() +{ + // end turn and advance to next player + Player *plr; + + while( (plr = ++(*currentPlayer)) && !(plr->isInPlay()) ) {} + + if( !plr ) { + // end of player list, new turn + currentPlayer->toFirst(); + nextTurn(); + } + + if( gameInProgress ) { + if (currentPlayer->current()->isAiPlayer()) { + gameState = AI_PLAYER; + } + else { + gameState = SOURCE_PLANET; + + } + turn(); + } +} + +//************************************************************************ +// A complete set of source, destination planets and ship count has been +// entered, so do something about it +//************************************************************************ +void +GameBoard::sendAttackFleet( Planet *source, Planet *dest, int ship ) +{ + bool ok; + + ok = currentPlayer->current()->NewAttack( source, dest, + ship, turnNumber ); + + if( !ok ) { + KMessageBox::error( this, + i18n("Not enough ships to send.") ); + } +} + +//************************************************************************ +// Toolbar items +//************************************************************************ +void +GameBoard::measureDistance() +{ + switch( gameState ) { + case SOURCE_PLANET: + gameState = RULER_SOURCE; + turn(); + default: + break; + } +} + +void +GameBoard::showScores() +{ + ScoreDlg *scoreDlg = new ScoreDlg( this, i18n("Current Standings"), &players ); + scoreDlg->show(); +} + +void +GameBoard::showFleets() +{ + FleetDlg *fleetDlg = new FleetDlg( this, &(currentPlayer->current()->getAttackList()) ); + fleetDlg->show(); +} diff --git a/konquest/gameboard.h b/konquest/gameboard.h new file mode 100644 index 00000000..5e3cddfa --- /dev/null +++ b/konquest/gameboard.h @@ -0,0 +1,119 @@ +#ifndef _GAMEBOARD_H_ +#define _GAMEBOARD_H_ + +#include + +#include "planet_info.h" +#include "map_widget.h" + +//************************************************************************ +// forward declarations +//************************************************************************ +class QSlider; +class QLabel; +class QListBox; +class QPushButton; +class QLineEdit; +class QTextEdit; + +enum GameState { NONE, SOURCE_PLANET, DEST_PLANET, SHIP_COUNT, RULER_SOURCE, RULER_DEST, AI_PLAYER }; + +//************************************************************************ +// GameBoard Widget +//************************************************************************ +class GameBoard : public QWidget +{ + Q_OBJECT + +public: + GameBoard( QWidget *parent ); + virtual ~GameBoard(); + + bool isGameInProgress(void) const { return gameInProgress; } + +// virtual QSize sizeHint() const; + +protected slots: + void startNewGame(); + void shutdownGame(); + void planetSelected( Planet * ); + void newShipCount(); + void nextPlayer(); + + //*************************************************************** + // Toolbar items + //*************************************************************** + void measureDistance(); + void showScores(); + void showFleets(); + +signals: + void newGameState( GameState newState ); + + //*************************************************************** + // Event Handlers + //*************************************************************** +protected: + virtual void keyPressEvent( QKeyEvent * ); + +private: + void turn(); + void nextTurn(); + void gameOver(); + + void resolveShipsInFlight(); + void sendAttackFleet( Planet *source, Planet *dest, int ships ); + void doFleetArrival( AttackFleet *arrivingFleet ); + void scanForSurvivors(); + + void gameMsg(const QString &msg, Player *player = 0, Planet *planet = 0, Player *planetPlayer = 0); + + void changeGameBoard( bool inPlay ); + void cleanupGame(); + Player *findWinner(); + + QString playerString(Player *player = 0); + + //*************************************************************** + // Game State information + //*************************************************************** + bool gameInProgress; + GameState gameState; + PlayerListIterator *currentPlayer; + + //*************************************************************** + // Display Widgets + //*************************************************************** + ConquestMap *mapWidget; + PlanetInfo *planetInfo; + QLabel *gameMessage; + QLabel *turnCounter; + QPushButton *endTurn; + QLineEdit *shipCountEdit; + QLabel *splashScreen; + QTextEdit *msgWidget; + + + //*************************************************************** + // Game objects + //*************************************************************** + int turnNumber; + int lastTurn; + + PlayerList players; + PlanetList planets; + Player *neutralPlayer; + Map *map; + + bool haveSourcePlanet; + Planet *sourcePlanet; + + bool haveDestPlanet; + Planet *destPlanet; + + bool haveShipCount; + int shipCount; + +}; + +#endif diff --git a/konquest/gamecore.cc b/konquest/gamecore.cc new file mode 100644 index 00000000..843c1a92 --- /dev/null +++ b/konquest/gamecore.cc @@ -0,0 +1,646 @@ +#include "gamecore.h" +#include "gamecore.moc" + +#include +#include + +#include + +//******************************************************************* +// Game Core Logic +//******************************************************************* + +bool CoreLogic::class_init = false; + +CoreLogic::CoreLogic() +{ + random.setSeed(0); +} + +void +CoreLogic::generatePlanetCoordinates( int &x, int &y ) +{ + // 0 - 15 + x = random.getLong(16); + y = random.getLong(16); +} + +double +CoreLogic::generateKillPercentage() +{ + // 0.30 - 0.90 + return 0.30 + random.getDouble()*0.60; +} + +int +CoreLogic::generatePlanetProduction() +{ + // 5 - 15 + return 5 + random.getLong(10); +} + +double +CoreLogic::generateMorale() +{ + // constant + return 0.50; +} + +double +CoreLogic::distance( Planet *p1, Planet *p2 ) +{ + int k = (p1->getSector().getRow() - p2->getSector().getRow()) / 2; + int l = (p1->getSector().getColumn() - p2->getSector().getColumn()) / 2; + + return sqrt(double((k*k) + (l*l))); +} + +double +CoreLogic::roll() +{ + // 0.00 - 1.00 + return random.getDouble(); +} + +//--------------------------------------------------------------------------- +// class Map +//--------------------------------------------------------------------------- + + +Map::Map() + : QObject( 0, 0 ), freezeUpdates( false ), + rows( BOARD_ROWS ), columns( BOARD_COLS ), + hasSelectedSector( false ) +{ + // initialize the grid of Sectors + for( int x = 0; x < rows; x++ ) + { + for( int y = 0; y < columns; y++ ) + { + grid[x][y] = Sector( this, x, y ); + connect( &grid[x][y], SIGNAL( update() ), this, SLOT( childSectorUpdate() )); + } + } +} + +Map::~Map() +{ +} + +void +Map::populateMap( PlayerList &players, Player *neutral, + int numNeutralPlanets, PlanetList &thePlanets ) +{ + Freeze(); + + int index = 0; + QString names( "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(),.<>;:[]{}/?-+\\|" ); + + // Create a planet for each player + Player *plr; + for( plr = players.first(); plr != 0; plr = players.next() ) + { + QString newName( names.mid( index++, 1 ) ); + Sector § = findRandomFreeSector(); + Planet *plrPlanet = Planet::createPlayerPlanet( sect, plr, newName ); + + thePlanets.append( plrPlanet ); + } + + for( int x = 0; x < numNeutralPlanets; x++ ) + { + QString newName( names.mid( index++, 1 ) ); + Sector § = findRandomFreeSector(); + Planet *neutralPlanet = Planet::createNeutralPlanet( sect, neutral, newName ); + + thePlanets.append( neutralPlanet ); + } + + Thaw(); + + emit update(); +} + +void +Map::clearMap() +{ + Freeze(); + + int x,y; + + for( x = 0; x < rows; x++ ) + for( y = 0; y < columns; y++ ) + { + grid[x][y].removePlanet(); + } + + + Thaw(); + + emit update(); +} + +Sector & +Map::findRandomFreeSector() +{ + CoreLogic cl; + + bool found = false; + + while( !found ) + { + int x,y; + + cl.generatePlanetCoordinates( x,y ); + + if( !grid[x][y].hasPlanet() ) + { + return grid[x][y]; + } + } + + // TODO: get rid of this + return grid[0][0]; + +} + +bool +Map::selectedSector( int &x, int &y ) const +{ + if( hasSelectedSector) + { + x = sel_x; + y = sel_y; + } + + return hasSelectedSector; + +} + +void +Map::setSelectedSector( int x, int y ) +{ + hasSelectedSector = true; + sel_x = x; + sel_y = y; + + emit update(); +} + +void +Map::setSelectedSector( const Planet &planet ) +{ + hasSelectedSector = true; + sel_x = planet.getSector().getRow(); + sel_y = planet.getSector().getColumn(); + + emit update(); +} + +void +Map::setSelectedSector() +{ + hasSelectedSector = false; + + emit update(); +} + + +void Map::childSectorUpdate() +{ + if( !freezeUpdates ) + emit update(); +} + +void Map::Freeze() +{ + freezeUpdates = true; +} + +void Map::Thaw() +{ + freezeUpdates = false; +} + +Sector &Map::getSector( int x, int y ) +{ + return grid[x][y]; +} + +const int Map::getRows() const +{ + return rows; +} + +const int Map::getColumns() const +{ + return columns; +} + +//--------------------------------------------------------------------------- +// class Sector +//--------------------------------------------------------------------------- + +Sector::Sector() +: QObject(0,0), planet( NULL ), parentMap(NULL ), x(0), y(0) +{} + +Sector::Sector( Map *newParentMap, int xPos, int yPos ) +: QObject(0,0), planet(NULL), parentMap( newParentMap ), x(xPos), y(yPos) +{ +} + +Sector::Sector( const Sector & other ) +: QObject(0,0), planet(other.planet), parentMap(other.parentMap), x(other.x), y(other.y) +{ +} + +bool Sector::hasPlanet() const +{ + return (planet != NULL); +} + + +void Sector::setPlanet( Planet *newPlanet ) +{ + planet = newPlanet; + + connect( planet, SIGNAL( update() ), this, SLOT( childPlanetUpdate() ) ); + + emit update(); +} + +Planet *Sector::getPlanet() +{ + return planet; +} + +void Sector::removePlanet() +{ + planet = NULL; + + emit update(); +} + + +void Sector::childPlanetUpdate() +{ + emit update(); +} + +Sector & +Sector::operator=( const Sector &other ) +{ + x = other.x; + y = other.y; + planet = other.planet; + parentMap = other.parentMap; + + return *this; +} + +void +Sector::select() +{ + parentMap->setSelectedSector( x, y ); + emit selected(); +} + +int Sector::getRow() +{ + return x; +} + +int Sector::getColumn() +{ + return y; +} + +//--------------------------------------------------------------------------- +// class Planet +//--------------------------------------------------------------------------- + +Planet::Planet( QString planetName, Sector &newParentSector, Player *initialOwner, + int newProd, double newKillP, double newMorale ) + : QObject(0,0), name(planetName), owner(initialOwner), parentSector(newParentSector), + homeFleet( this, newProd ), killPercentage(newKillP), morale( newMorale ), productionRate(newProd) + + +{ + parentSector.setPlanet( this ); +} + +Planet::~Planet() {} + +Planet * +Planet::createPlayerPlanet( Sector &parentSector, Player *initialOwner, QString planetName ) +{ + CoreLogic clogic; + + double morale = clogic.generateMorale(); + + return new Planet( planetName, parentSector, initialOwner, + 10, 0.400, morale ); +} + +Planet * +Planet::createNeutralPlanet( Sector &parentSector, Player *initialOwner, QString planetName ) +{ + CoreLogic clogic; + double morale = clogic.generateMorale(); + + double killP = clogic.generateKillPercentage(); + + int productionRate = (int)clogic.generatePlanetProduction(); + + return new Planet( planetName, parentSector, + initialOwner, productionRate, killP, morale ); +} + +double +Planet::getKillPercentage() +{ + return killPercentage; +} + +void +Planet::setKillPercentage( double newValue ) +{ + killPercentage = newValue; + + emit update(); +} + +double +Planet::getMorale() +{ + return morale; +} + +void +Planet::setMorale( double newMorale ) +{ + morale = newMorale; +} + +int +Planet::getProduction() +{ + return productionRate; +} + +void +Planet::setProduction( int newProduction ) +{ + productionRate = newProduction; +} + +void +Planet::select() +{ + parentSector.select(); + + emit selected(); +} + +DefenseFleet &Planet::getFleet() +{ + return homeFleet; +} + +Player * +Planet::getPlayer() const +{ + return owner; +} + +const QString & +Planet::getName() const +{ + return name; +} + +Sector & +Planet::getSector() const +{ + return parentSector; +} + +void +Planet::conquer( AttackFleet *conqueringFleet ) +{ + owner = conqueringFleet->owner; + owner->statPlanetsConquered(1); + homeFleet.become( conqueringFleet ); +} + +void +Planet::coup( Player *luckyPlayer ) +{ + owner = luckyPlayer; +} + +void +Planet::turn() +{ + if( !(owner->isNeutral()) ) { + homeFleet.addShips( productionRate ); + owner->statShipsBuilt( productionRate ); + } else { + homeFleet.addShips( 1 ); + } +} + +//--------------------------------------------------------------------------- +// class Player +//--------------------------------------------------------------------------- +Player::Player( QString newName, QColor newColor, int newPlrNum, bool isAi ) : name( newName ), color( newColor ), +playerNum( newPlrNum ), inPlay( true ), aiPlayer( isAi ), shipsBuilt(0), planetsConquered(0), fleetsLaunched(0), +enemyFleetsDestroyed(0), enemyShipsDestroyed(0) +{ +} + +Player::~Player() +{ +} + +bool +Player::operator==( const Player &otherPlayer ) const +{ + if( playerNum == otherPlayer.playerNum ) + return true; + else + return false; +} + +QString & +Player::getName() +{ + return name; +} + +QString +Player::getColoredName() +{ + return QString("%2").arg(color.name(), name); +} + +Player *Player::createPlayer( QString newName, QColor color, int playerNum, bool isAi ) +{ + return new Player( newName, color, playerNum, isAi ); +} + +Player *Player::createNeutralPlayer() +{ + return new Player( QString::null, gray, NEUTRAL_PLAYER_NUMBER, false ); +} + +QColor &Player::getColor() +{ + return color; +} + +bool +Player::isNeutral() +{ + if( playerNum == NEUTRAL_PLAYER_NUMBER ) { + return true; + } else { + return false; + } +} + +bool Player::isInPlay() +{ + return inPlay; +} + +void Player::setInPlay( bool status ) +{ + inPlay = status; +} + +AttackFleetList & +Player::getAttackList() +{ + return attackList; +} + +bool +Player::NewAttack( Planet *sourcePlanet, Planet *destPlanet, int shipCount, int turn ) +{ + CoreLogic cl; + + double arrival = cl.distance( sourcePlanet, destPlanet ) + turn; + + AttackFleet *fleet = sourcePlanet->getFleet().spawnAttackFleet( destPlanet, shipCount, arrival ); + + + if( fleet ) { + attackList.append(fleet); + + statFleetsLaunched( 1 ); + + return true; + } + + return false; +} + +// Player Statistics collection +void Player::statShipsBuilt( int x ) +{ + shipsBuilt += x; +} + +void Player::statPlanetsConquered( int x ) +{ + planetsConquered += x; +} + +void Player::statFleetsLaunched( int x ) +{ + fleetsLaunched += x; +} + +void Player::statEnemyFleetsDestroyed( int x ) +{ + enemyFleetsDestroyed += x; +} + +void Player::statEnemyShipsDestroyed( int x ) +{ + enemyShipsDestroyed += x; +} + +bool Player::isAiPlayer() { + return aiPlayer; +} + +//--------------------------------------------------------------------------- +// class Fleet +// \---class AttackFleet +// \---class DefenseFleet +//--------------------------------------------------------------------------- + + +Fleet::Fleet( int initialShipCount ) +: shipCount( initialShipCount ) +{ +} + +int +Fleet::getShipCount() +{ + return shipCount; +} + +void +Fleet::removeShips( int lostShips ) +{ + shipCount -= lostShips; +} + +AttackFleet::AttackFleet( Planet *source, Planet *dest, int initialCount, double arrival ) +: Fleet( initialCount ), owner( source->getPlayer() ), destination( dest ), arrivalTurn( arrival ), +killPercentage( source->getKillPercentage() ) +{ +} + +DefenseFleet::DefenseFleet( Planet *newHome, int initialCount ) : Fleet( initialCount ), home( newHome ) +{ +} + +void +DefenseFleet::absorb( AttackFleet *fleet ) +{ + shipCount += fleet->getShipCount(); +} + +void +DefenseFleet::become( AttackFleet *fleet ) +{ + shipCount = fleet->getShipCount(); +} + + +AttackFleet * +DefenseFleet::spawnAttackFleet( Planet *dest, int count, double arrivalTurn ) +{ + if( shipCount < count ) { + return NULL; + } + + AttackFleet *newFleet = new AttackFleet( home, dest, count, arrivalTurn ); + + removeShips( count ); + + return newFleet; +} + +void +DefenseFleet::addShips( int newShips ) +{ + shipCount += newShips; +} + diff --git a/konquest/gamecore.h b/konquest/gamecore.h new file mode 100644 index 00000000..c8c7c39a --- /dev/null +++ b/konquest/gamecore.h @@ -0,0 +1,331 @@ +#ifndef _GAMECORE_H_ +#define _GAMECORE_H_ + +#include + +#include +#include +#include +#include + +// Board Size Constants +#define BOARD_ROWS 16 +#define BOARD_COLS 16 + +// Maximum Number of Players +#define MAX_PLAYERS 10 + + +//********************************************************** +// Forward declarations for classes in this file +//********************************************************** +class Player; +class Planet; +class Sector; +class Map; +class Fleet; + + +//********************************************************** +// Core Logic routines +//********************************************************** + +class CoreLogic +{ +public: + CoreLogic(); + + void generatePlanetCoordinates( int &x, int &y ); + double generateKillPercentage(); + int generatePlanetProduction(); + double generateMorale(); + + double distance( Planet *p1, Planet *p2 ); + + double roll(); + +private: + KRandomSequence random; + static bool class_init; +}; + + +//********************************************************** +// class Fleet +// \--- class AttackFleet +// \--- class DefenseFleet +//********************************************************** + +class Fleet : public QObject +{ + +public: + + Fleet( int initialShipCount ); + virtual ~Fleet() {} + + int getShipCount(); + void removeShips( int lostShips ); + +protected: + int shipCount; +}; + +class AttackFleet : public Fleet +{ + +public: + AttackFleet( Planet *source, Planet *dest, int initialCount, double arrivalTurn ); + + Player *owner; + Planet *destination; + double arrivalTurn; + double killPercentage; + +}; + + +class DefenseFleet : public Fleet +{ + +public: + DefenseFleet( Planet *newHome, int initialCount ); + + void absorb( AttackFleet *fleet ); + void become( AttackFleet *fleet ); + + void addShips( int newShips ); + + AttackFleet *spawnAttackFleet( Planet *destination, int shipCount, double arrivalTurn ); + + Planet *home; + +}; + + +//************************************************************** +// class Player +//************************************************************** + +class Player : public QObject +{ + +public: + Player( QString newName, QColor color, int number, bool isAi ); + virtual ~Player(); + + enum { NEUTRAL_PLAYER_NUMBER = -1 }; + +public: + QString &getName(); + QString getColoredName(); + QColor &getColor(); + bool isNeutral(); + QPtrList &getAttackList(); + + // factory functions + static Player *createPlayer( QString newName, QColor newColor, int playerNum, bool isAi ); + static Player *createNeutralPlayer(); + + bool NewAttack( Planet *sourcePlanet, Planet *destPlanet, int shipCount, int departureTurn ); + + bool operator==( const Player &otherPlayer ) const; + + bool isInPlay(); + void setInPlay( bool ); + +private: + QString name; + QColor color; + int playerNum; + bool inPlay; + bool aiPlayer; + + QPtrList attackList; + + // statistics counters + int shipsBuilt; + int planetsConquered; + int fleetsLaunched; + int enemyFleetsDestroyed; + int enemyShipsDestroyed; + +public: + void statShipsBuilt( int ); + void statPlanetsConquered( int ); + void statFleetsLaunched( int ); + void statEnemyFleetsDestroyed( int ); + void statEnemyShipsDestroyed( int ); + + int getShipsBuilt() { return shipsBuilt; } + int getPlanetsConquered() { return planetsConquered; } + int getFleetsLaunched() { return fleetsLaunched; } + int getEnemyFleetsDestroyed() { return enemyFleetsDestroyed; } + int getEnemyShipsDestroyed() { return enemyShipsDestroyed; } + bool isAiPlayer(); + +}; + + +//************************************************************** +// class Planet +//************************************************************** + +class Planet : public QObject +{ + Q_OBJECT + +private: + + Planet( QString planetName, Sector &newParentSector, + Player *initialOwner, int newProd, + double newKillP, double newMorale ); + +public: + virtual ~Planet(); + + static Planet *createPlayerPlanet( Sector &parentSector, + Player *initialOwner, QString planetName ); + static Planet *createNeutralPlanet( Sector &parentSector, + Player *initialOwner, QString planetName ); + + Sector &getSector() const; + Player *getPlayer() const; + const QString &getName() const; + DefenseFleet &getFleet(); + + double getKillPercentage(); + void setKillPercentage( double newValue ); + double getMorale(); + void setMorale( double ); + int getProduction(); + void setProduction( int ); + + void select(); + void conquer( AttackFleet *conqueringFleet ); + void coup( Player *luckyPlayer ); + void turn(); + +signals: + void update(); + void selected(); + +private: + QString name; + Player *owner; + Sector &parentSector; + DefenseFleet homeFleet; + + double killPercentage; + double morale; + int productionRate; +}; + +//*************************************************************** +// class Sector +//*************************************************************** + +class Sector : public QObject +{ + Q_OBJECT + +public: + + // constructors + Sector(); + Sector( Map *parentMap, int xpos, int ypos ); + Sector( const Sector & ); + + // assignment operator (makes initialization easy) + Sector &operator=( const Sector & ); + + bool hasPlanet() const; + void setPlanet( Planet *newPlanet ); + Planet *getPlanet(); + void removePlanet(); + + void select(); + + int getRow(); + int getColumn(); + +signals: + void update(); + void selected(); + +protected slots: + void childPlanetUpdate( ); + + +protected: + Planet *planet; // a sector has 0 or 1 planets + Map *parentMap; + int x, y; + +}; + +//***************************************************************** +// class Map +//***************************************************************** + +class Map : public QObject +{ + Q_OBJECT + +public: + Map(); + virtual ~Map(); + + const int getRows() const; + const int getColumns() const; + + void populateMap( QPtrList &players, Player *neutral, + int numNeutralPlanets, QPtrList &thePlanets ); + void clearMap(); + + bool selectedSector( int &x, int &y ) const; + void setSelectedSector( int x, int y ); + void setSelectedSector( const Planet & ); + void setSelectedSector(); + + Sector &getSector( int x, int y ); + +protected slots: + void childSectorUpdate(); + +signals: + void update(); + +protected: + void Freeze(); + void Thaw(); + bool freezeUpdates; + +private: + Sector &findRandomFreeSector(); + +protected: + Sector grid[BOARD_ROWS][BOARD_COLS]; // a map is a 2-D array of sectors; + const int rows; // size of grid in sectors + const int columns; + + // This is used to implement a selected sector, + // one who's boarder flashes. + bool hasSelectedSector; + int sel_x, sel_y; +}; + +//--------------------------------------------------------------------------------- +// Typedefs +//--------------------------------------------------------------------------------- +typedef QPoint Coordinate; // Gotta start using this instead of int x,y crap +typedef QPtrList AttackFleetList; +typedef QPtrListIterator AttackFleetListIterator; +typedef QPtrList PlayerList; +typedef QPtrList PlanetList; +typedef QPtrListIterator PlayerListIterator; +typedef QPtrListIterator PlanetListIterator; + +#endif // _GAMECORE_H_ + + diff --git a/konquest/gameenddlg.cc b/konquest/gameenddlg.cc new file mode 100644 index 00000000..080a8477 --- /dev/null +++ b/konquest/gameenddlg.cc @@ -0,0 +1,76 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "gameenddlg.h" +#include "gameenddlg.moc" + +GameEndDlg::GameEndDlg( QWidget *parent ) + : KDialogBase( i18n("Out of Turns"), + KDialogBase::Yes|KDialogBase::No, KDialogBase::Yes, KDialogBase::No, + parent, "end_game_dialog", true, true ) +{ + QVBox *page = makeVBoxMainWidget(); + + // Create controls + QLabel *label1 = new QLabel( i18n("This is the last turn.\nDo you wish to add extra turns?")+"\n\n", page ); + label1->setAlignment( AlignCenter ); + + turnCountLbl = new QLabel( page ); + turnCount = new QSlider( 1, 40, 1, 5, Qt::Horizontal, page ); + + KGuiItem addTurns(i18n("&Add Turns"), QString::null, QString::null, + i18n("Add the specified number of turns to the game and continue playing.")); + KGuiItem gameOver(i18n("&Game Over"), QString::null, QString::null, + i18n("Terminate the current game.")); + + setButtonGuiItem(KDialogBase::Yes, addTurns); + setButtonGuiItem(KDialogBase::No, gameOver); + + init(); + + connect( turnCount, SIGNAL(valueChanged( int )), this, SLOT(turnCountChange( int )) ); +} + +GameEndDlg::~GameEndDlg() +{ +} + +void +GameEndDlg::init() +{ + KConfig *config = kapp->config(); + config->setGroup("Game"); + int turns = config->readNumEntry("ExtraTurns", 10); + turnCount->setValue(turns); + turnCountChange(turns); +} + +void +GameEndDlg::slotYes() +{ + KConfig *config = kapp->config(); + config->setGroup("Game"); + config->writeEntry("ExtraTurns", extraTurns()); + config->sync(); + KDialogBase::slotYes(); +} + +int +GameEndDlg::extraTurns() +{ + return turnCount->value(); +} + +void +GameEndDlg::turnCountChange( int newTurnCount ) +{ + QString newLbl = i18n("Extra turns: %1").arg( newTurnCount ); + turnCountLbl->setText( newLbl); +} diff --git a/konquest/gameenddlg.h b/konquest/gameenddlg.h new file mode 100644 index 00000000..d1c982e3 --- /dev/null +++ b/konquest/gameenddlg.h @@ -0,0 +1,32 @@ +#ifndef _GAMEENDDLG_H_ +#define _GAMEENDDLG_H_ + +#include + +class QSlider; +class QPushButton; + +class GameEndDlg : public KDialogBase +{ + Q_OBJECT + +public: + GameEndDlg( QWidget *parent ); + virtual ~GameEndDlg(); + + int extraTurns(); + +private: + void init(); + +private slots: + void turnCountChange( int ); + void slotYes(); + +private: + QSlider *turnCount; + QLabel *turnCountLbl; +}; + +#endif // _GAMEENDDLG_H_ + diff --git a/konquest/hi128-app-konquest.png b/konquest/hi128-app-konquest.png new file mode 100644 index 00000000..3d980350 Binary files /dev/null and b/konquest/hi128-app-konquest.png differ diff --git a/konquest/hi16-app-konquest.png b/konquest/hi16-app-konquest.png new file mode 100644 index 00000000..87f6a2d5 Binary files /dev/null and b/konquest/hi16-app-konquest.png differ diff --git a/konquest/hi22-app-konquest.png b/konquest/hi22-app-konquest.png new file mode 100644 index 00000000..c9165e2f Binary files /dev/null and b/konquest/hi22-app-konquest.png differ diff --git a/konquest/hi32-app-konquest.png b/konquest/hi32-app-konquest.png new file mode 100644 index 00000000..fc252dfe Binary files /dev/null and b/konquest/hi32-app-konquest.png differ diff --git a/konquest/hi48-app-konquest.png b/konquest/hi48-app-konquest.png new file mode 100644 index 00000000..bd67b1b9 Binary files /dev/null and b/konquest/hi48-app-konquest.png differ diff --git a/konquest/hi64-app-konquest.png b/konquest/hi64-app-konquest.png new file mode 100644 index 00000000..7014729e Binary files /dev/null and b/konquest/hi64-app-konquest.png differ diff --git a/konquest/images.h b/konquest/images.h new file mode 100644 index 00000000..4c436a18 --- /dev/null +++ b/konquest/images.h @@ -0,0 +1,17 @@ +#ifndef _IMAGES_H +#include +#define _IMAGES_H + +#define IMAGE_SPLASH locate("appdata", "pics/konquest-splash.png") +#define IMAGE_PLANET_1 locate("appdata", "pics/planet1.xpm") +#define IMAGE_PLANET_2 locate("appdata", "pics/planet2.xpm") +#define IMAGE_PLANET_3 locate("appdata", "pics/planet3.xpm") +#define IMAGE_PLANET_4 locate("appdata", "pics/planet4.xpm") +#define IMAGE_PLANET_5 locate("appdata", "pics/planet5.xpm") +#define IMAGE_PLANET_6 locate("appdata", "pics/planet6.xpm") +#define IMAGE_PLANET_7 locate("appdata", "pics/planet7.xpm") +#define IMAGE_PLANET_8 locate("appdata", "pics/planet8.xpm") +#define IMAGE_PLANET_9 locate("appdata", "pics/planet9.xpm") + +#endif // _IMAGES_H + diff --git a/konquest/int_validator.cc b/konquest/int_validator.cc new file mode 100644 index 00000000..85fd1779 --- /dev/null +++ b/konquest/int_validator.cc @@ -0,0 +1,53 @@ +#include + +#include "int_validator.h" +#include "int_validator.moc" + +IntValidator::IntValidator( QWidget *parent, const char *name ) : + QValidator( parent, name ) +{ +#ifdef INT_MIN + v_bottom = INT_MIN; +#else + v_bottom = ~INT_MAX; +#endif + v_top = INT_MIN; +} + +IntValidator::IntValidator( int bottom, int top, QWidget *parent, const char *name ) : +QValidator( parent, name ) +{ + v_bottom = bottom; + v_top = top; +} + +IntValidator::~IntValidator() {} + +QValidator::State +IntValidator::validate( QString &input, int & ) const +{ + if( input.isEmpty() ) { + return QValidator::Valid; + } else { + bool ok; + + int value = input.toInt( &ok ); + + if( !ok ) + return QValidator::Invalid; + + if( value < v_bottom || value > v_top ) + return QValidator::Valid; + + return QValidator::Acceptable; + } +} + +void +IntValidator::setRange( int b, int t ) +{ + v_bottom = b; + v_top = t; +} + + diff --git a/konquest/int_validator.h b/konquest/int_validator.h new file mode 100644 index 00000000..e5973fee --- /dev/null +++ b/konquest/int_validator.h @@ -0,0 +1,31 @@ +#ifndef _INT_VALIDATOR_H_ +#define _INT_VALIDATOR_H_ + +#include + + +class IntValidator : public QValidator +{ + Q_OBJECT + +public: + IntValidator( QWidget *parent, const char *name = 0 ); + IntValidator( int bottom, int top, QWidget *parent, const char *name = 0 ); + + virtual ~IntValidator(); + + virtual QValidator::State validate( QString &, int & ) const; + + virtual void setRange( int bottom, int top ); + + int bottom() const { return v_bottom; } + int top() const { return v_top; } + +private: + int v_bottom, v_top; +}; + + + +#endif /* _INT_VALIDATOR_H_ */ + diff --git a/konquest/konquest.desktop b/konquest/konquest.desktop new file mode 100644 index 00000000..e300059d --- /dev/null +++ b/konquest/konquest.desktop @@ -0,0 +1,74 @@ +[Desktop Entry] +Icon=konquest +Name=Konquest +Name[ar]=لعبة الإحتلال (Konquest) +Name[be]=Заваёва +Name[bn]=কনকোয়েসà§à¦Ÿ +Name[br]=KAloubadur +Name[da]=Erobring +Name[eo]=Konkero +Name[et]=Vallutus +Name[hi]=कॉनà¥à¤•à¥à¤µà¥‡à¤¸à¥à¤Ÿ +Name[hu]=Hódítás +Name[is]=Geimstyrjöld +Name[lt]=Užkariautojas +Name[ne]=कनकà¥à¤µà¥‡à¤·à¥à¤Ÿ +Name[pt]=Conquista +Name[ta]=கானà¯à®•à¯à®•à¯à®µà¯†à®¸à¯à®Ÿà¯ +Name[tg]=ИÑтило +Name[zh_TW]=Konquest å¾æœ +Exec=konquest %i %m -caption %c +Type=Application +GenericName=Galactic Strategy Game +GenericName[be]=СтратÑÐ³Ñ–Ñ‡Ð½Ð°Ñ Ð³Ð°Ð»Ð°ÐºÑ‚Ñ‹Ñ‡Ð½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ +GenericName[bg]=ГалактичеÑка игра +GenericName[bn]=গà§à¦¯à¦¾à¦²à¦¾à¦•à¦Ÿà¦¿à¦• কৌশলের খেলা +GenericName[bs]=GalaktiÄka strateÅ¡ka igra +GenericName[ca]=Joc d'estratègia galàctica +GenericName[cs]=Strategická galaktická hra +GenericName[cy]=Gêm Strategaeth Galaethol +GenericName[da]=Galaktisk strategispil +GenericName[de]=Galaktisches Strategiespiel +GenericName[el]=Γαλαξιακό παιχνίδι στÏατηγικής +GenericName[eo]=Galaksia Strategiludo +GenericName[es]=Juego de estrategia espacial +GenericName[et]=Galaktiline strateegiamäng +GenericName[eu]=Estrategia galaktikoaren jokoa +GenericName[fa]=بازی Galactic Strategy +GenericName[fi]=Galaktinen avaruuspeli +GenericName[fr]=Jeu de stratégie galactique +GenericName[he]=משחק ×סטרטגיה גלקטי +GenericName[hr]=GalaktiÄka igra strategije +GenericName[hu]=Stratégiai +GenericName[is]=Herkænskuleikur +GenericName[it]=Gioco galattico di strategia +GenericName[ja]=宇宙戦略ゲーム +GenericName[km]=ល្បែង​យុទ្ធសាស្ážáŸ’រ​ផ្កាយ +GenericName[ko]=우주 ì „ëžµ 게임 +GenericName[lt]=Galaktikos strateginis žaidimas +GenericName[lv]=StratÄ“Ä£iska kosmiskÄ spÄ“le +GenericName[mk]=Игра на галактичка Ñтратегија +GenericName[nb]=Strategispillet Galactic +GenericName[nds]= Galaktsch Strategiespeel +GenericName[ne]=गà¥à¤¯à¤¾à¤²à¤¾à¤•à¥à¤Ÿà¤¿à¤• रणनीति खेल +GenericName[nl]=Strategisch ruimtespel +GenericName[nn]=Strategispelet Galactic +GenericName[pl]=Gra strategiczna w kosmosie +GenericName[pt]=Jogo de Estratégia Galáctica +GenericName[pt_BR]=Jogo de Estratégia galáctico +GenericName[ru]=Завоевание +GenericName[se]=Strategiijaspeallu Galactic +GenericName[sk]=Galaktická strategická hra +GenericName[sl]=GalaktiÄna strateÅ¡ka igra +GenericName[sr]=Галактичка Ñтратешка игра +GenericName[sr@Latn]=GalaktiÄka strateÅ¡ka igra +GenericName[sv]=Galaktiskt strategispel +GenericName[ta]=கலாடà¯à®Ÿà®¿à®•à¯ தநà¯à®¤à®¿à®° விளையாடà¯à®Ÿà¯ +GenericName[uk]=Галактична гра на Ñтратегію +GenericName[wa]=Djeu di stratedjeye galactike +GenericName[zh_TW]=銀河戰略éŠæˆ² +Terminal=false +DocPath=konquest/index.html +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;StrategyGame; diff --git a/konquest/konquestui.rc b/konquest/konquestui.rc new file mode 100644 index 00000000..aff9e71a --- /dev/null +++ b/konquest/konquestui.rc @@ -0,0 +1,21 @@ + + + + + &Game + + + + + + +Main Toolbar + + + + + + + + + diff --git a/konquest/mainwin.cc b/konquest/mainwin.cc new file mode 100644 index 00000000..496d3ac0 --- /dev/null +++ b/konquest/mainwin.cc @@ -0,0 +1,77 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "version.h" +#include "gamecore.h" +#include "mainwin.h" +#include "mainwin.moc" +#include "gameboard.h" + +// KonquestMainWindow + + +MainWindow::MainWindow() +{ + setCaption( i18n("Galactic Conquest") ); + + setupGameBoard(); + setupKAction(); + setupGUI(); +} + +MainWindow::~MainWindow() +{ +} + +void +MainWindow::setupKAction() +{ + KStdGameAction::gameNew( gameBoard, SLOT( startNewGame() ), actionCollection() ); + KStdGameAction::quit( this, SLOT( close() ), actionCollection() ); + endAction = KStdGameAction::end( gameBoard, SLOT( shutdownGame() ), actionCollection() ); + endAction->setEnabled(false); + + //AB: there is no icon for disabled - KToolBar::insertButton shows the + //different state - KAction not :-( + measureAction = new KAction( i18n("&Measure Distance"), "ruler", 0, gameBoard, SLOT( measureDistance() ), actionCollection(), "game_measure" ); + measureAction->setEnabled(false); + standingAction = new KAction( i18n("&Show Standings"), "help", 0, gameBoard, SLOT( showScores() ), actionCollection(), "game_scores" ); + standingAction->setEnabled(false); + fleetAction = new KAction( i18n("&Fleet Overview"), "launch", 0, gameBoard, SLOT( showFleets() ), actionCollection(), "game_fleets" ); + fleetAction->setEnabled(false); + toolBar()->setBarPos( KToolBar::Left ); + toolBar()->setMovingEnabled( false ); +} + +void +MainWindow::setupGameBoard() +{ + gameBoard = new GameBoard( this ); + setCentralWidget(gameBoard); + + connect( gameBoard, SIGNAL( newGameState( GameState )), this, SLOT( gameStateChange( GameState ) ) ); +} + + +void +MainWindow::gameStateChange( GameState newState ) +{ + endAction->setEnabled( gameBoard->isGameInProgress() ); + measureAction->setEnabled( newState==SOURCE_PLANET ); + standingAction->setEnabled( newState==SOURCE_PLANET ); + fleetAction->setEnabled( newState==SOURCE_PLANET ); +} + + + diff --git a/konquest/mainwin.h b/konquest/mainwin.h new file mode 100644 index 00000000..2954039c --- /dev/null +++ b/konquest/mainwin.h @@ -0,0 +1,33 @@ +#ifndef _MAIN_WIN_H +#define _MAIN_WIN_H + +#include + +#include "gameboard.h" + +class ConquestMap; +class PlanetStatusTable; + +class MainWindow : public KMainWindow +{ + Q_OBJECT + +public: + MainWindow(); + ~MainWindow(); + +protected: + void setupKAction(); + void setupGameBoard(); + +private: + GameBoard *gameBoard; + KAction *endAction, *measureAction, *standingAction, *fleetAction; + +private slots: + void gameStateChange( GameState ); + +}; + +#endif + diff --git a/konquest/map_widget.cc b/konquest/map_widget.cc new file mode 100644 index 00000000..9bb1a632 --- /dev/null +++ b/konquest/map_widget.cc @@ -0,0 +1,243 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include "map_widget.moc" + +ConquestMap::ConquestMap( Map *newMap, QWidget *parent ) + : QGridView( parent ), + SECTOR_HEIGHT( 28 ), SECTOR_WIDTH( 28 ), + BOARD_HEIGHT( newMap->getRows() * SECTOR_HEIGHT ), + BOARD_WIDTH( newMap->getColumns() * SECTOR_WIDTH ), + map( newMap ), gridColor( 50, 80, 50 ), + hiLiteRow( -1 ), hiLiteCol( -1 ) +{ + labelFont = KGlobalSettings::generalFont(); + labelFont.setPointSize( 8 ); + + setFrameStyle( NoFrame ); + setPaletteBackgroundColor( black ); + setMinimumSize( BOARD_HEIGHT, BOARD_WIDTH ); + + setCellWidth( SECTOR_WIDTH ); + setCellHeight( SECTOR_HEIGHT ); + setNumRows( map->getRows() ); + setNumCols( map->getColumns() ); + + setMinimumSize( BOARD_HEIGHT, BOARD_WIDTH ); + setMaximumSize( BOARD_HEIGHT, BOARD_WIDTH ); + + connect( map, SIGNAL( update() ), this, SLOT( mapUpdate() ) ); + + QTimer *timer = new QTimer( this ); + connect( timer, SIGNAL(timeout()), this, SLOT(squareBlink()) ); + timer->start( 500, false ); + + viewport()->setMouseTracking( true ); + setMouseTracking( true ); + + show(); + + +} + +ConquestMap::~ConquestMap() +{ +} + + +void +ConquestMap::contentsMousePressEvent( QMouseEvent *e ) +{ + int row, col; + + row = rowAt( e->y() ); + col = columnAt( e->x() ); + + if( map->getSector( row, col ).hasPlanet() ) { + emit planetSelected( map->getSector( row, col ).getPlanet() ); + } + +} + +void +ConquestMap::contentsMouseMoveEvent( QMouseEvent *e ) +{ + // highlight the square under the mouse + int row, col; + + row = rowAt( e->y() ); + col = columnAt( e->x() ); + + // Check to make sure the mouse is in a valid grid location + if( (row < 0 || col < 0) || + (row >= BOARD_ROWS || col >= BOARD_COLS) ) { + return; + } + + + if( (hiLiteRow != -1) && (hiLiteCol != -1) ) { + QPainter p( viewport() ); + + p.translate( hiLiteCol * cellWidth(), hiLiteRow * cellHeight() ); + + drawSector( &p, map->getSector(hiLiteRow,hiLiteCol) ); + + hiLiteRow = -1; + hiLiteCol = -1; + + } + + if( map->getSector( row, col ).hasPlanet() ) { + QPainter p( viewport() ); + + p.translate( col * cellWidth(),row * cellHeight() ); + + drawSector( &p, map->getSector(row,col), false, true ); + emit planetHighlighted(map->getSector( row, col ).getPlanet() ); + + hiLiteRow = row; + hiLiteCol = col; + + } + +} + +void +ConquestMap::unselectPlanet() +{ + map->setSelectedSector(); +} + + +void +ConquestMap::paintCell( QPainter *p, int row, int col ) +{ + drawSector( p, map->getSector( row, col ) ); +} + +void +ConquestMap::squareBlink() +{ + static bool blinkState = true; + + int row, col; + if( map->selectedSector( row, col ) ) { + QPainter p( this, true ); + + p.translate( col * cellWidth(), row * cellHeight() ); + + if( blinkState ) { + drawSector( &p, map->getSector(row,col), true ); + } else { + drawSector( &p, map->getSector(row,col), false ); + } + } + + if( blinkState ) + blinkState = false; + else + blinkState = true; +} + + +void +ConquestMap::mapUpdate() +{ + viewport()->repaint(false); +} + + +void +ConquestMap::drawSector( QPainter *p, Sector §or, bool borderStrobe, bool highlight ) +{ + QColor labelColor( white ); + QPoint labelCorner; + + if( sector.hasPlanet() ) { + QPixmap pm; + + // simple (pathetic) way to "randomize" + // the planet graphic + // and also a really dirty hack to make the planet + // name more visible (hard coded pixel offsets) + switch( ((sector.getRow()+sector.getColumn()) % 9) + 1 ) { + case 1 : + pm = QPixmap( IMAGE_PLANET_1 ); + labelCorner = QPoint( 18, 14 ); + break; + case 2 : + pm = QPixmap( IMAGE_PLANET_2 ); + labelCorner = QPoint( 2, 14 ); + break; + case 3 : + pm = QPixmap( IMAGE_PLANET_3 ); + labelCorner = QPoint( 2, 26 ); + break; + case 4 : + pm = QPixmap( IMAGE_PLANET_4 ); + labelCorner = QPoint( 18, 26 ); + break; + case 5 : + pm = QPixmap( IMAGE_PLANET_5 ); + labelCorner = QPoint( 18, 26 ); + break; + case 6 : + pm = QPixmap( IMAGE_PLANET_6 ); + labelCorner = QPoint( 18, 26 ); + break; + case 7 : + pm = QPixmap( IMAGE_PLANET_7 ); + labelCorner = QPoint( 18, 26 ); + break; + case 8 : + pm = QPixmap( IMAGE_PLANET_8 ); + labelCorner = QPoint( 18, 26 ); + break; + case 9 : + pm = QPixmap( IMAGE_PLANET_9 ); + labelCorner = QPoint( 18, 26 ); + break; + } + + QPoint pos; + + pos.setX( ( SECTOR_HEIGHT / 2 ) - ( pm.height() / 2 ) ); + pos.setY( ( SECTOR_WIDTH / 2 ) - ( pm.width() / 2 ) ); + + p->drawPixmap( pos, pm, QRect(0, 0, pm.height(), pm.width() ) ); + + p->setFont( labelFont ); + p->setPen( labelColor ); + + p->drawText( labelCorner, sector.getPlanet()->getName() ); + + if( borderStrobe ) { + QPen gridPen( sector.getPlanet()->getPlayer()->getColor() ); + p->setPen( gridPen ); + } else if( highlight ) { + QPen gridPen( white ); + p->setPen( gridPen ); + } else { + QPen gridPen( gridColor ); + p->setPen( gridPen ); + } + + } else { + p->eraseRect( 0, 0, SECTOR_WIDTH, SECTOR_HEIGHT ); + + QPen gridPen( gridColor ); + + p->setPen( gridPen ); + } + + p->drawRect( 0, 0, SECTOR_HEIGHT, SECTOR_WIDTH ); + +} + + diff --git a/konquest/map_widget.h b/konquest/map_widget.h new file mode 100644 index 00000000..052d6e9d --- /dev/null +++ b/konquest/map_widget.h @@ -0,0 +1,59 @@ +#ifndef _MAP_WIDGET_H +#define _MAP_WIDGET_H + + +#include +#include +#include + +#include + +#include + +#include "gamecore.h" +#include "images.h" + +class ConquestMap : public QGridView +{ + Q_OBJECT + + // Constructors +public: + ConquestMap( Map *newMap, QWidget *parent = 0 ); + virtual ~ConquestMap(); + + // Interface +public: + void unselectPlanet(); + +protected: + virtual void contentsMousePressEvent( QMouseEvent *e ); + virtual void contentsMouseMoveEvent( QMouseEvent *e ); + virtual void paintCell( QPainter *p, int row, int col ); + +private slots: + void mapUpdate(); + void squareBlink(); + +signals: + void planetSelected( Planet * ); + void planetHighlighted( Planet * ); + +private: + const int SECTOR_HEIGHT; + const int SECTOR_WIDTH; + + const int BOARD_HEIGHT; + const int BOARD_WIDTH; + + void drawSector( QPainter *, Sector &, bool borderStrobe = true, bool highlight = false ); + + Map *map; + QColor gridColor; + QFont labelFont; + + int hiLiteRow, hiLiteCol; +}; + + +#endif diff --git a/konquest/minimap.cc b/konquest/minimap.cc new file mode 100644 index 00000000..eee1237b --- /dev/null +++ b/konquest/minimap.cc @@ -0,0 +1,79 @@ +#include +#include + +#include +#include + +#include "minimap.h" +#include "minimap.moc" + +MiniMap::MiniMap( QWidget *parent, const char *name ) + : QGridView( parent, name ), + SECTOR_HEIGHT( 12 ), SECTOR_WIDTH( 12 ), + BOARD_HEIGHT( 10 * SECTOR_HEIGHT ), + BOARD_WIDTH( 10 * SECTOR_WIDTH ), + map( 0 ) +{ + setFrameStyle( NoFrame ); + setPaletteBackgroundColor( black ); + setMinimumSize( BOARD_HEIGHT, BOARD_WIDTH ); + + setCellWidth( SECTOR_WIDTH ); + setCellHeight( SECTOR_HEIGHT ); + setNumRows( 10 ); + setNumCols( 10 ); + + setMinimumSize( BOARD_HEIGHT, BOARD_WIDTH ); + setMaximumSize( BOARD_HEIGHT, BOARD_WIDTH ); +} + +void +MiniMap::setMap(Map *newMap) +{ + map = newMap; + BOARD_HEIGHT = map->getRows() * SECTOR_HEIGHT; + BOARD_WIDTH = map->getColumns() * SECTOR_WIDTH; + setNumRows( map->getRows() ); + setNumCols( map->getColumns() ); + + setMinimumSize( BOARD_HEIGHT, BOARD_WIDTH ); + setMaximumSize( BOARD_HEIGHT, BOARD_WIDTH ); + + connect( map, SIGNAL( update() ), this, SLOT( mapUpdate() ) ); +} + +MiniMap::~MiniMap() +{ +} + + +void +MiniMap::paintCell( QPainter *p, int row, int col ) +{ + drawSector( p, map->getSector( row, col ) ); +} + +void +MiniMap::mapUpdate() +{ + updateContents(); +} + + +void +MiniMap::drawSector( QPainter *p, Sector §or ) +{ + QRect r( 0, 0, SECTOR_WIDTH, SECTOR_HEIGHT ); + + p->setPen( black ); + p->setBrush( black ); + p->drawRect( r ); + + if( sector.hasPlanet() ) { + p->setPen( sector.getPlanet()->getPlayer()->getColor() ); + p->setBrush( sector.getPlanet()->getPlayer()->getColor() ); + + p->drawPie( r, 0, (360 * 16)-1 ); + } +} + diff --git a/konquest/minimap.h b/konquest/minimap.h new file mode 100644 index 00000000..9d205abb --- /dev/null +++ b/konquest/minimap.h @@ -0,0 +1,44 @@ +#ifndef _MINIMAP_H +#define _MINIMAP_H + +#include +#include +#include +#include +#include + +#include "gamecore.h" +#include "images.h" + + +class MiniMap : public QGridView +{ + Q_OBJECT + + // Constructors +public: + MiniMap( QWidget *parent = 0, const char* name = 0 ); + virtual ~MiniMap(); + + void setMap( Map *newMap ); + +protected: + void paintCell( QPainter *p, int row, int col ); + +private slots: + void mapUpdate(); + +private: + int SECTOR_HEIGHT; + int SECTOR_WIDTH; + + int BOARD_HEIGHT; + int BOARD_WIDTH; + + void drawSector( QPainter *, Sector & ); + + Map *map; +}; + + +#endif // _MINIMAP_H_ diff --git a/konquest/newGameDlg_ui.ui b/konquest/newGameDlg_ui.ui new file mode 100644 index 00000000..2768e5f8 --- /dev/null +++ b/konquest/newGameDlg_ui.ui @@ -0,0 +1,440 @@ + +NewGameDlgUI + + + NewGameDlgUI + + + + 0 + 0 + 661 + 461 + + + + + unnamed + + + + layout9 + + + + unnamed + + + + layout4 + + + + unnamed + + + + labelPlayers + + + + + + sliderPlayers + + + + + sliderPlayers + + + 2 + + + 9 + + + Horizontal + + + + + labelPlayerList + + + Player list: + + + listPlayers + + + + + + Name + + + true + + + true + + + + + New Column + + + true + + + true + + + + listPlayers + + + + + layout3 + + + + unnamed + + + + labelNewPlayer + + + &Human player: + + + newPlayer + + + + + newPlayer + + + + 7 + 0 + 1 + 0 + + + + + + + + layout2 + + + + unnamed + + + + spacer2 + + + Horizontal + + + Expanding + + + + 0 + 20 + + + + + + addPlayer + + + &Add Human Player + + + + + spacer1 + + + Horizontal + + + Expanding + + + + 0 + 20 + + + + + + + + + + spacer5 + + + Horizontal + + + Fixed + + + + 20 + 20 + + + + + + layout8 + + + + unnamed + + + + labelPlanets + + + + + + sliderPlanets + + + + + sliderPlanets + + + 1 + + + 35 + + + 1 + + + Horizontal + + + + + labelMap + + + Preview map: + + + + + layout7 + + + + unnamed + + + + spacer7 + + + Horizontal + + + Expanding + + + + 0 + 20 + + + + + + map + + + + + spacer7_2 + + + Horizontal + + + Expanding + + + + 0 + 20 + + + + + + + + spacer6 + + + Vertical + + + Expanding + + + + 20 + 16 + + + + + + layout5 + + + + unnamed + + + + spacer3 + + + Horizontal + + + Expanding + + + + 31 + 20 + + + + + + rejectMap + + + Reject &Map + + + + + spacer4 + + + Horizontal + + + Expanding + + + + 51 + 20 + + + + + + + + + + + + line1 + + + HLine + + + Sunken + + + Horizontal + + + + + labelTurns + + + + + + sliderTurns + + + + + sliderTurns + + + 5 + + + 40 + + + 5 + + + Horizontal + + + + + + + MiniMap +
minimap.h
+ + 250 + 250 + + 0 + + 0 + 0 + 0 + 0 + + image0 +
+
+ + + 89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000003b149444154789cad945f4c5b551cc73fe7dc4b7b4bcba0762d45c43114323599ee6192609c51d883892ce083f1718b3ebb185f8dc91e972cf39d2d2a2f1af664b6f1e0fe3863a0718969700eb0c52142da0242a1bd6d696f7bcff101585203ceb8fd9ece39f99dcff9fe7edf939f88c562ec465f5f9fe609442c161362173c3e3eae7b7a7ac8e7f36432196cdbfe4f907c3e4f2291201e8fe338cec3737357e9e8e828aded1e229d650e1f2d51754b082110124c13a4dc5ea341eb9dc284c0558a853f3ce8cb0677ef500fde7d39d2596679e326597b8e9abb85d7a770ab16ab6983ec5a05b487a70e36f0f4e10afe408d6a558310980108478dba4a1e8233990c5d474b64ed39aa3a8fe5f3317fbf81dbd70bccfeb205947632fd74f6589c1c6ea2f70d03a58ba0c1f2c9bdc1b66de3b8256a6e11cbe7e3ee1d181b590124fe2693aeee08d223c82c3a2c24b7b874bec8f26288774f7bd054504aef0dde6e99c0eb83f9fb266323cb80a27fb0958141836044605a2ee5523393371cc646fee2da37195aa35d0c0c5b4859ac03d7e91712dcaac5adab3650a3ff9d08ef7dd8404bb48869e5d958b5b87dadc4c9a1464e9f0d0326df7ebd86bd2e310cb1bf62d384d59441f2d70a070e1c60e09489929b988681bdd9cc97170bcc4c65595f71f8e0e3301337fc24a7732467831875a47f289652b0be5e4151e6d07316c1b0c0340d8ab92023e76d66a6b2840e36d2fb7a13fee632475e6edc367ea98a90fb98b7dd6310ca0328a44761582e1bab41befabcc0ec940d28bc5e93b68e064cab84e1d9beaeb48934eac1f53b01c1b000fca496aa54b61a99fcde61662a4b4b4b23d1680be9d426173e4df3602a48ea411989a4fd590f52a8fd156b05ed9d350e3defe3cfdf4b4c7ce770ea7d3fb9f520afbe1620daeee5c26735d20b9b9cfb6811a754a439e4e5c5639a4caa1e5caf586bfc0197b78702005cb9b4cae4cd3267ce8638fe964bd72b393e39d74928d242617303a756a37f284447770dcdbffc6384a05a85de1306e9a52057c7527c7131c3c42d3f475eb2303c82d4fc3276d6811db37efeb148723082d9b08f79f97c1e5729109a9a28307cc622d2d6cdf52b2b24efe548dedb00142009862cfa879ee1a71f6cec928353511472fbf4389148b0b0e0c108081412458dfe21c9f11351e67e7358595468246d1d1e5e38a6e9e851bc39d84ab502a669331dafec0d8ec7e3e8cb06e1a881d727d1ae40180a434a8c9db129a54126ad48a7358c2b4c5352c8c374bcccdab2bb37d8719cba79fab8211f9df218e0582c261e95f8bfc04f1a1e8bc5c4dfe0a19017a725d8c60000000049454e44ae426082 + + + + + minimap.h + +
diff --git a/konquest/newgamedlg.cc b/konquest/newgamedlg.cc new file mode 100644 index 00000000..c1beae4f --- /dev/null +++ b/konquest/newgamedlg.cc @@ -0,0 +1,325 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "newgamedlg.h" +#include "newgamedlg.moc" + +#include "newGameDlg_ui.h" + +/************************************************************************* + New Game Dialog Members + ************************************************************************/ + +NewGameDlg::NewGameDlg( QWidget *parent, Map *pmap, PlayerList *players, + Player *neutralPlayer, PlanetList *planets ) + : KDialogBase( parent, "new_game_dialog", true, i18n("Start New Game"), + KDialogBase::Ok|KDialogBase::Default|KDialogBase::Cancel, KDialogBase::NoDefault, true ), + plrList(players), plnetList(planets), neutral(neutralPlayer), + map(pmap) +{ + w = new NewGameDlgUI(this); + w->map->setMap(map); + w->listPlayers->header()->hide(); +// w->listPlayers->setMinimumSize( 100, 150 ); + w->listPlayers->setSortColumn(-1); + w->listPlayers->setHScrollBarMode(QScrollView::AlwaysOff); + w->sliderPlayers->setMinimumWidth(270); + w->sliderPlanets->setMinimumWidth(270); + + w->newPlayer->setMaxLength( 8 ); + + connect(w->sliderPlayers, SIGNAL(valueChanged(int)), this, SLOT(slotPlayerCount(int))); + connect(w->sliderPlanets, SIGNAL(valueChanged(int)), this, SLOT(slotNewMap())); + connect(w->sliderTurns, SIGNAL(valueChanged(int)), this, SLOT(slotTurns())); + connect(w->rejectMap, SIGNAL(clicked()), this, SLOT(slotNewMap())); + connect(w->newPlayer, SIGNAL(textChanged(const QString &)), this, SLOT(slotNewPlayer())); + connect(w->newPlayer, SIGNAL(returnPressed()), this, SLOT(slotAddPlayer())); + connect(w->addPlayer, SIGNAL(clicked()), this, SLOT(slotAddPlayer())); + + init(); + + setMainWidget(w); +} + +void +NewGameDlg::slotDefault() +{ + w->sliderPlayers->setValue(2); + w->sliderPlanets->setValue(3); + w->sliderTurns->setValue(15); + + w->listPlayers->clear(); + + setPlayerCount(2); + + updateMiniMap(); + updateLabels(); +} + +void +NewGameDlg::init() +{ + KConfig *config = kapp->config(); + config->setGroup("Game"); + int nrOfPlayers = config->readNumEntry("NrOfPlayers"); + if (nrOfPlayers < 2) + nrOfPlayers = 2; + if (nrOfPlayers > MAX_PLAYERS) + nrOfPlayers = MAX_PLAYERS; + + int nrOfPlanets = config->readNumEntry("NrOfPlanets", 3); + int nrOfTurns = config->readNumEntry("NrOfTurns", 15); + + w->sliderPlayers->setValue(nrOfPlayers); + w->sliderPlanets->setValue(nrOfPlanets); + w->sliderTurns->setValue(nrOfTurns); + setPlayerCount(nrOfPlayers); + slotNewPlayer(); + + // Restore player names + int plrNum = 0; + for( QListViewItem *item = w->listPlayers->firstChild(); + item; item = item->nextSibling(), plrNum++ ) + { + QString key = QString("Player_%1").arg(plrNum); + + QString playerName = config->readEntry(key); + if (playerName.isEmpty()) + continue; + + item->setText(2, "H"); // Human + item->setText(1, i18n("Human Player")); + item->setText(0, playerName); + } + + updateMiniMap(); + updateLabels(); +} + +void +NewGameDlg::slotNewPlayer() +{ + w->addPlayer->setEnabled(!w->newPlayer->text().isEmpty()); +} + +void +NewGameDlg::slotAddPlayer() +{ + QString playerName = w->newPlayer->text(); + if (playerName.isEmpty()) + return; + + QListViewItem *item; + do + { + item = w->listPlayers->firstChild(); + while( item ) + { + if (item->text(2) == "A") + break; + + item = item->nextSibling(); + } + if (!item) + { + int nrPlayers = w->listPlayers->childCount(); + if (nrPlayers >= MAX_PLAYERS) + return; // Too bad + nrPlayers++; + w->sliderPlayers->setValue(nrPlayers); + setPlayerCount(nrPlayers); + } + } + while(!item); + + item->setText(2, "H"); // Human + item->setText(1, i18n("Human Player")); + item->setText(0, playerName); + + w->newPlayer->setText(QString::null); + + updateMiniMap(); + updateLabels(); +} + +void +NewGameDlg::setPlayerCount(int playerCount) +{ + QColor PlayerColors[MAX_PLAYERS] = { QColor( 130, 130, 255 ), yellow, red, green, + white, cyan, magenta, QColor( 235, 153, 46 ), + QColor( 106, 157, 104 ), QColor( 131, 153, 128) }; + + int i = 0; + QListViewItem *lastItem = 0; + QListViewItem *item = 0; + QListViewItem *nextItem = w->listPlayers->firstChild(); + while( (item = nextItem) ) + { + nextItem = item->nextSibling(); + if (i >= playerCount) + { + delete item; + } + else + { + lastItem = item; + } + i++; + } + + while(w->listPlayers->childCount() < playerCount) + { + QString playerName = i18n("Generated AI player name", "Comp%1").arg(i+1); + QPixmap pm(16,16); + QColor color(PlayerColors[i]); + pm.fill(color); + QListViewItem *item = new QListViewItem(w->listPlayers, lastItem, playerName, i18n("Computer Player"), "A", color.name()); + item->setPixmap(0, pm); + lastItem = item; + i++; + } +} + +void +NewGameDlg::slotPlayerCount(int playerCount) +{ + if (w->listPlayers->childCount() == playerCount) + return; + + setPlayerCount(playerCount); + + updateMiniMap(); + updateLabels(); +} + +void +NewGameDlg::slotTurns() +{ + updateLabels(); +} + +void +NewGameDlg::slotNewMap() +{ + updateMiniMap(); + updateLabels(); +} + +int +NewGameDlg::turns() +{ + return w->sliderTurns->value(); +} + +void +NewGameDlg::updateLabels() +{ + w->labelPlayers->setText(i18n("Number of &players: %1").arg(w->sliderPlayers->value())); + w->labelPlanets->setText(i18n("Number of neutral p&lanets: %1").arg(w->sliderPlanets->value())); + w->labelTurns->setText(i18n("Number of &turns: %1").arg(w->sliderTurns->value())); +} + +void +NewGameDlg::slotOk() +{ + bool hasHumans = false; + for( QListViewItem *item = w->listPlayers->firstChild(); + item; item = item->nextSibling() ) + { + bool ai = (item->text(2) == "A"); + if (!ai) + hasHumans = true; + } + + if (!hasHumans) + { + KMessageBox::information(this, i18n("The game is much more fun when you add a human player!")); + w->newPlayer->setFocus(); + return; + } + KDialogBase::slotOk(); +} + +void +NewGameDlg::save() +{ + KConfig *config = kapp->config(); + config->setGroup("Game"); + + config->writeEntry("NrOfPlayers", w->sliderPlayers->value()); + config->writeEntry("NrOfPlanets", w->sliderPlanets->value()); + config->writeEntry("NrOfTurns", w->sliderTurns->value()); + + int plrNum = 0; + for( QListViewItem *item = w->listPlayers->firstChild(); + item; item = item->nextSibling() ) + { + QString key = QString("Player_%1").arg(plrNum); + QString playerName = item->text(0); + bool ai = (item->text(2) == "A"); + if (ai) + { + if (config->hasKey(key)) + config->deleteEntry(key); + } + else + { + config->writeEntry(key, playerName); + } + plrNum++; + } + config->sync(); +} + +void +NewGameDlg::updateMiniMap() +{ + // Clear map,, player and planet lists + map->clearMap(); + + Planet *planet; + planet = plnetList->first(); + for( planet = plnetList->take(); planet != 0; planet = plnetList->take() ) { + delete planet; + } + + Player *player; + player = plrList->first(); + for( player = plrList->take(); player != 0; player = plrList->take() ) { + delete player; + } + + // Make player list + // Does the name already exist in the list + int plrNum = 0; + for( QListViewItem *item = w->listPlayers->firstChild(); + item; item = item->nextSibling() ) + { + QString playerName = item->text(0); + bool ai = (item->text(2) == "A"); + QColor color(item->text(3)); + plrList->append( Player::createPlayer( playerName, color, plrNum, ai )); + plrNum++; + } + + // make the planets + map->populateMap( *plrList, neutral, + w->sliderPlanets->value(), + *plnetList ); +} + diff --git a/konquest/newgamedlg.h b/konquest/newgamedlg.h new file mode 100644 index 00000000..ed2d48d6 --- /dev/null +++ b/konquest/newgamedlg.h @@ -0,0 +1,51 @@ +#ifndef _NEWGAMEDLG_H_ +#define _NEWGAMEDLG_H_ + +#include + +#include "gamecore.h" +#include "minimap.h" + +class NewGameDlgUI; + +/************************************************************************* + New Game Dialog + ************************************************************************/ + +class NewGameDlg : public KDialogBase +{ + Q_OBJECT + +public: + NewGameDlg( QWidget *parent, Map *map, PlayerList *playerList, + Player *neutralPlayer, PlanetList *planetList ); + + int turns( void ); + + void save(); + +protected slots: + void slotPlayerCount(int playerCount); + void slotNewMap(); + void slotTurns(); + void slotNewPlayer(); + void slotAddPlayer(); + void slotDefault(); + void slotOk(); + +private: + void init(); + void updateMiniMap(); + void updateLabels(); + void setPlayerCount(int playerCount); + +private: + PlayerList *plrList; + PlanetList *plnetList; + Player *neutral; + Map *map; + + NewGameDlgUI *w; +}; + +#endif diff --git a/konquest/pics/Makefile.am b/konquest/pics/Makefile.am new file mode 100644 index 00000000..6c702a0e --- /dev/null +++ b/konquest/pics/Makefile.am @@ -0,0 +1,7 @@ + +# add here all files +pics_DATA = konquest-splash.png planet1.xpm planet2.xpm planet3.xpm planet4.xpm \ + planet5.xpm planet6.xpm planet7.xpm planet8.xpm planet9.xpm ruler.xpm + +picsdir = $(kde_datadir)/konquest/pics + diff --git a/konquest/pics/konquest-splash.png b/konquest/pics/konquest-splash.png new file mode 100644 index 00000000..6640b1a4 Binary files /dev/null and b/konquest/pics/konquest-splash.png differ diff --git a/konquest/pics/planet1.xpm b/konquest/pics/planet1.xpm new file mode 100644 index 00000000..fc867ba3 --- /dev/null +++ b/konquest/pics/planet1.xpm @@ -0,0 +1,228 @@ +/* XPM */ +static char *noname[] = { +/* width height ncolors chars_per_pixel */ +"20 20 201 2", +/* colors */ +" c #12222C", +" . c #121A2C", +" X c #96B2B4", +" o c #5E6E5C", +" O c #0A1E24", +" + c #161A1C", +" @ c #5E665C", +" # c #0A1A24", +" $ c #16161C", +" % c #0A1624", +" & c #A6AA9C", +" * c #8EA6AC", +" = c #3E6664", +" - c #4A625C", +" ; c #0E0E14", +" : c #AAA28C", +" > c #425E54", +" , c #7E969C", +" < c #3A524C", +" 1 c #2E5254", +" 2 c #9A927C", +" 3 c #324E44", +" 4 c #BEDAD4", +" 5 c #324A44", +" 6 c #264A4C", +" 7 c #324644", +" 8 c #7A8E84", +" 9 c #C2D2C4", +" 0 c #1E3E44", +" q c #8A866C", +" w c #7E8274", +" e c #BACABC", +" r c #AECAC4", +" t c #AEC6C4", +" y c #BAC2BC", +" u c #16363C", +" i c #5E827C", +" p c #222E34", +" a c #62766C", +" s c #0E2A34", +" d c #6E7264", +" f c #426E74", +" g c #121E24", +" h c #121A24", +" j c #52665C", +" k c #466664", +" l c #0A161C", +" z c #0A121C", +" x c #9AAA9C", +" c c #0A0E1C", +" v c #9AA69C", +" b c #92A294", +" n c #9E9E8C", +" m c #42564C", +" M c #365654", +" N c #2A565C", +" B c #365254", +" V c #729E9C", +" C c #8A968C", +" Z c #2E524C", +" A c #DEDAC4", +" S c #D2DACC", +" D c #829284", +" F c #1A464C", +" G c #32423C", +" H c #BECECC", +" J c #868A74", +" K c #2A3E34", +" L c #C2CABC", +" P c #B6CAC4", +" I c #2A3A34", +" U c #C2C6BC", +" Y c #1E3A3C", +" T c #1E363C", +" R c #BAC2B4", +" E c #22322C", +" W c #BABEB4", +" Q c #A2BEC4", +" ! c #162E34", +" ~ c #467A84", +" ^ c #B2B6AC", +" / c #3E767C", +" ( c #627264", +" ) c #1A2224", +" _ c #0E222C", +" ` c #92BAB4", +" ' c #1A1E24", +" ] c #0E1E2C", +" [ c #5A6A5C", +" { c #12161C", +" } c #12121C", +" | c #061224", +". c #526254", +".. c #DEEEE4", +".X c #3A6264", +".o c #0A0E14", +".O c #82A6A4", +".+ c #0A0A14", +".@ c #0A0614", +".# c #325A5C", +".$ c #32565C", +".% c #9E9A84", +".& c #929A8C", +".* c #869A94", +".= c #3E4A54", +".- c #2A5254", +".; c #3A4A3C", +".: c #D2D6C4", +".> c #2E4A44", +"., c #2E4644", +".< c #2E4244", +".1 c #22424C", +".2 c #26423C", +".3 c #BECEC4", +".4 c #1A4244", +".5 c #1A3E44", +".6 c #263A3C", +".7 c #6E867C", +".8 c #56828C", +".9 c #1E2E34", +".0 c #5A727C", +".q c #162A2C", +".w c #46767C", +".e c #566664", +".r c #0E1624", +".t c #AAAE9C", +".y c #566264", +".u c #0E1224", +".i c #426664", +".p c #2E6264", +".a c #9A9E8C", +".s c #325654", +".d c #D6DED4", +".f c #869E8C", +".g c #325254", +".h c #3E4E4C", +".j c #26465C", +".k c #7A8E94", +".l c #2A4A4C", +".z c #2A464C", +".x c #669294", +".c c #728E8C", +".v c #D2D2BC", +".b c #224644", +".n c #D2CEBC", +".m c #224244", +".M c #16424C", +".N c #5E828C", +".B c #BEC6BC", +".V c #1A3A3C", +".C c #6E8274", +".Z c #1A363C", +".A c #0E3644", +".S c #1A323C", +".D c #C2C2AC", +".F c #123234", +".G c #122E34", +".H c #66766C", +".J c #122A34", +".K c #5E7664", +".L c #A2B6B4", +".P c #5E7264", +".I c #467274", +".U c #0A222C", +".Y c #6A6A5C", +".T c #0A1E2C", +".R c #161A24", +".E c #A6AEA4", +".W c #3E6A6C", +".Q c #4A6664", +".! c #0E161C", +".~ c #0E121C", +".^ c #0E0E1C", +"./ c #AAA694", +".( c #9EA69C", +".) c #0E0A1C", +"._ c #E6EADC", +".` c #7EA6A4", +".' c #96A294", +".] c #42565C", +".[ c #7EA2A4", +".{ c #060614", +".} c #3A5E54", +".| c #D2EADC", +"X c #2E5A5C", +"X. c #3A5654", +"XX c #2E565C", +"Xo c #32524C", +"XO c #C2DACC", +"X+ c #CED6C4", +"X@ c #1E424C", +"X# c #728A84", +"X$ c #668A8C", +"X% c #2E3A34", +"X& c #B2C6BC", +"X* c #1A3234", +"X= c #6E7A6C", +"X- c #6E766C", +"X; c #B6BAAC", +"X: c #AABAB4", +"X> c #9EBABC", +/* pixels */ +" ).R {.~.~.^.^.^.^.).o.+.+ c ; ;.o ;.~ +", +" '.~.^.^.+.o c.+.+ %.r c.).+.+.+.).+ ; }", +" +.~ c.^.^ ..0 *X> Q X.[.N B ..+.+.+.o }", +" +.~.^.^.e HX+ yX&.L.c.W = f ~.j c.+.).~", +" }.o.u ,.B.E x C.tX& i.XX N.I.8 f z ;.~", +" {.^.k W./ C bX: eX+ H t.` XX>.x.p.A.+ }", +" }.] U.(.t L.n._.:.B.3.d.. r.x.4.A.M.T.~", +" +X: 9X;.a n & J q.D A S.3.. 4 V / F.5.^", +".=.v AX; w ( o aX#.B P.f C R S.| ` f.p g", +".y n & : 8.& v ^.a w DX=.H D.' vXO.O.w.S", +".yX- @. 2.%.% @ I E E.q K.K.C.>.C.*X$ ", +".y [.;X%X% d m.q g.r.! l ] I o.}.2 ZXo h", +".h. G.9.q.Y < _.q # # # O.G 7.7.i 6.5.~", +" p > K.9X* w., ! O O.U.U.FX* T >.W.G ;", +" { - 5.6 T.H TX* !.S s.F u u.V 0.bX | ;", +" } p j.,.h [.ZX* T.V.S.Z.m 3.#.l.V.U.+ c", +" $.o.h.P -.H < 3 5 M MX. -.#XXX@.U.{.@ ;", +" h.^.+.< k.Q M.g.sX..gXX 1 6 Y _.{.{.@.^", +" $ ;.+.@ g.1XX.$.#.-.z.b.4 s |.@.{.{.{ ;", +" + c.+.@.@.@ c ].J.J.U %.+.{.{.{.@.+.o.^" +}; diff --git a/konquest/pics/planet2.xpm b/konquest/pics/planet2.xpm new file mode 100644 index 00000000..f77d9ddb --- /dev/null +++ b/konquest/pics/planet2.xpm @@ -0,0 +1,190 @@ +/* XPM */ +static char *noname[] = { +/* width height ncolors chars_per_pixel */ +"20 20 163 2", +/* colors */ +" c #3E161C", +" . c #CEAE9C", +" X c #7A5A64", +" o c #EAAE7C", +" O c #7A5664", +" + c #361214", +" @ c #360E14", +" # c #F6EEE4", +" $ c #72525C", +" % c #2E0E0C", +" & c #724E5C", +" * c #A292AC", +" = c #EEE6DC", +" - c #FEEEC4", +" ; c #F2EACC", +" : c #AA968C", +" > c #C2927C", +" , c #E6DED4", +" < c #FADEAC", +" 1 c #A28684", +" 2 c #DECECC", +" 3 c #A27E84", +" 4 c #F2CEA4", +" 5 c #8E7684", +" 6 c #927674", +" 7 c #927274", +" 8 c #DABEB4", +" 9 c #CEBEBC", +" 0 c #766694", +" q c #DEC6A4", +" w c #D2C2AC", +" e c #A26E5C", +" r c #D2BAAC", +" t c #DEB6A4", +" y c #8A6A6C", +" u c #7E5E74", +" i c #CAB2A4", +" p c #FAFAF4", +" a c #DAB28C", +" s c #765A6C", +" d c #B6A6A4", +" f c #C2A29C", +" g c #360E0C", +" h c #6E4E64", +" j c #F6EEDC", +" k c #664A5C", +" l c #D69A74", +" z c #66425C", +" x c #B29A8C", +" c c #6A4A4C", +" v c #5E4654", +" b c #A69294", +" n c #5E3A54", +" m c #9E828C", +" M c #56324C", +" N c #927E94", +" B c #5A3A3C", +" V c #967E84", +" C c #DECAC4", +" Z c #D2CACC", +" A c #4E2A44", +" S c #8A768C", +" D c #523234", +" F c #522A34", +" G c #4A222C", +" H c #CEBAB4", +" J c #DEBE9C", +" K c #866674", +" L c #96625C", +" P c #7E626C", +" I c #3A1A1C", +" U c #CAB29C", +" Y c #CAAE9C", +" T c #3A121C", +" R c #320E14", +" E c #CEA68C", +" W c #B6A29C", +" Q c #F2EAE4", +" ! c #AA9AA4", +" ~ c #D2967C", +" ^ c #664A54", +" / c #AE9694", +" ( c #664654", +" ) c #663E54", +" _ c #EEE2CC", +" ` c #9E8284", +" ' c #F6D6AC", +" ] c #DECEBC", +" [ c #967A7C", +" { c #D2C6C4", +" } c #C6BACC", +" | c #A67264", +". c #8E7274", +".. c #CABEBC", +".X c #8E6E74", +".o c #BEB6C4", +".O c #4A2224", +".+ c #4A1E24", +".@ c #86666C", +".# c #C2B2B4", +".$ c #FEFEFC", +".% c #86626C", +".& c #7A5E74", +".* c #C6B2A4", +".= c #7E5E64", +".- c #3A1214", +".; c #FAF6E4", +".: c #765A5C", +".> c #BEA69C", +"., c #825254", +".< c #BE9E9C", +".1 c #320E0C", +".2 c #6A4E64", +".3 c #320A0C", +".4 c #6A4A64", +".5 c #E69E74", +".6 c #6A4664", +".7 c #B69E94", +".8 c #B69A94", +".9 c #6E4A54", +".0 c #E6DEE4", +".q c #664A4C", +".w c #EAE2D4", +".e c #EEDEC4", +".r c #B28E7C", +".t c #A68A84", +".y c #A68684", +".u c #E2D2CC", +".i c #D6CED4", +".p c #9E7A7C", +".a c #EACAAC", +".s c #DECAB4", +".d c #967674", +".f c #D2C2BC", +".g c #D2BEBC", +".h c #CABAB4", +".j c #8E666C", +".k c #CEB6A4", +".l c #E6B294", +".z c #866264", +".x c #C2AAAC", +".c c #B6AAB4", +".v c #DEAE8C", +".b c #F6F2EC", +".n c #725664", +".m c #725264", +".M c #6A465C", +".N c #AA9294", +".B c #624254", +".V c #BA927C", +".C c #664244", +".Z c #AE8E84", +".A c #968694", +".S c #5A364C", +".D c #E2D6C4", +".F c #D6CECC", +".G c #927A7C", +".H c #DAC6BC", +".J c #4A263C", +".K c #FACE8C", +".L c #4E222C", +".P c #D6C2A4", +".I c #BEAEB4", +/* pixels */ +".1.3.1.1.1.1.1.1.1.1.1.1.1.1 g g g g g @", +".3.1.3.3.3.1.1.q.j.d 7.C.-.3.3.3.3.3.3.3", +".3 % % %.3.: Z ; t.l 4.a Y ( R R @ @ @ @", +" %.3.3 @.Z - <.K l > E.j.& u.= @ R.3 @.3", +" % %.1 ` ' a | L.V.v ~ (.m.2.k c R.3 @ @", +".3.3.S r f Y.* / q J X [ :.h m K.L R.3 R", +" % % N :.#.u _ w = ].7.N.m.@.t 1.9 R.3 @", +".3 I.o S 6 x.P _ _ U.7 1.B.B.@.<.d T R.1", +".3.C.c.I b V a 4 Y.z., y X.S z.@.p.O.3 @", +" % v *.# 5 H.5 o e.9.S A n h ( z.m F R R", +" % ^ 0.G s.B.N '.r O M.J A.n.G ).4 F.3 R", +" % B } N...h.@.. #.w 9 $.= X P k & G R g", +" % I.0 !.# / $ i W.$.b. .< Y. b.6 +.3 @", +" g.1.A Z.x.f d , p.e.>.X 1.F.D r ) @ R.1", +".3.3 D.w {.g #.D j.s ..8.y & 3 O.+ R.3 @", +".1 R.3 P.# 2.f = Q.w =.H Y 8.<.M.3.3 @.3", +".3.1 R @ m.;.$.$.$.$.$ p.; C.% + @.3 @ @", +".1.3.3 @ @ $.i.$.$.$.$ p.u O @.3.3 @.3.3", +".3.1.1.1.1.3 @ ^ V b V.9 R @ @.3 @ @ @", +" %.3.3.3.3 R.1.1 %.3.3 % %.3.3.3 @.3.3 g" +}; diff --git a/konquest/pics/planet3.xpm b/konquest/pics/planet3.xpm new file mode 100644 index 00000000..6375bfd1 --- /dev/null +++ b/konquest/pics/planet3.xpm @@ -0,0 +1,133 @@ +/* XPM */ +static char *noname[] = { +/* width height ncolors chars_per_pixel */ +"20 20 106 2", +/* colors */ +" c #BA6A2C", +" . c #EAB27C", +" X c #8E623C", +" o c #76624C", +" O c #825644", +" + c #120604", +" @ c #D29664", +" # c #5E3A34", +" $ c #DA8A44", +" % c #CE8A4C", +" & c #B6865C", +" * c #9E866C", +" = c #D2823C", +" - c #BA824C", +" ; c #CA7A34", +" : c #BE7A3C", +" > c #CA7634", +" , c #BE763C", +" < c #B27644", +" 1 c #3A2A24", +" 2 c #C2722C", +" 3 c #926E4C", +" 4 c #AE6A2C", +" 5 c #BA6624", +" 6 c #7E664C", +" 7 c #2A1A14", +" 8 c #B2661C", +" 9 c #965A3C", +" 0 c #8E6234", +" q c #7E564C", +" w c #82623C", +" e c #EEA664", +" r c #E69A5C", +" t c #DA9A64", +" y c #CE9A6C", +" u c #060204", +" i c #DE9254", +" p c #C68E64", +" a c #CA8A54", +" s c #763E1C", +" d c #B28A64", +" f c #4A3E2C", +" g c #4A3A2C", +" h c #C67E3C", +" j c #423224", +" k c #BA7644", +" l c #9A6E4C", +" z c #3A221C", +" x c #B66E2C", +" c c #EAA66C", +" v c #965634", +" b c #765A3C", +" n c #6A5A44", +" m c #CAA274", +" M c #E29E64", +" N c #924E1C", +" B c #C29A6C", +" V c #CE9664", +" C c #DE8E4C", +" Z c #CE823C", +" A c #B67E4C", +" S c #9E765C", +" D c #8A725C", +" F c #BE722C", +" G c #9A6E44", +" H c #F2AE6C", +" J c #32120C", +" K c #1E160C", +" L c #1E120C", +" P c #1E0E0C", +" I c #DEA26C", +" U c #725644", +" Y c #C69E7C", +" T c #160A04", +" R c #E2965C", +" E c #BE9674", +" W c #6A463C", +" Q c #020204", +" ! c #C29264", +" ~ c #663E24", +" ^ c #7E3A14", +" / c #AE8664", +" ( c #D6863C", +" ) c #CA8244", +" _ c #6A3214", +" ` c #C27E3C", +" ' c #4A321C", +" ] c #C27A3C", +" [ c #92765C", +" { c #BA7634", +" } c #C6722C", +" | c #AE723C", +". c #BE6E24", +".. c #8E6A44", +".X c #EEB274", +".o c #E6A66C", +".O c #DAA674", +".+ c #120E0C", +".@ c #C69A74", +".# c #0A0204", +".$ c #62422C", +".% c #D28244", +".& c #C6824C", +".* c #CA7E3C", +".= c #AA7644", +".- c #86725C", +/* pixels */ +" Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q", +" Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q", +" Q Q Q Q Q Q Q Q Q 7 W O q # T Q Q Q Q Q", +" Q Q Q Q Q Q Q P X y @ @ @ t k s.# Q Q Q", +" Q Q Q Q Q Q L S p t $ ) ) F 8 N + Q Q", +" Q Q Q Q Q Q U Y.O ! % ` ]. . ) 5 ^ Q Q", +" Q Q Q Q Q L *.@ ! A : { , 2 > Z ; 5 J Q", +" Q Q Q Q Q 1 * d & - : ].* Z = } ; > _ Q", +" Q Q Q Q Q z [ l G A.& ` h R $ >.% R v Q", +" Q Q Q Q Q 7 6.. 0 | | 4 4 % ( = C H 9 Q", +" Q Q Q Q Q +.$ w X.= < x ,.* = $ r i ~ Q", +" Q Q Q Q Q Q z b 3 & & !.o c e M t.& + Q", +" Q Q Q Q Q Q u g D / V I ..X c I a ' Q Q", +" Q Q Q Q Q Q Q.# g.- / E Y m B d j Q Q Q", +" Q Q Q Q Q Q Q Q Q K g n o n f.+ Q Q Q Q", +" Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q", +" Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q", +" Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q", +" Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q", +" Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q" +}; diff --git a/konquest/pics/planet4.xpm b/konquest/pics/planet4.xpm new file mode 100644 index 00000000..5d34f2df --- /dev/null +++ b/konquest/pics/planet4.xpm @@ -0,0 +1,122 @@ +/* XPM */ +static char *noname[] = { +/* width height ncolors chars_per_pixel */ +"20 20 95 2", +/* colors */ +" c #2A221C", +" . c #7E5E54", +" X c #221A14", +" o c #765A4C", +" O c #1A120C", +" + c #6E5644", +" @ c #6E5244", +" # c #E2AA4C", +" $ c #D6A654", +" % c #664E3C", +" & c #D6A254", +" * c #CE9E4C", +" = c #BE9264", +" - c #CE9A4C", +" ; c #B68E5C", +" : c #4A3A34", +" > c #AA8264", +" , c #42362C", +" < c #A27A5C", +" 1 c #9A7654", +" 2 c #3A2A24", +" 3 c #32261C", +" 4 c #866A54", +" 5 c #866654", +" 6 c #2A2214", +" 7 c #8A6A44", +" 8 c #8A6644", +" 9 c #7E624C", +" 0 c #F2B654", +" q c #765A44", +" w c #0E0A0C", +" e c #624E44", +" r c #060604", +" t c #C69A64", +" y c #060204", +" u c #D6A24C", +" i c #5A463C", +" p c #CA9A54", +" a c #BA8E44", +" s c #A27E54", +" d c #8E6E54", +" f c #866A4C", +" g c #261E1C", +" h c #FABE54", +" j c #7E6244", +" k c #725A4C", +" l c #16120C", +" z c #6A5644", +" x c #DEA64C", +" c c #624E3C", +" v c #D2A254", +" b c #624A3C", +" n c #BE9254", +" m c #B2865C", +" M c #B68A4C", +" N c #AA8254", +" B c #A27A4C", +" V c #362A24", +" C c #8A6E5C", +" Z c #2E261C", +" A c #261E14", +" S c #866644", +" D c #7A624C", +" F c #7A5E4C", +" G c #725644", +" H c #E6AE4C", +" J c #D69E64", +" K c #DAAA54", +" L c #DAA654", +" P c #020204", +" I c #D29E4C", +" U c #C69654", +" Y c #4E3E34", +" T c #B28654", +" R c #463A2C", +" E c #A6825C", +" W c #9E7A54", +" Q c #3E2E24", +" ! c #8A6A54", +" ~ c #8E6A44", +" ^ c #F6BA54", +" / c #1A1614", +" ( c #120E0C", +" ) c #665244", +" _ c #D6A25C", +" ` c #0A0A04", +" ' c #0A0604", +" ] c #5E4A3C", +" [ c #564234", +" { c #B68A64", +" } c #C6924C", +" | c #BA8E54", +". c #BE8E44", +".. c #927254", +".X c #322624", +/* pixels */ +" P P y P P P P P P P P P P P P P P y ' P", +" P y P y y y y y y y y y y y y y 3 c k l", +" P r r r r P r r r r r P r r / b 4 . ] r", +" P P P P P r P P P w ( ` P / . F 5 9 3 y", +" P P P y P r r l R q 9 @ 3 l Q A 9 [ r P", +" P y y r y y l ) 1 N T B j 2 y , o ( P r", +" P r P r r r e < | U } M N S F X y P y", +" P P P P P V W n _ u * } - ~ G 3 r r y r", +" P P y y y ) ; $ K L I #. j b P P P P P", +" P r P r r.. p & x u 0 u 8 < i r y r y r", +" P r P P r 1 p v # h # 7 T m : r P r P y", +" P P P P O.. | L ^ u 7 { U > 3 P P P y r", +" P y r ( + 5 p H a f | J = ! ` y y r P P", +" P r r ] Y Z M f ! | J t E.X y r r P P r", +" P y , F / w [ d { t = s , r P P P y P y", +" P X D % : k c Q D C z g r P y P y r P r", +" r [ d ! 9 , r P P y r y y P r r P r P P", +" X z D ] X y r y y r y r r r r P P P P r", +" / : 6 r P P P r P P P P P P y y y P P P", +" P P P P y y r P P r y P y P r r P r P r" +}; diff --git a/konquest/pics/planet5.xpm b/konquest/pics/planet5.xpm new file mode 100644 index 00000000..339e086e --- /dev/null +++ b/konquest/pics/planet5.xpm @@ -0,0 +1,143 @@ +/* XPM */ +static char *noname[] = { +/* width height ncolors chars_per_pixel */ +"20 20 116 2", +/* colors */ +" c #321624", +" . c #D2AE8C", +" X c #72625C", +" o c #D2AA8C", +" O c #2A121C", +" + c #2A0E1C", +" @ c #6A5A54", +" # c #CAA284", +" $ c #220A14", +" % c #6A5654", +" & c #220614", +" * c #62524C", +" = c #1A020C", +" - c #120204", +" ; c #BA9274", +" : c #FAD2AC", +" > c #523A3C", +" , c #B2826C", +" < c #927E74", +" 1 c #F2C6A4", +" 2 c #DEC6A4", +" 3 c #EABE9C", +" 4 c #42222C", +" 5 c #E2BA94", +" 6 c #E2B694", +" 7 c #8E6A5C", +" 8 c #CEB694", +" 9 c #DAB28C", +" 0 c #32161C", +" q c #725E54", +" w c #C6A68C", +" e c #725A54", +" r c #725654", +" t c #22060C", +" y c #22020C", +" u c #BE9A84", +" i c #6A4E4C", +" p c #1A0204", +" a c #B6927C", +" s c #F6DAB4", +" d c #FACEA4", +" f c #523634", +" g c #FACAA4", +" h c #462E3C", +" j c #F2C69C", +" k c #9E7A64", +" l c #F2C29C", +" z c #4A262C", +" x c #966E5C", +" c c #DEBA9C", +" v c #8E6A54", +" b c #D6B294", +" n c #82665C", +" m c #CEAA8C", +" M c #6E5E5C", +" N c #C6A684", +" B c #C69E84", +" V c #16020C", +" C c #FEE2B4", +" Z c #B69274", +" A c #FEDEB4", +" S c #564644", +" D c #B68E74", +" F c #AE866C", +" G c #EAD2B4", +" H c #AE826C", +" J c #4E363C", +" K c #4E323C", +" L c #EECAA4", +" P c #927264", +" I c #765E5C", +" U c #2E0E1C", +" Y c #765A5C", +" T c #C2A68C", +" R c #260A14", +" E c #6E5654", +" W c #260614", +" Q c #C69E7C", +" ! c #1E020C", +" ~ c #160204", +" ^ c #A69284", +" / c #5E4244", +" ( c #FED6AC", +" ) c #F2D6B4", +" _ c #AE8264", +" ` c #968274", +" ' c #462E2C", +" ] c #9A7664", +" [ c #E2C2A4", +" { c #9A7264", +" } c #DABE9C", +" | c #867264", +". c #E6BA94", +".. c #361A1C", +".X c #CAAA8C", +".o c #765E54", +".O c #26060C", +".+ c #C29E84", +".@ c #26020C", +".# c #C29A84", +".$ c #1E0204", +".% c #12020C", +".& c #B28A74", +".* c #FAD6B4", +".= c #523E44", +".- c #4A363C", +".; c #AA7E6C", +".: c #F6C69C", +".> c #422A34", +"., c #8E7664", +".< c #9A725C", +".1 c #E2BE9C", +".2 c #8E7264", +".3 c #E2BA9C", +".4 c #DAB694", +".5 c #321A24", +/* pixels */ +".@ y y y.$.$ !.$.$.$ ! p.$ = p p p ! ! !", +" y y.$ p.$ !.$ ! !.O p p = p = = = p p p", +" y.$ ! ! p.$ f v.< r z y p ~ p p = = = =", +" y !.$.$ 0 a 9 Q , n / U V = V V ~ p p.$", +" ! ! =.. 8 l 6 # , n i...$ ~ ~ = = V V =", +" y.$ y N g l 9.# , x r 4 & V = ~ ~ p p p", +" !.$.o ( d l 9.# _ n e 4 t ~ V = = = V =", +" ! y 2 :.: 3 . Q F P X z U V p ~ ~ ~ ~ ~", +" y J C d.: 3 ..+ F P * f U V.% V V V V =", +" ! Y ( : j. N u H x e K W V - - - ~ ~ ~", +" ! ` A : l 5 o.+.; x E K R p - -.% - V V", +" ! < A : 1.3 m u F 7 r.- +.% - - - V ~ ~", +" ! % A.* L. b B ; { e K R V.% - -.% V ~", +" ! 0 s A : 1 9 m.& k I.= &.% -.%.% ~ - V", +" ! ! ^ C : 1.4 w D k q h & - - - - V ~ =", +" ! =.5 G.* L c w D ] M.> & V V ~ V ~ V ~", +" y.$.$ / ) L.1.X.&.2 @.> ~ -.% - - ~ V V", +" ! = V ~ > } [.X Z., * V V - V V V ~ =", +" ! ! = p V O | T D | S & ~ V ~ p ~ V ~ V", +" ! !.$ !.$ p = $ '.- O p V ~ V V = ~ ~ =" +}; diff --git a/konquest/pics/planet6.xpm b/konquest/pics/planet6.xpm new file mode 100644 index 00000000..196b5ed7 --- /dev/null +++ b/konquest/pics/planet6.xpm @@ -0,0 +1,110 @@ +/* XPM */ +static char *noname[] = { +/* width height ncolors chars_per_pixel */ +"20 20 83 1", +/* colors */ +" c #2E0E0C", +". c #CE5A04", +"X c #CE5604", +"o c #1A0E0C", +"O c #CE5204", +"+ c #1A0A0C", +"@ c #1A060C", +"# c #06020C", +"$ c #BA4604", +"% c #BA3E04", +"& c #A64A04", +"* c #BA3A04", +"= c #A63E04", +"- c #A63A04", +"; c #A63604", +": c #923A04", +"> c #923204", +", c #7E2604", +"< c #6A2E04", +"1 c #6A2A04", +"2 c #561A04", +"3 c #4A1A0C", +"4 c #36160C", +"5 c #D65E04", +"6 c #D65A04", +"7 c #220A0C", +"8 c #C24E04", +"9 c #0E060C", +"0 c #C24A04", +"q c #0E020C", +"w c #C24604", +"e c #C24204", +"r c #060204", +"t c #AE4604", +"y c #9A3E04", +"u c #722E04", +"i c #722204", +"p c #4A1604", +"a c #3E120C", +"s c #2A0E0C", +"d c #CA5604", +"f c #CA5204", +"g c #CA4E04", +"h c #16060C", +"j c #CA4A04", +"k c #B64E04", +"l c #B64604", +"z c #B64204", +"x c #B63A04", +"c c #A24604", +"v c #A24204", +"b c #A23604", +"n c #8E3204", +"m c #7A2A04", +"M c #7A2604", +"N c #662A04", +"B c #662204", +"V c #5A220C", +"C c #521A04", +"Z c #32160C", +"A c #D25A04", +"S c #D25604", +"D c #1E0A0C", +"F c #BE4A04", +"G c #0A020C", +"H c #BE4204", +"J c #AA3E04", +"K c #AA3604", +"L c #963A04", +"P c #963204", +"I c #6E2204", +"U c #3A1A0C", +"Y c #3A120C", +"T c #260A0C", +"R c #C65604", +"E c #12060C", +"W c #C64A04", +"Q c #C64604", +"! c #B24E04", +"~ c #8A3204", +"^ c #763204", +"/ c #762604", +"( c #621E04", +/* pixels */ +"#r#r#r#r#rr#r#r#r#r#", +"r#r#r#GhD+qr#r#r#r#r", +"r#r#GUum/(aTqr#r#r##", +"#rrqNLb-b>,CsGr#r#rr", +"##rN=z%%x-P,C7Grr###", +"rrZ=zwweHxbnIY9###rr", +"#r^lWgjQ%*;>m3Drr##r", +"r9y8fffWwxKP,CT##rr#", +"r+JROOOjw*KP,2TG##r#", +"ro&XAXOfw%;P,CTrrr#r", +"r9cd66Sf0%;>Mp@##r##", +"#r^R556Sgz;~ia@rr#rr", +"rrZ!XAA.f$-~B q#r##r", +"##rNkRd8FJ:mp+rr#rr#", +"r#r9 c #362234", +" , c #CA9ECC", +" < c #925A74", +" 1 c #2E1E2C", +" 2 c #1E121C", +" 3 c #6E4A64", +" 4 c #B28AB4", +" 5 c #0E0A0C", +" 6 c #C27A9C", +" 7 c #0E060C", +" 8 c #A27AA4", +" 9 c #060604", +" 0 c #060204", +" q c #56364C", +" w c #F6AEDC", +" e c #E2AEDC", +" r c #4E2E44", +" t c #826284", +" y c #D29ECC", +" u c #7A5A7C", +" i c #D696BC", +" p c #865674", +" a c #2E1A24", +" s c #725A74", +" d c #725274", +" f c #26161C", +" g c #BA8AB4", +" h c #624A64", +" j c #160A0C", +" k c #5E3A4C", +" l c #A27A9C", +" z c #523A54", +" x c #F2B6E4", +" c c #5E364C", +" v c #AE7294", +" b c #E2AAD4", +" n c #926A8C", +" m c #462634", +" M c #CEA2D4", +" N c #322634", +" B c #322234", +" V c #825E7C", +" C c #8E5A74", +" Z c #2A222C", +" A c #2A1A2C", +" S c #CA92BC", +" D c #1A121C", +" F c #C682A4", +" G c #62465C", +" H c #0A0A0C", +" J c #0A060C", +" K c #EEBAEC", +" L c #9E7AA4", +" P c #020204", +" I c #E6B6E4", +" U c #F2B2DC", +" Y c #E6B2E4", +" T c #DEAEDC", +" R c #4A2E44", +" E c #EE9EC4", +" W c #8A5E7C", +" Q c #C696C4", +" ! c #7A526C", +" ~ c #BE92BC", +" ^ c #22121C", +" / c #7E4A5C", +" ( c #1A0E14", +" ) c #AE82AC", +" _ c #120A0C", +" ` c #56425C", +" ' c #B27A9C", +" ] c #0A0604", +" [ c #AA7694", +" { c #9E769C", +" } c #967294", +" | c #D6A2CC", +". c #3A1E2C", +".. c #DA96BC", +".X c #8A5674", +/* pixels */ +" P P P P P P P P P P P P ( a ( P P P P P", +" P P P P P P P P P P 1 o / - j P P P P P", +" P P P P P P P P 5 W v.X @ m 7 P P P P P", +" P P P P P P P _ O.. [ p k. 5 P P P P P", +" P P P P P P 9 l b X n 3 R ^ P P P P P P", +" P P P P P P % M y O n + ; D P P P P P P", +" P P P P P 9 4 : ~ 8 t G m _ P P P P P P", +" P P P P P N T Y Q L u # B 5 P P P P P P", +" P P P P P h K I y ) V $ 1 J P P P P P P", +" P P P P P K e Q { z 1 9 P P P P P P", +" P P P P P s T | g = d & f P P P P P P P", +" P P P P P ` , , X } d & A 0 P P P P P P", +" P P P P P Z M : X { # B J P P P P P P", +" P P P P P 0 4 Y | O n + > ] P P P P P P", +" P P P P P P % x U S = 3 ; _ P P P P P P", +" P P P P P P P * w i v ! r ^ P P P P P P", +" P P P P P P P H ' E F C q ^ P P P P P P", +" P P P P P P P P 0 . 6 < c f P P P P P P", +" P P P P P P P P P P 2 c @. 0 P P P P P", +" P P P P P P P P P P P P 0 ( ] P P P P P" +}; diff --git a/konquest/pics/planet8.xpm b/konquest/pics/planet8.xpm new file mode 100644 index 00000000..f1fbe368 --- /dev/null +++ b/konquest/pics/planet8.xpm @@ -0,0 +1,189 @@ +/* XPM */ +static char *noname[] = { +/* width height ncolors chars_per_pixel */ +"20 20 162 2", +/* colors */ +" c #362214", +" . c #A2623C", +" X c #8E5A3C", +" o c #8E563C", +" O c #825644", +" + c #76564C", +" @ c #1A120C", +" # c #865234", +" $ c #7A523C", +" % c #7A4E3C", +" & c #6E4E44", +" * c #6E4A44", +" = c #EA9E54", +" - c #7E522C", +" ; c #66463C", +" : c #5A4644", +" > c #5A4244", +" , c #E2964C", +" < c #523E3C", +" 1 c #523A3C", +" 2 c #DA8644", +" 3 c #CE824C", +" 4 c #3E363C", +" 5 c #D27E3C", +" 6 c #C67E44", +" 7 c #C67A44", +" 8 c #2A2A3C", +" 9 c #BE723C", +" 0 c #32221C", +" q c #321E1C", +" w c #AA663C", +" e c #9E6244", +" r c #92624C", +" t c #A26234", +" y c #965E3C", +" u c #8A5A44", +" i c #825A3C", +" p c #82563C", +" a c #82523C", +" s c #765244", +" d c #764E44", +" f c #6A4E4C", +" g c #6E4E3C", +" h c #6E4A3C", +" j c #624644", +" k c #724E2C", +" l c #D28E5C", +" z c #5A423C", +" x c #4E4244", +" c c #D68A4C", +" v c #D6864C", +" b c #CA8654", +" n c #523A34", +" m c #46363C", +" M c #CE8244", +" N c #CE7E44", +" B c #C27A4C", +" V c #322E3C", +" C c #C6763C", +" Z c #A27254", +" A c #A26E54", +" S c #3A2A1C", +" D c #BE7234", +" F c #B26E3C", +" G c #B26A3C", +" H c #A66A44", +" J c #321E14", +" K c #9E623C", +" L c #925E44", +" P c #1E1614", +" I c #8A5A3C", +" U c #8A563C", +" Y c #16120C", +" T c #825234", +" R c #764E3C", +" E c #764A3C", +" W c #5E4A4C", +" Q c #E69A54", +" ! c #6E4634", +" ~ c #62463C", +" ^ c #62423C", +" / c #564244", +" ( c #DE924C", +" ) c #DE8E4C", +" _ c #423A44", +" ` c #D68644", +" ' c #CA824C", +" ] c #52362C", +" [ c #3A323C", +" { c #C67634", +" } c #362A24", +" | c #BA723C", +". c #1E2634", +".. c #A26A4C", +".X c #966654", +".o c #3A2614", +".O c #9A6244", +".+ c #925E3C", +".@ c #865A44", +".# c #7A564C", +".$ c #1E120C", +".% c #120E14", +".& c #7E523C", +".* c #724E44", +".= c #6A4A3C", +".- c #6A463C", +".; c #5E4644", +".: c #E6964C", +".> c #020204", +"., c #DA9254", +".< c #563E3C", +".1 c #4A3E44", +".2 c #664224", +".3 c #DE8A44", +".4 c #D2864C", +".5 c #C68254", +".6 c #CA7E44", +".7 c #BE7A4C", +".8 c #2E2E3C", +".9 c #3E2E24", +".0 c #C2763C", +".q c #C2723C", +".w c #1A263C", +".e c #9E6E54", +".r c #BA6E34", +".t c #AE6E3C", +".y c #AE6A3C", +".u c #A26A44", +".i c #AE663C", +".p c #A26644", +".a c #A66634", +".s c #9A5E3C", +".d c #8E5E44", +".f c #925A34", +".g c #86563C", +".h c #7A5244", +".j c #6E524C", +".k c #6E4E4C", +".l c #7E4E34", +".z c #724A3C", +".x c #664A44", +".c c #664644", +".v c #5A464C", +".b c #E29654", +".n c #5E463C", +".m c #524244", +".M c #523E44", +".N c #6E4224", +".B c #DA8A4C", +".V c #4A3A3C", +".C c #5A3A24", +".Z c #D28244", +".A c #BA7E54", +".S c #423234", +".D c #36323C", +".F c #CA7A3C", +".G c #B2764C", +".H c #B2724C", +".J c #B66E3C", +".K c #AA6E44", +".L c #322224", +".P c #9E6A4C", +/* pixels */ +".>.>.>.>.>.>.o -.a F.a.f.N .>.>.>.>.>.>", +".>.>.>.> J.t ).3.Z G M.B.G.y K J.>.>.>.>", +".>.>.>.C.F.B ) , ).4., b f.n.# H.2.>.>.>", +".>.>.C 7 v.b Q = c.4 B.e.;.D.w.V.P k.>.>", +".> J '.4 2 (.b.b.:.Z c.A.V. .w.w & l.o.>", +".>.p c 3 M.: Q =.b.0 B b < V. 4.m.X.G.>", +" 0.7 3.F.4.B ) Q c ` ' g ^ 4 8 4 W W.5 S", +" ! 9 3 N 5.Z v ) ) c.p s / [ 8.D : + A i", +" X 6 ' 3 M.4 ) `.Z.K u z j.1.8.8 x.j...u", +" e | N C t 7 `.6 F.d ; O.M.;.V _.1 f.H.7", +" u.J.6.0.J.F 5 | ..&.< $.<.M >.;.v f.P.G", +" ] X 9 {.0 C C G e e.p.@.c ; <.x : f.k R", +".L 1 a.i {.F.q |.O p h.h.h d *.<.V / > }", +" P R ! E w.r D.r w e a p.@.* ~.= m.< /.%", +".> !.g T U.s.i.y.y.y L.d.@ z ~ < / ;.S.>", +".>.$ # #.g X.+ y . I n.- a % R n ; ; @.>", +".>.> .l T # o.f.s y I.+.+ a d R $ q.>.>", +".>.>.> q.N E h R O u o U O.& h h 0.>.>.>", +".>.>.>.> Y ] ;.- E $.z.h +.#.<.%.>.>.>.>", +".>.>.>.>.>.> @.9.n r Z.e h 0.>.>.>.>.>.>" +}; diff --git a/konquest/pics/planet9.xpm b/konquest/pics/planet9.xpm new file mode 100644 index 00000000..a8a96c17 --- /dev/null +++ b/konquest/pics/planet9.xpm @@ -0,0 +1,168 @@ +/* XPM */ +static char *noname[] = { +/* width height ncolors chars_per_pixel */ +"20 20 141 2", +/* colors */ +" c #760604", +" . c #620604", +" X c #4E0A04", +" o c #4E0604", +" O c #3A0604", +" + c #260604", +" @ c #E23204", +" # c #120604", +" $ c #E22E04", +" % c #E21E04", +" & c #CE2604", +" * c #CE1604", +" = c #CE1204", +" - c #F2AA0C", +" ; c #CE0A04", +" : c #F2A20C", +" > c #BA1204", +" , c #BA0E04", +" < c #F2960C", +" 1 c #BA0A04", +" 2 c #A60A04", +" 3 c #F2820C", +" 4 c #920E04", +" 5 c #920A04", +" 6 c #920604", +" 7 c #7E0A04", +" 8 c #6A0A04", +" 9 c #6A0604", +" 0 c #560604", +" q c #EA5204", +" w c #420604", +" e c #EA4604", +" r c #EA4204", +" t c #EA3A04", +" y c #EA3604", +" u c #D63E04", +" i c #1A0604", +" p c #060604", +" a c #D61A04", +" s c #C22604", +" d c #D60E04", +" f c #AE1E04", +" g c #C20E04", +" h c #AE1A04", +" j c #C20A04", +" k c #AE1604", +" l c #AE0E04", +" z c #AE0A04", +" x c #9A0E04", +" c c #9A0A04", +" v c #860A04", +" b c #860604", +" n c #F27204", +" m c #720A04", +" M c #720604", +" N c #5E0A04", +" B c #F25A04", +" V c #5E0604", +" C c #4A0604", +" Z c #F24604", +" A c #360604", +" S c #DE4604", +" D c #220604", +" F c #0E0604", +" G c #DE2604", +" H c #F6BA14", +" J c #DE1A04", +" K c #CA1204", +" L c #CA0E04", +" P c #B60E04", +" I c #8E2604", +" U c #B60A04", +" Y c #A20A04", +" T c #A20604", +" R c #8E0A04", +" E c #8E0604", +" W c #7A0E04", +" Q c #7A0A04", +" ! c #7A0604", +" ~ c #660A04", +" ^ c #660604", +" / c #520A04", +" ( c #520604", +" ) c #3E0604", +" _ c #2A0A04", +" ` c #2A0604", +" ' c #D24604", +" ] c #160604", +" [ c #E62A04", +" { c #F2C21C", +" } c #E62204", +" | c #020604", +". c #020204", +".. c #D21E04", +".X c #D21A04", +".o c #D21604", +".O c #BE1E04", +".+ c #D20E04", +".@ c #BE1604", +".# c #BE1204", +".$ c #BE0E04", +".% c #BE0A04", +".& c #AA0E04", +".* c #AA0A04", +".= c #AA0604", +".- c #960A04", +".; c #820A04", +".: c #6E0A04", +".> c #6E0604", +"., c #EE6604", +".< c #E2660C", +".1 c #EE6204", +".2 c #5A0A04", +".3 c #EE5A04", +".4 c #5A0604", +".5 c #EE5604", +".6 c #460A04", +".7 c #460604", +".8 c #EE3E04", +".9 c #320604", +".0 c #EE3604", +".q c #EE3204", +".w c #1E0604", +".e c #0A0604", +".r c #DA1E04", +".t c #DA1A04", +".y c #DA1204", +".u c #C61604", +".i c #F2A214", +".p c #C61204", +".a c #C60E04", +".s c #C60A04", +".d c #B21604", +".f c #B20E04", +".g c #B20A04", +".h c #9E0A04", +".j c #9E0604", +".k c #F27614", +".l c #8A0A04", +".z c #8A0604", +".x c #760E04", +/* pixels */ +" p p p | | p p p ] ` D F p p p | | | p p", +". . . . . _ m 5 c.-.; M o.w. . . . . . ", +" | | p p.2 l , 5.f l.- ! w . ( + p | | |", +". . . C v.l M R ,.p 1 v M.4 o 0 (. . . ", +" p p.9.> O ` (.d.t a , , 5 ~ E z X p |", +". # C ~ 0.4.2.p Y 7 / 9 2 l ..7 z i. ", +". (.:.9.2 9.7.9 O ) w.4 D.2 8 m.w v N |", +" i .l.4 C 0 D.7.x.; E.z V 2 x z b.; 6. ", +" O.2 m.- ( M.6 w h.l > 1.h 4 % * * =.+ D", +" X b c.l R.u.. f.d s.<.<.X a.q Z.q.a g O", +" ).> Y 2 K P.* G.1 - { : r a.8 <.k G.h O", +" `.g E.- J.r.r t 3.i - H n t 3 < H.3.@ D", +" # , 8.6 l W P.$ = = $ q ' u.5.i., n S p", +". Q.z.z.= N U L.s T ! & k } B.5 e [ I. ", +" p _.4.$.* x.g 1.s b V.& y.q.t.0 @.O i. ", +". . N g U L.% j U.* 5.$.a > J.t.#.7. p", +" p. .e.-.a L.s d.+ ; U U.= ,.o.g 0. . . ", +". |. .e Q.s d.y d.s 1.=.% j.j ). p. p", +". . . . . A 5 j ; ;.%.% U ^ #. . p. |", +" p p p p p | |.e.9 ) O +.e p. p p. p. " +}; diff --git a/konquest/pics/ruler.xpm b/konquest/pics/ruler.xpm new file mode 100644 index 00000000..6e60655c --- /dev/null +++ b/konquest/pics/ruler.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * ruler_xpm[] = { +"16 16 2 1", +" c None", +". c #000000000000", +" ", +"................", +"................", +" . . . ", +" . . . ", +" . . . ", +" . . ", +" . . ", +" . ... ", +" . ..... ", +" . .. .. ", +" . ... ", +" ... ", +" .. ", +" ..... ", +" ..... "}; diff --git a/konquest/planet_info.cc b/konquest/planet_info.cc new file mode 100644 index 00000000..4c1d4be1 --- /dev/null +++ b/konquest/planet_info.cc @@ -0,0 +1,161 @@ +#include +#include +#include +#include +#include +#include + +#include "planet_info.h" +#include +#include "planet_info.moc" + +PlanetInfo::PlanetInfo( QWidget *parent, QPalette palette ) + : QFrame( parent ) +{ + setPalette( palette ); + + name = new QLabel( this ); + name->setMinimumWidth( 100 ); + owner = new QLabel( this ); + owner->setMinimumWidth( 100 ); + ships = new QLabel( this ); + ships->setMinimumWidth( 100 ); + production = new QLabel( this ); + production->setMinimumWidth( 100 ); + kill_percent = new QLabel( this ); + kill_percent->setMinimumWidth( 100 ); + + clearDisplay(); + + QVBoxLayout *layout1 = new QVBoxLayout( this ); + + layout1->addWidget( name ); + layout1->addWidget( owner ); + layout1->addWidget( ships ); + layout1->addWidget( production ); + layout1->addWidget( kill_percent ); + layout1->addStretch(1); + + setMouseTracking( true ); + + setMinimumSize( sizeHint() ); + setMaximumHeight( sizeHint().height() ); +} + +PlanetInfo::~PlanetInfo() +{ + emptyPlanetInfoList(); +} + +QSize PlanetInfo::sizeHint() const +{ + int height; + + height = name->sizeHint().height() + + owner->sizeHint().height() + + ships->sizeHint().height() + + production->sizeHint().height()+ + kill_percent->sizeHint().height(); + + return QSize( 100, height ); +} + +void PlanetInfo::setPlanetList( PlanetList &newPlanets ) +{ + emptyPlanetInfoList(); + + PlanetListIterator itr( newPlanets ); + + Planet *p; + while( (p = itr()) ) { + planet_info_buffer *stats = new planet_info_buffer; + stats->planet = p; + planet_stats.append( stats ); + } + + rescanPlanets(); +} + +void PlanetInfo::rescanPlanets() +{ + PlanetInfoListIterator itr( planet_stats ); + planet_info_buffer *p; + + while( (p = itr()) ) { + p->production = p->planet->getProduction(); + p->ships = p->planet->getFleet().getShipCount(); + p->killRate = p->planet->getKillPercentage(); + } +} + +void PlanetInfo::clearDisplay() +{ + QString temp; + + temp = "" + i18n("Planet name: "); + name->setText( temp ); + + temp = "" + i18n("Owner: "); + owner->setText( temp ); + + temp = "" + i18n("Ships: "); + ships->setText( temp ); + + temp = "" + i18n("Production: "); + production->setText( temp ); + + temp = "" + i18n("Kill percent: "); + kill_percent->setText( temp ); +} + +void PlanetInfo::emptyPlanetInfoList() +{ + planet_stats.first(); + + planet_info_buffer *p; + while( (p = planet_stats.take()) ) { + delete p; + } + +} + +void PlanetInfo::showPlanet( Planet *planet ) +{ + if( planet->getPlayer()->isNeutral() ) { + clearDisplay(); + + QString temp; + + temp = "" + i18n("Planet name: %1").arg(planet->getName()); + name->setText( temp ); + return; + } + + QString nameToShow = planet->getName(); + + PlanetInfoListIterator itr( planet_stats ); + planet_info_buffer *p; + + while( (p = itr()) ) { + if( p->planet == planet ) { + + QString temp; + + temp = "" + i18n("Planet name: %1").arg(p->planet->getName()); + name->setText( temp ); + + temp = "" + i18n("Owner: %1").arg(p->planet->getPlayer()->getColoredName()); + owner->setText( temp ); + + temp = "" + i18n("Ships: %1").arg( KGlobal::locale()->formatNumber(p->ships, 0) ); + ships->setText( temp ); + + temp = "" + i18n("Production: %1").arg( KGlobal::locale()->formatNumber(p->production, 0) ); + production->setText( temp ); + + temp = "" + i18n("Kill percent: %1").arg( KGlobal::locale()->formatNumber(p->killRate, 3) ); + kill_percent->setText( temp ); + } + } +} + diff --git a/konquest/planet_info.h b/konquest/planet_info.h new file mode 100644 index 00000000..4080f6b9 --- /dev/null +++ b/konquest/planet_info.h @@ -0,0 +1,53 @@ +#ifndef _PLANET_INFO_H_ +#define _PLANET_INFO_H_ + +#include +#include +#include +#include + +#include "gamecore.h" + +class QLabel; + +struct planet_info_buffer { + Planet *planet; + int production; + int ships; + float killRate; +}; + +typedef QPtrList PlanetInfoList; +typedef QPtrListIterator PlanetInfoListIterator; + +class PlanetInfo : public QFrame +{ + Q_OBJECT + +public: + PlanetInfo( QWidget *parent, QPalette palette ); + virtual ~PlanetInfo(); + + void setPlanetList( PlanetList &newPlanets ); + void rescanPlanets(); + QSize sizeHint() const; + +public slots: + void showPlanet( Planet * ); + +private: + void emptyPlanetInfoList(); + void clearDisplay(); + + PlanetList *planets; + PlanetInfoList planet_stats; + + QLabel *name; + QLabel *owner; + QLabel *ships; + QLabel *production; + QLabel *kill_percent; +}; + +#endif // _PLANET_INFO_H_ + diff --git a/konquest/scoredlg.cc b/konquest/scoredlg.cc new file mode 100644 index 00000000..685ba9c5 --- /dev/null +++ b/konquest/scoredlg.cc @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include + +#include "scoredlg.h" + +ScoreDlgListViewItem::ScoreDlgListViewItem(QListView *parent, QString s1, QString s2, QString s3, QString s4, QString s5, QString s6) : QListViewItem(parent, s1, s2, s3, s4, s5, s6) +{ +} + +int ScoreDlgListViewItem::compare(QListViewItem *i, int col, bool) const +{ + if (col == 0) + { + if (text(col) > i -> text(col)) return 1; + else if (text(col) < i -> text(col)) return -1; + else return 0; + } + else + { + if (text(col).toInt() > i -> text(col).toInt()) return 1; + else if (text(col).toInt() < i -> text(col).toInt()) return -1; + else return compare(i, 0, true); + } +} + + +ScoreDlg::ScoreDlg( QWidget *parent, const QString& title, PlayerList *players ) + : QDialog(parent, "ScoreDlg", true ), plrList(players) +{ + setCaption( kapp->makeStdCaption(title) ); + + scoreTable = new KListView( this, 0 ); + scoreTable->addColumn(i18n("Player")); + scoreTable->addColumn(i18n("Ships Built")); + scoreTable->addColumn(i18n("Planets Conquered")); + scoreTable->addColumn(i18n("Fleets Launched")); + scoreTable->addColumn(i18n("Fleets Destroyed")); + scoreTable->addColumn(i18n("Ships Destroyed")); + scoreTable->setMinimumSize( scoreTable->sizeHint() ); + + KPushButton *okButton = new KPushButton( KStdGuiItem::ok(), this ); + okButton->setMinimumSize( okButton->sizeHint() ); + okButton->setDefault(true); + + QVBoxLayout *layout1 = new QVBoxLayout( this ); + QHBoxLayout *layout2 = new QHBoxLayout; + + layout1->addWidget( scoreTable, 1 ); + layout1->addLayout( layout2 ); + + layout2->addStretch( 2 ); + layout2->addWidget( okButton ); + layout2->addStretch( 2 ); + + connect( okButton, SIGNAL(clicked()), this, SLOT(accept()) ); + + init(); + + resize( 580, 140 ); +} + +void +ScoreDlg::init() +{ + Player *curPlayer; + PlayerListIterator itr( *plrList ); + + for( ;(curPlayer = itr()); ) + new ScoreDlgListViewItem(scoreTable, + curPlayer->getName(), + QString("%1").arg(curPlayer->getShipsBuilt()), + QString("%1").arg(curPlayer->getPlanetsConquered()), + QString("%1").arg(curPlayer->getFleetsLaunched()), + QString("%1").arg(curPlayer->getEnemyFleetsDestroyed()), + QString("%1").arg(curPlayer->getEnemyShipsDestroyed())); +} + diff --git a/konquest/scoredlg.h b/konquest/scoredlg.h new file mode 100644 index 00000000..570a3b36 --- /dev/null +++ b/konquest/scoredlg.h @@ -0,0 +1,32 @@ +#ifndef _SCOREDLG_H_ +#define _SCOREDLG_H_ + +#include + +#include + +#include "gamecore.h" + +class ScoreDlgListViewItem : public QListViewItem +{ + public: + ScoreDlgListViewItem(QListView *parent, QString s1, QString s2, QString s3, QString s4, QString s5, QString s6); + int compare(QListViewItem *i, int col, bool) const; +}; + +class ScoreDlg : public QDialog +{ + +public: + ScoreDlg( QWidget *parent, const QString& title, PlayerList *players ); + +private: + void init(); + + PlayerList *plrList; + QListView *scoreTable; + +}; + +#endif // _SCOREDLG_H_ + diff --git a/konquest/version.h b/konquest/version.h new file mode 100644 index 00000000..df59820a --- /dev/null +++ b/konquest/version.h @@ -0,0 +1,4 @@ +#ifndef KONQUEST_VERSION +#define KONQUEST_VERSION "1.1" +#endif + diff --git a/kpat/AUTHORS b/kpat/AUTHORS new file mode 100644 index 00000000..cc9f0a73 --- /dev/null +++ b/kpat/AUTHORS @@ -0,0 +1,6 @@ +Olav Tvete (original author) +Matthias Ettrich +Mario Weilguni +Rodolfo Borges +Peter H. Ruegg +Stephan Kulow (current maintainer) diff --git a/kpat/CHANGES b/kpat/CHANGES new file mode 100644 index 00000000..2480f12e --- /dev/null +++ b/kpat/CHANGES @@ -0,0 +1,150 @@ +2005-06-25 Inge Wallin + + Fix bug 131587: "cannot win this game" message just after + start of mod3 game + - Check for ace at the store in addition to empty + +2005-06-25 Inge Wallin + + Code cleaning + - Rename Card::Value --> Card::Rank + - Rename Card::Suites --> Card::Suit + +2005-02-18 Inge Wallin + + Bumped version number to 2.2.2 because of all the bug fixes + during the last months and the upcoming release of KDE 3.4. + +2005-01-07 Inge Wallin + + Fix bug 91317: Klondike (draw 3) incorrect unwinnable game message + - Fix criteria for lost game in klondike. + + ---- CVS commit here ---- + + Fix bug 96531: KPat: Mod3 incorrect unwinnable game message + - Fix criteria for lost game in mod3. + +2004-11-10 Inge Wallin + + Continue the code cleaning + - card.h, card.cpp + +2004-11-04 Inge Wallin + + Fix the fix for 92002 below: + - Only ask for confirmation if the user actively aborts the game. + + ---- CVS commit here ---- + + Start of a thorough code cleaning: + - Start with hide.h and dealer.cpp + +2004-10-31 Inge Wallin + + - fix wish 92002: Restart game button should have a confirmation dialog + +2004-10-15 Inge Wallin + + - fix bug 89276: kpat: make nag screen "you have lost this game" an option + +2004-10-14 Inge Wallin + + - fix bug 87451: crash in patience when selecting 'unknown' as game type + ---- CVS commit here ---- + + - fix bug 89755: Aces up move counter doesn't work (idiot.cpp) + - bump version to 2.2.1 + +------------------ KDE 3.3.1 released here --------------------- + +since kpat-0.7.3 (18/Nov/1999) + + - bugfix in idiot.cpp + - bugfixes in grandf.cpp + - added CTRL-R for new game + +1999-02-01 Mario Weilguni + + * fixes to avoid many warning from egcs + +Sat May 2 13:49:46 1998 Mario Weilguni + + * replaced all locale->translate with i18n + +kapt 0.7.1 + - [Robert Williams] added getHelpMenu() + - [Robert Williams] added -caption "%c" to kpat.kdelnk + +since kpat-0.7 (11/Dec/1997) + + - added "Mod3" game (Rodolfo) + - added "Freecell" game (Rodolfo) + - Deck class can now handle any multiple of 52 cards (Rodolfo) + +since kpat-0.6 + + - bugfix for "Grandfather"-hint (Rodolfo) + - new KDE FSSTND compliant + - depending on display colors sets either a + 16 color or high color icon and miniicon + - fixes for new KConfig + +since kpat-0.5 + + - fixed the bug in Napoleon (cards not completely drawn) + - added locale + - german translation + +since kpat-0.4 + + - major rewrite of pwidget.(cpp|h) + - KDE compliant menubar and dialogs + - kpat now stores preferences (type of game...) + - window title includes current game type + - misc. cleanups + - kpat uses now kfixedtopwidget (similar to ktoplevelwidget) + - toolbar added (though it does not have many buttons) + - uses KMsgBox instead of QMessageBox + - now kpat has a fixed size + - (hopefully) KDE compliant shortcut keys + - did a lot of reformatting the sourcecode to make + it more readable (had to many empty lines IMHO) + - pwidget does not decide which sizes to use for + the different type of games, now the games itself supply + a sizehint (will make it more extendible) + - HTML help + - animation on startup, since loading the cards may take + several seconds to finish on slow machines. + - a lot more not mentioned here... + +since kpat-0.3 + + - kpat is a KApplication now (Matthias) + +since kpat-0.2 + + - added "MicroSolitaire" game (Paul) + - removed "cheating" option from Klondike (Paul) + - set wholeColumn for Klondike (Paul) + - Changed Klondike accelerator to Ctrl+K (Paul) + - Added menu bar hot keys (Paul) + +since kpat-0.1 + + - changed shading colorGroup (Paul) + - fixed drawing of suit symbols (Paul) + - renamed "very easy" to "cheating" :) (Paul) + - fixed spurious core dump(Paul) + - handle mousePress outside cards (Paul) + - menu cleanup (Paul) + +since "patience" + + - renamed the stuff kpat-0.1 (Matthias) + - improved drag'n'drop (no more weird jumping of cards) (Matthias) + - improved look (shaded borders around the cards and piles) (Matthias) + - hopefully improved cards-background (Matthias) + - "very easy"-option for klondike to give only one card. (Matthias) + - unset wholeColumn for klondike, but this all belongs into OPTIONS (Matthias) + diff --git a/kpat/Makefile.am b/kpat/Makefile.am new file mode 100644 index 00000000..1243bf9f --- /dev/null +++ b/kpat/Makefile.am @@ -0,0 +1,30 @@ + +INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes) + +bin_PROGRAMS = kpat + +kpat_SOURCES = main.cpp cardmaps.cpp card.cpp dealer.cpp \ + pwidget.cpp pile.cpp deck.cpp pile_algorithms.cpp kings.cpp freecell.cpp klondike.cpp simon.cpp grandf.cpp \ + mod3.cpp idiot.cpp napoleon.cpp computation.cpp gypsy.cpp fortyeight.cpp \ + yukon.cpp clock.cpp golf.cpp spider.cpp \ + gamestatsimpl.cpp \ + gamestats.ui +kpat_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kpat_LDADD = $(LIB_KFILE) $(LIB_KDEGAMES) ./freecell-solver/libfcs.la +kpat_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +METASOURCES = AUTO + +SUBDIRS = icons freecell-solver + +xdg_apps_DATA = kpat.desktop + +rcdir = $(kde_datadir)/kpat +rc_DATA = kpatui.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kpat.pot + +bgdir = $(kde_datadir)/kpat/backgrounds +bg_DATA = green.png + diff --git a/kpat/README b/kpat/README new file mode 100644 index 00000000..72a2cd6a --- /dev/null +++ b/kpat/README @@ -0,0 +1,41 @@ +Hello, + +this is the very first release of kpat, the KDE solitaire patience game. + +It is an almost unchanged "patience" from Paul Olav Tvete, who uploaded +this stuff some months ago on www.troll.no. + +My changes are so far: + + - renamed the stuff kpat-0.1 + - improved drag'n'drop (no more weird jumping of cards) + - improved look (shaded borders around the cards and piles) + - hopefully improved cards-background + - "very easy"-option for klondike to give only one card. + - unset wholeColumn for klondike, but this all belongs into OPTIONS + +Anway, kpat is IMO already yet the best X-based solitaire-like game, +thanks to Paul Olav Tvete and Qt :-) + + +Greets, + + Matthias + + + +----- original README (probably Paul): + +This is an implementation of patience (solitaire). It consists of a +general class library and six games implemented using it. + +I wrote this program when learning Qt. There are a number of things I +would have done differently now. One of the silliest is that all the +intelligence lies in the cards (which are individual widgets, by the way) +instead of in a Patience class. + +There are a lot of static variables. Don't try to instantiate more than one +dealer at a time -- that way lies madness. + +The general base classes could doubtlessly have been a lot more general. + diff --git a/kpat/README.tkcTrump b/kpat/README.tkcTrump new file mode 100644 index 00000000..93cbba95 --- /dev/null +++ b/kpat/README.tkcTrump @@ -0,0 +1,8 @@ +If you bought a binary of tkcTrump from theKompany and want to modify +the source code to fit your needs, check out +http://developer.kde.org/~coolo/tkcTrump-path.diff.bz2 + +Please note: I do not support the code, but if you saw something in tkcTrump +worth backporting, look at the patch. + +Stephan Kulow diff --git a/kpat/TODO b/kpat/TODO new file mode 100644 index 00000000..3f4e41c1 --- /dev/null +++ b/kpat/TODO @@ -0,0 +1,42 @@ +Cleaning: + type Dealer --> Patience + type Card::Value --> Card::Rank + + +TODO: + - more options (wholeColumn, etc.) + kpat already supports a lot, but they aren't accessible via GUI yet + - implement support for nice (configurable) backgrounds, like + color gradients, background pixmaps (tiled, wallpaper, etc. ). + - give feedback in the statusbar if there is no move except draw + possible. Open a messagebox when no more moves are possible at all. + (ie. Game Over). + - separate the dealer and related classed into a library, so + that other card games can use it. + +DONE: + - preview on cards that are hidden but faceup (RMB) + - animation when a card moves back automatically + - nice animation when you win a game + - game numbers as in MS freecell (thanks Marcus) + - undo + - ambigious place - choose one + - flip animation + - dblclick to put cards directly onto the piles. + - more colorful cards (there are some nice with xpat2) + same cards as KPoker (shamelessly stolen :-) + - better scaling (what about a 640x480 resolution?) + - more options (different backgrounds) + - kpat still uses the WidgetAt function when dropping cards. That + means it is not possible to put the hot spot onto the middle of the + dragged cards what would be better. + +Solutions: +#1 Wilson Callan +3a 32 7b 3c 37 37 b7 8b 87 48 +82 a8 4a 34 57 54 85 8d 87 c7 +d7 b8 38 23 28 32 6b 6c 78 a3 +73 7a 7c 74 c7 67 63 56 8h b8 +5b 51 b5 24 25 6h 6h 24 26 a4 +37 2a 8h 4h 1h 17 1h 1b 8h 4h +4b 4c 4d a2 42 46 3h 7h 13 diff --git a/kpat/card.cpp b/kpat/card.cpp new file mode 100644 index 00000000..983a901d --- /dev/null +++ b/kpat/card.cpp @@ -0,0 +1,380 @@ +/****************************************************** + + Card.cpp -- support classes for patience type card games + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +*******************************************************/ + +#include +#include + +#include + +#include + +#include "card.h" +#include "pile.h" +#include "cardmaps.h" + + +static const char *suit_names[] = {"Clubs", "Diamonds", "Hearts", "Spades"}; +static const char *rank_names[] = {"Ace", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", + "Nine", "Ten", "Jack", "Queen", "King" }; + +// Run time type id +const int Card::RTTI = 1001; + + +Card::Card( Rank r, Suit s, QCanvas* _parent ) + : QCanvasRectangle( _parent ), + m_suit( s ), m_rank( r ), + m_source(0), scaleX(1.0), scaleY(1.0), tookDown(false) +{ + // Set the name of the card + // FIXME: i18n() + m_name = QString("%1 %2").arg(suit_names[s-1]).arg(rank_names[r-1]).utf8(); + + // Default for the card is face up, standard size. + m_faceup = true; + setSize( cardMap::CARDX(), cardMap::CARDY() ); + + m_destX = 0; + m_destY = 0; + m_destZ = 0; + + m_flipping = false; + m_animSteps = 0; + m_flipSteps = 0; +} + + +Card::~Card() +{ + // If the card is in a pile, remove it from there. + if (source()) + source()->remove(this); + + hide(); +} + + +// ---------------------------------------------------------------- +// Member functions regarding graphics + + +// Return the pixmap of the card +// +QPixmap Card::pixmap() const +{ + return cardMap::self()->image( m_rank, m_suit ); +} + + +// Turn the card if necessary. If the face gets turned up, the card +// is activated at the same time. +// +void Card::turn( bool _faceup ) +{ + if (m_faceup != _faceup) { + m_faceup = _faceup; + setActive(!isActive()); // abuse + } +} + +// Draw the card on the painter 'p'. +// +void Card::draw( QPainter &p ) +{ + QPixmap side; + + // Get the image to draw (front / back) + if( isFaceUp() ) + side = cardMap::self()->image( m_rank, m_suit, isSelected()); + else + side = cardMap::self()->backSide(); + + // Rescale the image if necessary. + if (scaleX <= 0.98 || scaleY <= 0.98) { + QWMatrix s; + s.scale( scaleX, scaleY ); + side = side.xForm( s ); + int xoff = side.width() / 2; + int yoff = side.height() / 2; + p.drawPixmap( int(x() + cardMap::CARDX()/2 - xoff), + int(y() + cardMap::CARDY()/2 - yoff), side ); + } else + p.drawPixmap( int(x()), int(y()), side ); +} + + +void Card::moveBy(double dx, double dy) +{ + QCanvasRectangle::moveBy(dx, dy); +} + + +// Return the X of the cards real position. This is the destination +// of the animation if animated, and the current X otherwise. +// +int Card::realX() const +{ + if (animated()) + return m_destX; + else + return int(x()); +} + + +// Return the Y of the cards real position. This is the destination +// of the animation if animated, and the current Y otherwise. +// +int Card::realY() const +{ + if (animated()) + return m_destY; + else + return int(y()); +} + + +// Return the > of the cards real position. This is the destination +// of the animation if animated, and the current Z otherwise. +// +int Card::realZ() const +{ + if (animated()) + return m_destZ; + else + return int(z()); +} + + +// Return the "face up" status of the card. +// +// This is the destination of the animation if animated and animation +// is more than half way, the original if animated and animation is +// less than half way, and the current "face up" status otherwise. +// + +bool Card::realFace() const +{ + if (animated() && m_flipping) { + bool face = isFaceUp(); + if ( m_animSteps >= m_flipSteps / 2 - 1 ) + return !face; + else + return face; + } else + return isFaceUp(); +} + + +/// the following copyright is for the flipping code +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qt Palmtop Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + + +// Used to create an illusion of the card being lifted while flipped. +static const double flipLift = 1.2; + +// The current maximum Z value. This is used so that new cards always +// get placed on top of the old ones and don't get placed in the +// middle of a destination pile. +int Card::Hz = 0; + + +void Card::setZ(double z) +{ + QCanvasRectangle::setZ(z); + if (z > Hz) + Hz = int(z); +} + + +// Start a move of the card using animation. +// +// 'steps' is the number of steps the animation should take. +// +void Card::moveTo(int x2, int y2, int z2, int steps) +{ + m_destX = x2; + m_destY = y2; + m_destZ = z2; + + double x1 = x(); + double y1 = y(); + double dx = x2 - x1; + double dy = y2 - y1; + + if (!dx && !dy) { + setZ(z2); + return; + } + setZ(Hz++); + + if (steps) { + // Ensure a good speed + while ( fabs(dx/steps)+fabs(dy/steps) < 5.0 && steps > 4 ) + steps--; + + setAnimated(true); + setVelocity(dx/steps, dy/steps); + + m_animSteps = steps; + + } else { + // _really_ fast + setAnimated(true); + setAnimated(false); + emit stoped(this); + } +} + + +// Animate a move to (x2, y2), and at the same time flip the card. +// +void Card::flipTo(int x2, int y2, int steps) +{ + // Check that we are not already animating. + assert(!animated()); + + int x1 = (int)x(); + int y1 = (int)y(); + double dx = x2 - x1; + double dy = y2 - y1; + + // Mark this animation as a flip as well. + m_flipping = true; + m_flipSteps = steps; + + // Set the target of the animation + m_destX = x2; + m_destY = y2; + m_destZ = int(z()); + + // Let the card be above all others during the animation. + setZ(Hz++); + + m_animSteps = steps; + setVelocity(dx/m_animSteps, dy/m_animSteps-flipLift); + + setAnimated(TRUE); +} + + +// Advance a card animation one step. This function adds flipping of +// the card to the translation animation that QCanvasRectangle offers. +// +void Card::advance(int stage) +{ + if ( stage==1 ) { + // If the animation is finished, emit stoped. (FIXME: name) + if ( m_animSteps-- <= 0 ) { + setAnimated(false); + emit stoped(this); + } else { + // Animation is not finished. Check for flipping and add + // that animation to the simple translation. + if ( m_flipping ) { + if ( m_animSteps > m_flipSteps / 2 ) { + // animSteps = flipSteps .. flipSteps/2 (flip up) -> 1..0 + scaleX = ((double)m_animSteps/m_flipSteps-0.5)*2; + } else { + // animSteps = flipSteps/2 .. 0 (flip down) -> 0..1 + scaleX = 1-((double)m_animSteps/m_flipSteps)*2; + } + if ( m_animSteps == m_flipSteps / 2-1 ) { + setYVelocity(yVelocity()+flipLift*2); + turn( !isFaceUp() ); + } + } + } + } + + // Animate the translation of the card. + QCanvasRectangle::advance(stage); +} + + +// Set 'animated' status to a new value, and set secondary values as +// well. +// +void Card::setAnimated(bool anim) +{ + // If no more animation, reset some other values as well. + if (animated() && !anim) { + // Reset all things that might have changed during the animation. + scaleX = 1.0; + scaleY = 1.0; + m_flipping = FALSE; + setVelocity(0, 0); + + // Move the card to its destination immediately. + move(m_destX, m_destY); + setZ(m_destZ); + } + + QCanvasRectangle::setAnimated(anim); +} + + +void Card::setTakenDown(bool td) +{ + if (td) + kdDebug(11111) << "took down " << name() << endl; + tookDown = td; +} + + +bool Card::takenDown() const +{ + return tookDown; +} + + +// Get the card to the top. + +void Card::getUp(int steps) +{ + m_destZ = int(z()); + m_destX = int(x()); + m_destY = int(y()); + setZ(Hz+1); + + // Animation + m_animSteps = steps; + setVelocity(0, 0); + setAnimated(TRUE); +} + +#include "card.moc" diff --git a/kpat/card.h b/kpat/card.h new file mode 100644 index 00000000..61f07399 --- /dev/null +++ b/kpat/card.h @@ -0,0 +1,131 @@ +/*****************-*-C++-*-**************** + + + + Card.h -- movable and stackable cards + with check for legal moves + + + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + + ****************************************************/ + + +#ifndef PATIENCE_CARD +#define PATIENCE_CARD + +#include + +// The following classes are defined in other headers: +class cardPos; +class Deck; +class Dealer; +class Pile; +class Card; + + +// A list of cards. Used in many places. +typedef QValueList CardList; + + +// In kpat, a Card is an object that has at least two purposes: +// - It has card properties (Suit, Rank, etc) +// - It is a graphic entity on a QCanvas that can be moved around. +// +class Card: public QObject, public QCanvasRectangle { + Q_OBJECT + +public: + enum Suit { Clubs = 1, Diamonds, Hearts, Spades }; + enum Rank { None = 0, Ace = 1, Two, Three, Four, Five, Six, Seven, + Eight, Nine, Ten, Jack, Queen, King }; + + Card( Rank r, Suit s, QCanvas *parent=0); + virtual ~Card(); + + // Properties of the card. + Suit suit() const { return m_suit; } + Rank rank() const { return m_rank; } + const QString name() const { return m_name; } + + // Some basic tests. + bool isRed() const { return m_suit==Diamonds || m_suit==Hearts; } + bool isFaceUp() const { return m_faceup; } + + QPixmap pixmap() const; + + void turn(bool faceup = true); + + static const int RTTI; + + Pile *source() const { return m_source; } + void setSource(Pile *p) { m_source = p; } + + virtual int rtti() const { return RTTI; } + + virtual void moveBy(double dx, double dy); + void moveTo(int x2, int y2, int z, int steps); + void flipTo(int x, int y, int steps); + virtual void setAnimated(bool anim); + void setZ(double z); + void getUp(int steps = 12); + + int realX() const; + int realY() const; + int realZ() const; + bool realFace() const; + + void setTakenDown(bool td); + bool takenDown() const; + +signals: + void stoped(Card *c); + +protected: + void draw( QPainter &p ); // Redraw the card. + void advance(int stage); + +private: + // The card values. + Suit m_suit; + Rank m_rank; + QString m_name; + + // Grapics properties. + bool m_faceup; // True if card lies with the face up. + Pile *m_source; + + double scaleX; + double scaleY; + + bool tookDown; + + // Used for animation + int m_destX; // Destination point. + int m_destY; + int m_destZ; + int m_animSteps; // Let the animation take this many steps. + + // Used if flipping during an animated move. + bool m_flipping; + int m_flipSteps; + + // The maximum Z ever used. + static int Hz; +}; + + +#endif diff --git a/kpat/cardmaps.cpp b/kpat/cardmaps.cpp new file mode 100644 index 00000000..5a2decae --- /dev/null +++ b/kpat/cardmaps.cpp @@ -0,0 +1,248 @@ +/***********************-*-C++-*-******** + + cardmaps.cpp defines pixmaps for playing cards + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +****************************************/ + +#include +#include + +#include + +#include + +#include +#include "cardmaps.h" +#include + +#include +#include +#include +#include +#include "version.h" +#include +#include +#include +#include +#include +#include + +cardMap *cardMap::_self = 0; +static KStaticDeleter cms; + +cardMap::cardMap(const QColor &dim) : dimcolor(dim) +{ + assert(!_self); + + card_width = 0; + card_height = 0; + + kdDebug(11111) << "cardMap\n"; + KConfig *config = kapp->config(); + KConfigGroupSaver cs(config, settings_group ); + + QString bg = config->readEntry( "Back", KCardDialog::getDefaultDeck()); + setBackSide( bg, false); + + QString dir = config->readEntry("Cards", KCardDialog::getDefaultCardDir()); + setCardDir( dir ); + + cms.setObject(_self, this); +// kdDebug(11111) << "card " << CARDX << " " << CARDY << endl; +} + +bool cardMap::setCardDir( const QString &dir) +{ + KConfig *config = kapp->config(); + KConfigGroupSaver cs(config, settings_group ); + + // create an animation window while loading pixmaps (this + // may take a while (approx. 3 seconds on my AMD K6PR200) + bool animate = config->readBoolEntry( "Animation", true); + + QWidget* w = 0; + QPainter p; + QTime t1, t2; + + QString imgname = KCardDialog::getCardPath(dir, 11); + + QImage image; + image.load(imgname); + if( image.isNull()) { + kdDebug(11111) << "cannot load card pixmap \"" << imgname << "\" in " << dir << "\n"; + p.end(); + delete w; + return false; + } + + int old_card_width = card_width; + int old_card_height = card_height; + + card_width = image.width(); + card_height = image.height(); + + const int diff_x_between_cards = QMAX(card_width / 9, 1); + QString wait_message = i18n("please wait, loading cards..."); + QString greeting = i18n("KPatience - a Solitaire game"); + + const int greeting_width = 20 + diff_x_between_cards * 52 + card_width; + + if( animate ) { + t1 = QTime::currentTime(); + w = new QWidget( 0, "", Qt::WStyle_Customize | Qt::WStyle_NoBorder | Qt::WStyle_Tool ); + QRect dg = KGlobalSettings::splashScreenDesktopGeometry(); + w->setBackgroundColor( Qt::darkGreen ); + w->setGeometry( dg.left() + ( dg.width() - greeting_width ) / 2, dg.top() + ( dg.height() - 180 ) / 2, greeting_width, 180); + w->show(); + qApp->processEvents(); + + p.begin( w ); + p.drawText(0, 150, greeting_width, 20, Qt::AlignCenter, + wait_message ); + + p.setFont(QFont("Times", 24)); + p.drawText(0, 0, greeting_width, 40, Qt::AlignCenter, + greeting); + + p.setPen(QPen(QColor(0, 0, 0), 4)); + p.setBrush(Qt::NoBrush); + p.drawRect(0, 0, greeting_width, 180); + p.flush(); + } + + setBackSide(back, true); + + for(int idx = 1; idx < 53; idx++) + { + // translate index to suit/rank + // this is necessary since kpoker uses another + // mapping in the pictures + int rank = (idx - 1) / 4; + if(rank != 0) + rank = 13 - rank; + int suit = 0; + switch((idx - 1) % 4) { + case 0: + suit = 0; + break; + case 1: + suit = 3; + break; + case 2: + suit = 2; + break; + case 3: + suit = 1; + break; + } + + imgname = KCardDialog::getCardPath(dir, idx); + image.load(imgname); + + if( image.isNull() || image.width() != card_width || image.height() != card_height ) { + kdDebug(11111) << "cannot load card pixmap \"" << imgname << "\" in (" << idx << ") " << dir << "\n"; + p.end(); + delete w; + card_width = old_card_width; + card_height = old_card_height; + + setBackSide(back, true); + return false; + } + + img[rank][suit].normal.convertFromImage(image); + KImageEffect::fade(image, 0.4, dimcolor); + img[rank][suit].inverted.convertFromImage(image); + + if( animate ) + { + if( idx > 1 ) + p.drawPixmap( 10 + ( idx - 1 ) * diff_x_between_cards, 45, back ); + p.drawPixmap( 10 + idx * diff_x_between_cards, 45, img[ rank ][ suit ].normal ); + p.flush(); + } + } + + if( animate ) + { + const int time_to_see = 900; + p.end(); + t2 = QTime::currentTime(); + if(t1.msecsTo(t2) < time_to_see) + usleep((time_to_see-t1.msecsTo(t2))*1000); + delete w; + } + + return true; +} + +bool cardMap::setBackSide( const QPixmap &pm, bool scale ) +{ + if (pm.isNull()) + return false; + + back = pm; + + if(scale && (back.width() != card_width || + back.height() != card_height)) + { + kdDebug(11111) << "scaling back!!\n"; + // scale to fit size + QWMatrix wm; + wm.scale(((float)(card_width))/back.width(), + ((float)(card_height))/back.height()); + back = back.xForm(wm); + } + + return true; +} + +int cardMap::CARDX() { + return self()->card_width; // 72; +} + +int cardMap::CARDY() { + return self()->card_height; // 96; +} + +QPixmap cardMap::backSide() const +{ + return back; +} + +QPixmap cardMap::image( Card::Rank _rank, Card::Suit _suit, bool inverted) const +{ + if( 1 <= _rank && _rank <= 13 + && 1 <= _suit && _suit <= 4 ) + { + if (inverted) + return img[ _rank - 1 ][ _suit - 1 ].inverted; + else + return img[ _rank - 1 ][ _suit - 1 ].normal; + } + else + { + kdError() << "access to invalid card " << int(_rank) << ", " << int(_suit) << endl; + } + return 0; +} + +cardMap *cardMap::self() { + assert(_self); + return _self; +} + diff --git a/kpat/cardmaps.h b/kpat/cardmaps.h new file mode 100644 index 00000000..8bc8d92c --- /dev/null +++ b/kpat/cardmaps.h @@ -0,0 +1,59 @@ +/***********************-*-C++-*-******** + + cardmaps.h defines pixmaps for playing cards + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +****************************************/ + +#ifndef P_HACK_CARDMAP +#define P_HACK_CARDMAP + +#include "card.h" + +class cardMap +{ +public: + + static cardMap *self(); + cardMap(const QColor &dimcolor); + + static int CARDX(); + static int CARDY(); + + static const int NumColors = 4; + static const int CardsPerColor = 13; + + QPixmap image( Card::Rank _rank, Card::Suit _suit, bool inverted = false) const; + QPixmap backSide() const; + bool setCardDir( const QString &dir); + bool setBackSide( const QPixmap & _pix, bool scale = true); + +private: + + cardMap(); + struct + { + QPixmap normal; + QPixmap inverted; + } img[ CardsPerColor ][ NumColors ]; + QPixmap back; + QColor dimcolor; + int card_width, card_height; + + static cardMap *_self; +}; + +#endif diff --git a/kpat/clock.cpp b/kpat/clock.cpp new file mode 100644 index 00000000..7fcb94b4 --- /dev/null +++ b/kpat/clock.cpp @@ -0,0 +1,91 @@ +#include "clock.h" +#include +#include "deck.h" +#include +#include "cardmaps.h" + +Clock::Clock( KMainWindow* parent, const char *name ) + : Dealer( parent, name ) +{ + const int dist_x = cardMap::CARDX() * 11 / 10 + 1; + const int dist_y = cardMap::CARDY() * 11 / 10 + 1; + + deck = Deck::new_deck(this); + deck->move(10, 10+dist_y*3); + deck->hide(); + + for (int i=0; i<12; i++) { + target[i] = new Pile(i+1, this); + const double ys[12] = { 0./96, 15./96, 52./96, 158./96, 264./96, 301./96, 316./96, 301./96, 264./96, 158./96, 52./96, 15./96}; + const double xs[12] = { 200./72, 280./72, 360./72, 400./72, 360./72, 280./72, 200./72, 120./72, 40./72, 0./72, 40./72, 120./72}; + target[i]->move(15 + cardMap::CARDX() * 24 / 5 + xs[i] * cardMap::CARDX(), 10 + ys[i] * cardMap::CARDY()); + target[i]->setCheckIndex(1); + target[i]->setTarget(true); + target[i]->setRemoveFlags(Pile::disallow); + } + + for (int i=0; i<8; i++) { + store[i] = new Pile(14+i, this); + store[i]->move(15+dist_x*(i%4), 10 + cardMap::CARDY() * 5 / 2 * (i/4)); + store[i]->setAddFlags(Pile::addSpread); + store[i]->setCheckIndex(0); + } + + setActions(Dealer::Hint | Dealer::Demo); +} + +void Clock::restart() +{ + deck->collectAndShuffle(); + deal(); +} + +bool Clock::checkAdd( int ci, const Pile *c1, const CardList& c2) const +{ + Card *newone = c2.first(); + if (ci == 0) { + if (c1->isEmpty()) + return true; + + return (newone->rank() == c1->top()->rank() - 1); + } else { + if (c1->top()->suit() != newone->suit()) + return false; + if (c1->top()->rank() == Card::King) + return (newone->rank() == Card::Ace); + return (newone->rank() == c1->top()->rank() + 1); + } +} + +void Clock::deal() { + static const Card::Suit suits[12] = { Card::Diamonds, Card::Spades, Card::Hearts, Card::Clubs, + Card::Diamonds, Card::Spades, Card::Hearts, Card::Clubs, + Card::Diamonds, Card::Spades, Card::Hearts, Card::Clubs, }; + static const Card::Rank ranks[12] = { Card::Nine, Card::Ten, Card::Jack, Card::Queen, + Card::King, Card::Two, Card::Three, Card::Four, + Card::Five, Card::Six, Card::Seven, Card::Eight}; + + int j = 0; + while (!deck->isEmpty()) { + Card *c = deck->nextCard(); + for (int i = 0; i < 12; i++) + if (c->rank() == ranks[i] && c->suit() == suits[i]) { + target[i]->add(c, false, true); + c = 0; + break; + } + if (c) + store[j++]->add(c, false, true); + if (j == 8) + j = 0; + } +} + +static class LocalDealerInfo11 : public DealerInfo +{ +public: + LocalDealerInfo11() : DealerInfo(I18N_NOOP("G&randfather's Clock"), 11) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Clock(parent); } +} gfi11; + +#include "clock.moc" diff --git a/kpat/clock.h b/kpat/clock.h new file mode 100644 index 00000000..42521862 --- /dev/null +++ b/kpat/clock.h @@ -0,0 +1,24 @@ +#ifndef CLOCK_H +#define CLOCK_H + +#include "dealer.h" + +class Clock : public Dealer { + Q_OBJECT + +public: + Clock( KMainWindow* parent=0, const char* name=0); + virtual bool checkAdd ( int checkIndex, const Pile *c1, const CardList& c2) const; + virtual bool startAutoDrop() { return false; } + +public slots: + void deal(); + virtual void restart(); + +private: + Pile* store[8]; + Pile* target[12]; + Deck *deck; +}; + +#endif diff --git a/kpat/computation.cpp b/kpat/computation.cpp new file mode 100644 index 00000000..22f104aa --- /dev/null +++ b/kpat/computation.cpp @@ -0,0 +1,120 @@ +/***********************-*-C++-*-******** + + computation.h implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +// +// This one was discussed on the newsgroup rec.games.abstract +// +****************************************/ + +#include "computation.h" +#include +#include "deck.h" +#include +#include "cardmaps.h" + +Computation::Computation( KMainWindow *parent, const char *name ) + :Dealer( parent, name) +{ + deck = Deck::new_deck(this); + deck->hide(); + + for (int i = 0; i < 4; i++) { + play[i] = new Pile(1 + i, this); + play[i]->move(10 + (i+1) * cardMap::CARDX() * 14 / 10, 10 + cardMap::CARDY() * 15 / 10); + play[i]->setAddFlags(Pile::addSpread); + play[i]->setCheckIndex(1); + + target[i] = new Pile(5 + i, this); + target[i]->move(10 + (i+1) * cardMap::CARDX() * 14 / 10, 10); + target[i]->setRemoveFlags(Pile::disallow); + target[i]->setCheckIndex(0); + target[i]->setTarget(true); + } + + pile = new Pile(13, this); + pile->setAddFlags(Pile::disallow); + pile->setRemoveFlags(Pile::autoTurnTop); + pile->move(10, 10); + + setActions(Dealer::Demo | Dealer::Hint); +} + +void Computation::restart() { + deck->collectAndShuffle(); + deal(); +} + +void Computation::deal() { + while (!deck->isEmpty()) { + Card *c = deck->nextCard(); + pile->add(c, true, false); + } + // no animation + pile->top()->turn(true); +} + +inline bool matches(const CardList &cl, Card *start, int offset) +{ + Card *before = start; // maybe 0 for ignore first card + for (CardList::ConstIterator it = cl.begin(); it != cl.end(); ++it) + { + if (before && (*it)->rank() % 13 != (before->rank() + offset) % 13) + return false; + before = *it; + } + return true; +} + +bool Computation::checkStore( const Pile*, const CardList& cl) const +{ + if (cl.count() != 1) + return false; + return (cl.first()->source()->index() == 13); +} + +bool Computation::checkAdd( int index, const Pile* c1, const CardList& cl) const +{ + if (index == 1) + return checkStore(c1, cl); + + assert(c1->index() >= 5 && c1->index() <= 8); + + if ( c1->top() && c1->top()->rank() == Card::King) // finished + return false; + + if ( c1->cardsLeft() == 13 ) + return false; + + int offset = c1->index() - 4; + + if (c1->isEmpty()) { + Card::Rank start = static_cast(Card::Ace + (offset - 1)); + return cl.first()->rank() == start && matches(cl, 0, offset); + } + + return matches(cl, c1->top(), offset); +} + +static class LocalDealerInfo6 : public DealerInfo +{ +public: + LocalDealerInfo6() : DealerInfo(I18N_NOOP("&Calculation"), 6) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Computation(parent); } +} ldi6; + +#include "computation.moc" diff --git a/kpat/computation.h b/kpat/computation.h new file mode 100644 index 00000000..3b9b3fbc --- /dev/null +++ b/kpat/computation.h @@ -0,0 +1,53 @@ +/***********************-*-C++-*-******** + + computation.h implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +// +// This one was discussed on the newsgroup rec.games.abstract +// + + +****************************************/ + +#ifndef P_COMPUTATION_H +#define P_COMPUTATION_H + +#include "dealer.h" + +class Computation : public Dealer { + Q_OBJECT + +public: + Computation( KMainWindow *parent = 0, const char *name=0 ); + + virtual void restart(); + +private: + Card *getCardByValue( char v ); + void deal(); + + bool checkStore(const Pile* c1, const CardList& c2) const; + virtual bool checkAdd( int index, const Pile* c1, const CardList& c2) const; + + Deck *deck; + Pile *pile; + + Pile *play[4]; + Pile *target[4]; +}; + +#endif diff --git a/kpat/copyright.h b/kpat/copyright.h new file mode 100644 index 00000000..5e1f2b5b --- /dev/null +++ b/kpat/copyright.h @@ -0,0 +1,154 @@ +/* + + + Patience -- a general class for patience card games + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + + except for the card bitmaps contained in the files + + newface.bm rank.bm suit.bm + +which are + + + (c) Copyright 1989, Donald R. Woods and Sun Microsystems, Inc. + (c) Copyright 1990, David Lemke and Network Computing Devices Inc. + + + + See the statement below for the terms of the copyright. + + + */ + + + + + + +/**** Copyright statement from spider/copyright.h follows this line ****/ + + + + + +/* + * Copyright 1990 Heather Rose and Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the names of Donald Woods and Sun Microsystems not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Heather Rose and Sun Microsystems not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Heather Rose and Sun Microsystems make + * no representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + * + * THE ABOVE-NAMED DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT + * SHALL HEATHER ROSE OR SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + * + * Author: + * Heather Rose + * hrose@sun.com + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, CA 94043 + */ + +/* + * Copyright 1990 David Lemke and Network Computing Devices + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Network Computing Devices not be + * used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. Network Computing + * Devices makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied + * warranty. + * + * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, + * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE + * OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: + * Dave Lemke + * lemke@ncd.com + * + * Network Computing Devices, Inc + * 350 North Bernardo Ave + * Mountain View, CA 94043 + * + * @(#)copyright.h 2.2 90/04/27 + * + */ + +/* +% Copyright (c) 1989, Donald R. Woods and Sun Microsystems, Inc. +% +% Permission to use, copy, modify, distribute, and sell this software and its +% documentation for any purpose is hereby granted without fee, provided that +% the above copyright notice appear in all copies and that both that copyright +% notice and this permission notice appear in supporting documentation, and +% that the names of Donald Woods and Sun Microsystems not be used in +% advertising or publicity pertaining to distribution of the software without +% specific, written prior permission. Donald Woods and Sun Microsystems make +% no representations about the suitability of this software for any purpose. +% It is provided "as is" without express or implied warranty. +% +% THE ABOVE-NAMED DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +% INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT +% SHALL DONALD WOODS OR SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR +% CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +% DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +% TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +% OF THIS SOFTWARE. +% +% History: Spider is a solitaire card game that can be found in various books +% of same; the rules are presumed to be in the public domain. The author's +% first computer implementation was on the Stanford Artificial Intelligence Lab +% system (SAIL). It was later ported to the Xerox Development Environment. +% The card images are loosely based on scanned-in images but were largely +% redrawn by the author with help from Larry Rosenberg. +% +% This program is written entirely in NeWS and runs on OPEN WINDOWS 1.0. +% It could be made to run much faster if parts of it were written in C, using +% NeWS mainly for its display and input capabilities, but that is left as an +% exercise for the reader. Spider may also run with little or no modification +% on subsequent releases of OPEN WINDOWS, but no guarantee is made on this +% point (nor any other; see above!). To run Spider, feed this file to 'psh'. +% +% Author: Don Woods +% woods@sun.com +% +% Sun Microsystems, Inc. +% 2550 Garcia Avenue +% Mountain View, CA 94043 +*/ diff --git a/kpat/dealer.cpp b/kpat/dealer.cpp new file mode 100644 index 00000000..2100ae2e --- /dev/null +++ b/kpat/dealer.cpp @@ -0,0 +1,1455 @@ +#include "dealer.h" +#include +#include +#include "deck.h" +#include +#include "kmainwindow.h" +#include +#include +#include +#include +#include +#include "cardmaps.h" +#include "speeds.h" +#include +#include "version.h" + + +// ================================================================ +// class MoveHint + + +MoveHint::MoveHint(Card *card, Pile *to, bool d) +{ + m_card = card; + m_to = to; + m_dropiftarget = d; +} + + +// ================================================================ +// class DealerInfoList + + +DealerInfoList *DealerInfoList::_self = 0; +static KStaticDeleter dl; + + +DealerInfoList *DealerInfoList::self() +{ + if (!_self) + _self = dl.setObject(_self, new DealerInfoList()); + return _self; +} + +void DealerInfoList::add(DealerInfo *dealer) +{ + list.append(dealer); +} + + +// ================================================================ +// class Dealer + + +Dealer *Dealer::s_instance = 0; + + +Dealer::Dealer( KMainWindow* _parent , const char* _name ) + : QCanvasView( 0, _parent, _name ), + towait(0), + myActions(0), + ademo(0), + ahint(0), + aredeal(0), + takeTargets(false), + _won(false), + _waiting(0), + stop_demo_next(false), + _autodrop(true), + _gameRecorded(false) +{ + setResizePolicy(QScrollView::Manual); + setVScrollBarMode(AlwaysOff); + setHScrollBarMode(AlwaysOff); + + setGameNumber(kapp->random()); + myCanvas.setAdvancePeriod(30); + // myCanvas.setBackgroundColor( darkGreen ); + setCanvas(&myCanvas); + myCanvas.setDoubleBuffering(true); + + undoList.setAutoDelete(true); + + demotimer = new QTimer(this); + + connect(demotimer, SIGNAL(timeout()), SLOT(demo())); + + assert(!s_instance); + s_instance = this; +} + + +const Dealer *Dealer::instance() +{ + return s_instance; +} + + +void Dealer::setBackgroundPixmap(const QPixmap &background, const QColor &midcolor) +{ + _midcolor = midcolor; + canvas()->setBackgroundPixmap(background); + for (PileList::Iterator it = piles.begin(); it != piles.end(); ++it) { + (*it)->resetCache(); + (*it)->initSizes(); + } +} + +void Dealer::setupActions() { + + QPtrList actionlist; + + kdDebug(11111) << "setupActions " << actions() << endl; + + if (actions() & Dealer::Hint) { + + ahint = new KAction( i18n("&Hint"), QString::fromLatin1("wizard"), Key_H, this, + SLOT(hint()), + parent()->actionCollection(), "game_hint"); + actionlist.append(ahint); + } else + ahint = 0; + + if (actions() & Dealer::Demo) { + ademo = new KToggleAction( i18n("&Demo"), QString::fromLatin1("1rightarrow"), CTRL+Key_D, this, + SLOT(toggleDemo()), + parent()->actionCollection(), "game_demo"); + actionlist.append(ademo); + } else + ademo = 0; + + if (actions() & Dealer::Redeal) { + aredeal = new KAction (i18n("&Redeal"), QString::fromLatin1("queue"), 0, this, + SLOT(redeal()), + parent()->actionCollection(), "game_redeal"); + actionlist.append(aredeal); + } else + aredeal = 0; + + parent()->guiFactory()->plugActionList( parent(), QString::fromLatin1("game_actions"), actionlist); +} + +Dealer::~Dealer() +{ + if (!_won) + countLoss(); + clearHints(); + parent()->guiFactory()->unplugActionList( parent(), QString::fromLatin1("game_actions")); + + while (!piles.isEmpty()) + delete piles.first(); // removes itself + + if (s_instance == this) + s_instance = 0; +} + +KMainWindow *Dealer::parent() const +{ + return dynamic_cast(QCanvasView::parent()); +} + + +// ---------------------------------------------------------------- + + +void Dealer::hint() +{ + unmarkAll(); + clearHints(); + getHints(); + for (HintList::ConstIterator it = hints.begin(); it != hints.end(); ++it) + mark((*it)->card()); + clearHints(); + canvas()->update(); +} + + +void Dealer::getHints() +{ + for (PileList::Iterator it = piles.begin(); it != piles.end(); ++it) + { + if (!takeTargetForHints() && (*it)->target()) + continue; + + Pile *store = *it; + if (store->isEmpty()) + continue; +// kdDebug(11111) << "trying " << store->top()->name() << endl; + + CardList cards = store->cards(); + while (cards.count() && !cards.first()->realFace()) cards.remove(cards.begin()); + + CardList::Iterator iti = cards.begin(); + while (iti != cards.end()) + { + if (store->legalRemove(*iti)) { +// kdDebug(11111) << "could remove " << (*iti)->name() << endl; + for (PileList::Iterator pit = piles.begin(); pit != piles.end(); ++pit) + { + Pile *dest = *pit; + if (dest == store) + continue; + if (store->indexOf(*iti) == 0 && dest->isEmpty() && !dest->target()) + continue; + if (!dest->legalAdd(cards)) + continue; + + bool old_prefer = checkPrefering( dest->checkIndex(), dest, cards ); + if (!takeTargetForHints() && dest->target()) + newHint(new MoveHint(*iti, dest)); + else { + store->hideCards(cards); + // if it could be here as well, then it's no use + if ((store->isEmpty() && !dest->isEmpty()) || !store->legalAdd(cards)) + newHint(new MoveHint(*iti, dest)); + else { + if (old_prefer && !checkPrefering( store->checkIndex(), + store, cards )) + { // if checkPrefers says so, we add it nonetheless + newHint(new MoveHint(*iti, dest)); + } + } + store->unhideCards(cards); + } + } + } + cards.remove(iti); + iti = cards.begin(); + } + } +} + +bool Dealer::checkPrefering( int /*checkIndex*/, const Pile *, const CardList& ) const +{ + return false; +} + +void Dealer::clearHints() +{ + for (HintList::Iterator it = hints.begin(); it != hints.end(); ++it) + delete *it; + hints.clear(); +} + +void Dealer::newHint(MoveHint *mh) +{ + hints.append(mh); +} + +bool Dealer::isMoving(Card *c) const +{ + return movingCards.find(c) != movingCards.end(); +} + +void Dealer::contentsMouseMoveEvent(QMouseEvent* e) +{ + if (movingCards.isEmpty()) + return; + + moved = true; + + for (CardList::Iterator it = movingCards.begin(); it != movingCards.end(); ++it) + { + (*it)->moveBy(e->pos().x() - moving_start.x(), + e->pos().y() - moving_start.y()); + } + + PileList sources; + QCanvasItemList list = canvas()->collisions(movingCards.first()->rect()); + + for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) + { + if ((*it)->rtti() == Card::RTTI) { + Card *c = dynamic_cast(*it); + assert(c); + if (!c->isFaceUp()) + continue; + if (c->source() == movingCards.first()->source()) + continue; + if (sources.findIndex(c->source()) != -1) + continue; + sources.append(c->source()); + } else { + if ((*it)->rtti() == Pile::RTTI) { + Pile *p = static_cast(*it); + if (p->isEmpty() && !sources.contains(p)) + sources.append(p); + } else { + kdDebug(11111) << "unknown object " << *it << " " << (*it)->rtti() << endl; + } + } + } + + // TODO some caching of the results + unmarkAll(); + + for (PileList::Iterator it = sources.begin(); it != sources.end(); ++it) + { + bool b = (*it)->legalAdd(movingCards); + if (b) { + if ((*it)->isEmpty()) { + (*it)->setSelected(true); + marked.append(*it); + } else { + mark((*it)->top()); + } + } + } + + moving_start = e->pos(); + canvas()->update(); +} + +void Dealer::mark(Card *c) +{ + c->setSelected(true); + if (!marked.contains(c)) + marked.append(c); +} + +void Dealer::unmarkAll() +{ + for (QCanvasItemList::Iterator it = marked.begin(); it != marked.end(); ++it) + { + (*it)->setSelected(false); + } + marked.clear(); +} + +void Dealer::contentsMousePressEvent(QMouseEvent* e) +{ + unmarkAll(); + stopDemo(); + if (waiting()) + return; + + QCanvasItemList list = canvas()->collisions(e->pos()); + + kdDebug(11111) << "mouse pressed " << list.count() << " " << canvas()->allItems().count() << endl; + moved = false; + + if (!list.count()) + return; + + if (e->button() == LeftButton) { + if (list.first()->rtti() == Card::RTTI) { + Card *c = dynamic_cast(list.first()); + assert(c); + CardList mycards = c->source()->cardPressed(c); + for (CardList::Iterator it = mycards.begin(); it != mycards.end(); ++it) + (*it)->setAnimated(false); + movingCards = mycards; + moving_start = e->pos(); + } + return; + } + + if (e->button() == RightButton) { + if (list.first()->rtti() == Card::RTTI) { + Card *preview = dynamic_cast(list.first()); + assert(preview); + if (!preview->animated() && !isMoving(preview)) + preview->getUp(); + } + return; + } + + // if it's nothing else, we move the cards back + contentsMouseReleaseEvent(e); + +} + +class Hit { +public: + Pile *source; + QRect intersect; + bool top; +}; +typedef QValueList HitList; + +void Dealer::contentsMouseReleaseEvent( QMouseEvent *e) +{ + if (!moved) { + if (!movingCards.isEmpty()) { + movingCards.first()->source()->moveCardsBack(movingCards); + movingCards.clear(); + } + QCanvasItemList list = canvas()->collisions(e->pos()); + if (list.isEmpty()) + return; + QCanvasItemList::Iterator it = list.begin(); + if ((*it)->rtti() == Card::RTTI) { + Card *c = dynamic_cast(*it); + assert(c); + if (!c->animated()) { + if ( cardClicked(c) ) { + countGame(); + } + takeState(); + canvas()->update(); + } + return; + } + if ((*it)->rtti() == Pile::RTTI) { + Pile *c = dynamic_cast(*it); + assert(c); + pileClicked(c); + takeState(); + canvas()->update(); + return; + } + } + + if (!movingCards.count()) + return; + Card *c = static_cast(movingCards.first()); + assert(c); + + unmarkAll(); + + QCanvasItemList list = canvas()->collisions(movingCards.first()->rect()); + HitList sources; + + for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) + { + if ((*it)->rtti() == Card::RTTI) { + Card *c = dynamic_cast(*it); + assert(c); + if (!c->isFaceUp()) + continue; + if (c->source() == movingCards.first()->source()) + continue; + Hit t; + t.source = c->source(); + t.intersect = c->rect().intersect(movingCards.first()->rect()); + t.top = (c == c->source()->top()); + + bool found = false; + for (HitList::Iterator hi = sources.begin(); hi != sources.end(); ++hi) + { + if ((*hi).source == c->source()) { + found = true; + if ((*hi).intersect.width() * (*hi).intersect.height() > + t.intersect.width() * t.intersect.height()) + { + (*hi).intersect = t.intersect; + (*hi).top |= t.top; + } + } + } + if (found) + continue; + + sources.append(t); + } else { + if ((*it)->rtti() == Pile::RTTI) { + Pile *p = static_cast(*it); + if (p->isEmpty()) + { + Hit t; + t.source = p; + t.intersect = p->rect().intersect(movingCards.first()->rect()); + t.top = true; + sources.append(t); + } + } else { + kdDebug(11111) << "unknown object " << *it << " " << (*it)->rtti() << endl; + } + } + } + + for (HitList::Iterator it = sources.begin(); it != sources.end(); ) + { + if (!(*it).source->legalAdd(movingCards)) + it = sources.remove(it); + else + ++it; + } + + if (sources.isEmpty()) { + c->source()->moveCardsBack(movingCards); + } else { + HitList::Iterator best = sources.begin(); + HitList::Iterator it = best; + for (++it; it != sources.end(); ++it ) + { + if ((*it).intersect.width() * (*it).intersect.height() > + (*best).intersect.width() * (*best).intersect.height() + || ((*it).top && !(*best).top)) + { + best = it; + } + } + countGame(); + c->source()->moveCards(movingCards, (*best).source); + takeState(); + } + movingCards.clear(); + canvas()->update(); +} + +void Dealer::contentsMouseDoubleClickEvent( QMouseEvent*e ) +{ + stopDemo(); + unmarkAll(); + if (waiting()) + return; + + if (!movingCards.isEmpty()) { + movingCards.first()->source()->moveCardsBack(movingCards); + movingCards.clear(); + } + QCanvasItemList list = canvas()->collisions(e->pos()); + if (list.isEmpty()) + return; + QCanvasItemList::Iterator it = list.begin(); + if ((*it)->rtti() != Card::RTTI) + return; + Card *c = dynamic_cast(*it); + assert(c); + if (!c->animated()) { + if ( cardDblClicked(c) ) { + countGame(); + } + takeState(); + } +} + +QSize Dealer::minimumCardSize() const +{ + return minsize; +} + +void Dealer::resizeEvent(QResizeEvent *e) +{ + int x = width(); + int y = height(); + int hs = horizontalScrollBar()->sizeHint().height(); + int vs = verticalScrollBar()->sizeHint().width(); + + int mx = minsize.width(); + int my = minsize.height(); + + int dx = x; + int dy = y; + bool showh = false; + bool showv = false; + + if (mx > x) { + dx = mx; + if (my + vs < y) + dy -= vs; + else { + showv = true; + } + showh = true; + } else if (my > y) { + dy = my; + if (mx + hs < x) + dx -= hs; + else + showh = true; + showv = true; + } + canvas()->resize(dx, dy); + resizeContents(dx, dy); + setVScrollBarMode(showv ? AlwaysOn : AlwaysOff); + setHScrollBarMode(showh ? AlwaysOn : AlwaysOff); + + if (!e) + updateScrollBars(); + else + QCanvasView::resizeEvent(e); +} + +bool Dealer::cardClicked(Card *c) { + return c->source()->cardClicked(c); +} + +void Dealer::pileClicked(Pile *c) { + c->cardClicked(0); +} + +bool Dealer::cardDblClicked(Card *c) +{ + if (c->source()->cardDblClicked(c)) + return true; + + if (c->animated()) + return false; + + if (c == c->source()->top() && c->realFace()) { + Pile *tgt = findTarget(c); + if (tgt) { + CardList empty; + empty.append(c); + c->source()->moveCards(empty, tgt); + canvas()->update(); + return true; + } + } + return false; +} + +void Dealer::startNew() +{ + if (!_won) + countLoss(); + if ( ahint ) + ahint->setEnabled( true ); + if ( ademo ) + ademo->setEnabled( true ); + if ( aredeal ) + aredeal->setEnabled( true ); + toldAboutLostGame = false; + minsize = QSize(0,0); + _won = false; + _waiting = 0; + _gameRecorded=false; + kdDebug(11111) << "startNew stopDemo\n"; + stopDemo(); + kdDebug(11111) << "startNew unmarkAll\n"; + unmarkAll(); + kdDebug(11111) << "startNew setAnimated(false)\n"; + QCanvasItemList list = canvas()->allItems(); + for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) { + if ((*it)->rtti() == Card::RTTI) + static_cast(*it)->disconnect(); + + (*it)->setAnimated(true); + (*it)->setAnimated(false); + } + + undoList.clear(); + emit undoPossible(false); + emit updateMoves(); + kdDebug(11111) << "startNew restart\n"; + restart(); + takeState(); + Card *towait = 0; + for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) { + if ((*it)->rtti() == Card::RTTI) { + towait = static_cast(*it); + if (towait->animated()) + break; + } + } + + kdDebug(11111) << "startNew takeState\n"; + if (!towait) + takeState(); + else + connect(towait, SIGNAL(stoped(Card*)), SLOT(slotTakeState(Card *))); + resizeEvent(0); +} + +void Dealer::slotTakeState(Card *c) { + if (c) + c->disconnect(); + takeState(); +} + +void Dealer::enlargeCanvas(QCanvasRectangle *c) +{ + if (!c->isVisible() || c->animated()) + return; + + bool changed = false; + + if (c->x() + c->width() + 10 > minsize.width()) { + minsize.setWidth(int(c->x()) + c->width() + 10); + changed = true; + } + if (c->y() + c->height() + 10 > minsize.height()) { + minsize.setHeight(int(c->y()) + c->height() + 10); + changed = true; + } + if (changed) + resizeEvent(0); +} + +class CardState { +public: + Card *it; + Pile *source; + double x; + double y; + double z; + bool faceup; + bool tookdown; + int source_index; + CardState() {} +public: + // as every card is only once we can sort after the card. + // < is the same as <= in that context. == is different + bool operator<(const CardState &rhs) const { return it < rhs.it; } + bool operator<=(const CardState &rhs) const { return it <= rhs.it; } + bool operator>(const CardState &rhs) const { return it > rhs.it; } + bool operator>=(const CardState &rhs) const { return it > rhs.it; } + bool operator==(const CardState &rhs) const { + return (it == rhs.it && source == rhs.source && x == rhs.x && + y == rhs.y && z == rhs.z && faceup == rhs.faceup + && source_index == rhs.source_index && tookdown == rhs.tookdown); + } + void fillNode(QDomElement &e) const { + e.setAttribute("value", it->rank()); + e.setAttribute("suit", it->suit()); + e.setAttribute("source", source->index()); + e.setAttribute("x", x); + e.setAttribute("y", y); + e.setAttribute("z", z); + e.setAttribute("faceup", faceup); + e.setAttribute("tookdown", tookdown); + e.setAttribute("source_index", source_index); + } +}; + +typedef class QValueList CardStateList; + +bool operator==( const State & st1, const State & st2) { + return st1.cards == st2.cards && st1.gameData == st2.gameData; +} + +State *Dealer::getState() +{ + QCanvasItemList list = canvas()->allItems(); + State * st = new State; + + for (QCanvasItemList::ConstIterator it = list.begin(); it != list.end(); ++it) + { + if ((*it)->rtti() == Card::RTTI) { + Card *c = dynamic_cast(*it); + assert(c); + CardState s; + s.it = c; + s.source = c->source(); + if (!s.source) { + kdDebug(11111) << c->name() << " has no parent\n"; + assert(false); + } + s.source_index = c->source()->indexOf(c); + s.x = c->realX(); + s.y = c->realY(); + s.z = c->realZ(); + s.faceup = c->realFace(); + s.tookdown = c->takenDown(); + st->cards.append(s); + } + } + qHeapSort(st->cards); + + // Game specific information + st->gameData = getGameState( ); + + return st; +} + +void Dealer::setState(State *st) +{ + CardStateList * n = &st->cards; + QCanvasItemList list = canvas()->allItems(); + + for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) + { + if ((*it)->rtti() == Pile::RTTI) { + Pile *p = dynamic_cast(*it); + assert(p); + CardList cards = p->cards(); + for (CardList::Iterator it = cards.begin(); it != cards.end(); ++it) + (*it)->setTakenDown(p->target()); + p->clear(); + } + } + + for (CardStateList::ConstIterator it = n->begin(); it != n->end(); ++it) + { + Card *c = (*it).it; + CardState s = *it; + bool target = c->takenDown(); // abused + s.source->add(c, s.source_index); + c->setVisible(s.source->isVisible()); + c->setAnimated(false); + c->setX(s.x); + c->setY(s.y); + c->setZ(int(s.z)); + c->setTakenDown(s.tookdown || (target && !s.source->target())); + c->turn(s.faceup); + } + + // restore game-specific information + setGameState( st->gameData ); + + delete st; + canvas()->update(); +} + +void Dealer::takeState() +{ + kdDebug(11111) << "takeState\n"; + + State *n = getState(); + + if (!undoList.count()) { + emit updateMoves(); + undoList.append(n); + } else { + State *old = undoList.last(); + + if (*old == *n) { + delete n; + n = 0; + } else { + emit updateMoves(); + undoList.append(n); + } + } + + if (n) { + if (isGameWon()) { + won(); + return; + } + else if (isGameLost() && !toldAboutLostGame) { + if ( ahint ) + ahint->setEnabled( false ); + if ( ademo ) + ademo->setEnabled( false ); + if ( aredeal ) + aredeal->setEnabled( false ); + QTimer::singleShot(400, this, SIGNAL(gameLost())); + toldAboutLostGame = true; + return; + } + } + if (!demoActive() && !waiting()) + QTimer::singleShot(TIME_BETWEEN_MOVES, this, SLOT(startAutoDrop())); + + emit undoPossible(undoList.count() > 1 && !waiting()); +} + +void Dealer::saveGame(QDomDocument &doc) { + QDomElement dealer = doc.createElement("dealer"); + doc.appendChild(dealer); + dealer.setAttribute("id", _id); + dealer.setAttribute("number", QString::number(gameNumber())); + QString data = getGameState(); + if (!data.isEmpty()) + dealer.setAttribute("data", data); + dealer.setAttribute("moves", QString::number(getMoves())); + + bool taken[1000]; + memset(taken, 0, sizeof(taken)); + + QCanvasItemList list = canvas()->allItems(); + for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) + { + if ((*it)->rtti() == Pile::RTTI) { + Pile *p = dynamic_cast(*it); + assert(p); + if (taken[p->index()]) { + kdDebug(11111) << "pile index " << p->index() << " taken twice\n"; + return; + } + taken[p->index()] = true; + + QDomElement pile = doc.createElement("pile"); + pile.setAttribute("index", p->index()); + + CardList cards = p->cards(); + for (CardList::Iterator it = cards.begin(); + it != cards.end(); + ++it) + { + QDomElement card = doc.createElement("card"); + card.setAttribute("suit", (*it)->suit()); + card.setAttribute("value", (*it)->rank()); + card.setAttribute("faceup", (*it)->isFaceUp()); + card.setAttribute("x", (*it)->realX()); + card.setAttribute("y", (*it)->realY()); + card.setAttribute("z", (*it)->realZ()); + card.setAttribute("name", (*it)->name()); + pile.appendChild(card); + } + dealer.appendChild(pile); + } + } + + /* + QDomElement eList = doc.createElement("undo"); + + QPtrListIterator it(undoList); + for (; it.current(); ++it) + { + State *n = it.current(); + QDomElement state = doc.createElement("state"); + if (!n->gameData.isEmpty()) + state.setAttribute("data", n->gameData); + QDomElement cards = doc.createElement("cards"); + for (QValueList::ConstIterator it2 = n->cards.begin(); + it2 != n->cards.end(); ++it2) + { + QDomElement item = doc.createElement("item"); + (*it2).fillNode(item); + cards.appendChild(item); + } + state.appendChild(cards); + eList.appendChild(state); + } + dealer.appendChild(eList); + */ + // kdDebug(11111) << doc.toString() << endl; +} + +void Dealer::openGame(QDomDocument &doc) +{ + unmarkAll(); + QDomElement dealer = doc.documentElement(); + + setGameNumber(dealer.attribute("number").toULong()); + undoList.clear(); + + QDomNodeList piles = dealer.elementsByTagName("pile"); + + QCanvasItemList list = canvas()->allItems(); + + CardList cards; + for (QCanvasItemList::ConstIterator it = list.begin(); it != list.end(); ++it) + if ((*it)->rtti() == Card::RTTI) + cards.append(static_cast(*it)); + + Deck::deck()->collectAndShuffle(); + + for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) + { + if ((*it)->rtti() == Pile::RTTI) + { + Pile *p = dynamic_cast(*it); + assert(p); + + for (uint i = 0; i < piles.count(); ++i) + { + QDomElement pile = piles.item(i).toElement(); + if (pile.attribute("index").toInt() == p->index()) + { + QDomNodeList pcards = pile.elementsByTagName("card"); + for (uint j = 0; j < pcards.count(); ++j) + { + QDomElement card = pcards.item(j).toElement(); + Card::Suit s = static_cast(card.attribute("suit").toInt()); + Card::Rank v = static_cast(card.attribute("value").toInt()); + + for (CardList::Iterator it2 = cards.begin(); + it2 != cards.end(); ++it2) + { + if ((*it2)->suit() == s && (*it2)->rank() == v) { + if (QString((*it2)->name()) == "Diamonds Eight") { + kdDebug(11111) << i << " " << j << endl; + } + p->add(*it2); + (*it2)->setAnimated(false); + (*it2)->turn(card.attribute("faceup").toInt()); + (*it2)->setX(card.attribute("x").toInt()); + (*it2)->setY(card.attribute("y").toInt()); + (*it2)->setZ(card.attribute("z").toInt()); + (*it2)->setVisible(p->isVisible()); + cards.remove(it2); + break; + } + } + } + } + } + } + } + setGameState( dealer.attribute("data") ); + + if (undoList.count() > 1) { + setState(undoList.take(undoList.count() - 1)); + takeState(); // copying it again + emit undoPossible(undoList.count() > 1); + } + + emit updateMoves(); + takeState(); +} + +void Dealer::undo() +{ + unmarkAll(); + stopDemo(); + if (undoList.count() > 1) { + undoList.removeLast(); // the current state + setState(undoList.take(undoList.count() - 1)); + emit updateMoves(); + takeState(); // copying it again + emit undoPossible(undoList.count() > 1); + if ( toldAboutLostGame ) { // everything's possible again + if ( ahint ) + ahint->setEnabled( true ); + if ( ademo ) + ademo->setEnabled( true ); + toldAboutLostGame = false; + } + } +} + +Pile *Dealer::findTarget(Card *c) +{ + if (!c) + return 0; + + CardList empty; + empty.append(c); + for (PileList::ConstIterator it = piles.begin(); it != piles.end(); ++it) + { + if (!(*it)->target()) + continue; + if ((*it)->legalAdd(empty)) + return *it; + } + return 0; +} + +void Dealer::setWaiting(bool w) +{ + if (w) + _waiting++; + else + _waiting--; + emit undoPossible(!waiting()); + kdDebug(11111) << "setWaiting " << w << " " << _waiting << endl; +} + +void Dealer::setAutoDropEnabled(bool a) +{ + _autodrop = a; + QTimer::singleShot(TIME_BETWEEN_MOVES, this, SLOT(startAutoDrop())); +} + +bool Dealer::startAutoDrop() +{ + if (!autoDrop()) + return false; + + QCanvasItemList list = canvas()->allItems(); + + for (QCanvasItemList::ConstIterator it = list.begin(); it != list.end(); ++it) + if ((*it)->animated()) { + QTimer::singleShot(TIME_BETWEEN_MOVES, this, SLOT(startAutoDrop())); + return true; + } + + kdDebug(11111) << "startAutoDrop\n"; + + unmarkAll(); + clearHints(); + getHints(); + for (HintList::ConstIterator it = hints.begin(); it != hints.end(); ++it) { + MoveHint *mh = *it; + if (mh->pile()->target() && mh->dropIfTarget() && !mh->card()->takenDown()) { + setWaiting(true); + Card *t = mh->card(); + CardList cards = mh->card()->source()->cards(); + while (cards.count() && cards.first() != t) cards.remove(cards.begin()); + t->setAnimated(false); + t->turn(true); + int x = int(t->x()); + int y = int(t->y()); + t->source()->moveCards(cards, mh->pile()); + t->move(x, y); + kdDebug(11111) << "autodrop " << t->name() << endl; + t->moveTo(int(t->source()->x()), int(t->source()->y()), int(t->z()), STEPS_AUTODROP); + connect(t, SIGNAL(stoped(Card*)), SLOT(waitForAutoDrop(Card*))); + return true; + } + } + clearHints(); + return false; +} + +void Dealer::waitForAutoDrop(Card * c) { + kdDebug(11111) << "waitForAutoDrop " << c->name() << endl; + setWaiting(false); + c->disconnect(); + takeState(); +} + +long Dealer::gameNumber() const +{ + return gamenumber; +} + +void Dealer::setGameNumber(long gmn) +{ + // Deal in the range of 1 to INT_MAX. + gamenumber = ((gmn < 1) ? 1 : ((gmn > 0x7FFFFFFF) ? 0x7FFFFFFF : gmn)); +} + +void Dealer::addPile(Pile *p) +{ + piles.append(p); +} + +void Dealer::removePile(Pile *p) +{ + piles.remove(p); +} + +void Dealer::stopDemo() +{ + kdDebug(11111) << "stopDemo " << waiting() << " " << stop_demo_next << endl; + if (waiting()) { + stop_demo_next = true; + return; + } else stop_demo_next = false; + + if (towait == (Card*)-1) + towait = 0; + + if (towait) { + towait->disconnect(); + towait = 0; + } + demotimer->stop(); + if (ademo) + ademo->setChecked(false); +} + +bool Dealer::demoActive() const +{ + return (towait || demotimer->isActive()); +} + +void Dealer::toggleDemo() +{ + if (demoActive()) { + stopDemo(); + } else + demo(); +} + +class CardPtr +{ + public: + Card *ptr; +}; + +bool operator <(const CardPtr &p1, const CardPtr &p2) +{ + return ( p1.ptr->z() < p2.ptr->z() ); +} + +void Dealer::won() +{ + if (_won) + return; + _won = true; + + // update score, 'win' in demo mode also counts (keep it that way?) + { // wrap in own scope to make KConfigGroupSave work + KConfig *config = kapp->config(); + KConfigGroupSaver kcs(config, scores_group); + unsigned int n = config->readUnsignedNumEntry(QString("won%1").arg(_id),0) + 1; + config->writeEntry(QString("won%1").arg(_id),n); + n = config->readUnsignedNumEntry(QString("winstreak%1").arg(_id),0) + 1; + config->writeEntry(QString("winstreak%1").arg(_id),n); + unsigned int m = config->readUnsignedNumEntry(QString("maxwinstreak%1").arg(_id),0); + if (n>m) + config->writeEntry(QString("maxwinstreak%1").arg(_id),n); + config->writeEntry(QString("loosestreak%1").arg(_id),0); + } + + // sort cards by increasing z + QCanvasItemList list = canvas()->allItems(); + QValueList cards; + for (QCanvasItemList::ConstIterator it=list.begin(); it!=list.end(); ++it) + if ((*it)->rtti() == Card::RTTI) { + CardPtr p; + p.ptr = dynamic_cast(*it); + assert(p.ptr); + cards.push_back(p); + } + qHeapSort(cards); + + // disperse the cards everywhere + QRect can(0, 0, canvas()->width(), canvas()->height()); + QValueList::ConstIterator it = cards.begin(); + for (; it != cards.end(); ++it) { + (*it).ptr->turn(true); + QRect p(0, 0, (*it).ptr->width(), (*it).ptr->height()); + int x, y; + do { + x = 3*canvas()->width()/2 - kapp->random() % (canvas()->width() * 2); + y = 3*canvas()->height()/2 - (kapp->random() % (canvas()->height() * 2)); + p.moveTopLeft(QPoint(x, y)); + } while (can.intersects(p)); + + (*it).ptr->moveTo( x, y, 0, STEPS_WON); + } + + bool demo = demoActive(); + stopDemo(); + canvas()->update(); + emit gameWon(demo); +} + +MoveHint *Dealer::chooseHint() +{ + if (hints.isEmpty()) + return 0; + + for (HintList::ConstIterator it = hints.begin(); it != hints.end(); ++it) + { + if ((*it)->pile()->target() && (*it)->dropIfTarget()) + return *it; + } + + return hints[randseq.getLong(hints.count())]; +} + +void Dealer::demo() { + if (waiting()) + return; + + if (stop_demo_next) { + stopDemo(); + return; + } + stop_demo_next = false; + unmarkAll(); + towait = (Card*)-1; + clearHints(); + getHints(); + demotimer->stop(); + + MoveHint *mh = chooseHint(); + if (mh) { + // assert(mh->card()->source()->legalRemove(mh->card())); + + CardList empty; + CardList cards = mh->card()->source()->cards(); + bool after = false; + for (CardList::Iterator it = cards.begin(); it != cards.end(); ++it) { + if (*it == mh->card()) + after = true; + if (after) + empty.append(*it); + } + + assert(!empty.isEmpty()); + + int *oldcoords = new int[2*empty.count()]; + int i = 0; + + for (CardList::Iterator it = empty.begin(); it != empty.end(); ++it) { + Card *t = *it; + Q_ASSERT(!t->animated()); + t->setAnimated(false); + t->turn(true); + oldcoords[i++] = int(t->realX()); + oldcoords[i++] = int(t->realY()); + } + + assert(mh->card()->source() != mh->pile()); + // assert(mh->pile()->legalAdd(empty)); + + mh->card()->source()->moveCards(empty, mh->pile()); + + i = 0; + + for (CardList::Iterator it = empty.begin(); it != empty.end(); ++it) { + Card *t = *it; + int x1 = oldcoords[i++]; + int y1 = oldcoords[i++]; + int x2 = int(t->realX()); + int y2 = int(t->realY()); + t->move(x1, y1); + t->moveTo(x2, y2, int(t->z()), STEPS_DEMO); + } + + delete [] oldcoords; + + newDemoMove(mh->card()); + + } else { + Card *t = demoNewCards(); + if (t) { + newDemoMove(t); + } else if (isGameWon()) { + canvas()->update(); + emit gameWon(true); + return; + } else + stopDemo(); + } + + takeState(); +} + +Card *Dealer::demoNewCards() +{ + return 0; +} + +void Dealer::newDemoMove(Card *m) +{ + towait = m; + connect(m, SIGNAL(stoped(Card*)), SLOT(waitForDemo(Card*))); +} + +void Dealer::waitForDemo(Card *t) +{ + if (t == (Card*)-1) + return; + if (towait != t) + return; + t->disconnect(); + towait = 0; + demotimer->start(250, true); +} + +bool Dealer::isGameWon() const +{ + for (PileList::ConstIterator it = piles.begin(); it != piles.end(); ++it) + { + if (!(*it)->target() && !(*it)->isEmpty()) + return false; + } + return true; +} + +bool Dealer::isGameLost() const +{ + return false; +} + +bool Dealer::checkRemove( int, const Pile *, const Card *) const { + return true; +} + +bool Dealer::checkAdd( int, const Pile *, const CardList&) const { + return true; +} + +void Dealer::drawPile(KPixmap &pixmap, Pile *pile, bool selected) +{ + QPixmap bg = myCanvas.backgroundPixmap(); + QRect bounding(int(pile->x()), int(pile->y()), cardMap::CARDX(), cardMap::CARDY()); + + pixmap.resize(bounding.width(), bounding.height()); + pixmap.fill(Qt::white); + + if (!bg.isNull()) { + for (int x=bounding.x()/bg.width(); + x<(bounding.x()+bounding.width()+bg.width()-1)/bg.width(); x++) + { + for (int y=bounding.y()/bg.height(); + y<(bounding.y()+bounding.height()+bg.height()-1)/bg.height(); y++) + { + int sx = 0; + int sy = 0; + int dx = x*bg.width()-bounding.x(); + int dy = y*bg.height()-bounding.y(); + int w = bg.width(); + int h = bg.height(); + if (dx < 0) { + sx = -dx; + dx = 0; + } + if (dy < 0) { + sy = -dy; + dy = 0; + } + bitBlt(&pixmap, dx, dy, &bg, + sx, sy, w, h, Qt::CopyROP, true); + } + } + } + + + float s = -0.4; + float n = -0.3; + + int mid = QMAX( QMAX(midColor().red(), midColor().green()), midColor().blue()); + + // if it's too dark - light instead of dark + if (mid < 120) { + s *= -1; + n = 0.4; + } + + KPixmapEffect::intensity(pixmap, selected ? s : n); +} + +int Dealer::freeCells() const +{ + int n = 0; + for (PileList::ConstIterator it = piles.begin(); it != piles.end(); ++it) + if ((*it)->isEmpty() && !(*it)->target()) + n++; + return n; +} + +void Dealer::setAnchorName(const QString &name) +{ + kdDebug(11111) << "setAnchorname " << name << endl; + ac = name; +} + +QString Dealer::anchorName() const { return ac; } + +void Dealer::wheelEvent( QWheelEvent *e ) +{ + QWheelEvent ce( viewport()->mapFromGlobal( e->globalPos() ), + e->globalPos(), e->delta(), e->state()); + viewportWheelEvent(&ce); + if ( !ce.isAccepted() ) { + if ( e->orientation() == Horizontal && hScrollBarMode () == AlwaysOn ) + QApplication::sendEvent( horizontalScrollBar(), e); + else if (e->orientation() == Vertical && vScrollBarMode () == AlwaysOn ) + QApplication::sendEvent( verticalScrollBar(), e); + } else { + e->accept(); + } +} + +void Dealer::countGame() +{ + if ( !_gameRecorded ) { + kdDebug(11111) << "counting game as played." << endl; + KConfig *config = kapp->config(); + KConfigGroupSaver kcs(config, scores_group); + unsigned int Total = config->readUnsignedNumEntry(QString("total%1").arg(_id),0); + ++Total; + config->writeEntry(QString("total%1").arg(_id),Total); + _gameRecorded = true; + } +} + +void Dealer::countLoss() +{ + if ( _gameRecorded ) { + // update score + KConfig *config = kapp->config(); + KConfigGroupSaver kcs(config, scores_group); + unsigned int n = config->readUnsignedNumEntry(QString("loosestreak%1").arg(_id),0) + 1; + config->writeEntry(QString("loosestreak%1").arg(_id),n); + unsigned int m = config->readUnsignedNumEntry(QString("maxloosestreak%1").arg(_id),0); + if (n>m) + config->writeEntry(QString("maxloosestreak%1").arg(_id),n); + config->writeEntry(QString("winstreak%1").arg(_id),0); + } +} + +#include "dealer.moc" diff --git a/kpat/dealer.h b/kpat/dealer.h new file mode 100644 index 00000000..c5593cbd --- /dev/null +++ b/kpat/dealer.h @@ -0,0 +1,228 @@ +#ifndef _DEALER_H_ +#define _DEALER_H_ + +#include "pile.h" +#include "hint.h" +#include + +class QDomDocument; +class KMainWindow; +class Dealer; +class DealerInfo; +class KAction; +class KSelectAction; +class KToggleAction; +class KPixmap; + +class DealerInfoList { +public: + static DealerInfoList *self(); + void add(DealerInfo *); + + const QValueList games() const { return list; } +private: + QValueList list; + static DealerInfoList *_self; +}; + +class DealerInfo { +public: + DealerInfo(const char *_name, int _index) + : name(_name), + gameindex(_index) +{ + DealerInfoList::self()->add(this); +} + const char *name; + uint gameindex; + virtual Dealer *createGame(KMainWindow *parent) = 0; +}; + +class CardState; + +typedef QValueList CardStateList; + +struct State +{ + CardStateList cards; + QString gameData; +}; + + +/*************************************************************** + + Dealer -- abstract base class of all varieties of patience + +***************************************************************/ +class Dealer: public QCanvasView +{ + Q_OBJECT + +public: + + Dealer( KMainWindow* parent = 0, const char* name = 0 ); + virtual ~Dealer(); + + static const Dealer *instance(); + + void enlargeCanvas(QCanvasRectangle *c); + void setGameNumber(long gmn); + long gameNumber() const; + + virtual bool isGameWon() const; + virtual bool isGameLost() const; + + void setViewSize(const QSize &size); + + void addPile(Pile *p); + void removePile(Pile *p); + + virtual bool checkRemove( int checkIndex, const Pile *c1, const Card *c) const; + virtual bool checkAdd ( int checkIndex, const Pile *c1, const CardList& c2) const; + virtual bool checkPrefering( int checkIndex, const Pile *c1, const CardList& c2) const; + + virtual Card *demoNewCards(); + + virtual void setupActions(); + + bool demoActive() const; + + void drawPile(KPixmap &, Pile *p, bool selected); + + QColor midColor() const { return _midcolor; } + void setBackgroundPixmap(const QPixmap &background, const QColor &midcolor); + + void saveGame(QDomDocument &doc); + void openGame(QDomDocument &doc); + + void setGameId(int id) { _id = id; } + int gameId() const { return _id; } + + void setTakeTargetForHints(bool e) { takeTargets = e; } + bool takeTargetForHints() const { return takeTargets; } + + bool isMoving(Card *c) const; + + virtual QSize minimumCardSize() const; + virtual void resizeEvent(QResizeEvent *); + + int freeCells() const; + + QString anchorName() const; + void setAnchorName(const QString &name); + + void setAutoDropEnabled(bool a); + bool autoDrop() const { return _autodrop; } + + int getMoves() const { return undoList.count(); } + +public slots: + + // restart is pure virtual, so we need something else + virtual void startNew(); + void undo(); + virtual void takeState(); + virtual bool startAutoDrop(); + void hint(); + void slotTakeState(Card *c); + +signals: + void undoPossible(bool poss); + void gameWon(bool withhelp); + void gameLost(); + void saveGame(); // emergency + void gameInfo(const QString &info); + void updateMoves(); + +public slots: + virtual void demo(); + void waitForDemo(Card *); + void toggleDemo(); + virtual void stopDemo(); + void waitForAutoDrop(Card *); + +protected: + + enum { None = 0, Hint = 1, Demo = 2, Redeal = 4 } Actions; + + void setActions(int actions) { myActions = actions; } + int actions() const { return myActions; } + + virtual void restart() = 0; + + virtual void contentsMousePressEvent(QMouseEvent* e); + virtual void contentsMouseMoveEvent( QMouseEvent* ); + virtual void contentsMouseReleaseEvent( QMouseEvent* ); + virtual void contentsMouseDoubleClickEvent( QMouseEvent* ); + virtual void wheelEvent( QWheelEvent *e ); + + void unmarkAll(); + void mark(Card *c); + Pile *findTarget(Card *c); + virtual bool cardClicked(Card *); + virtual void pileClicked(Pile *); + virtual bool cardDblClicked(Card *); + void won(); + + virtual void getHints(); + void newHint(MoveHint *mh); + void clearHints(); + // it's not const because it changes the random seed + virtual MoveHint *chooseHint(); + + KMainWindow *parent() const; + + bool waiting() const { return _waiting != 0; } + void setWaiting(bool w); + +protected: + PileList piles; + + State *getState(); + void setState(State *); + + // reimplement this to add game-specific information in the state structure + virtual QString getGameState() const { return QString::null; } + // reimplement this to use the game-specific information from the state structure + virtual void setGameState( const QString & ) {} + + virtual void newDemoMove(Card *m); + + bool moved; + CardList movingCards; + QCanvasItemList marked; + QPoint moving_start; + Dealer( Dealer& ); // don't allow copies or assignments + void operator = ( Dealer& ); // don't allow copies or assignments + QCanvas myCanvas; + QSize minsize; + QSize viewsize; + QPtrList undoList; + long gamenumber; + QValueList hints; + Card *towait; + QTimer *demotimer; + int myActions; + bool toldAboutLostGame; + + KToggleAction *ademo; + KAction *ahint, *aredeal; + + KRandomSequence randseq; + QColor _midcolor; + Q_UINT32 _id; + bool takeTargets; + bool _won; + int _waiting; + bool stop_demo_next; + QString ac; + static Dealer *s_instance; + bool _autodrop; + bool _gameRecorded; + +private: + void countLoss(); + void countGame(); +}; + +#endif diff --git a/kpat/deck.cpp b/kpat/deck.cpp new file mode 100644 index 00000000..c2b6e0e5 --- /dev/null +++ b/kpat/deck.cpp @@ -0,0 +1,154 @@ +#include +#include "deck.h" +#include "dealer.h" +#include +#include + +const int NumberOfCards = 52; + + +Deck *Deck::my_deck = 0; + + +Deck::Deck( Dealer* parent, int m, int s ) + : Pile( 0, parent ), mult( m ) +{ + _deck = new Card * [mult*NumberOfCards]; + Q_CHECK_PTR (_deck); + + // only allow 1, 2, or 4 suits + if ( s == 1 || s == 2 ) + suits = s; + else + suits = 4; + + makedeck(); + addToDeck(); + shuffle(); + + setAddFlags(Pile::disallow); + setRemoveFlags(Pile::disallow); +} + + +Deck::~Deck() +{ + for (uint i=0; i < mult*NumberOfCards; i++) { + delete _deck[i]; + } + m_cards.clear(); + delete [] _deck; +} + + +// ---------------------------------------------------------------- + + +Deck *Deck::new_deck( Dealer *parent, int m, int s ) +{ + my_deck = new Deck(parent, m, s); + return my_deck; +} + + +void Deck::makedeck() +{ + int i=0; + + show(); + for ( uint m = 0; m < mult; m++) + { + for ( int r = Card::Ace; r <= Card::King; r++) + { + for ( int s = Card::Spades-1; s >= Card::Clubs-1 ; s--) + { + _deck[i] = new Card(static_cast(r), + static_cast(Card::Spades - (s % suits)), + dealer()->canvas()); + _deck[i]->move(x(), y()); + i++; + } + } + } +} + + +void Deck::collectAndShuffle() +{ + addToDeck(); + shuffle(); +} + + +Card* Deck::nextCard() +{ + CardList::Iterator c; + + c = m_cards.fromLast(); // Dealing from bottom of deck .... + if ( c != m_cards.end() ) { + return *c; + } else + return 0; +} + + +// ---------------------------------------------------------------- + + +static long pseudoRandomSeed = 0; + +static void pseudoRandom_srand(long seed) +{ + pseudoRandomSeed=seed; +} + + +// Documented as in +// http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q28150 +// + +static long pseudoRandom_random() { + pseudoRandomSeed = 214013*pseudoRandomSeed+2531011; + return (pseudoRandomSeed >> 16) & 0x7fff; +} + + +// Shuffle deck, assuming all cards are in m_cards + +void Deck::shuffle() +{ + + assert(m_cards.count() == uint(mult*NumberOfCards)); + + assert(dealer()->gameNumber() >= 0); + pseudoRandom_srand(dealer()->gameNumber()); + + kdDebug(11111) << "first card " << m_cards[0]->name() << " " << dealer()->gameNumber() << endl; + + Card* t; + long z; + int left = mult*NumberOfCards; + for (uint i = 0; i < mult*NumberOfCards; i++) { + z = pseudoRandom_random() % left; + t = m_cards[z]; + m_cards[z] = m_cards[left-1]; + m_cards[left-1] = t; + left--; + } +} + + +// add cards in deck[] to Deck +// FIXME: Rename to collectCards() + +void Deck::addToDeck() +{ + clear(); + + for (uint i = 0; i < mult*NumberOfCards; i++) { + _deck[i]->setTakenDown(false); + add( _deck[i], true, false ); + } +} + + diff --git a/kpat/deck.h b/kpat/deck.h new file mode 100644 index 00000000..f5239fe0 --- /dev/null +++ b/kpat/deck.h @@ -0,0 +1,46 @@ +#ifndef _DECK_H_ +#define _DECK_H_ + +#include "pile.h" +class dealer; + +/*************************************** + + Deck (Pile with id 0) -- create and shuffle 52 cards + +**************************************/ +class Deck: public Pile +{ + +private: + Deck( Dealer* parent = 0, int m = 1, int s = 4 ); + virtual ~Deck(); + +public: + static Deck *new_deck( Dealer *parent = 0, int m = 1, int s = 4 ); + static Deck *deck() { return my_deck; } + + static const long n; + + void collectAndShuffle(); + + Card* nextCard(); + + uint decksNum() const { return mult; } + +private: // functions + + void makedeck(); + void addToDeck(); + void shuffle(); + +private: + + uint mult; + uint suits; + Card** _deck; + + static Deck *my_deck; +}; + +#endif diff --git a/kpat/fortyeight.cpp b/kpat/fortyeight.cpp new file mode 100644 index 00000000..1d867378 --- /dev/null +++ b/kpat/fortyeight.cpp @@ -0,0 +1,205 @@ +#include "fortyeight.h" +#include +#include +#include "deck.h" +#include +#include "cardmaps.h" + +HorLeftPile::HorLeftPile( int _index, Dealer* parent) + : Pile(_index, parent) +{ + // TODO: create a pile that moves the cards together when filling space + setHSpread( cardMap::CARDX() / 11 + 1 ); +} + +QSize HorLeftPile::cardOffset( bool _spread, bool, const Card *) const +{ + if (_spread) + return QSize(-hspread(), 0); + + return QSize(0, 0); +} + +void HorLeftPile::initSizes() +{ + Pile::initSizes(); + setHSpread( cardMap::CARDX() / 11 + 1 ); +} + + +Fortyeight::Fortyeight( KMainWindow* parent, const char* name) + : Dealer(parent,name) +{ + deck = Deck::new_deck(this, 2); + + const int dist_x = cardMap::CARDX() * 11 / 10 + 1; + const int dist_y = cardMap::CARDY() * 11 / 10 + 1; + + connect(deck, SIGNAL(clicked(Card*)), SLOT(deckClicked(Card*))); + deck->move(10 + cardMap::CARDX() * 82 / 10, 10 + cardMap::CARDX() * 56 / 10); + deck->setZ(20); + + pile = new HorLeftPile(20, this); + pile->setAddFlags(Pile::addSpread | Pile::disallow); + pile->move(10 + cardMap::CARDX() * 69 / 10, 10 + cardMap::CARDX() * 56 / 10 ); + + for (int i = 0; i < 8; i++) { + + target[i] = new Pile(9 + i, this); + target[i]->move(8+dist_x*i, 10); + target[i]->setType(Pile::KlondikeTarget); + + stack[i] = new Pile(1 + i, this); + stack[i]->move(8+dist_x*i, 10 + dist_y); + stack[i]->setAddFlags(Pile::addSpread); + stack[i]->setRemoveFlags(Pile::autoTurnTop); + stack[i]->setCheckIndex(1); + stack[i]->setSpread(stack[i]->spread() * 3 / 4); + } + + setActions(Dealer::Hint | Dealer::Demo); +} + +//-------------------------------------------------------------------------// + +void Fortyeight::restart() +{ + lastdeal = false; + deck->collectAndShuffle(); + deal(); +} + +void Fortyeight::deckClicked(Card *) +{ + if (deck->isEmpty()) { + if (lastdeal) + return; + lastdeal = true; + while (!pile->isEmpty()) { + Card *c = pile->at(pile->cardsLeft()-1); + c->setAnimated(false); + deck->add(c, true, false); + } + } + Card *c = deck->nextCard(); + pile->add(c, true, true); + int x = int(c->x()); + int y = int(c->y()); + c->move(deck->x(), deck->y()); + c->flipTo(x, y, 8); +} + +Card *Fortyeight::demoNewCards() +{ + if (deck->isEmpty() && lastdeal) + return 0; + deckClicked(0); + return pile->top(); +} + +bool Fortyeight::checkAdd(int, const Pile *c1, const CardList &c2) const +{ + if (c1->isEmpty()) + return true; + + // ok if in sequence, same suit + return (c1->top()->suit() == c2.first()->suit()) + && (c1->top()->rank() == (c2.first()->rank()+1)); +} + +void Fortyeight::deal() +{ + for (int r = 0; r < 4; r++) + { + for (int column = 0; column < 8; column++) + { + if (false) { // doesn't look + stack[column]->add(deck->nextCard(), true, true); + stack[column]->top()->turn(true); + } else { + stack[column]->add(deck->nextCard(), false, true); + } + } + } + pile->add(deck->nextCard(), false, false); +} + +QString Fortyeight::getGameState() const +{ + return QString::number(lastdeal); +} + +void Fortyeight::setGameState( const QString &s ) +{ + lastdeal = s.toInt(); +} + +bool Fortyeight::isGameLost() const +{ + kdDebug(11111) << "isGameLost ?" << endl; + if(!lastdeal) + return false; + if(!deck->isEmpty()) + return false; + + Card *c; + for(int i=0; i < 8; i++) + { + if(stack[i]->isEmpty()) + return false; + + c=stack[i]->top(); + + if(c->rank() == Card::Ace) + return false; + + if(!pile->isEmpty()) { + if(pile->top()->suit() == c->suit() && + pile->top()->rank()+1 == c->rank()) + return false; + + if ( !target[i]->isEmpty() && + pile->top()->suit() == target[i]->top()->suit() && + pile->top()->rank() == target[i]->top()->rank()+1) + return false; + } + for(int j=0; j <8;j++){ + if(target[j]->isEmpty()) + continue; + if(c->suit() == target[j]->top()->suit() && + c->rank()-1 ==target[j]->top()->rank()) + return false; + } + for(int j=1; j < 8; j++) { + int k=(i+j) % 8; + if (stack[k]->isEmpty()) + continue; + if(c->suit() == stack[k]->top()->suit() && + c->rank()+1 ==stack[k]->top()->rank()){ + int indexi=stack[i]->indexOf(c); + if(indexi==0) + return false; + Card *c2=stack[i]->at(indexi-1); + if(c2->rank()!=stack[k]->top()->rank() || + c2->suit()!=stack[k]->top()->suit()) + return false; + } + } + } + + return true; +} + +static class LocalDealerInfo8 : public DealerInfo +{ +public: + LocalDealerInfo8() : DealerInfo(I18N_NOOP("Forty && &Eight"), 8) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Fortyeight(parent); } +} ldi9; + +//-------------------------------------------------------------------------// + +#include "fortyeight.moc" + +//-------------------------------------------------------------------------// + diff --git a/kpat/fortyeight.h b/kpat/fortyeight.h new file mode 100644 index 00000000..858e6e15 --- /dev/null +++ b/kpat/fortyeight.h @@ -0,0 +1,46 @@ +#ifndef _FORTY_EIGHT_H +#define _FORTY_EIGHT_H + +#include "dealer.h" + +class HorLeftPile : public Pile +{ + Q_OBJECT + +public: + HorLeftPile( int _index, Dealer* parent = 0); + virtual QSize cardOffset( bool _spread, bool _facedown, const Card *before) const; + virtual void initSizes(); +}; + +class Fortyeight : public Dealer +{ + Q_OBJECT + +public: + Fortyeight( KMainWindow* parent=0, const char* name=0); + virtual bool isGameLost() const; + +public slots: + void deal(); + virtual void restart(); + void deckClicked(Card *c); + +protected: + virtual bool checkAdd( int checkIndex, const Pile *c1, const CardList& c2) const; + virtual Card *demoNewCards(); + virtual QString getGameState() const; + virtual void setGameState( const QString & stream ); + +private: + Pile *stack[8]; + Pile *target[8]; + HorLeftPile *pile; + Deck *deck; + bool lastdeal; +}; + +#endif + + +//-------------------------------------------------------------------------// diff --git a/kpat/freecell-solver/CREDITS b/kpat/freecell-solver/CREDITS new file mode 100644 index 00000000..7ce9e216 --- /dev/null +++ b/kpat/freecell-solver/CREDITS @@ -0,0 +1,57 @@ +Shlomi Fish (me) - doing most of the work on Freecell Solver. + +Eric Warmenhoven - sending a program that generates the board of GNOME +Freecell. + +Hai Huang - noting several boards of Microsoft Freecell that could not be +solved by Freecell Solver. + +Magnus Reftel - noting the correct procedure for calculating how many cards +can be moved as a function of the free freecells and free stacks. + +Colin Plumb - writing the MD5 code. + +Ron Rivest - inventing the MD5 hashing algorithm. + +Jim Horne - supplying the shuffling algorithm for +Microsoft Freecell/Freecell Pro. + +Tom Holroyd - sending several Seahaven Towers games which Freecell Solver +was unable to solve, thus making me improve the algorithm. + +Markus F. X. J. Oberhumer - writing PySol on whose code the board generation +program for it is based. Also, contributing some patches. + +Justin-Heyes Jones - wrote a nice introduction to the A* algorithm, and +wrote the basis for the pqueue.c code. + +Stephan Kulow - integrated Freecell Solver into the kpat Solitaire suite for +KDE 2.1 and onwards; reported several bugs and memory leaks. + +Michael Keller - Contributing some useful input about some minor features +lacking and the Spades/Clubs mix-up. + +GeYong - He created Freecell Tool, whose randomized scan provided +inspiration for the random-DFS scan of Freecell Solver. + +Adrian Ettlinger - Integrating Freecell Solver into "Freecell Pro", and +contributing some input. + +The perl 5.x Hackers - I copied its hash function. + +Gergeley Kontra - wrote a Vim script to align text which I used. + +Bob Jenkins - wrote the lookup2 hash function, which I now use as the +primary hash. +(check http://burtleburtle.net/bob/hash/) + +Tzafrir Cohen - His "RPM" lecture provided help in creating the RPM +Spec. + +Yotam Rubin - Preparing an initial Debian Package. + +Risko Gergely - Maintaining the current Debian Package. + +Chris Moore - Pointing to an out-of-date comment regarding the +MAX_NUM_CARDS_IN_A_STACK which I updated. + diff --git a/kpat/freecell-solver/INSTALL b/kpat/freecell-solver/INSTALL new file mode 100644 index 00000000..9b718633 --- /dev/null +++ b/kpat/freecell-solver/INSTALL @@ -0,0 +1,70 @@ +INSTALL file for Freecell Solver +================================ + +Quick and Dirty Compilation +--------------------------- + +Usually typing "./configure" followed by "make" and "make install" will +build and install "fc-solve" which is the Freecell Solver executable for you. + +It will also build and install the board generation program, more +information about which can be found in the "board_gen" sub-directory of +this distribution. + +Changing the Maximal number of Freecells or Stacks or Cards per Stack +--------------------------------------------------------------------- + +The following parameters to the "configure" script which accept an argument +control the hard-coded parameters of the Freecell Solver executables: + +"--enable-max-num-freecells=$NUM" - The maximal number of freecells + +"--enable-max-num-stacks=$NUM" - The maximal number of stacks + +"--enable-max-num-initial-cards-per-stack=$NUM" - The maximal number of initial +cards per stack. + +Notice that it's very important to set the maximal number of initial cards +per stack, or else it's possible that a stack will eventually overflow. + +"Compact" States +--------------------- + +In Compact States, the contents of the card stacks are stored inside the +states, rather than in a central collection (where the states contain only +pointers). Despite its name, it actually consume more memory than Indirect +Stack States which is the default. + +Compact states used to be faster than Indirect Stack States, but now it +seems indirect stack states is at least slightly faster even for games +whose stacks are not very long. If you still would wish to enable it, +run ./configure with the "--enable-states-type=compact" flag. + +Installing under Win32 +---------------------- + +Freecell Solver is distributed with a makefile suitable for use with +Microsoft Visual C++. To build it using it follow the following steps: + +1. Copy "config.h.win32" to "config.h" and "prefix.h.win32" to "prefix.h"; +In the directory Presets/ copy presetrc.win32 to presetrc. + +2. Optionally, edit it to set its preferences + +3. Type "nmake /f Makefile.win32". + +If you have an IDE of some sort you can take the following steps to compile +Freecell Solver: + +1. Open a project for Freecell Solver. + +2. Add all the C files except "test_multi_parallel.c" to the project. + +3. Copy the file config.h.win32 to config.h and prefix.h.win32 to prefix.h. + +4. Build. + +If you are using gcc or some other command-line compiler, you should +write the makefile based on the files "Makefile" or "Makefile.lite", +and then compile according to it. + diff --git a/kpat/freecell-solver/Makefile.am b/kpat/freecell-solver/Makefile.am new file mode 100644 index 00000000..b0f5acff --- /dev/null +++ b/kpat/freecell-solver/Makefile.am @@ -0,0 +1,5 @@ + +noinst_LTLIBRARIES = libfcs.la +AM_CPPFLAGS = -DFCS_STATE_STORAGE=FCS_STATE_STORAGE_INTERNAL_HASH -DFCS_STACK_STORAGE=FCS_STACK_STORAGE_INTERNAL_HASH +libfcs_la_SOURCES = alloc.c app_str.c caas.c card.c cl_chop.c cmd_line.c fcs_dm.c fcs_hash.c fcs_isa.c freecell.c intrface.c lib.c lookup2.c move.c pqueue.c preset.c rand.c scans.c simpsim.c state.c + diff --git a/kpat/freecell-solver/Makefile.lite b/kpat/freecell-solver/Makefile.lite new file mode 100644 index 00000000..2a6248eb --- /dev/null +++ b/kpat/freecell-solver/Makefile.lite @@ -0,0 +1,94 @@ + +CC = gcc +OFLAGS = -Wall -O3 -Wno-long-long -Wundef -Wcast-align -Wconversion -Wchar-subscripts -W -Wpointer-arith -Wwrite-strings -Wformat-security -Wmissing-format-attribute -fno-common -g +OLFLAGS = -Wall +DLFLAGS = + +END_OLFLAGS = +END_DLFLAGS = + +INCLUDES = alloc.h app_str.h caas.h card.h cl_chop.h fcs_config.h fcs_cl.h fcs.h fcs_dm.h fcs_enums.h fcs_hash.h fcs_isa.h fcs_move.h fcs_user.h inline.h jhjtypes.h lookup2.h move.h ms_ca.h prefix.h pqueue.h preset.h rand.h state.h test_arr.h tests.h + +TARGETS = fc-solve + +all: $(TARGETS) + +board_gen: dummy + make -C board_gen/ + +dummy: + +#<<>>OBJECTS.END + +fc-solve: $(OBJECTS) + $(CC) $(OLFLAGS) -o $@ $(OBJECTS) -lm + +clean: + rm -f *.o $(TARGETS) $(DTARGETS) + + diff --git a/kpat/freecell-solver/README b/kpat/freecell-solver/README new file mode 100644 index 00000000..4902e3a7 --- /dev/null +++ b/kpat/freecell-solver/README @@ -0,0 +1,105 @@ +1. Introduction +--------------- + +This is Freecell Solver version 2.8.x, a program that automatically +solves most Freecell and Simple Simon games. + +Freecell Solver is distributed under the public domain. + +I hope you'll enjoy using it, and make the best of it. + + Shlomi Fish (shlomif@vipe.technion.ac.il) + +2. Building +----------- + +Read the file "INSTALL" for information on how to do that. For the impatient: +type: + +./configure +make +make install + +3. Usage +-------- + +The program is called "fc-solve". You invoke it like this: + +fc-solve board_file + +board_file is the filename with a valid Freecell startup board. The file is +built as follows: + +It has the 8 Freecell stacks. +Each stack contain its number of cards separated by a whitespace +and terminated with a newline character( it's important that the last stack +will also be terminated with a newline !). The cards in the line are ordered +from the bottom-most card in the left to the topmost card in the right. + +A card string contains the card number followed by the card deck. The card +number is one of: A,1,2,3,4,5,6,7,8,9,10,J,Q,K. The card deck is one of: +H,S,D,C (standing for Hearts, Spades, Diamonds and Clubs respectively). + +Here is an example board: (PySol board No. 24) + +4S 2S 9S 8S QC 4C 2H +5H QH 3S AS 3H 4H QD +QS 9C 6H 9H 3C KC 3D +5D 2C JS 5S JH 6D AC +2D KD 10H 10S 10D 8D +7H JC KH 10C KS 7S +AH 5C 6C AD 8H JD +7C 6S 7D 4D 8C 9D + +And another one: (PySol board No. 198246790) + +KD JH 5H 7D 9H KS 9D +3H JD 5D 8H QH 7H 2D +4D 3C QS 3S 6C QC KC +10S 9C 6D 9S QD 8C 10D +10C 8S 7C 10H 2S AC +8D AS AH 4H JS 4S +6H 7S 4C 5C 5S JC +AD KH 6S 2H 3D 2C + +You can specify the contents of the freecells by prefixing the line with +"FC:". For example: +FC: 3H QC + +will specify that the cards 3 of hearts and queen of clubs are present in +the freecells. To specify an empty freecell use a "-" as its designator. + +If there's another "FC:" line, the previous line will be overriden. + +You can specify the contents of the foundations by prefixing the line with +"Founds:" and then using a format as follows: + +Founds: H-5 C-A S-0 D-K + +Hence, the deck ID followed by a dash followed by the card number in the +foundation. A suit that is not present will be assumed to be 0. Again, if +there's more than one then the previous lines will be overriden. + + +The program will stop processing the input as soon as it read 8 lines of +standard stacks. Therefore, it is recommended that the foundations and +freecells lines will come at the beginning of the file. + +The program will process the board and try to solve it. If it succeeds it +will output the states from the initial board to its final solution to the +standard output. If it fails, it will notify it. + +For information about the various command-line switches that Freecell +Solver accepts, read the USAGE file in this directory. + +To solve Simple Simon boards append "--game simple_simon" right after +the "fc-solve" program name. + +4. The board generation programs +-------------------------------- + +Several programs which can generate the initial boards of various Freecell +implementations can be found in the "board_gen/" sub-directory. Read the +"README" file there for details on how they can be compiled and used. + +In any case, they can save you the time of inputting the board yourself. diff --git a/kpat/freecell-solver/USAGE b/kpat/freecell-solver/USAGE new file mode 100644 index 00000000..f78295b2 --- /dev/null +++ b/kpat/freecell-solver/USAGE @@ -0,0 +1,518 @@ +Freecell Solver's Command-Line Syntax and Usage +=============================================== + + +1. The programs +--------------- + +Most command-line switches have two versions: a short POSIX one which +is a dash followed by a letter; and a long GNU one which is two dashes +followed by the command string. Note, that Freecell Solver does not +support specifying more than one command letter after a dash, (e.g: +"-sip"). Furthermore, a command that accepts a parameter, will require +this parameter to be present in the next command-line argument, not in +the GNU manner of "--command=option". + +I don't use getopt because I want Freecell Solver to be a pure ANSI C +program, so I'm sorry for the inconvenience. + + +2. Getting Help +--------------- + +-h --help + +This option displays a help text on the screen. This help +text summarizes the command-line options and their meaning, as well as +the signal combinations that fc-solve accepts. + + +3. Output Options +----------------- + +-p --parseable-output + +This option will display the stacks in a format that can be more easily +manipulated by text-processing programs such as grep or perl. Namely, +The freecells will be displayed in one line, and the foundations in a +separate line. Plus, Each stack will be displayed horizontally, in its +own line, while beginning with a ":". + + +-t --display-10-as-t + +This option will display the 10 cards as a capital T instead of a 10. +Thus, the cards will be more properly aligned. + + +-c --canonized-order-output + +Freecell Solver re-arranges the stacks and freecells in a given state +according to their first card. It keeps their actual position in a +separate place, but internally it uses their canonized place. Use +this option, if you want Freecell Solver to display them in that order. +One should be warned that that way the place of a given stack in the +board will not be preserved throughout the solution. + + +-m --display-moves + +This option will display the moves instead of the intermediate states. +Each move will be displayed in a separate line, in a format that is +human-readable, but that can also be parsed and analyzed by a computer +program with some effort on the programmer's part. + + +-sn --standard-notation + +This option will display the moves in standard notation in which every +move consists of two characters and there are ten moves in a line. Naturally, +this option will only become apparent if the display moves is specified. +(it does not implicitly specify it, though). + +For more information regarding standard notation refer to the following +web-page: + +http://home.earthlink.net/~fomalhaut/freecell.html + +-snx --standard-notation-extended + +This option is similar to the previous one, only that when a sequence +move is made to an empty stack with more than one card in the sequence, +the move will be followed with "v" and the number of cards moved in +hexadecimal. + +-sam --display-states-and-moves + +This option will display both the intermediate states and the moves that +are needed to move from one to another. The standard notation +option applies to it to. + + +-pi --display-parent-iter + +This option (assuming the -s and -i options are specified) will also +display the iteration index of the state from which the current state +was derived. This is especially useful for A* or BFS scans. + +4. Game Variants Options +------------------------ + + +--freecells-num [Number of Freecells] + +This option specifies the number of freecells which are available to +the program. Freecell Solver can use any number of freecells as long as +it does not exceed its maximal number. + +This maximum is hard-coded into the program, and can be specified at +compile-time by modifying the file "config.h". See the file INSTALL for +details. + + +--stacks-num [Number of Stacks] + +This option specifies the number of stacks present in the board. Again, +this number cannot exceed the maximal number of stacks, which can be +specified in the file "config.h" during compile-time of Freecell +Solver. + + +--decks-num [Number of Decks] + +This options specifies how many decks are found in the board. This number +cannot exceed the maximal number of decks, which can be specified in the +file "config.h" during compile time of Freecell Solver. + + +--sequences-are-built-by {suit|alternate_color|rank} + +This option specifies whether a card sequence is built by suit or by +alternate colour or by rank regardless of suit. + + +--sequence-move {limited|unlimited} + +This option specifies whether the sequence move is limited by the +number of freecells or vacant stacks or not. + + +--empty-stacks-filled-by {kings|none|all} + +Specifies which cards can fill an empty stack. + + +--game [game] +--preset [game] +-g [game] + +Specifies the type of game. Each preset implies several of the +settings options above and sometimes even the tests order below. +Available presets: + + bakers_dozen - Baker's Dozen + bakers_game - Baker's Game + beleaguered_castle - Beleaguered Castle + citadel - Citadel + cruel - Cruel + der_katz - Der Katzenschwanz + die_schlange - Die Schlange + eight_off - Eight Off + fan - Fan + forecell - Forecell + freecell - Freecell + good_measure - Good Measure + ko_bakers_game - Kings' Only Baker's Game + relaxed_freecell - Relaxed Freecell + relaxed_sehaven - Relaxed Seahaven Towers + seahaven - Seahaven Towers + simple_simon - Simple Simon + streets_and_alleys - Streets and Alleys + +Note: in order to solve Der Katzenschwanz and Die Schlange I recommend you +compile Freecell Solver with the INDIRECT_STACK_STATES option, or else it will +consume much more memory. For details consult the file INSTALL. + +5. Solving Algorithm Options +---------------------------- + +-mi [Maximal number of iterations] +--max-iters [Maximal number of iterations] + +This parameter limits the maximal number of states to check. This will +give a rough estimate on the time spent to solve a given board. + + +-md [Maximal depth] +--max-depth [Maximal depth] + +Freecell Solver recurses into the solution. This parameter specifies a +maximal recursion depth. Generally speaking, it's not a good idea to +set it, because that way several important intermediate states become +inaccessible. + +-mss [Maximal States' Number] +--max-stored-states [Maximal States' Number] + +Limits the number of the states stored by the program in the computer's +memory. This differs from the maximal number of iterations in the sense, that +it is possible that a stored state was not checked yet. + + +-to [Test's Order] +--tests-order [Test's Order] + +This option specifies the order in which Freecell Solver will try the +different types of moves that it can perform. Each move is specified by +one character, and they are performed in the order in which they appear +in the parameter string. You can omit tests by not including their +corresponding characters in the string. + +The tests along with their characters are: + +Freecell Tests: + +'0' - put top stack cards in the foundations. +'1' - put freecell cards in the foundations. +'2' - put freecell cards on top of stacks. +'3' - put non-top stack cards in the foundations. +'4' - move stack cards to different stacks. +'5' - move stack cards to a parent card on the same stack. +'6' - move sequences of cards onto free stacks. +'7' - put freecell cards on empty stacks. +'8' - move cards to a different parent. +'9' - empty an entire stack into the freecells. + +Atomic Freecell Tests: + +'A' - move a stack card to an empty stack. +'B' - move a stack card to a parent on a different stack. +'C' - move a stack card to a freecell. +'D' - move a freecell card to a parent. +'E' - move a freecell card to an empty stack. + + +Simple Simon Tests: + +'a' - move a full sequence to the foundations. +'b' - move a sequence to a true parent of his. +'c' - move a whole stack sequence to a false parent (in order to clear + the stack) +'d' - move a sequence to a true parent that has some cards above it. +'e' - move a sequence with some cards above it to a true parent. +'f' - move a sequence with a junk sequence above it to a true parent that + has some cards above it. +'g' - move a whole stack sequence to a false parent which has some + cards above it. +'h' - move a sequence to a parent on the same stack. + +Manipulating the tests order can be very helpful to the quick solution +of a given board. If you found that a certain board cannot be solved in +after a long time or in a certain maximal number of iterations, you +should try different tests' orders. Usually, one can find a test order +that solves a board very quickly. + +Note that this test order usually makes sense only for the Depth-First +Search scans (see the "--method" option below). + +Also note that Freecell tests are not suitable for solving Simple Simon games +and Simple Simon tests are not suitable for solving anything except Simple +Simon. + +Tests can be grouped together into random groups using parenthesis +(e.g: "(0123)") or square brackets ("[012][3456789]"). Such grouping is +only relevant to the Random DFS scan (see below). + + +-me [Solving Method] +--method [Solving Method] + +This option specifies the solving method that will be used to solve the +board. Currently, the following methods are available: + +a-star - An A* scan +bfs - A Breadth-First Search (or BFS) scan +dfs - A Depth-First Search (or DFS) scan +random-dfs - A randomized DFS scan +soft-dfs - A "soft" DFS scan + +The difference between "dfs" and "soft-dfs" is that the soft DFS does not +use procedural recursion but rather its own internal stack. "random-dfs" is +similar to "soft-dfs" only it determines to which states to recurse into +randomly. Its behaviour will differ depending on the seed you supply to it. +(see the "-seed" option below.) + +BFS does not yield good results, and A* has a mixed behaviour, so for +the time being I recommend using either DFS or Soft-DFS. + +The Random-DFS scan processes every tests' random group, randomizes the +states that it found and recurses into them one by one. Renegade tests +that do not belong to any group, are processed in a non-random manner. + + +-asw [A* Weights] +--a-star-weight [A* Weights] + +Specify weights for the A* scan, assuming it is used. The parameter +should be a comma-separated list of numbers, each one is proportional +to the weight of its corresponding test. + +The numbers are, in order: +1. The number of cards out. +2. The maximal sequence move. +3. The number of cards under sequences. +4. The length of the sequences which are found over renegade cards. +5. The depth of the board in the solution. + +The default weights are respectively: 0.5,0,0.3,0,0.2 + + +-seed [Seed Number] + +Specifies a seed to be used by Freecell Solver's internal random number +generator. This seed may alter the behaviour and speed of the "random-dfs" +scan. + + +-opt +--optimize-solution + +This option instructs Freecell Solver to try and optimize the solution +path so it will have a smaller number of moves. + + +-opt-to [tests order] +--optimization-tests-order [tests order] + +This argument specifies the test order for the optimization scan, in case +it should be different than an order that contains all the tests that were +used in all the normal scans. + + +--reparent-states + +This option specifies that states that were encountered whose depth in the +states graph can be improved should be reparented to the new parent. This +option can possibly make solutions shorter. + + +--calc-real-depth + +This options become effective only if --reparent-states is specified. What it +does, is explicitly calculate the depth of the state by tracing its path +to the initial state. This may make depth consideration more accurate. + + +6. Running Several Scans in Parallel: +------------------------------------- + +Starting from Version 2.4.0, Freecell Solver can run several scans in +parallel on the same state collection. Each scan resides in its own +"Soft Thread". By specifying several soft threads on the command line +one can create use several parallel scans. Once one of the scans +reaches a solution, the solution will be displayed. + + +-nst +--next-soft-thread + +This option creates a new soft-thread and let the other scan-specific options +initialize it. For example: + +# fc-solve --method a-star -nst --method soft-dfs -to 0123467 myboard.txt + +will run an A* scan and a Soft-DFS scan with a tests order of 0123467 on +myboard.txt. + + +-step [Number of Iterations in the Step] +--soft-thread-step [Number of Iterations in the Step] + +This option will set the number of iterations with which to run the +soft thread before switching to the next one. By specifying a larger +step, one can give a certain scan a longer run-time and a higher priority. + + +-nht +--next-hard-thread + +This argument lets one initialize the next hard thread. If Freecell Solver was +compiled with such support, then it is possible to run each hard thread in its +own system thread. Each hard-thread contains one or more soft threads. + + +--st-name [soft thread name] + +This argument sets the name used to identify the current soft thread. This name +can later be used to construct the prelude (see below). + + +--prelude [i1@st1{,i2@st2{,i3@st3...}}] + +Sets the prelude for the hard thread. At the beginning of the search, the +hard thread plays a static sequence of iterations at each of the soft threads +specified in the prelude, for the number of iterations specified. + +For example, if you had three soft threads named "foo", "bar" and "rin", then +the following prelude: + + --prelude 500@foo,1590@bar,100@foo,200@rin + +Will run 500 iterations in "foo", then 1590 in "bar", then 100 in "foo" again, +and then 200 in "rin". After the prelude finishes, the hard thread would +run the scans one after the other in the sequence they were defined for their +step number. + + +--scans-synergy {none|dead-ends-mark} + +Specifies the synergy between the various scans, or how much they cooperate +between themselves. "none" means they do not cooperate and only share +the same memory resources. "dead-end-marks" means they try to mark states +that they have withdrawn from, and states whose all their derived states are +such, as "dead ends". This may or may not improve the speed of the solution. + + +-ni +--next-instance + +This option allows to run two or more separate solvers one after the +other. If the first one returned an unsolvable verdict, then the second +one would run and so on. One use of it is to run an atomic moves scan +after a meta-moves scan, so we will always get an accurate verdict and +still enjoy some of the speed of the meta-moves scan. + + +7. Meta-Options +--------------- + + +--reset + +This option resets the program to its initial state, losing all the +logic that was inputted to it up to that state. Afterwards, it can +be set to a different configuration, again. + + +--read-from-file [{num_skip},]filename + +This option will read the configuration options from a file. The format +of the file is similar to that used by the UNIX Bourne Shell. (i.e: +spaces denote separate arguments, double-quotes encompass arguments, +backslash escapes characters). + +The filename can be preceeded by an optional number of the arguments to +skip followed by a comma. (the default is 0) + + +-l [preset] +--load-config [preset] + +Reads the configuration specified by [preset] and configures the solver +accordingly. A preset is a set of command line arguments to be analyzed +in the place of this option. They are read from a set of presetrc files +: one installed system-wide, the other at $HOME/.freecell-solver/presetrc +and the third at the path specified by the FREECELL_SOLVER_PRESETRC +environment variable. You can add more presets at any of these places. +(refer to http://groups.yahoo.com/group/fc-solve-discuss/message/403 +for information about their format) + +Presets that are shipped with Freecell Solver: + + abra-kadabra - a meta-moves preset + cool-jives - a meta-moves preset + crooked-nose - an atomic-moves preset (guarantees an accurate verdict) + fools-gold - an atomic-moves preset + good-intentions - runs cool-jives and then fools-gold + hello-world - a meta-moves preset + john-galt-line - a meta-moves preset + rin-tin-tin - a meta-moves preset + yellow-brick-road - a meta-moves preset + +They can be abbreviated into their lowercase acronym (i.e: "ak" or "rtt"). + + +8. Run-time Display Options +--------------------------- + + +-i +--iter-output + +This option tells fc-solve to print the iteration number and the +recursion depth of every state which is checked, to the standard +output. It's a good way to keep track of how it's doing, but the output +slows it down a bit. + + +-s +--state-output + +This option implies -i. If specified, this option outputs the cards and +formation of the board itself, for every state that is checked. +"fc-solve -s" yields a nice real-time display of the progress of +Freecell Solver, but you usually cannot make what is going on because +it is so fast. + + +9. Signal Combinations +---------------------- + +If you are working on a UNIX or a similar system then you can set some +run-time options in "fc-solve" by sending it some signal +combinations. + +If you send the signal USR1, without sending any other signals before +that, then "fc-solve" will output the present number of +iterations. This method is a good way to monitor an instance that takes +a long time to solve. + +If you send it the signal USR2 and then USR1, then "fc-solve" +will print the iteration number and depth on every state that it +checks. It is the equivalent of specifying (or unspecifying) the +option -i/--iter-output. + +If you send it two USR2 signals and then USR1, then "fc-solve" +will also print the board of every state. Again, this will only be done +assuming the iteration output is turned on. + diff --git a/kpat/freecell-solver/alloc.c b/kpat/freecell-solver/alloc.c new file mode 100644 index 00000000..81abdcc5 --- /dev/null +++ b/kpat/freecell-solver/alloc.c @@ -0,0 +1,127 @@ +/* + * alloc.c - a dynamic memory allocator. It allocates blocks of relatively + * small size, in a contiguous, compact manner. The most recent block can + * be released, but otherwise the blocks are kept for prosperity. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2002 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#include +#include + +#include "fcs_config.h" + +#include "alloc.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#define ALLOCED_SIZE (8*1024-10*sizeof(char *)) + +fcs_compact_allocator_t * + freecell_solver_compact_allocator_new(void) +{ + fcs_compact_allocator_t * allocator; + + + allocator = (fcs_compact_allocator_t *)malloc(sizeof(*allocator)); + allocator->max_num_packs = IA_STATE_PACKS_GROW_BY; + allocator->packs = (char * *)malloc(sizeof(allocator->packs[0]) * allocator->max_num_packs); + allocator->num_packs = 1; + allocator->max_ptr = + (allocator->ptr = + allocator->rollback_ptr = + allocator->packs[0] = + malloc(ALLOCED_SIZE)) + + ALLOCED_SIZE; + + return allocator; +} + +void freecell_solver_compact_allocator_extend( + fcs_compact_allocator_t * allocator + ) +{ + /* Allocate a new pack */ + if (allocator->num_packs == allocator->max_num_packs) + { + allocator->max_num_packs += IA_STATE_PACKS_GROW_BY; + allocator->packs = (char * *)realloc(allocator->packs, sizeof(allocator->packs[0]) * allocator->max_num_packs); + } + + allocator->max_ptr = + (allocator->ptr = + allocator->rollback_ptr = + allocator->packs[allocator->num_packs++] = + malloc(ALLOCED_SIZE)) + + ALLOCED_SIZE; +} + +#if 0 +char * + freecell_solver_compact_allocator_alloc( + fcs_compact_allocator_t * allocator, + int how_much + ) +{ + if (allocator->max_ptr - allocator->ptr < how_much) + { + freecell_solver_compact_allocator_extend(allocator); + } + allocator->rollback_ptr = allocator->ptr; + allocator->ptr += (how_much+(4-(how_much&0x3))); + return allocator->rollback_ptr; +} + +void freecell_solver_compact_allocator_release(fcs_compact_allocator_t * allocator) +{ + allocator->ptr = allocator->rollback_ptr; +} +#endif + +void freecell_solver_compact_allocator_finish(fcs_compact_allocator_t * allocator) +{ + int a; + for(a=0;anum_packs;a++) + { + free(allocator->packs[a]); + } + free(allocator->packs); + free(allocator); +} + +void freecell_solver_compact_allocator_foreach( + fcs_compact_allocator_t * allocator, + int data_width, + void (*ptr_function)(void *, void *), + void * context + ) +{ + int pack; + char * ptr, * max_ptr; + for(pack=0;packnum_packs-1;pack++) + { + ptr = allocator->packs[pack]; + max_ptr = ptr + ALLOCED_SIZE - data_width; + while (ptr <= max_ptr) + { + ptr_function(ptr, context); + ptr += data_width; + } + } + /* Run the callback on the last pack */ + ptr = allocator->packs[pack]; + max_ptr = allocator->rollback_ptr; + while (ptr <= max_ptr) + { + ptr_function(ptr, context); + ptr += data_width; + } +} + + + + diff --git a/kpat/freecell-solver/alloc.h b/kpat/freecell-solver/alloc.h new file mode 100644 index 00000000..5b339f24 --- /dev/null +++ b/kpat/freecell-solver/alloc.h @@ -0,0 +1,86 @@ + +#ifndef FC_SOLVE__ALLOC_H +#define FC_SOLVE__ALLOC_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + +struct fcs_compact_allocator_struct +{ + char * * packs; + int max_num_packs; + int num_packs; + char * max_ptr; + char * ptr; + char * rollback_ptr; +}; + +typedef struct fcs_compact_allocator_struct fcs_compact_allocator_t; + +extern fcs_compact_allocator_t * + freecell_solver_compact_allocator_new(void); + +extern void freecell_solver_compact_allocator_extend( + fcs_compact_allocator_t * allocator + ); +#if 0 +extern char * + freecell_solver_compact_allocator_alloc( + fcs_compact_allocator_t * allocator, + int how_much + ); +#else +#define fcs_compact_alloc_into_var(result,allocator_orig,what_t) \ +{ \ + register fcs_compact_allocator_t * allocator = (allocator_orig); \ + if (allocator->max_ptr - allocator->ptr < sizeof(what_t)) \ + { \ + freecell_solver_compact_allocator_extend(allocator); \ + } \ + allocator->rollback_ptr = allocator->ptr; \ + allocator->ptr += ((sizeof(what_t))+(sizeof(char *)-((sizeof(what_t))&(sizeof(char *)-1)))); \ + result = (what_t *)allocator->rollback_ptr; \ +} + +#define fcs_compact_alloc_typed_ptr_into_var(result, type_t, allocator_orig, how_much_orig) \ +{ \ + register fcs_compact_allocator_t * allocator = (allocator_orig); \ + register int how_much = (how_much_orig); \ + if (allocator->max_ptr - allocator->ptr < how_much) \ + { \ + freecell_solver_compact_allocator_extend(allocator); \ + } \ + allocator->rollback_ptr = allocator->ptr; \ + /* Round ptr to the next pointer boundary */ \ + allocator->ptr += ((how_much)+((sizeof(char *)-((how_much)&(sizeof(char *)-1)))&(sizeof(char*)-1))); \ + result = (type_t *)allocator->rollback_ptr; \ +} + +#endif + +#if 0 +extern void freecell_solver_compact_allocator_release(fcs_compact_allocator_t * allocator); +#else +#define fcs_compact_alloc_release(allocator) \ +{ \ + (allocator)->ptr = (allocator)->rollback_ptr; \ +} +#endif + +extern void freecell_solver_compact_allocator_finish(fcs_compact_allocator_t * allocator); + +extern void freecell_solver_compact_allocator_foreach( + fcs_compact_allocator_t * allocator, + int data_width, + void (*ptr_function)(void *, void *), + void * context + ); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/kpat/freecell-solver/app_str.c b/kpat/freecell-solver/app_str.c new file mode 100644 index 00000000..0a1ced21 --- /dev/null +++ b/kpat/freecell-solver/app_str.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include + +#define GROW_BY 4000 + +struct freecell_solver_append_string_struct +{ + char * buffer; + char * end_of_buffer; + size_t max_size; + size_t size_of_margin; +}; + +typedef struct freecell_solver_append_string_struct freecell_solver_append_string_t; + +freecell_solver_append_string_t * freecell_solver_append_string_alloc(int size_margin) +{ + freecell_solver_append_string_t * app_str; + + if (size_margin > GROW_BY) + { + return NULL; + } + + app_str = malloc(sizeof(freecell_solver_append_string_t)); + app_str->max_size = GROW_BY; + app_str->end_of_buffer = app_str->buffer = malloc(app_str->max_size); + app_str->size_of_margin = size_margin; + + return app_str; +} + +int freecell_solver_append_string_sprintf( + freecell_solver_append_string_t * app_str, + char * format, + ... + ) +{ + int num_chars_written; + va_list my_va_list; + + va_start(my_va_list, format); + num_chars_written = vsprintf(app_str->end_of_buffer, format, my_va_list); + app_str->end_of_buffer += num_chars_written; + /* + * Check to see if we don't have enough space in which case we should + * resize + * */ + if (app_str->buffer + app_str->max_size - app_str->end_of_buffer < (int)app_str->size_of_margin ) + { + char * old_buffer = app_str->buffer; + app_str->max_size += GROW_BY; + app_str->buffer = realloc(app_str->buffer, app_str->max_size); + /* + * Adjust end_of_buffer to the new buffer start + * */ + app_str->end_of_buffer = app_str->buffer + (app_str->end_of_buffer - old_buffer); + } + + return num_chars_written; +} + +char * freecell_solver_append_string_finalize( + freecell_solver_append_string_t * app_str + ) +{ + char * ret; + ret = strdup(app_str->buffer); + free(app_str->buffer); + free(app_str); + return ret; +} diff --git a/kpat/freecell-solver/app_str.h b/kpat/freecell-solver/app_str.h new file mode 100644 index 00000000..c6b6732a --- /dev/null +++ b/kpat/freecell-solver/app_str.h @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +#ifndef FC_SOLVE__APP_STR_H +#define FC_SOLVE__APP_STR_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct freecell_solver_append_string_struct +{ + char * buffer; + char * end_of_buffer; + int max_size; + int size_of_margin; +}; + +typedef struct freecell_solver_append_string_struct freecell_solver_append_string_t; + +extern freecell_solver_append_string_t * freecell_solver_append_string_alloc(int size_margin); + +extern int freecell_solver_append_string_sprintf( + freecell_solver_append_string_t * app_str, + const char * format, + ... + ); + +extern char * freecell_solver_append_string_finalize( + freecell_solver_append_string_t * app_str + ); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef FC_SOLVE__APP_STR_H */ diff --git a/kpat/freecell-solver/caas.c b/kpat/freecell-solver/caas.c new file mode 100644 index 00000000..82492f34 --- /dev/null +++ b/kpat/freecell-solver/caas.c @@ -0,0 +1,629 @@ +/* + * caas.c - the various possible implementations of the function + * freecell_solver_check_and_add_state(). + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#ifndef FC_SOLVE__CAAS_C +#define FC_SOLVE__CAAS_C + +#include +#include +#include + +#include "fcs_dm.h" +#include "fcs.h" + +#include "fcs_isa.h" + +#include "lookup2.h" + + +#ifdef INDIRECT_STACK_STATES +#include "fcs_hash.h" +#endif + +#include "caas.h" +#include "ms_ca.h" + +#include "test_arr.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + + +/* + The objective of the fcs_caas_check_and_insert macros is: + 1. To check if new_state is already in the prev_states collection. + 2. If not, to add it and to set check to true. + 3. If so, to set check to false. + */ + + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) +#ifdef FCS_WITH_MHASH +#define fcs_caas_check_and_insert() \ + /* \ + Calculate the has function of the state. \ + */ \ + { \ + char * temp_ptr; \ + instance->mhash_context = mhash_init(instance->mhash_type); \ + mhash(instance->mhash_context, (void *)new_state, sizeof(fcs_state_t)); \ + temp_ptr = mhash_end(instance->mhash_context); \ + /* Retrieve the first 32 bits and make them the hash value */ \ + hash_value_int = *(SFO_hash_value_t*)temp_ptr; \ + free(temp_ptr); \ + } \ + \ + if (hash_value_int < 0) \ + { \ + /* \ + * This is a bit mask that nullifies the sign bit of the \ + * number so it will always be positive \ + * */ \ + hash_value_int &= (~(1<<((sizeof(hash_value_int)<<3)-1))); \ + } \ + check = ((*existing_state = freecell_solver_hash_insert( \ + instance->hash, \ + new_state, \ + hash_value_int, \ + 1 \ + )) == NULL); + + + +#else +#define fcs_caas_check_and_insert() \ + { \ + const char * s_ptr = (char*)new_state; \ + const char * s_end = s_ptr+sizeof(fcs_state_t); \ + hash_value_int = 0; \ + while (s_ptr < s_end) \ + { \ + hash_value_int += (hash_value_int << 5) + *(s_ptr++); \ + } \ + hash_value_int += (hash_value_int>>5); \ + } \ + if (hash_value_int < 0) \ + { \ + /* \ + * This is a bit mask that nullifies the sign bit of the \ + * number so it will always be positive \ + * */ \ + hash_value_int &= (~(1<<((sizeof(hash_value_int)<<3)-1))); \ + } \ + check = ((*existing_state = freecell_solver_hash_insert( \ + instance->hash, \ + new_state, \ + freecell_solver_lookup2_hash_function( \ + (ub1 *)new_state, \ + sizeof(fcs_state_t), \ + 24 \ + ), \ + hash_value_int, \ + 1 \ + )) == NULL); + +#endif +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT) +#define fcs_caas_check_and_insert() \ + /* Try to see if the state is found in indirect_prev_states */ \ + if ((pos_ptr = (fcs_state_with_locations_t * *)bsearch(&new_state, \ + instance->indirect_prev_states, \ + instance->num_indirect_prev_states, \ + sizeof(fcs_state_with_locations_t *), \ + freecell_solver_state_compare_indirect)) == NULL) \ + { \ + /* It isn't in prev_states, but maybe it's in the sort margin */ \ + pos_ptr = (fcs_state_with_locations_t * *)freecell_solver_bsearch( \ + &new_state, \ + instance->indirect_prev_states_margin, \ + instance->num_prev_states_margin, \ + sizeof(fcs_state_with_locations_t *), \ + freecell_solver_state_compare_indirect_with_context, \ + NULL, \ + &found); \ + \ + if (found) \ + { \ + check = 0; \ + *existing_state = *pos_ptr; \ + } \ + else \ + { \ + /* Insert the state into its corresponding place in the sort \ + * margin */ \ + memmove((void*)(pos_ptr+1), \ + (void*)pos_ptr, \ + sizeof(fcs_state_with_locations_t *) * \ + (instance->num_prev_states_margin- \ + (pos_ptr-instance->indirect_prev_states_margin) \ + )); \ + *pos_ptr = new_state; \ + \ + instance->num_prev_states_margin++; \ + \ + if (instance->num_prev_states_margin >= PREV_STATES_SORT_MARGIN) \ + { \ + /* The sort margin is full, let's combine it with the main array */ \ + if (instance->num_indirect_prev_states + instance->num_prev_states_margin > instance->max_num_indirect_prev_states) \ + { \ + while (instance->num_indirect_prev_states + instance->num_prev_states_margin > instance->max_num_indirect_prev_states) \ + { \ + instance->max_num_indirect_prev_states += PREV_STATES_GROW_BY; \ + } \ + instance->indirect_prev_states = realloc(instance->indirect_prev_states, sizeof(fcs_state_with_locations_t *) * instance->max_num_indirect_prev_states); \ + } \ + \ + freecell_solver_merge_large_and_small_sorted_arrays( \ + instance->indirect_prev_states, \ + instance->num_indirect_prev_states, \ + instance->indirect_prev_states_margin, \ + instance->num_prev_states_margin, \ + sizeof(fcs_state_with_locations_t *), \ + freecell_solver_state_compare_indirect_with_context, \ + NULL \ + ); \ + \ + instance->num_indirect_prev_states += instance->num_prev_states_margin; \ + \ + instance->num_prev_states_margin=0; \ + } \ + check = 1; \ + } \ + \ + } \ + else \ + { \ + *existing_state = *pos_ptr; \ + check = 0; \ + } + +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBREDBLACK_TREE) + +#define fcs_caas_check_and_insert() \ + *existing_state = (fcs_state_with_locations_t *)rbsearch(new_state, instance->tree); \ + check = ((*existing_state) == new_state); + +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE) || (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE) + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE) +#define fcs_libavl_states_tree_insert(a,b) avl_insert((a),(b)) +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE) +#define fcs_libavl_states_tree_insert(a,b) rb_insert((a),(b)) +#endif + +#define fcs_caas_check_and_insert() \ + *existing_state = fcs_libavl_states_tree_insert(instance->tree, new_state); \ + check = (*existing_state == NULL); + +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_TREE) +#define fcs_caas_check_and_insert() \ + *existing_state = g_tree_lookup(instance->tree, (gpointer)new_state); \ + if (*existing_state == NULL) \ + { \ + /* The new state was not found. Let's insert it. \ + * The value must be the same as the key, so g_tree_lookup() \ + * will return it. */ \ + g_tree_insert( \ + instance->tree, \ + (gpointer)new_state, \ + (gpointer)new_state \ + ); \ + check = 1; \ + } \ + else \ + { \ + check = 0; \ + } + + + +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH) +#define fcs_caas_check_and_insert() \ + *existing_state = g_hash_table_lookup(instance->hash, (gpointer)new_state); \ + if (*existing_state == NULL) \ + { \ + /* The new state was not found. Let's insert it. \ + * The value must be the same as the key, so g_tree_lookup() \ + * will return it. */ \ + g_hash_table_insert( \ + instance->hash, \ + (gpointer)new_state, \ + (gpointer)new_state \ + \ + ); \ + check = 1; \ + } \ + else \ + { \ + check = 0; \ + } + +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_DB_FILE) +#define fcs_caas_check_and_insert() \ + { \ + DBT key, value; \ + key.data = new_state; \ + key.size = sizeof(*new_state); \ + if (instance->db->get( \ + instance->db, \ + NULL, \ + &key, \ + &value, \ + 0 \ + ) == 0) \ + { \ + /* The new state was not found. Let's insert it. \ + * The value must be the same as the key, so g_tree_lookup() \ + * will return it. */ \ + \ + value.data = key.data; \ + value.size = key.size; \ + instance->db->put( \ + instance->db, \ + NULL, \ + &key, \ + &value, \ + 0); \ + check = 1; \ + } \ + else \ + { \ + check = 0; \ + *existing_state = (fcs_state_with_locations_t *)(value.data); \ + } \ + } + +#else +#error no define +#endif + +#ifdef INDIRECT_STACK_STATES +static GCC_INLINE void freecell_solver_cache_stacks( + freecell_solver_hard_thread_t * hard_thread, + fcs_state_with_locations_t * new_state + ) +{ + int a; +#if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH) + SFO_hash_value_t hash_value_int; +#endif + void * cached_stack; + fcs_card_t * new_ptr; + freecell_solver_instance_t * instance = hard_thread->instance; + int stacks_num = instance->stacks_num; + + + for(a=0 ; astacks_copy_on_write_flags & (1 << a))) + { + continue; + } + /* new_state->s.stacks[a] = realloc(new_state->s.stacks[a], fcs_stack_len(new_state->s, a)+1); */ + fcs_compact_alloc_typed_ptr_into_var(new_ptr, char, hard_thread->stacks_allocator, (fcs_stack_len(new_state->s, a)+1)); + memcpy(new_ptr, new_state->s.stacks[a], (fcs_stack_len(new_state->s, a)+1)); + new_state->s.stacks[a] = new_ptr; + +#if FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH + /* Calculate the hash value for the stack */ + /* This hash function was ripped from the Perl source code. + * (It is not derived work however). */ + { + const char * s_ptr = (char*)(new_state->s.stacks[a]); + const char * s_end = s_ptr+fcs_stack_len(new_state->s, a)+1; + hash_value_int = 0; + while (s_ptr < s_end) + { + hash_value_int += (hash_value_int << 5) + *(s_ptr++); + } + hash_value_int += (hash_value_int >> 5); + } + + if (hash_value_int < 0) + { + /* + * This is a bit mask that nullifies the sign bit of the + * number so it will always be positive + * */ + hash_value_int &= (~(1<<((sizeof(hash_value_int)<<3)-1))); + } + + cached_stack = (void *)freecell_solver_hash_insert( + instance->stacks_hash, + new_state->s.stacks[a], + (SFO_hash_value_t)freecell_solver_lookup2_hash_function( + (ub1 *)new_state->s.stacks[a], + (fcs_stack_len(new_state->s, a)+1), + 24 + ), + hash_value_int, + 1 + ); + +#define replace_with_cached(condition_expr) \ + if (cached_stack != NULL) \ + { \ + fcs_compact_alloc_release(hard_thread->stacks_allocator); \ + new_state->s.stacks[a] = cached_stack; \ + } + + replace_with_cached(cached_stack != NULL); + +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE) || (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE) + cached_stack = +#if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE) + avl_insert( +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE) + rb_insert( +#endif + instance->stacks_tree, + new_state->s.stacks[a] + ); +#if 0 + ) /* In order to settle gvim and other editors that + are keen on parenthesis matching */ +#endif + + replace_with_cached(cached_stack != NULL); + +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE) + cached_stack = (void *)rbsearch( + new_state->s.stacks[a], + instance->stacks_tree + ); + + replace_with_cached(cached_stack != new_state->s.stacks[a]); +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE) + cached_stack = g_tree_lookup( + instance->stacks_tree, + (gpointer)new_state->s.stacks[a] + ); + + /* replace_with_cached contains an if statement */ + replace_with_cached(cached_stack != NULL) + else + { + g_tree_insert( + instance->stacks_tree, + (gpointer)new_state->s.stacks[a], + (gpointer)new_state->s.stacks[a] + ); + } +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH) + cached_stack = g_hash_table_lookup( + instance->stacks_hash, + (gconstpointer)new_state->s.stacks[a] + ); + replace_with_cached(cached_stack != NULL) + else + { + g_hash_table_insert( + instance->stacks_hash, + (gpointer)new_state->s.stacks[a], + (gpointer)new_state->s.stacks[a] + ); + } +#endif + } +} +#else +#define freecell_solver_cache_stacks(instance, new_state) +#endif + + +#ifdef FCS_WITH_TALONS +void freecell_solver_cache_talon( + freecell_solver_instance_t * instance, + fcs_state_with_locations_t * new_state + ) +{ + void * cached_talon; + SFO_hash_value_t hash_value_int; + + new_state->s.talon = realloc(new_state->s.talon, fcs_klondike_talon_len(new_state->s)+1); +#error Add Hash Code + hash_value_int = *(SFO_hash_value_t*)instance->hash_value; + if (hash_value_int < 0) + { + /* + * This is a bit mask that nullifies the sign bit of the + * number so it will always be positive + * */ + hash_value_int &= (~(1<<((sizeof(hash_value_int)<<3)-1))); + } + + cached_talon = (void *)freecell_solver_hash_insert( + instance->talons_hash, + new_state->s.talon, + hash_value_int, + 1 + ); + + if (cached_talon != NULL) + { + free(new_state->s.talon); + new_state->s.talon = cached_talon; + } +} +#endif + + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH) +guint freecell_solver_hash_function(gconstpointer key) +{ + guint hash_value; + const char * s_ptr = (char*)key; + const char * s_end = s_ptr+sizeof(fcs_state_t); + hash_value = 0; + while (s_ptr < s_end) + { + hash_value += (hash_value << 5) + *(s_ptr++); + } + hash_value += (hash_value >> 5); + + return hash_value; +} +#endif + + +/* + * check_and_add_state() does the following things: + * + * 1. Check if the number of iterations exceeded its maximum, and if so + * return FCS_STATE_EXCEEDS_MAX_NUM_TIMES in order to terminate the + * solving process. + * 2. Check if the maximal depth was reached and if so return + * FCS_STATE_EXCEEDS_MAX_DEPTH + * 3. Canonize the state. + * 4. Check if the state is already found in the collection of the states + * that were already checked. + * If it is: + * + * 5a. Return FCS_STATE_ALREADY_EXISTS + * + * If it isn't: + * + * 5b. Call solve_for_state() on the board. + * + * */ + +GCC_INLINE int freecell_solver_check_and_add_state( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * new_state, + fcs_state_with_locations_t * * existing_state + ) +{ +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) + SFO_hash_value_t hash_value_int; +#endif +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT) + fcs_state_with_locations_t * * pos_ptr; + int found; +#endif + freecell_solver_hard_thread_t * hard_thread = soft_thread->hard_thread; + freecell_solver_instance_t * instance = hard_thread->instance; + + int check; + + if (check_if_limits_exceeded()) + { + return FCS_STATE_BEGIN_SUSPEND_PROCESS; + } + + freecell_solver_cache_stacks(hard_thread, new_state); + + fcs_canonize_state(new_state, instance->freecells_num, instance->stacks_num); + + fcs_caas_check_and_insert(); + if (check) + { + /* The new state was not found in the cache, and it was already inserted */ + if (new_state->parent) + { + new_state->parent->num_active_children++; + } + instance->num_states_in_collection++; + + if (new_state->moves_to_parent != NULL) + { + new_state->moves_to_parent = + freecell_solver_move_stack_compact_allocate( + hard_thread, + new_state->moves_to_parent + ); + } + + return FCS_STATE_DOES_NOT_EXIST; + } + else + { + return FCS_STATE_ALREADY_EXISTS; + } +} + + + +/* + * This implementation crashes for some reason, so don't use it. + * + * */ + + +#if 0 + +static char meaningless_data[16] = "Hello World!"; + +int freecell_solver_check_and_add_state(freecell_solver_instance_t * instance, fcs_state_with_locations_t * new_state, int depth) +{ + DBT key, value; + + if ((instance->max_num_times >= 0) && + (instance->max_num_times <= instance->num_times)) + { + return FCS_STATE_EXCEEDS_MAX_NUM_TIMES; + } + + if ((instance->max_depth >= 0) && + (instance->max_depth <= depth)) + { + return FCS_STATE_EXCEEDS_MAX_DEPTH; + } + + fcs_canonize_state(new_state, instance->freecells_num, instance->stacks_num); + + freecell_solver_cache_stacks(instance, new_state); + + key.data = new_state; + key.size = sizeof(*new_state); + + if (instance->db->get( + instance->db, + NULL, + &key, + &value, + 0 + ) == 0) + { + /* The new state was not found. Let's insert it. + * The value should be non-NULL or else g_hash_table_lookup() will + * return NULL even if it exists. */ + + value.data = meaningless_data; + value.size = 8; + instance->db->put( + instance->db, + NULL, + &key, + &value, + 0); + if (freecell_solver_solve_for_state(instance, new_state, depth+1,0) == FCS_STATE_WAS_SOLVED) + { + return FCS_STATE_WAS_SOLVED; + } + else + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + } + else + { + /* free (value.data) ; */ + return FCS_STATE_ALREADY_EXISTS; + } +} + + +#endif + +#endif /* #ifndef FC_SOLVE__CAAS_C */ diff --git a/kpat/freecell-solver/caas.h b/kpat/freecell-solver/caas.h new file mode 100644 index 00000000..e1969488 --- /dev/null +++ b/kpat/freecell-solver/caas.h @@ -0,0 +1,28 @@ + +#ifndef FC_SOLVE__CAAS_H +#define FC_SOLVE__CAAS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* #define FCS_USE_INLINE */ + +/* + * check_and_add_state is defined in caas.c. + * + * DFS stands for Depth First Search which is the type of scan Freecell + * Solver uses to solve a given board. + * */ + +extern int freecell_solver_check_and_add_state( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * new_state, + fcs_state_with_locations_t * * existing_state + ); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef FC_SOLVE__CAAS_H */ diff --git a/kpat/freecell-solver/card.c b/kpat/freecell-solver/card.c new file mode 100644 index 00000000..d4df80f7 --- /dev/null +++ b/kpat/freecell-solver/card.c @@ -0,0 +1,286 @@ +/* + * card.c - functions to convert cards and card components to and from + * its user representation. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#include + +#include "card.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + + +#define uc(c) ( (((c)>='a') && ((c)<='z')) ? ((c)+'A'-'a') : (c)) + +/* + * This function converts a card number from its user representation + * (e.g: "A", "K", "9") to its card number that can be used by + * the program. + * */ +int freecell_solver_u2p_card_number(const char * string) +{ + char rest; + + while (1) + { + rest = uc(*string); + + if ((rest == '\0') || (rest == ' ') || (rest == '\t')) + { + return 0; + } + if (rest == 'A') + { + return 1; + } + else if (rest =='J') + { + return 11; + } + else if (rest == 'Q') + { + return 12; + } + else if (rest == 'K') + { + return 13; + } + else if (rest == '1') + { + return (*(string+1) == '0')?10:1; + } + else if ((rest == '0') || (rest == 'T')) + { + return 10; + } + else if ((rest >= '2') && (rest <= '9')) + { + return (rest-'0'); + } + else + { + string++; + } + } +} + + +/* + * This function converts a string containing a suit letter (that is + * one of H,S,D,C) into its suit ID. + * + * The suit letter may come somewhat after the beginning of the string. + * + * */ +int freecell_solver_u2p_suit(const char * suit) +{ + char c; + + c = uc(*suit); + while ( + (c != 'H') && + (c != 'S') && + (c != 'D') && + (c != 'C') && + (c != ' ') && + (c != '\0')) + { + suit++; + c = uc(*suit); + } + + if (c == 'H') + return 0; + else if (c == 'C') + return 1; + else if (c == 'D') + return 2; + else if (c == 'S') + return 3; + else + return 0; +} + +static int fcs_u2p_flipped_status(const char * str) +{ + while (*str != '\0') + { + if ((*str != ' ') && (*str != '\t')) + { + return (*str == '<'); + } + str++; + } + return 0; +} +/* + * This function converts an entire card from its string representations + * (e.g: "AH", "KS", "8D"), to a fcs_card_t data type. + * */ +fcs_card_t freecell_solver_card_user2perl(const char * str) +{ + fcs_card_t card; +#if defined(COMPACT_STATES)||defined(INDIRECT_STACK_STATES) + card = 0; +#endif + fcs_card_set_flipped(card, fcs_u2p_flipped_status(str)); + fcs_card_set_num(card, fcs_u2p_card_number(str)); + fcs_card_set_suit(card, fcs_u2p_suit(str)); + + return card; +} + + +/* + * Those strings contain the string representations of the different cards. + * If CARD_DEBUG_PRES is defined then an asterisk is printed as an empty card. + * + * Notice that there are two of them: one prints 10 and one prints T for the + * 10 card. + * + * */ +#ifdef CARD_DEBUG_PRES +static char card_map_3_10[14][4] = { "*", "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" }; + +static char card_map_3_T[14][4] = { "*", "A", "2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K" }; + +#else +static char card_map_3_10[14][4] = { " ", "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" }; + +static char card_map_3_T[14][4] = { " ", "A", "2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K" }; + +#endif + +/* + * Converts a card_number from its internal representation to a string. + * + * num - the card number + * str - the string to output to. + * card_num_is_null - a pointer to a bool that indicates whether + * the card number is out of range or equal to zero + * t - whether 10 should be printed as T or not. + * flipped - whether the card is face down + * */ +char * freecell_solver_p2u_card_number( + int num, + char * str, + int * card_num_is_null, + int t, + int flipped) +{ + char (*card_map_3) [4] = card_map_3_10; + if (t) + { + card_map_3 = card_map_3_T; + } +#ifdef CARD_DEBUG_PRES + if (0) + { + } +#else + if (flipped) + { + strncpy(str, "*", 2); + *card_num_is_null = 0; + } +#endif + else + { + if ((num >= 0) && (num <= 13)) + { + strncpy(str, card_map_3[num], strlen(card_map_3[num])+1); + *card_num_is_null = (num == 0); + } + else + { + strncpy(str, card_map_3[0], strlen(card_map_3[0])+1); + *card_num_is_null = 1; + } + } + return str; +} + +/* + * Converts a suit to its user representation. + * + * */ +char * freecell_solver_p2u_suit(int suit, char * str, int card_num_is_null, int flipped) +{ +#ifndef CARD_DEBUG_PRES + if (flipped) + { + strncpy(str, "*", 2); + } + else +#endif + if (suit == 0) + { + if (card_num_is_null) +#ifdef CARD_DEBUG_PRES + strncpy(str, "*", 2); +#else + strncpy(str, " ", 2); +#endif + else + strncpy(str, "H", 2); + } + else if (suit == 1) + strncpy(str, "C", 2); + else if (suit == 2) + strncpy(str, "D", 2); + else if (suit == 3) + strncpy(str, "S", 2); + else + strncpy(str, " ", 2); + return str; +} + +/* + * Convert an entire card to its user representation. + * + * */ +char * freecell_solver_card_perl2user(fcs_card_t card, char * str, int t) +{ + int card_num_is_null; +#ifdef CARD_DEBUG_PRES + if (fcs_card_get_flipped(card)) + { + *str = '<'; + str++; + } +#endif + + fcs_p2u_card_number( + fcs_card_card_num(card), + str, + &card_num_is_null, + t, + fcs_card_get_flipped(card) + ); + /* + * Notice that if card_num_is_null is found to be true + * it will affect the output of the suit too. + * + * */ + fcs_p2u_suit( + fcs_card_suit(card), + str+strlen(str), + card_num_is_null, + fcs_card_get_flipped(card) + ); + +#ifdef CARD_DEBUG_PRES + if (fcs_card_get_flipped(card)) + { + strcat(str, ">"); + } +#endif + + return str; +} diff --git a/kpat/freecell-solver/card.h b/kpat/freecell-solver/card.h new file mode 100644 index 00000000..d67ea645 --- /dev/null +++ b/kpat/freecell-solver/card.h @@ -0,0 +1,100 @@ +/* + * card.h - header file for card functions for Freecell Solver + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + + +#ifndef FC_SOLVE__CARD_H +#define FC_SOLVE__CARD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef FC_SOLVE__STATE_H +#include "state.h" +#endif + +/* + * This function converts an entire card from its string representations + * (e.g: "AH", "KS", "8D"), to a fcs_card_t data type. + * */ +extern fcs_card_t freecell_solver_card_user2perl(const char * str); +#define fcs_card_user2perl(str) (freecell_solver_card_user2perl(str)) + + + +/* + * Convert an entire card to its user representation. + * + * */ +extern char * freecell_solver_card_perl2user( + fcs_card_t card, + char * str, + int t + ); + +#define fcs_card_perl2user(card,str,t) (freecell_solver_card_perl2user((card),(str),(t))) + + + +/* + * Converts a card_number from its internal representation to a string. + * + * num - the card number + * str - the string to output to. + * card_num_is_null - a pointer to a bool that indicates whether + * the card number is out of range or equal to zero + * t - whether 10 should be printed as T or not. + * */ +extern char * freecell_solver_p2u_card_number( + int num, + char * str, + int * card_num_is_null, + int t, + int flipped + ); + +#define fcs_p2u_card_number(num,str,card_num_is_null,t,flipped) \ + (freecell_solver_p2u_card_number((num),(str),(card_num_is_null),(t),(flipped))) + +/* + * Converts a suit to its user representation. + * + * */ +char * freecell_solver_p2u_suit( + int suit, + char * str, + int card_num_is_null, + int flipped + ); + +#define fcs_p2u_suit(suit,str,card_num_is_null,flipped) \ + (freecell_solver_p2u_suit((suit),(str),(card_num_is_null),(flipped))) + +/* + * This function converts a card number from its user representation + * (e.g: "A", "K", "9") to its card number that can be used by + * the program. + * */ +extern int freecell_solver_u2p_card_number(const char * string); +#define fcs_u2p_card_number(string) (freecell_solver_u2p_card_number(string)) + +/* + * This function converts a string containing a suit letter (that is + * one of H,S,D,C) into its suit ID. + * + * The suit letter may come somewhat after the beginning of the string. + * + * */ +extern int freecell_solver_u2p_suit(const char * deck); +#define fcs_u2p_suit(deck) (freecell_solver_u2p_suit(deck)) + +#ifdef __cplusplus +} +#endif + +#endif /* FC_SOLVE__CARD_H */ diff --git a/kpat/freecell-solver/cl_chop.c b/kpat/freecell-solver/cl_chop.c new file mode 100644 index 00000000..4bb82aab --- /dev/null +++ b/kpat/freecell-solver/cl_chop.c @@ -0,0 +1,245 @@ +#include +#include +#include + +#include "cl_chop.h" + +#ifdef DMALLOC +#include +#endif + +#define ARGS_MAN_GROW_BY 30 + +args_man_t * freecell_solver_args_man_alloc(void) +{ + args_man_t * ret; + ret = malloc(sizeof(args_man_t)); + ret->argc = 0; + ret->max_num_argv = ARGS_MAN_GROW_BY; + ret->argv = malloc(sizeof(ret->argv[0]) * ret->max_num_argv); + return ret; +} + +void freecell_solver_args_man_free(args_man_t * manager) +{ + int a; + for(a=0;aargc;a++) + { + free(manager->argv[a]); + } + free(manager->argv); + free(manager); +} + +#define skip_ws() { while((*s == ' ') || (*s == '\t')) { s++; } } +#define skip_non_ws() { while((*s != ' ') && (*s != '\t') && (*s)) { s++; }} + +#define add_to_last_arg(c) \ + { \ + *(last_arg_ptr++) = (c); \ + if (last_arg_ptr == last_arg_end) \ + { \ + new_last_arg = realloc(last_arg, (size_t)(last_arg_end-last_arg+1024)); \ + last_arg_ptr += new_last_arg - last_arg; \ + last_arg_end += new_last_arg - last_arg + 1024; \ + last_arg = new_last_arg; \ + } \ + } + +#define push_args_last_arg() { \ + new_arg = malloc((size_t)(last_arg_ptr-last_arg+1)); \ + strncpy(new_arg, last_arg, (size_t)(last_arg_ptr-last_arg)); \ + new_arg[last_arg_ptr-last_arg] = '\0'; \ + manager->argv[manager->argc] = new_arg; \ + manager->argc++; \ + if (manager->argc == manager->max_num_argv) \ + { \ + manager->max_num_argv += ARGS_MAN_GROW_BY; \ + manager->argv = realloc(manager->argv, sizeof(manager->argv[0]) * manager->max_num_argv); \ + } \ + \ + /* Reset last_arg_ptr so we will have an entirely new argument */ \ + last_arg_ptr = last_arg; \ + } + +#define is_whitespace(c) \ + (((c) == ' ') || ((c) == '\t') || ((c) == '\n') || ((c) == '\r')) + +int freecell_solver_args_man_chop(args_man_t * manager, char * string) +{ + char * s = string; + char * new_arg; + char * last_arg, * last_arg_ptr, * last_arg_end, * new_last_arg; + char next_char; + int in_arg; + + last_arg_ptr = last_arg = malloc(1024); + last_arg_end = last_arg + 1023; + + while (*s != '\0') + { +LOOP_START: + in_arg = 0; + while (is_whitespace(*s)) + { + s++; + } + if (*s == '\0') + { + break; + } + if (*s == '#') + { + in_arg = 0; + /* Skip to the next line */ + while((*s != '\0') && (*s != '\n')) + { + s++; + } + continue; + } +AFTER_WS: + while ((*s != ' ') && (*s != '\t') && (*s != '\n') && + (*s != '\r') && + (*s != '\\') && (*s != '\"') && (*s != '\0') && + (*s != '#')) + { + in_arg = 1; + add_to_last_arg(*s); + s++; + } + + + if ((*s == ' ') || (*s == '\t') || (*s == '\n') || (*s == '\0') || (*s == '\r')) + { +NEXT_ARG: + push_args_last_arg(); + in_arg = 0; + + if (*s == '\0') + { + break; + } + } + else if (*s == '\\') + { + char next_char = *(++s); + s++; + if (next_char == '\0') + { + s--; + goto NEXT_ARG; + } + else if ((next_char == '\n') || (next_char == '\r')) + { + if (in_arg) + { + goto AFTER_WS; + } + else + { + goto LOOP_START; + } + } + else + { + add_to_last_arg(next_char); + } + } + else if (*s == '\"') + { + s++; + in_arg = 1; + while ((*s != '\"') && (*s != '\0')) + { + if (*s == '\\') + { + next_char = *(++s); + if (next_char == '\0') + { + push_args_last_arg(); + + goto END_OF_LOOP; + } + else if ((next_char == '\n') || (next_char == '\r')) + { + /* Do nothing */ + } + else if ((next_char == '\\') || (next_char == '\"')) + { + add_to_last_arg(next_char); + } + else + { + add_to_last_arg('\\'); + add_to_last_arg(next_char); + } + } + else + { + add_to_last_arg(*s); + } + s++; + } + s++; + goto AFTER_WS; + } + else if (*s == '#') + { + in_arg = 0; + /* Skip to the next line */ + while((*s != '\0') && (*s != '\n')) + { + s++; + } + goto NEXT_ARG; + } + } +END_OF_LOOP: + + free(last_arg); + + return 0; +} + +#ifdef CMD_LINE_CHOP_WITH_MAIN +int main(int argc, char * * argv) +{ + args_man_t * args_man; + char * string; + +#if 0 + string = argv[1]; +#else + { + FILE * f; + + f = fopen(argv[1],"rb"); + string = calloc(4096,1); + fread(string, 4095, 1, f); + fclose(f); + } + +#endif + + /* Initialize an arg man */ + args_man = args_man_alloc(); + /* Call it on string */ + args_man_chop(args_man, string); + + /* Now use args_man->argc and args_man->argv */ + { + int a; + for(a=0;aargc;a++) + { + printf("argv[%i] = \"%s\"\n", a, args_man->argv[a]); + } + } + /* Free the allocated memory */ + args_man_free(args_man); + + free(string); + + return 0; +} +#endif diff --git a/kpat/freecell-solver/cl_chop.h b/kpat/freecell-solver/cl_chop.h new file mode 100644 index 00000000..3f6a873e --- /dev/null +++ b/kpat/freecell-solver/cl_chop.h @@ -0,0 +1,19 @@ + +#ifndef FC_SOLVE__CMD_LINE_CHOP_H +#define FC_SOLVE__CMD_LINE_CHOP_H + +struct args_man_struct +{ + int argc; + char * * argv; + int max_num_argv; +}; + +typedef struct args_man_struct args_man_t; + +extern args_man_t * freecell_solver_args_man_alloc(void); +extern void freecell_solver_args_man_free(args_man_t * manager); +extern int freecell_solver_args_man_chop(args_man_t * manager, char * string); + +#endif /* #ifndef FC_SOLVE__CMD_LINE_CHOP_H */ + diff --git a/kpat/freecell-solver/cmd_line.c b/kpat/freecell-solver/cmd_line.c new file mode 100644 index 00000000..63fbf6c9 --- /dev/null +++ b/kpat/freecell-solver/cmd_line.c @@ -0,0 +1,964 @@ +#include +#include +#include +#include + +#include "fcs_user.h" +#include "fcs_cl.h" +#include "cl_chop.h" + +#include "prefix.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +static int read_preset(char * preset_name, args_man_t * * args, char * * opened_files_dir_to_assign, char * user_preset_dir) +{ + int ret_code = 1; + char * home_dir_presetrc = NULL, * global_presetrc = NULL, * env_var_presetrc = NULL; + char * path; + char * * presetrc_pathes[5] = {&env_var_presetrc, &home_dir_presetrc, &global_presetrc, &user_preset_dir, NULL}; + int path_idx; + char line[8192]; + FILE * f = NULL; + char * fgets_ret; + char * opened_files_dir = NULL; + int read_next_preset = 0; + + { + char * home_dir; + home_dir = getenv("HOME"); + if (home_dir) + { + home_dir_presetrc = malloc(strlen(home_dir) + 50); + sprintf(home_dir_presetrc, + "%s/.freecell-solver/presetrc", home_dir + ); + } + } + env_var_presetrc = getenv("FREECELL_SOLVER_PRESETRC"); + + global_presetrc = (FREECELL_SOLVER_PKG_DATA_DIR "/presetrc"); + + for(path_idx=0;(presetrc_pathes[path_idx] != NULL) ; path_idx++) + { + path = (*presetrc_pathes[path_idx]); + if (path == NULL) + { + continue; + } + f = fopen(path, "rt"); + if (f == NULL) + { + continue; + } + while(1) + { + fgets_ret = fgets(line, sizeof(line), f); + if (fgets_ret == NULL) + { + break; + } + if (!strncmp(line, "dir=", 4)) + { +#define nullify_newline() \ + { \ + char * s; \ + \ + s = strchr(line, '\n'); \ + if (s != NULL) \ + { \ + *s = '\0'; \ + } \ + } + nullify_newline(); + + if (opened_files_dir != NULL) + { + free(opened_files_dir); + } + opened_files_dir = strdup(line+4); + } + else if (!strncmp(line, "name=", 5)) + { + nullify_newline(); + if (!strcmp(line+5, preset_name)) + { + read_next_preset = 1; + } + } + else if (!strncmp(line, "command=", 8)) + { + if (read_next_preset) + { + *args = freecell_solver_args_man_alloc(); + freecell_solver_args_man_chop(*args, line+8); + ret_code = 0; + goto HAVE_PRESET; + } + } + } + fclose(f); + f = NULL; +#undef nullify_newline + } +HAVE_PRESET: + + if (f) + { + fclose(f); + } + + if (home_dir_presetrc) + { + free(home_dir_presetrc); + } + + if (ret_code == 0) + { + *opened_files_dir_to_assign = opened_files_dir; + } + else + { + if (opened_files_dir) + { + free(opened_files_dir); + } + } + + return ret_code; +} + + +int freecell_solver_user_cmd_line_parse_args_with_file_nesting_count( + void * instance, + int argc, + const char * argv[], + int start_arg, + char * * known_parameters, + freecell_solver_user_cmd_line_known_commands_callback_t callback, + void * callback_context, + char * * error_string, + int * last_arg, + int file_nesting_count, + char * opened_files_dir + ) +{ + int arg; + char * * known_param; + int num_to_skip; + int callback_ret; + int ret; + + *error_string = NULL; + + for(arg=start_arg;arg '9') && (*start_num < '0') && (*start_num != '\0')) + { + start_num++; + } + if (*start_num == '\0') + { + break; + } + end_num = start_num+1; + while ((((*end_num >= '0') && (*end_num <= '9')) || (*end_num == '.')) && (*end_num != '\0')) + { + end_num++; + } + num_copy = malloc(end_num-start_num+1); + memcpy(num_copy, start_num, end_num-start_num); + num_copy[end_num-start_num] = '\0'; + freecell_solver_user_set_a_star_weight( + instance, + a, + atof(num_copy) + ); + free(num_copy); + start_num=end_num+1; + } + } + } + else if ((!strcmp(argv[arg], "-opt")) || (!strcmp(argv[arg], "--optimize-solution"))) + { + freecell_solver_user_set_solution_optimization(instance, 1); + } + else if ((!strcmp(argv[arg], "-seed"))) + { + arg++; + if (arg == argc) + { + *last_arg = arg-1; + return FCS_CMD_LINE_PARAM_WITH_NO_ARG; + } + + freecell_solver_user_set_random_seed(instance, atoi(argv[arg])); + } + else if ((!strcmp(argv[arg], "-mss")) || (!strcmp(argv[arg], "--max-stored-states"))) + { + arg++; + if (arg == argc) + { + *last_arg = arg-1; + return FCS_CMD_LINE_PARAM_WITH_NO_ARG; + } + + freecell_solver_user_limit_num_states_in_collection( + instance, + atoi(argv[arg]) + ); + } + else if ( + (!strcmp(argv[arg], "-nst")) || + (!strcmp(argv[arg], "--next-soft-thread")) || + (!strcmp(argv[arg], "-nht")) || + (!strcmp(argv[arg], "--next-hard-thread")) + ) + { + int ret; + int is_st = ((!strcmp(argv[arg], "-nst")) || (!strcmp(argv[arg], "--next-soft-thread"))); + + ret = + is_st ? + freecell_solver_user_next_soft_thread(instance) : + freecell_solver_user_next_hard_thread(instance) + ; + + if (ret) + { + char * errstr; + + errstr = strdup("The maximal number of soft threads has been exceeded\n"); + + *error_string = errstr; + + *last_arg = arg; + + return FCS_CMD_LINE_ERROR_IN_ARG; + } + } + else if ((!strcmp(argv[arg], "-step")) || (!strcmp(argv[arg], "--soft-thread-step"))) + { + arg++; + if (arg == argc) + { + *last_arg = arg-1; + return FCS_CMD_LINE_PARAM_WITH_NO_ARG; + } + + freecell_solver_user_set_soft_thread_step( + instance, + atoi(argv[arg]) + ); + } + else if ((!strcmp(argv[arg], "--reparent-states"))) + { + freecell_solver_user_set_reparent_states( + instance, + 1 + ); + } + else if ((!strcmp(argv[arg], "--calc-real-depth"))) + { + freecell_solver_user_set_calc_real_depth( + instance, + 1); + } + else if ((!strcmp(argv[arg], "--st-name"))) + { + arg++; + if (arg == argc) + { + *last_arg = arg-1; + return FCS_CMD_LINE_PARAM_WITH_NO_ARG; + } + freecell_solver_user_set_soft_thread_name(instance, argv[arg]); + } + else if ((!strcmp(argv[arg], "--prelude"))) + { + arg++; + if (arg == argc) + { + *last_arg = arg-1; + return FCS_CMD_LINE_PARAM_WITH_NO_ARG; + } + freecell_solver_user_set_hard_thread_prelude(instance, argv[arg]); + } + else if ((!strcmp(argv[arg], "-opt-to")) || (!strcmp(argv[arg], "--optimization-tests-order"))) + { + char * fcs_user_errstr; + arg++; + if (arg == argc) + { + *last_arg = arg-1; + return FCS_CMD_LINE_PARAM_WITH_NO_ARG; + } + + ret = freecell_solver_user_set_optimization_scan_tests_order( + instance, + argv[arg], + &fcs_user_errstr + ); + + if (ret != 0) + { + char * errstr = malloc(strlen(fcs_user_errstr)+500); + sprintf( + errstr, + "Error in the optimization scan's tests' order!\n%s\n", + fcs_user_errstr + ); + free(fcs_user_errstr); + + *error_string = errstr; + + *last_arg = arg; + return FCS_CMD_LINE_ERROR_IN_ARG; + } + } + else if ((!strcmp(argv[arg], "--scans-synergy"))) + { + int value; + + arg++; + if (arg == argc) + { + *last_arg = arg-1; + return FCS_CMD_LINE_PARAM_WITH_NO_ARG; + } + + if (!strcmp(argv[arg], "none")) + { + value = 0; + } + else if (!strcmp(argv[arg], "dead-end-marks")) + { + value = 1; + } + else + { + char * errstr; + + errstr = malloc(strlen(argv[arg])+500); + + sprintf(errstr, "Unknown scans' synergy type \"%s\"!\n", argv[arg]); + *last_arg = arg; + *error_string = errstr; + return FCS_CMD_LINE_ERROR_IN_ARG; + } + + freecell_solver_user_set_scans_synergy( + instance, + value + ); + } + else if ((!strcmp(argv[arg], "-ni")) || + (!strcmp(argv[arg], "--next-instance"))) + { + freecell_solver_user_next_instance(instance); + } + else if (!strcmp(argv[arg], "--reset")) + { + freecell_solver_user_reset(instance); + } + else if (!strcmp(argv[arg], "--read-from-file")) + { + arg++; + if (arg == argc) + { + *last_arg = arg-1; + return FCS_CMD_LINE_PARAM_WITH_NO_ARG; + } + if (file_nesting_count == 0) + { + /* do nothing */ + } + else + { + int num_to_skip = 0; + char * s, * buffer; + FILE * f; + long file_len; + int ret; + size_t num_read; + args_man_t * args_man; + + s = argv[arg]; + while(isdigit(*s)) + { + s++; + } + if (*s == ',') + { + num_to_skip = atoi(argv[arg]); + s++; + } + + if (opened_files_dir) + { + char * complete_path; + + complete_path = malloc(strlen(opened_files_dir)+strlen(s)+1); + sprintf(complete_path, "%s%s", opened_files_dir, s); + f = fopen(complete_path, "rt"); + free(complete_path); + } + else + { + /* + * Initialize f to NULL so it will be initialized + * */ + f = NULL; + } + + /* Try to open from the local path */ + if (f == NULL) + { + f = fopen(s, "rt"); + } + + /* If we still could not open it return an error */ + if (f == NULL) + { + char * err_str; + + err_str = malloc(strlen(s)+100); + sprintf(err_str, + "Could not open file \"%s\"!\nQuitting.\n", + s); + + *error_string = err_str; + *last_arg = arg; + + return FCS_CMD_LINE_ERROR_IN_ARG; + } + fseek(f, 0, SEEK_END); + file_len = ftell(f); + buffer=malloc(file_len+1); + if (buffer == NULL) + { + *error_string = strdup("Could not allocate enough memory to parse the file. Quitting.\n"); + fclose(f); + + *last_arg = arg; + + return FCS_CMD_LINE_ERROR_IN_ARG; + } + fseek(f,0,SEEK_SET); + num_read = fread(buffer, 1, file_len, f); + fclose(f); + buffer[num_read] = '\0'; + + args_man = freecell_solver_args_man_alloc(); + ret = freecell_solver_args_man_chop(args_man, buffer); + free(buffer); + if (ret != 0) + { + *error_string = + strdup("Could not parse the file. Quitting\n"); + freecell_solver_args_man_free(args_man); + + *last_arg = arg; + + return FCS_CMD_LINE_ERROR_IN_ARG; + } + + if (num_to_skip >= args_man->argc) + { + /* Do nothing */ + } + else + { + ret = freecell_solver_user_cmd_line_parse_args_with_file_nesting_count( + instance, + args_man->argc - num_to_skip, + args_man->argv + num_to_skip, + 0, + known_parameters, + callback, + callback_context, + error_string, + last_arg, + ((file_nesting_count < 0) ? file_nesting_count : (file_nesting_count-1)), + opened_files_dir + ); + + if (ret == FCS_CMD_LINE_UNRECOGNIZED_OPTION) + { + /* Do nothing - continue */ + } + else if (ret != FCS_CMD_LINE_OK) + { + freecell_solver_args_man_free(args_man); + return ret; + } + } + freecell_solver_args_man_free(args_man); + } + } + else if ((!strcmp(argv[arg], "-l")) || (!strcmp(argv[arg], "--load-config"))) + { + arg++; + if (arg == argc) + { + *last_arg = arg-1; + return FCS_CMD_LINE_PARAM_WITH_NO_ARG; + } + { + int status; + args_man_t * preset_args = 0; + char * dir = NULL; + + status = read_preset(argv[arg], &preset_args, &dir, NULL); + if (status != 0) + { + char * err_str; + err_str = malloc(strlen(argv[arg]) + 100); + sprintf(err_str, "Unable to load the \"%s\" configuration!\n", argv[arg]); + *error_string = err_str; + + *last_arg = arg; + + return FCS_CMD_LINE_ERROR_IN_ARG; + } + else + { + ret = freecell_solver_user_cmd_line_parse_args_with_file_nesting_count( + instance, + preset_args->argc, + preset_args->argv, + 0, + known_parameters, + callback, + callback_context, + error_string, + last_arg, + ((file_nesting_count < 0) ? file_nesting_count : (file_nesting_count-1)), + dir ? dir : opened_files_dir + ); + + if (dir) + { + free(dir); + } + freecell_solver_args_man_free(preset_args); + + if (ret == FCS_CMD_LINE_UNRECOGNIZED_OPTION) + { + /* Do nothing - continue */ + } + else if (ret != FCS_CMD_LINE_OK) + { + return ret; + } + } + } + } + else + { + *last_arg = arg; + return FCS_CMD_LINE_UNRECOGNIZED_OPTION; + } + } + + *last_arg = arg; + return FCS_CMD_LINE_OK; +} + +int freecell_solver_user_cmd_line_parse_args( + void * instance, + int argc, + const char * argv[], + int start_arg, + char * * known_parameters, + freecell_solver_user_cmd_line_known_commands_callback_t callback, + void * callback_context, + char * * error_string, + int * last_arg + ) +{ + return freecell_solver_user_cmd_line_parse_args_with_file_nesting_count( + instance, + argc, + argv, + start_arg, + known_parameters, + callback, + callback_context, + error_string, + last_arg, + -1, + NULL + ); +} + diff --git a/kpat/freecell-solver/fcs.h b/kpat/freecell-solver/fcs.h new file mode 100644 index 00000000..43300310 --- /dev/null +++ b/kpat/freecell-solver/fcs.h @@ -0,0 +1,797 @@ +/* + * fcs.h - header file of freecell_solver_instance and of user-level + * functions for Freecell Solver + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#ifndef FC_SOLVE__FCS_H +#define FC_SOLVE__FCS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "fcs_config.h" +#include "state.h" +#include "move.h" +#include "fcs_enums.h" + +#include "rand.h" + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBREDBLACK_TREE) || (defined(INDIRECT_STACK_STATES) && (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE)) + +#include + +#endif + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE) || (defined(INDIRECT_STACK_STATES) && (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE)) + +#include + +#endif + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE) || (defined(INDIRECT_STACK_STATES) && (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE)) + +#include + +/* #define TREE_IMP_PREFIX(func_name) rb_##func_name */ + +#endif + + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_TREE) || (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH) || (defined(INDIRECT_STACK_STATES) && ((FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE) || (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH))) + +#include + +#endif + + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) + +#include "fcs_hash.h" + +#endif + +#ifdef INDIRECT_STACK_STATES +#include "fcs_hash.h" + +#endif + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_DB_FILE) +#include +#include +#include +#endif + +#include "pqueue.h" + +#include "alloc.h" + +/* + * This is a linked list item that is used to implement a queue for the BFS + * scan. + * */ +struct fcs_states_linked_list_item_struct +{ + fcs_state_with_locations_t * s; + struct fcs_states_linked_list_item_struct * next; +}; + +typedef struct fcs_states_linked_list_item_struct fcs_states_linked_list_item_t; + +/* + * Conventions for use of the tests' order flags: + * A test that should be scanned sequentially should have both flags cleared. + * The first test in its random group should have both flags set. All the + * other tests in the group should contain the FLAG_RANDOM flag. + * + * For instance: 123(45)(67)8 translates into: + * 1 , 2, 3, 4|RANDOM|START_RANDOM_GROUP, 5|RANDOM, + * 6|RANDOM_START_RANDOM_GROUP, 7|RANDOM, 8 + * + * */ +enum FCS_TESTS_ORDER_FLAGS +{ + FCS_TEST_ORDER_NO_FLAGS_MASK = 0xFFFFFF, + FCS_TEST_ORDER_FLAG_RANDOM = 0x1000000, + FCS_TEST_ORDER_FLAG_START_RANDOM_GROUP = 0x2000000 +}; + +#ifdef FCS_WITH_TALONS +#define FCS_TESTS_NUM 27 +#else +#define FCS_TESTS_NUM 25 +#endif + +/* + * Declare these structures because they will be used within + * freecell_solver_instance, and they will contain a pointer to it. + * */ +struct freecell_solver_hard_thread_struct; +struct freecell_solver_soft_thread_struct; + +typedef struct freecell_solver_hard_thread_struct freecell_solver_hard_thread_t; + +struct fcs_tests_order_struct +{ + int num; + int * tests; + int max_num; +}; + +typedef struct fcs_tests_order_struct fcs_tests_order_t; + +typedef struct freecell_solver_instance +{ +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT) + /* The sort-margin */ + fcs_state_with_locations_t * indirect_prev_states_margin[PREV_STATES_SORT_MARGIN]; + + /* The number of states in the sort margin */ + int num_prev_states_margin; + + /* The sorted cached states, their number and their maximal size. + * max_num_indirect_prev_states may increase as the + * indirect_prev_states is realloced. + * */ + fcs_state_with_locations_t * * indirect_prev_states; + int num_indirect_prev_states; + int max_num_indirect_prev_states; +#endif + + /* The number of states that were checked by the solving algorithm. + * Badly named, should be renamed to num_iters or num_checked_states */ + int num_times; + + /* + * A move stack that contains the moves leading to the solution. + * + * It is created only after the solution was found by swallowing + * all the stacks of each depth. + * */ + fcs_move_stack_t * solution_moves; + + /* + * Limits for the maximal depth and for the maximal number of checked + * states. max_num_times is useful because it enables the process to + * stop before it consumes too much memory. + * + * max_depth is quite dangerous because it blocks some intermediate moves + * and doesn't allow a program to fully reach its solution. + * + * */ + int max_depth; + int max_num_times; + + /* + * The debug_iter_output variables provide a programmer programmable way + * to debug the algorithm while it is running. This works well for DFS + * and Soft-DFS scans but at present support for A* and BFS is not + * too good, as its hard to tell which state came from which parent state. + * + * debug_iter_output is a flag that indicates whether to use this feature + * at all. + * + * debug_iter_output_func is a pointer to the function that performs the + * debugging. + * + * debug_iter_output_context is a user-specified context for it, that + * may include data that is not included in the instance structure. + * + * This feature is used by the "-s" and "-i" flags of fc-solve-debug. + * */ + int debug_iter_output; + void (*debug_iter_output_func)( + void * context, + int iter_num, + int depth, + void * instance, + fcs_state_with_locations_t * state, + int parent_iter_num + ); + void * debug_iter_output_context; + + /* + * tree is the balanced binary tree that is used to store and index + * the checked states. + * + * */ +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBREDBLACK_TREE) + struct rbtree * tree; +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE) + avl_tree * tree; +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE) + rb_tree * tree; +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_TREE) + GTree * tree; +#endif + + /* + * hash is the hash table that is used to store the previous + * states of the scan. + * */ +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH) + GHashTable * hash; +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) + SFO_hash_t * hash; +#endif + +#if defined(INDIRECT_STACK_STATES) + /* + * The storage mechanism for the stacks assuming INDIRECT_STACK_STATES is + * used. + * */ +#if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH) + SFO_hash_t * stacks_hash; +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE) + avl_tree * stacks_tree; +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE) + rb_tree * stacks_tree; +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE) + struct rbtree * stacks_tree; +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE) + GTree * stacks_tree; +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH) + GHashTable * stacks_hash; +#endif +#endif + + /* + * Storing using Berkeley DB is not operational for some reason so + * pay no attention to it for the while + * */ +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_DB_FILE) + DB * db; +#endif + + /* + * The number of Freecells, Stacks and Foundations present in the game. + * + * freecells_num and stacks_num are variable and may be specified at + * the beginning of the execution of the algorithm. However, there + * is a maximal limit to them which is set in config.h. + * + * decks_num can be 4 or 8 + * */ + int freecells_num; + int stacks_num; + int decks_num; + + /* What two adjacent cards in the same sequence can be: */ + int sequences_are_built_by; + /* Whether an entire sequence can be moved from one place to the + * other regardless of the number of unoccupied Freecells there are. */ + int unlimited_sequence_move; + /* + * With what cards can empty stacks be filled with. + * */ + int empty_stacks_fill; + +#ifdef FCS_WITH_TALONS + /* + * The talon for Gypsy-like games. Since only the position changes from + * state to state. + * We can keep it here. + * + * */ + fcs_card_t * gypsy_talon; + + /* + * The length of the Gypsy talon + * */ + int gypsy_talon_len; + + int talon_type; + + /* The Klondike Talons' Cache */ + SFO_hash_t * talons_hash; + +#endif + + /* A flag that indicates whether to optimize the solution path + at the end of the scan */ + int optimize_solution_path; + + /* This is a place-holder for the initial state */ + fcs_state_with_locations_t * state_copy_ptr; + + /* This is the final state that the scan recommends to the + * interface + * */ + fcs_state_with_locations_t * final_state; + + /* + * This is the number of states in the state collection. + * + * It gives a rough estimate of the memory occupied by the instance. + * */ + int num_states_in_collection; + + /* + * A limit to the above. + * */ + int max_num_states_in_collection; + + int num_hard_threads; + struct freecell_solver_hard_thread_struct * * hard_threads; + + /* + * The next ID to allocate for a soft-thread. + * */ + int next_soft_thread_id; + + /* + * A persistent counters that os used to iterate over the + * threads one by one + * */ + int ht_idx; + + /* + * This is the master tests order. It is used to initialize all + * the new Soft-Threads. + * */ + fcs_tests_order_t instance_tests_order; + + /* + * This is the hard-thread used for the optimization scan. + * */ + struct freecell_solver_hard_thread_struct * optimization_thread; + + /* + * A counter that determines how many of the hard threads that belong + * to this hard thread have already finished. If it becomes num_hard_threads + * the instance terminates. + * */ + int num_hard_threads_finished; + + /* + * A flag that indicates whether or not to explicitly calculate + * the depth of a state that was reached. + * */ + int calc_real_depth; + + /* + * The tests order for the optimization scan as specified by the user. + * */ + int opt_tests_order_set; + + fcs_tests_order_t opt_tests_order; + + /* + * This flag indicates whether scans should or should not reparent the + * states their encounter to a lower depth in the depth tree + * */ + int to_reparent_states; + + /* + * This variable determines how the scans cooperate with each other. + * + * A value of 0 indicates that they don't and only share the same + * states collection. + * + * A value of 1 indicates that they mark states as dead-end, + * which may help or hinder other scans. + * */ + int scans_synergy; + +} freecell_solver_instance_t; + + + + +/***************************************************/ + + +struct fcs_prelude_item_struct +{ + int scan_idx; + int quota; +}; + +typedef struct fcs_prelude_item_struct fcs_prelude_item_t; + + +struct freecell_solver_hard_thread_struct +{ + freecell_solver_instance_t * instance; + + int num_soft_threads; + struct freecell_solver_soft_thread_struct * * soft_threads; + + /* + * The State Packs variables are used by all the state cache + * management routines. A pack stores as many states as can fit + * in a 64KB segment, and those variables manage an array of + * such packs. + * + * Such allocation is possible, because at the worst situation + * the last state is released. + * */ + fcs_state_with_locations_t * * state_packs; + int max_num_state_packs; + int num_state_packs; + int num_states_in_last_pack; + int state_pack_len; + + /* + * The hard thread count of how many states he checked himself. The + * instance num_times can be confusing because other threads modify it too. + * + * Thus, the soft thread switching should be done based on this variable + * */ + int num_times; + + /* + * The maximal limit for this variable. + * */ + int max_num_times; + + /* + * The Hard-Thread's global limit for the number of iterations + * to process + * */ + int ht_max_num_times; + + int num_times_step; + + /* + * This is the number of iterations that still have to be done for + * soft_threads[st_idx]. It is reset to (st_idx+1)->num_times_step + * when st_idx is incremented. + * */ + int num_times_left_for_soft_thread; + + /* + * These variables are used to compute the MD5 checksum of a state + * that is about to be checked. I decided to make them globals so + * they won't have to be re-allocated and freed all the time. + * + * Notice that it is only used with my internal hash implementation + * as GLib requires a dedicated hash function, which cannot + * access the instance. + * + * */ + + /* + * The index for the soft-thread that is currently processed + * */ + int st_idx; + + /* + * A counter that determines how many of the soft threads that belong + * to this hard thread have already finished. If it becomes num_soft_threads + * this thread is skipped. + * */ + int num_soft_threads_finished; + +#ifdef INDIRECT_STACK_STATES + /* + * This is the mechanism used to allocate memory for the stacks. + * */ + fcs_compact_allocator_t * stacks_allocator; +#endif + + /* + * This is a compact memory allocator for the move stacks associated + * with the states in the states collection. + * */ + fcs_compact_allocator_t * move_stacks_allocator; + + /* + * This is a move stack that is used and re-used by the + * tests functions of this hard thread + * */ + fcs_move_stack_t * reusable_move_stack; + +#ifdef INDIRECT_STACK_STATES + /* + * This is a buffer used to temporarily store the stacks of the duplicated + * state. + * */ + fcs_card_t indirect_stacks_buffer[MAX_NUM_STACKS << 7]; +#else + fcs_card_t indirect_stacks_buffer[1]; +#endif + + char * prelude_as_string; + + int prelude_num_items; + int prelude_idx; + fcs_prelude_item_t * prelude; + +}; + + + + + +/********************************************/ + + + + + + + + +struct fcs_soft_dfs_stack_item_struct +{ + fcs_state_with_locations_t * state; + fcs_derived_states_list_t derived_states_list; + int current_state_index; + int test_index; + int num_freestacks; + int num_freecells; + int derived_states_random_indexes_max_size; + int * derived_states_random_indexes; +}; + +typedef struct fcs_soft_dfs_stack_item_struct fcs_soft_dfs_stack_item_t; + +struct freecell_solver_soft_thread_struct +{ + freecell_solver_hard_thread_t * hard_thread; + + /* + * The ID of the soft thread inside the instance. + * Used for the state-specific flags. + * */ + int id; + + /* + * The tests' order indicates which tests (i.e: kinds of multi-moves) to + * do at what order. This is most relevant to DFS and Soft-DFS. + * + * tests_order_num is the number of tests in the test's order. Notice + * that it can be lower than FCS_TESTS_NUM, thus enabling several tests + * to be removed completely. + * */ + fcs_tests_order_t tests_order; + + + /* + * The (temporary) max depth of the Soft-DFS scans) + * */ + int dfs_max_depth; + /* + * The method (i.e: DFS, Soft-DFS, BFS or A*) that is used by this + * instance. + * + * */ + int method; + + /* + * A place-holder for the original method of the scan in case + * it is replaced by FCS_METHOD_OPTIMIZE + * + * */ + int orig_method; + + /* + * A linked list that serves as the queue for the BFS scan. + * */ + fcs_states_linked_list_item_t * bfs_queue; + /* + * The last item in the linked list, so new items can be added at it, + * thus making it a queue. + * */ + fcs_states_linked_list_item_t * bfs_queue_last_item; + + /* + * The priority queue of the A* scan */ + PQUEUE * a_star_pqueue; + double a_star_initial_cards_under_sequences; + + /* + * The A* weights of the different A* tests. Those weights determine the + * commulative weight of the state. + * + * */ + double a_star_weights[5]; + + /* + * The first state to be checked by the scan. It is a kind of bootstrap + * for the algorithm. + * */ + fcs_state_with_locations_t * first_state_to_check; + + /* + * These are stacks used by the Soft-DFS for various uses. + * + * states_to_check[i] - an array of states to be checked next. Not all + * of them will be checked because it is possible that future states + * already visited them. + * + * states_to_check_move_stacks[i] - an array of move stacks that lead + * to those states. + * + * num_states_to_check[i] - the size of states_to_check[i] + * + * max_num_states_to_check[i] - the limit of pointers that can be + * placed in states_to_check[i] without resizing. + * + * current_state_indexes[i] - the index of the last checked state + * in depth i. + * + * test_indexes[i] - the index of the test that was last performed. + * FCS performs each test separately, so states_to_check[i] and + * friends will not be overpopulated. + * + * num_freestacks[i] - the number of unoccpied stacks that correspond + * to solution_states[i]. + * + * num_freecells[i] - ditto for the freecells. + * + * */ + + fcs_soft_dfs_stack_item_t * soft_dfs_info; + + /* The depth of the DFS stacks */ + int num_solution_states; + + /* + * A pseudo-random number generator for use in the random-DFS scan + * */ + fcs_rand_t * rand_gen; + + /* + * The initial seed of this random number generator + * */ + unsigned int rand_seed; + + + /* + * A flag that indicates if this soft thread have already been + * initialized. + * */ + int initialized; + + /* + * The number of iterations with which to process this scan + * */ + int num_times_step; + + /* + * A flag that indicates if this scan contains all the tests that + * are accessible to all the other scans + * */ + int is_a_complete_scan; + + /* + * A flag that indicates if this scan has completed a scan. Used by + * solve_instance() to skip to the next scan. + * */ + int is_finished; + + /* + * A malloced string that serves as an identification for the user. + * */ + char * name; +}; + +typedef struct freecell_solver_soft_thread_struct freecell_solver_soft_thread_t; + + +#define FCS_SOFT_DFS_STATES_TO_CHECK_GROW_BY 32 + +/* + * An enum that specifies the meaning of each A* weight. + * */ +#define FCS_A_STAR_WEIGHT_CARDS_OUT 0 +#define FCS_A_STAR_WEIGHT_MAX_SEQUENCE_MOVE 1 +#define FCS_A_STAR_WEIGHT_CARDS_UNDER_SEQUENCES 2 +#define FCS_A_STAR_WEIGHT_SEQS_OVER_RENEGADE_CARDS 3 +#define FCS_A_STAR_WEIGHT_DEPTH 4 + +freecell_solver_instance_t * freecell_solver_alloc_instance(void); + +extern void freecell_solver_init_instance( + freecell_solver_instance_t * instance + ); + +extern void freecell_solver_free_instance( + freecell_solver_instance_t * instance + ); + +extern void freecell_solver_finish_instance( + freecell_solver_instance_t * instance + ); + +extern int freecell_solver_solve_instance( + freecell_solver_instance_t * instance, + fcs_state_with_locations_t * init_state + ); + +extern int freecell_solver_resume_instance( + freecell_solver_instance_t * instance + ); + +extern void freecell_solver_unresume_instance( + freecell_solver_instance_t * instance + ); + +extern freecell_solver_soft_thread_t * freecell_solver_instance_get_soft_thread( + freecell_solver_instance_t * instance, + int ht_idx, + int st_idx + ); + +extern freecell_solver_soft_thread_t * freecell_solver_new_soft_thread( + freecell_solver_soft_thread_t * soft_thread + ); + +extern freecell_solver_soft_thread_t * freecell_solver_new_hard_thread( + freecell_solver_instance_t * instance + ); + +extern int freecell_solver_hard_dfs_solve_for_state( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int depth, + int ignore_osins + ); + +extern int freecell_solver_soft_dfs_solve( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations_orig + ); + +extern int freecell_solver_random_dfs_solve( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations_orig + ); + + +extern void freecell_solver_a_star_initialize_rater( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations + ); + +extern int freecell_solver_a_star_or_bfs_do_solve_or_resume( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations_orig, + int resume + ); + +extern int freecell_solver_hard_dfs_resume_solution( + freecell_solver_soft_thread_t * soft_thread, + int depth + ); + +extern int freecell_solver_soft_dfs_resume_solution( + freecell_solver_soft_thread_t * soft_thread + ); + +extern int freecell_solver_random_dfs_resume_solution( + freecell_solver_soft_thread_t * soft_thread + ); + + +extern int freecell_solver_a_star_or_bfs_solve( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations_orig + ); + +extern int freecell_solver_a_star_or_bfs_resume_solution( + freecell_solver_soft_thread_t * soft_thread + ); + +extern int freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations_orig, + int resume, + int to_randomize + ); + +extern void freecell_solver_recycle_instance( + freecell_solver_instance_t * instance + ); + +#ifdef __cplusplus +} +#endif + +#endif /* FC_SOLVE__FCS_H */ diff --git a/kpat/freecell-solver/fcs_cl.h b/kpat/freecell-solver/fcs_cl.h new file mode 100644 index 00000000..e739c98e --- /dev/null +++ b/kpat/freecell-solver/fcs_cl.h @@ -0,0 +1,65 @@ + +#ifndef FC_SOLVE__FCS_CL_H +#define FC_SOLVE__FCS_CL_H + +#include "fcs_user.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*freecell_solver_user_cmd_line_known_commands_callback_t) + ( + void * instance, + int argc, + char * argv[], + int arg_index, + int * num_to_skip, + int * ret, + void * context + ); + +enum FCS_CMD_LINE_CALLBACK_RET_VALUES +{ + FCS_CMD_LINE_OK, + FCS_CMD_LINE_SKIP, + FCS_CMD_LINE_STOP, + FCS_CMD_LINE_UNRECOGNIZED_OPTION, + FCS_CMD_LINE_PARAM_WITH_NO_ARG, + FCS_CMD_LINE_ERROR_IN_ARG, + + FCS_CMD_LINE_USER = 0x10000 +}; + +extern int freecell_solver_user_cmd_line_parse_args( + void * instance, + int argc, + const char * argv[], + int start_arg, + char * * known_parameters, + freecell_solver_user_cmd_line_known_commands_callback_t callback, + void * callback_context, + char * * error_string, + int * last_arg + ); + +extern int freecell_solver_user_cmd_line_parse_args_with_file_nesting_count( + void * instance, + int argc, + const char * argv[], + int start_arg, + char * * known_parameters, + freecell_solver_user_cmd_line_known_commands_callback_t callback, + void * callback_context, + char * * error_string, + int * last_arg, + int file_nesting_count, + char * opened_files_dir + ); + + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef FC_SOLVE__FCS_CL_H */ diff --git a/kpat/freecell-solver/fcs_config.h b/kpat/freecell-solver/fcs_config.h new file mode 100644 index 00000000..8a25205d --- /dev/null +++ b/kpat/freecell-solver/fcs_config.h @@ -0,0 +1,95 @@ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ +/* + config.h - Configuration file for Freecell Solver + + Written by Shlomi Fish, 2000 + + This file is distributed under the public domain. + (It is not copyrighted). +*/ + +#ifndef FC_SOLVE__CONFIG_H +#define FC_SOLVE__CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* #undef DEBUG_STATES */ +/* #undef COMPACT_STATES */ +#define INDIRECT_STACK_STATES 1 + +/* #undef CARD_DEBUG_PRES */ + +/* + * Define this macro if the C compiler supports the keyword inline or + * a similar keyword that was found by Autoconf (and defined as inline). + * */ +#define HAVE_C_INLINE 1 + + +/* + The sort margin size for the previous states array. +*/ +#define PREV_STATES_SORT_MARGIN 32 +/* + The amount prev_states grow by each time it each resized. + Should be greater than 0 and in order for the program to be + efficient, should be much bigger than + PREV_STATES_SORT_MARGIN. +*/ +#define PREV_STATES_GROW_BY 128 + +/* + The amount the pack pointers array grows by. Shouldn't be too high + because it doesn't happen too often. +*/ +#define IA_STATE_PACKS_GROW_BY 32 + +/* + * The maximal number of Freecells. For efficiency's sake it should be a + * multiple of 4. + * */ + +#define MAX_NUM_FREECELLS 4 + +/* + * The maximal number of Stacks. For efficiency's sake it should be a + * multiple of 4. + * */ + +#define MAX_NUM_STACKS 10 +/* + * The maximal number of initial cards that can be found in a stack. + * */ +#define MAX_NUM_INITIAL_CARDS_IN_A_STACK 8 + +#define MAX_NUM_DECKS 2 + + +#define FCS_STATE_STORAGE_INDIRECT 0 +#define FCS_STATE_STORAGE_INTERNAL_HASH 1 +#define FCS_STATE_STORAGE_LIBAVL_AVL_TREE 2 +#define FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE 3 +#define FCS_STATE_STORAGE_LIBREDBLACK_TREE 4 +#define FCS_STATE_STORAGE_GLIB_TREE 5 +#define FCS_STATE_STORAGE_GLIB_HASH 6 +#define FCS_STATE_STORAGE_DB_FILE 7 + +#define FCS_STACK_STORAGE_INTERNAL_HASH 0 +#define FCS_STACK_STORAGE_LIBAVL_AVL_TREE 1 +#define FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE 2 +#define FCS_STACK_STORAGE_LIBREDBLACK_TREE 3 +#define FCS_STACK_STORAGE_GLIB_TREE 4 +#define FCS_STACK_STORAGE_GLIB_HASH 5 + +#define FCS_STATE_STORAGE FCS_STATE_STORAGE_INTERNAL_HASH +#define FCS_STACK_STORAGE FCS_STACK_STORAGE_INTERNAL_HASH + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/kpat/freecell-solver/fcs_dm.c b/kpat/freecell-solver/fcs_dm.c new file mode 100644 index 00000000..9fd8c9a8 --- /dev/null +++ b/kpat/freecell-solver/fcs_dm.c @@ -0,0 +1,146 @@ +/* + fcs_dm.c - Freecell Solver's data management routines. + + Written by Shlomi Fish, 2000 + + This file is distributed under the public domain. + (It's not copyrighted) +*/ + +#include +#include + +#include "fcs_dm.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +/* + freecell_solver_bsearch - an improved binary search function. Highlights: + + * The comparison function accepts a common context argument that + is passed to SFO_bsearch. + * If the item was not found the function returns the place in which + it should be placed, while setting *found to 0. If it was found + (*found) is set to 1. +*/ +void * freecell_solver_bsearch +( + void * key, + void * void_array, + size_t len, + size_t width, + int (* compare)(const void *, const void *, void *), + void * context, + int * found +) +{ + int low = 0; + int high = len-1; + int mid; + int result; + + char * array = void_array; + + while (low <= high) + { + mid = ((low+high)>>1); + + result = compare(key, (void*)(array+mid*width), context); + + if (result < 0) + { + high = mid-1; + } + else if (result > 0) + { + low = mid+1; + } + else + { + *found = 1; + return (void*)(array+mid*width); + } + } + + *found = 0; + return ((void*)(array+(high+1)*width)); +} + + + +/* + freecell_solver_merge_large_and_small_sorted_array - merges a large sorted + array with a small sorted array. The arrays could be of any length + whatsoever, but it works faster if the first is significantly bigger + than the second. + + This function assumes that big_array is allocated with enough + space to hold the extra elements. + + The array should be distinct or else there would be unexpected + results. +*/ +int freecell_solver_merge_large_and_small_sorted_arrays +( + void * void_big_array, + size_t size_big_array, + void * void_small_array, + size_t size_small_array, + size_t width, + int (*compare) (const void *, const void *, void *), + void * context +) +{ + int item_to_move, num_big_items_moved, pos; + char * pos_ptr; + char * big_array; + char * small_array; + int found; + int start_offset, end_offset; + + big_array = (char*)void_big_array; + small_array = (char*)void_small_array; + + num_big_items_moved = 0; + + for(item_to_move = size_small_array-1 ; item_to_move>=0; item_to_move--) + { + pos_ptr = freecell_solver_bsearch ( + small_array+item_to_move*width, + big_array, + size_big_array-num_big_items_moved, + width, + compare, + context, + &found + ); + + pos = (pos_ptr-big_array)/width; + + end_offset = size_big_array + size_small_array - + num_big_items_moved - + (size_small_array-item_to_move-1); + + start_offset = end_offset + pos - + (size_big_array - num_big_items_moved); + + memmove( + big_array+start_offset*width, + big_array+pos*width, + (end_offset-start_offset)*width + ); + + memcpy( + big_array+(start_offset-1)*width, + small_array+item_to_move*width, + width + ); + + num_big_items_moved += (end_offset - start_offset); + } + + return 1; +} + diff --git a/kpat/freecell-solver/fcs_dm.h b/kpat/freecell-solver/fcs_dm.h new file mode 100644 index 00000000..2cb6dc82 --- /dev/null +++ b/kpat/freecell-solver/fcs_dm.h @@ -0,0 +1,49 @@ +/* + fcs_dm.h - Header file for Freecell Solver's Data Management + routines. + + For more information consult fcs_dm.c. + + Written by Shlomi Fish, 2000 + This file is distributed under the public domain. + (It is not copyrighted) +*/ + +#ifndef FC_SOLVE__FCS_DATA_H +#define FC_SOLVE__FCS_DATA_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + + +void * freecell_solver_bsearch +( + void * key, + void * void_array, + size_t len, + size_t width, + int (* compare)(const void *, const void *, void *), + void * context, + int * found +); + +int freecell_solver_merge_large_and_small_sorted_arrays +( + void * void_big_array, + size_t size_big_array, + void * void_small_array, + size_t size_small_array, + size_t width, + int (*compare) (const void *, const void *, void *), + void * context +); + +#ifdef __cplusplus +} +#endif + +#endif /* FC_SOLVE__FCS_DATA_H */ + diff --git a/kpat/freecell-solver/fcs_enums.h b/kpat/freecell-solver/fcs_enums.h new file mode 100644 index 00000000..071383c9 --- /dev/null +++ b/kpat/freecell-solver/fcs_enums.h @@ -0,0 +1,77 @@ +/* + * fcs_enums.h - header file for various Freecell Solver Enumertaions. Common + * to the main program headers and to the library headers. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#ifndef FC_SOLVE__FCS_ENUMS_H +#define FC_SOLVE__FCS_ENUMS_H + +#ifdef __cplusplus +extern "C" { +#endif + +enum FCS_EMPTY_STACKS_FILL_T +{ + FCS_ES_FILLED_BY_ANY_CARD, + FCS_ES_FILLED_BY_KINGS_ONLY, + FCS_ES_FILLED_BY_NONE +}; + +enum FCS_SEQUENCES_ARE_BUILT_BY_T +{ + FCS_SEQ_BUILT_BY_ALTERNATE_COLOR, + FCS_SEQ_BUILT_BY_SUIT, + FCS_SEQ_BUILT_BY_RANK +}; + +enum FCS_TALON_T +{ + FCS_TALON_NONE, + FCS_TALON_GYPSY, + FCS_TALON_KLONDIKE +}; + +enum freecell_solver_state_solving_return_codes +{ + FCS_STATE_WAS_SOLVED, + FCS_STATE_IS_NOT_SOLVEABLE, + FCS_STATE_ALREADY_EXISTS, + FCS_STATE_EXCEEDS_MAX_NUM_TIMES, + FCS_STATE_BEGIN_SUSPEND_PROCESS, + FCS_STATE_SUSPEND_PROCESS, + FCS_STATE_EXCEEDS_MAX_DEPTH, + FCS_STATE_ORIGINAL_STATE_IS_NOT_SOLVEABLE, + FCS_STATE_INVALID_STATE, + FCS_STATE_NOT_BEGAN_YET, + FCS_STATE_DOES_NOT_EXIST, + FCS_STATE_OPTIMIZED +}; + +enum fcs_presets_return_codes +{ + FCS_PRESET_CODE_OK, + FCS_PRESET_CODE_NOT_FOUND, + FCS_PRESET_CODE_FREECELLS_EXCEED_MAX, + FCS_PRESET_CODE_STACKS_EXCEED_MAX, + FCS_PRESET_CODE_DECKS_EXCEED_MAX +}; + + +#define FCS_METHOD_NONE -1 +#define FCS_METHOD_HARD_DFS 0 +#define FCS_METHOD_SOFT_DFS 1 +#define FCS_METHOD_BFS 2 +#define FCS_METHOD_A_STAR 3 +#define FCS_METHOD_OPTIMIZE 4 +#define FCS_METHOD_RANDOM_DFS 5 + + +#ifdef __cplusplus +} +#endif + +#endif /* FC_SOLVE__FCS_ENUMS_H */ diff --git a/kpat/freecell-solver/fcs_hash.c b/kpat/freecell-solver/fcs_hash.c new file mode 100644 index 00000000..fde7a03f --- /dev/null +++ b/kpat/freecell-solver/fcs_hash.c @@ -0,0 +1,291 @@ +/* + * fcs_hash.c - an implementation of a simplistic (keys only) hash. This + * hash uses chaining and re-hashing and was found to be very fast. Not all + * of the functions of the hash ADT are implemented, but it is useful enough + * for Freecell Solver. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#include "fcs_config.h" + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) || (defined(INDIRECT_STACK_STATES) && (FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH)) + +#include +#include + +#define DEBUG + +#ifdef DEBUG +#include +#endif + +#include "fcs_hash.h" + +#include "alloc.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +static void SFO_hash_rehash(SFO_hash_t * hash); + + + +SFO_hash_t * freecell_solver_hash_init( + SFO_hash_value_t wanted_size, + int (*compare_function)(const void * key1, const void * key2, void * context), + void * context + ) +{ + int size; + SFO_hash_t * hash; + + /* Find a prime number that is greater than the initial wanted size */ + size = 256; + while (size < wanted_size) + { + size <<= 1; + } + + hash = (SFO_hash_t *)malloc(sizeof(SFO_hash_t)); + + hash->size = size; + hash->size_bitmask = size-1; + + hash->num_elems = 0; + + /* Allocate a table of size entries */ + hash->entries = (SFO_hash_symlink_t *)malloc( + sizeof(SFO_hash_symlink_t) * size + ); + + hash->compare_function = compare_function; + hash->context = context; + + /* Initialize all the cells of the hash table to NULL, which indicate + that the cork of the linked list is right at the start */ + memset(hash->entries, 0, sizeof(SFO_hash_symlink_t)*size); + + hash->allocator = freecell_solver_compact_allocator_new(); + + return hash; +} + +void * freecell_solver_hash_insert( + SFO_hash_t * hash, + void * key, + SFO_hash_value_t hash_value, + SFO_hash_value_t secondary_hash_value, + int optimize_for_caching + ) +{ + int place; + SFO_hash_symlink_t * list; + SFO_hash_symlink_item_t * item, * last_item; + + /* Get the index of the appropriate chain in the hash table */ + place = hash_value & (hash->size_bitmask); + + list = &(hash->entries[place]); + /* If first_item is non-existent */ + if (list->first_item == NULL) + { + /* Allocate a first item with that key */ + fcs_compact_alloc_into_var(item, hash->allocator, SFO_hash_symlink_item_t); + list->first_item = item; + item->next = NULL; + item->key = key; + item->hash_value = hash_value; + item->secondary_hash_value = secondary_hash_value; + + goto rehash_check; + } + + /* Initialize item to the chain's first_item */ + item = list->first_item; + last_item = NULL; + + while (item != NULL) + { + /* + We first compare the hash values, because it is faster than + comparing the entire data structure. + + */ + if ( + (item->hash_value == hash_value) && + (item->secondary_hash_value == secondary_hash_value) && + (!(hash->compare_function(item->key, key, hash->context))) + ) + { + if (optimize_for_caching) + { + /* + * Place the item in the beginning of the chain. + * If last_item == NULL it is already the first item so leave + * it alone + * */ + if (last_item != NULL) + { + last_item->next = item->next; + item->next = list->first_item; + list->first_item = item; + } + } + return item->key; + } + /* Cache the item before the current in last_item */ + last_item = item; + /* Move to the next item */ + item = item->next; + } + + if (optimize_for_caching) + { + /* Put the new element at the beginning of the list */ + fcs_compact_alloc_into_var(item, hash->allocator, SFO_hash_symlink_item_t); + item->next = list->first_item; + item->key = key; + item->hash_value = hash_value; + list->first_item = item; + item->secondary_hash_value = secondary_hash_value; + } + else + { + /* Put the new element at the end of the list */ + fcs_compact_alloc_into_var(item, hash->allocator, SFO_hash_symlink_item_t); + last_item->next = item; + item->next = NULL; + item->key = key; + item->hash_value = hash_value; + item->secondary_hash_value = secondary_hash_value; + } + +rehash_check: + + hash->num_elems++; + + if (hash->num_elems > ((hash->size*3)>>2)) + { + SFO_hash_rehash(hash); + } + + return NULL; +} + +void freecell_solver_hash_free_with_callback( + SFO_hash_t * hash, + void (*function_ptr)(void * key, void * context) + ) +{ + int i; + SFO_hash_symlink_item_t * item, * next_item; + + for(i=0;isize;i++) + { + item = hash->entries[i].first_item; + while (item != NULL) + { + function_ptr(item->key, hash->context); + next_item = item->next; + + item = next_item; + } + } + + freecell_solver_hash_free(hash); +} + +void freecell_solver_hash_free( + SFO_hash_t * hash + ) +{ + freecell_solver_compact_allocator_finish(hash->allocator); + + free(hash->entries); + + free(hash); +} + + +/* + This function "rehashes" a hash. I.e: it increases the size of its + hash table, allowing for smaller chains, and faster lookup. + + */ +static void SFO_hash_rehash( + SFO_hash_t * hash + ) +{ + int old_size, new_size, new_size_bitmask; + int i; +#if 0 + SFO_hash_t * new_hash; +#endif + SFO_hash_symlink_item_t * item, * next_item; + int place; + SFO_hash_symlink_t * new_entries; + + old_size = hash->size; + +#if 0 + /* Allocate a new hash with hash_init() */ + new_hash = freecell_solver_hash_init_proto( + old_size * 2, + hash->compare_function, + hash->context + ); +#endif + + old_size = hash->size; + new_size = old_size << 1; + new_size_bitmask = new_size - 1; + + new_entries = calloc(new_size, sizeof(SFO_hash_symlink_t)); + + /* Copy the items to the new hash while not allocating them again */ + for(i=0;ientries[i].first_item; + /* traverse the chain item by item */ + while(item != NULL) + { + /* The place in the new hash table */ + place = item->hash_value & new_size_bitmask; + + /* Store the next item in the linked list in a safe place, + so we can retrieve it after the assignment */ + next_item = item->next; + /* It is placed in front of the first element in the chain, + so it should link to it */ + item->next = new_entries[place].first_item; + + /* Make it the first item in its chain */ + new_entries[place].first_item = item; + + /* Move to the next item this one. */ + item = next_item; + } + }; + + /* Free the entries of the old hash */ + free(hash->entries); + + /* Copy the new hash to the old one */ +#if 0 + *hash = *new_hash; +#endif + hash->entries = new_entries; + hash->size = new_size; + hash->size_bitmask = new_size_bitmask; +} + +#else + +/* ANSI C doesn't allow empty compilation */ +static void freecell_solver_hash_c_dummy(); + +#endif /* (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) || defined(INDIRECT_STACK_STATES) */ diff --git a/kpat/freecell-solver/fcs_hash.h b/kpat/freecell-solver/fcs_hash.h new file mode 100644 index 00000000..fbe6c78c --- /dev/null +++ b/kpat/freecell-solver/fcs_hash.h @@ -0,0 +1,102 @@ +/* + * fcs_hash.h - header file of Freecell Solver's internal hash implementation. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#ifndef FC_SOLVE__FCS_HASH_H +#define FC_SOLVE__FCS_HASH_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "alloc.h" +#include "lookup2.h" + +typedef int SFO_hash_value_t; + +struct SFO_hash_symlink_item_struct +{ + /* A pointer to the data structure that is to be collected */ + void * key; + /* We also store the hash value corresponding to this key for faster + comparisons */ + SFO_hash_value_t hash_value; + /* + * We also store a secondary hash value, which is not used for indexing, + * but is used to speed up comparison. + * */ + SFO_hash_value_t secondary_hash_value; + /* The next item in the list */ + struct SFO_hash_symlink_item_struct * next; +}; + +typedef struct SFO_hash_symlink_item_struct SFO_hash_symlink_item_t; + +struct SFO_hash_symlink_struct +{ + SFO_hash_symlink_item_t * first_item; +}; + +typedef struct SFO_hash_symlink_struct SFO_hash_symlink_t; + +struct SFO_hash_struct +{ + /* The vector of the hash table itself */ + SFO_hash_symlink_t * entries; + /* A comparison function that can be used for comparing two keys + in the collection */ + int (*compare_function)(const void * key1, const void * key2, void * context); + /* The size of the hash table */ + int size; + + /* A bit mask that extract the lowest bits out of the hash value */ + int size_bitmask; + /* The number of elements stored inside the hash */ + int num_elems; + /* A context to pass to the comparison function */ + void * context; + + fcs_compact_allocator_t * allocator; +}; + +typedef struct SFO_hash_struct SFO_hash_t; + + +SFO_hash_t * freecell_solver_hash_init( + SFO_hash_value_t wanted_size, + int (*compare_function)(const void * key1, const void * key2, void * context), + void * context + ); + +void * freecell_solver_hash_insert( + SFO_hash_t * hash, + void * key, + SFO_hash_value_t hash_value, + SFO_hash_value_t secondary_hash_value, + int optimize_for_caching + ); + + +void freecell_solver_hash_free( + SFO_hash_t * hash + ); + +void freecell_solver_hash_free_with_callback( + SFO_hash_t * hash, + void (*function_ptr)(void * key, void * context) + ); + + +#ifdef __cplusplus +} +#endif + +#endif /* FC_SOLVE__FCS_HASH_H */ + + + + diff --git a/kpat/freecell-solver/fcs_isa.c b/kpat/freecell-solver/fcs_isa.c new file mode 100644 index 00000000..0a6ffe51 --- /dev/null +++ b/kpat/freecell-solver/fcs_isa.c @@ -0,0 +1,88 @@ +/* fcs_isa.c - Freecell Solver Indirect State Allocation Routines + + Written by Shlomi Fish, 2000 + This file is distributed under the public domain. +*/ + +#include +#include + +#include "fcs_config.h" + + +#include "state.h" +#include "fcs.h" + +#include "fcs_isa.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +void freecell_solver_state_ia_init(freecell_solver_hard_thread_t * hard_thread) +{ + hard_thread->max_num_state_packs = IA_STATE_PACKS_GROW_BY; + hard_thread->state_packs = (fcs_state_with_locations_t * *)malloc(sizeof(fcs_state_with_locations_t *) * hard_thread->max_num_state_packs); + hard_thread->num_state_packs = 1; + /* + * All the states should fit in one 64KB segment. Now, we allocate as + * many states as possible, minus one, so we would be certain that there + * would be place for the overhead required by the malloc algorithm. + * */ + hard_thread->state_pack_len = (0x010000 / sizeof(fcs_state_with_locations_t)) - 1; + hard_thread->state_packs[0] = malloc(hard_thread->state_pack_len*sizeof(fcs_state_with_locations_t)); + + hard_thread->num_states_in_last_pack = 0; +} + +#if 0 +fcs_state_with_locations_t * fcs_state_ia_alloc(freecell_solver_hard_thread_t * hard_thread) +{ + if (hard_thread->num_states_in_last_pack == hard_thread->state_pack_len) + { + if (hard_thread->num_state_packs == hard_thread->max_num_state_packs) + { + hard_thread->max_num_state_packs += IA_STATE_PACKS_GROW_BY; + hard_thread->state_packs = (fcs_state_with_locations_t * *)realloc(hard_thread->state_packs, sizeof(fcs_state_with_locations_t *) * hard_thread->max_num_state_packs); + } + hard_thread->state_packs[hard_thread->num_state_packs] = malloc(hard_thread->state_pack_len * sizeof(fcs_state_with_locations_t)); + hard_thread->num_state_packs++; + hard_thread->num_states_in_last_pack = 0; + } + return &(hard_thread->state_packs[hard_thread->num_state_packs-1][hard_thread->num_states_in_last_pack++]); +} +#endif + +#if 0 +void fcs_state_ia_release(freecell_solver_hard_thread_t * hard_thread) +{ + hard_thread->num_states_in_last_pack--; +} +#endif + +void freecell_solver_state_ia_finish(freecell_solver_hard_thread_t * hard_thread) +{ + int a; + for(a=0;anum_state_packs;a++) + { + free(hard_thread->state_packs[a]); + } + free(hard_thread->state_packs); + hard_thread->state_packs = NULL; +} + +void freecell_solver_state_ia_foreach(freecell_solver_hard_thread_t * hard_thread, void (*ptr_function)(fcs_state_with_locations_t *, void *), void * context) +{ + int p,s; + for(p=0;pnum_state_packs-1;p++) + { + for(s=0 ; s < hard_thread->state_pack_len ; s++) + { + ptr_function(&(hard_thread->state_packs[p][s]), context); + } + } + for(s=0; s < hard_thread->num_states_in_last_pack ; s++) + { + ptr_function(&(hard_thread->state_packs[p][s]), context); + } +} diff --git a/kpat/freecell-solver/fcs_isa.h b/kpat/freecell-solver/fcs_isa.h new file mode 100644 index 00000000..30a9a982 --- /dev/null +++ b/kpat/freecell-solver/fcs_isa.h @@ -0,0 +1,56 @@ +#ifndef FC_SOLVE__FCS_ISA_H +#define FC_SOLVE__FCS_ISA_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "state.h" +#include "fcs.h" + +extern void freecell_solver_state_ia_init(freecell_solver_hard_thread_t * hard_thread); +#if 0 +extern fcs_state_with_locations_t * fcs_state_ia_alloc(freecell_solver_instance_t * instance); +#else + +#define fcs_state_ia_alloc_into_var(ret, instance) \ +{ \ + if ((instance)->num_states_in_last_pack == (instance)->state_pack_len) \ + { \ + if (instance->num_state_packs == instance->max_num_state_packs) \ + { \ + instance->max_num_state_packs += IA_STATE_PACKS_GROW_BY; \ + instance->state_packs = (fcs_state_with_locations_t * *)realloc(instance->state_packs, sizeof(fcs_state_with_locations_t *) * instance->max_num_state_packs); \ + } \ + instance->state_packs[instance->num_state_packs] = malloc(instance->state_pack_len * sizeof(fcs_state_with_locations_t)); \ + instance->num_state_packs++; \ + instance->num_states_in_last_pack = 0; \ + } \ + ret = &(instance->state_packs[instance->num_state_packs-1][instance->num_states_in_last_pack++]); \ +} + +#endif + + +#if 0 +extern void fcs_state_ia_release(freecell_solver_instance_t * instance); +#else +#define fcs_state_ia_release(instance) \ +{ \ + (instance)->num_states_in_last_pack--; \ +} + + +#endif +extern void freecell_solver_state_ia_finish(freecell_solver_hard_thread_t * hard_thread); + +extern void freecell_solver_state_ia_foreach( + freecell_solver_hard_thread_t * hard_thread, + void (*ptr_function)(fcs_state_with_locations_t *, void *), + void * context + ); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/kpat/freecell-solver/fcs_move.h b/kpat/freecell-solver/fcs_move.h new file mode 100644 index 00000000..ecb5166f --- /dev/null +++ b/kpat/freecell-solver/fcs_move.h @@ -0,0 +1,122 @@ +/* + * fcs_move.h - header file for the move structure and enums of + * Freecell Solver. This file is common to the main code and to the + * library headers. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#ifndef FC_SOLVE__FCS_MOVE_H +#define FC_SOLVE__FCS_MOVE_H + +/* #define FCS_DEBUG_MOVES */ +#define FCS_COMPACT_MOVES + +#ifdef __cplusplus +extern "C" { +#endif + +enum fcs_move_types +{ + FCS_MOVE_TYPE_STACK_TO_STACK, + FCS_MOVE_TYPE_STACK_TO_FREECELL, + FCS_MOVE_TYPE_FREECELL_TO_STACK, + FCS_MOVE_TYPE_FREECELL_TO_FREECELL, + FCS_MOVE_TYPE_STACK_TO_FOUNDATION, + FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION, + FCS_MOVE_TYPE_FLIP_CARD, + FCS_MOVE_TYPE_DEAL_GYPSY_TALON, + FCS_MOVE_TYPE_KLONDIKE_TALON_TO_STACK, + FCS_MOVE_TYPE_KLONDIKE_FLIP_TALON, + FCS_MOVE_TYPE_KLONDIKE_REDEAL_TALON, + FCS_MOVE_TYPE_SEQ_TO_FOUNDATION, + FCS_MOVE_TYPE_CANONIZE, + FCS_MOVE_TYPE_SEPARATOR, + FCS_MOVE_TYPE_NULL +}; + +#ifdef FCS_DEBUG_MOVES +struct fcs_move_struct +{ + /* The index of the foundation, in case there are more than one decks */ + int foundation; + /* Used in the case of a stack to stack move */ + int num_cards_in_sequence; + /* There are two freecells, one for the source and the other + * for the destination */ + int src_freecell; + int dest_freecell; + /* Ditto for the stacks */ + int src_stack; + int dest_stack; + /* The type of the move see the enum fcs_move_types */ + int type; +}; + +#define fcs_move_set_src_stack(move,value) (move).src_stack = (value); +#define fcs_move_set_src_freecell(move,value) (move).src_freecell = (value); +#define fcs_move_set_dest_stack(move,value) (move).dest_stack = (value); +#define fcs_move_set_dest_freecell(move,value) (move).dest_freecell = (value); +#define fcs_move_set_foundation(move,value) (move).foundation = (value); +#define fcs_move_set_type(move,value) (move).type = (value); +#define fcs_move_set_num_cards_in_seq(move,value) (move).num_cards_in_sequence = (value); + +#define fcs_move_get_src_stack(move) ((move).src_stack) +#define fcs_move_get_src_freecell(move) ((move).src_freecell) +#define fcs_move_get_dest_stack(move) ((move).dest_stack) +#define fcs_move_get_dest_freecell(move) ((move).dest_freecell) +#define fcs_move_get_foundation(move) ((move).foundation) +#define fcs_move_get_type(move) ((move).type) +#define fcs_move_get_num_cards_in_seq(move) ((move).num_cards_in_sequence) + +#elif defined(FCS_COMPACT_MOVES) +struct fcs_move_struct +{ + unsigned char c[4]; +}; + +#define FCS_MOVE_TYPE 0 +#define FCS_MOVE_SRC 1 +#define FCS_MOVE_DEST 2 +#define FCS_MOVE_NUM_CARDS_IN_SEQ 3 +#define FCS_MOVE_NUM_CARDS_FLIPPED 3 + +#define fcs_move_set_src_stack(move,value) (move).c[FCS_MOVE_SRC] = (value); +#define fcs_move_set_src_freecell(move,value) (move).c[FCS_MOVE_SRC] = (value); +#define fcs_move_set_dest_stack(move,value) (move).c[FCS_MOVE_DEST] = (value); +#define fcs_move_set_dest_freecell(move,value) (move).c[FCS_MOVE_DEST] = (value); +#define fcs_move_set_foundation(move,value) (move).c[FCS_MOVE_DEST] = (value); +#define fcs_move_set_type(move,value) (move).c[FCS_MOVE_TYPE] = (value); +#define fcs_move_set_num_cards_in_seq(move,value) (move).c[FCS_MOVE_NUM_CARDS_IN_SEQ] = (value); +#define fcs_move_set_num_cards_flipped(move,value) (move).c[FCS_MOVE_NUM_CARDS_FLIPPED] = (value); + +#define fcs_move_get_src_stack(move) ((move).c[FCS_MOVE_SRC]) +#define fcs_move_get_src_freecell(move) ((move).c[FCS_MOVE_SRC]) +#define fcs_move_get_dest_stack(move) ((move).c[FCS_MOVE_DEST]) +#define fcs_move_get_dest_freecell(move) ((move).c[FCS_MOVE_DEST]) +#define fcs_move_get_foundation(move) ((move).c[FCS_MOVE_DEST]) +#define fcs_move_get_type(move) ((move).c[FCS_MOVE_TYPE]) +#define fcs_move_get_num_cards_in_seq(move) ((move).c[FCS_MOVE_NUM_CARDS_IN_SEQ]) +#define fcs_move_get_num_cards_flipped(move,value) ((move).c[FCS_MOVE_NUM_CARDS_FLIPPED]) +#define fcs_move_init(move) (memset((move).c, 0, 4)) +#endif + +typedef struct fcs_move_struct fcs_move_t; + +struct fcs_move_stack_struct +{ + fcs_move_t * moves; + int max_num_moves; + int num_moves; +}; + +typedef struct fcs_move_stack_struct fcs_move_stack_t; + + +#ifdef __cplusplus +} +#endif + +#endif /* FC_SOLVE__FCS_MOVE_H */ diff --git a/kpat/freecell-solver/fcs_user.h b/kpat/freecell-solver/fcs_user.h new file mode 100644 index 00000000..8ddbf6f2 --- /dev/null +++ b/kpat/freecell-solver/fcs_user.h @@ -0,0 +1,275 @@ +/* + * move.h - main header file for the Freecell Solver library. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ +#ifndef FC_SOLVE__FCS_USER_H +#define FC_SOLVE__FCS_USER_H + +#include "fcs_enums.h" +#include "fcs_move.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +extern void * freecell_solver_user_alloc(void); + +extern int freecell_solver_user_apply_preset( + void * instance, + const char * preset_name + ); + +extern void freecell_solver_user_limit_iterations( + void * user_instance, + int max_iters + ); + +extern int freecell_solver_user_set_tests_order( + void * user_instance, + const char * tests_order, + char * * error_string + ); + +extern int freecell_solver_user_solve_board( + void * user_instance, + const char * state_as_string + ); + +extern int freecell_solver_user_resume_solution( + void * user_instance + ); + +extern int freecell_solver_user_get_next_move( + void * user_instance, + fcs_move_t * move + ); + +extern char * freecell_solver_user_current_state_as_string( + void * user_instance, + int parseable_output, + int canonized_order_output, + int display_10_as_t + ); + +extern void freecell_solver_user_free( + void * user_instance + ); + +extern int freecell_solver_user_get_current_depth( + void * user_instance + ); + +extern void freecell_solver_user_set_solving_method( + void * user_instance, + int method + ); + +extern int freecell_solver_user_get_num_times( + void * user_instance + ); + +extern int freecell_solver_user_get_limit_iterations( + void * user_instance + ); + +extern int freecell_solver_user_get_moves_left( + void * user_instance + ); + +extern int freecell_solver_user_set_game( + void * user_instance, + int freecells_num, + int stacks_num, + int decks_num, + int sequences_are_built_by, + int unlimited_sequence_move, + int empty_stacks_fill + ); + +extern void freecell_solver_user_set_solution_optimization( + void * user_instance, + int optimize +); + +extern char * freecell_solver_user_move_to_string( + fcs_move_t move, + int standard_notation + ); + +extern char * freecell_solver_user_move_to_string_w_state( + void * user_instance, + fcs_move_t move, + int standard_notation + ); + +extern void freecell_solver_user_limit_depth( + void * user_instance, + int max_depth + ); + +extern int freecell_solver_user_set_num_freecells( + void * user_instance, + int freecells_num + ); + +extern int freecell_solver_user_get_max_num_freecells(void); + +extern int freecell_solver_user_set_num_stacks( + void * user_instance, + int stacks_num + ); + +extern int freecell_solver_user_get_max_num_stacks(void); + +extern int freecell_solver_user_set_num_decks( + void * user_instance, + int decks_num + ); + +extern int freecell_solver_user_get_max_num_decks(void); + + +extern char * freecell_solver_user_get_invalid_state_error_string( + void * user_instance, + int print_ts + ); + +extern int freecell_solver_user_set_sequences_are_built_by_type( + void * user_instance, + int sbb + ); + +extern int freecell_solver_user_set_empty_stacks_filled_by( + void * user_instance, + int es_fill + ); + +extern int freecell_solver_user_set_sequence_move( + void * user_instance, + int unlimited + ); + +extern int freecell_solver_user_set_a_star_weight( + void * user_instance, + int index, + double weight + ); + +typedef void (*freecell_solver_user_iter_handler_t) + ( + void * user_instance, + int iter_num, + int depth, + void * ptr_state, + int parent_iter_num, + void * context + ); + +extern void freecell_solver_user_set_iter_handler( + void * user_instance, + freecell_solver_user_iter_handler_t iter_handler, + void * iter_handler_context + ); + + +extern char * freecell_solver_user_iter_state_as_string( + void * user_instance, + void * ptr_state, + int parseable_output, + int canonized_order_output, + int display_10_as_t + ); + +extern void freecell_solver_user_set_random_seed( + void * user_instance, + int seed + ); + +extern int freecell_solver_user_get_num_states_in_collection( + void * user_instance + ); + +extern void freecell_solver_user_limit_num_states_in_collection( + void * user_instance, + int max_num_states + ); + +extern int freecell_solver_user_next_soft_thread( + void * user_instance + ); + +extern void freecell_solver_user_set_soft_thread_step( + void * user_instance, + int num_times_step + ); + +extern int freecell_solver_user_next_hard_thread( + void * user_instance + ); + +extern int freecell_solver_user_get_num_soft_threads_in_instance( + void * user_instance + ); + +extern void freecell_solver_user_set_calc_real_depth( + void * user_instance, + int calc_real_depth + ); + +extern void freecell_solver_user_set_soft_thread_name( + void * user_instance, + char * name + ); + +extern int freecell_solver_user_set_hard_thread_prelude( + void * user_instance, + char * prelude + ); + +extern void freecell_solver_user_recycle( + void * user_instance + ); + +extern int freecell_solver_user_set_optimization_scan_tests_order( + void * user_instance, + const char * tests_order, + char * * error_string + ); + +extern void freecell_solver_user_set_reparent_states( + void * user_instance, + int to_reparent_states + ); + +extern void freecell_solver_user_set_scans_synergy( + void * user_instance, + int synergy + ); + +extern void freecell_solver_user_limit_current_instance_iterations( + void * user_instance, + int max_iters + ); + +extern int freecell_solver_user_next_instance( + void * user_instance + ); + +/* + * This function resets the user_instance, making it lose + * all the previous command line arguments it encountered + * */ +extern int freecell_solver_user_reset( + void * user_instance + ); + + +#ifdef __cplusplus +} +#endif + +#endif /* FC_SOLVE__FCS_USER_H */ diff --git a/kpat/freecell-solver/freecell.c b/kpat/freecell-solver/freecell.c new file mode 100644 index 00000000..159772ff --- /dev/null +++ b/kpat/freecell-solver/freecell.c @@ -0,0 +1,2433 @@ +/* + * freecell.c - The various movement tests performed by Freecell Solver + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000-2001 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#include +#include +#include +#include +#include + + +#include "fcs_config.h" + +#if FCS_STATE_STORAGE==FCS_STATE_STORAGE_LIBREDBLACK_TREE +#include +#endif + +#include "state.h" +#include "card.h" +#include "fcs_dm.h" +#include "fcs.h" + +#include "fcs_isa.h" +#include "tests.h" +#include "ms_ca.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#define state_with_locations (*ptr_state_with_locations) +#define state (ptr_state_with_locations->s) +#define new_state_with_locations (*ptr_new_state_with_locations) +#define new_state (ptr_new_state_with_locations->s) + + +#ifndef min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif + +/* + * Throughout this code the following local variables are used to quickly + * access the instance's members: + * + * state_stacks_num - the number of stacks in the state + * state_freecells_num - the number of freecells in the state + * sequences_are_built_by - the type of sequences of this board. + * */ + +/* + * This function tries to move stack cards that are present at the + * top of stacks to the foundations. + * */ +int freecell_solver_sfs_move_top_stack_cards_to_founds( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + int stack; + int cards_num; + int deck; + fcs_card_t card; + fcs_card_t temp_card; + int check; + int state_stacks_num; + + fcs_move_t temp_move; + + tests_define_accessors(); + + moves = hard_thread->reusable_move_stack; + indirect_stacks_buffer = hard_thread->indirect_stacks_buffer; + + state_stacks_num = instance->stacks_num; + + for(stack=0;stackdecks_num;deck++) + { + if (fcs_foundation_value(state, deck*4+fcs_card_suit(card)) == fcs_card_card_num(card) - 1) + { + /* We can put it there */ + + sfs_check_state_begin(); + + + my_copy_stack(stack); + fcs_pop_stack_card(new_state, stack, temp_card); + + fcs_increment_foundation(new_state, deck*4+fcs_card_suit(card)); + + + fcs_move_init(temp_move); + fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FOUNDATION); + fcs_move_set_src_stack(temp_move,stack); + fcs_move_set_foundation(temp_move,deck*4+fcs_card_suit(card)); + + fcs_move_stack_push(moves, temp_move); + + fcs_flip_top_card(stack); + + /* The last move needs to be FCS_MOVE_TYPE_CANONIZE + * because it indicates that the internal order of the + * stacks + * and freecells may have changed. */ + fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE); + fcs_move_stack_push(moves, temp_move); + + sfs_check_state_end() + break; + } + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + + +/* + * This test moves single cards that are present in the freecells to + * the foundations. + * */ +int freecell_solver_sfs_move_freecell_cards_to_founds( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + int fc; + int deck; + fcs_card_t card; + int check; + fcs_move_t temp_move; + int state_freecells_num; + + tests_define_accessors(); + + state_freecells_num = instance->freecells_num; + + /* Now check the same for the free cells */ + for(fc=0;fcdecks_num;deck++) + { + if (fcs_foundation_value(state, deck*4+fcs_card_suit(card)) == fcs_card_card_num(card) - 1) + { + /* We can put it there */ + sfs_check_state_begin() + + fcs_empty_freecell(new_state, fc); + + fcs_increment_foundation(new_state, deck*4+fcs_card_suit(card)); + + fcs_move_init(temp_move); + fcs_move_set_type(temp_move,FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION); + fcs_move_set_src_freecell(temp_move,fc); + fcs_move_set_foundation(temp_move,deck*4+fcs_card_suit(card)); + + fcs_move_stack_push(moves, temp_move); + + fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE); + fcs_move_stack_push(moves, temp_move); + + sfs_check_state_end(); + } + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + +int freecell_solver_sfs_move_freecell_cards_on_top_of_stacks( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + + int dest_cards_num; + int ds, fc, dc; + fcs_card_t dest_card, src_card, temp_card, dest_below_card; + int check; + + fcs_move_t temp_move; + int is_seq_in_dest; + int num_cards_to_relocate; + int freecells_to_fill, freestacks_to_fill; + int a,b; + int state_freecells_num, state_stacks_num, sequences_are_built_by; + + tests_define_accessors(); + + state_freecells_num = instance->freecells_num; + state_stacks_num = instance->stacks_num; + sequences_are_built_by = instance->sequences_are_built_by; + + /* Let's try to put cards in the freecells on top of stacks */ + + /* ds stands for destination stack */ + for(ds=0;ds 0) + { + /* + * Let's search for a suitable card in the stack + * */ + for(dc=dest_cards_num-1;dc>=0;dc--) + { + dest_card = fcs_stack_card(state, ds, dc); + + /* Scan the freecells */ + for(fc=0;fc dc) + { + dest_below_card = fcs_stack_card(state, ds, dc+1); + if (fcs_is_parent_card(dest_below_card, dest_card)) + { + is_seq_in_dest = 1; + } + } + + + if (! is_seq_in_dest) + { + num_cards_to_relocate = dest_cards_num - dc - 1; + + freecells_to_fill = min(num_cards_to_relocate, num_freecells); + + num_cards_to_relocate -= freecells_to_fill; + + if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD) + { + freestacks_to_fill = min(num_cards_to_relocate, num_freestacks); + + num_cards_to_relocate -= freestacks_to_fill; + } + else + { + freestacks_to_fill = 0; + } + + if (num_cards_to_relocate == 0) + { + /* We can move it */ + + sfs_check_state_begin() + + + /* Fill the freecells with the top cards */ + + my_copy_stack(ds); + + for(a=0 ; afreecells_num; + state_stacks_num = instance->stacks_num; + + + + /* Now let's check if a card that is under some other cards can be placed + * in the foundations. */ + + for(stack=0;stack= 0 ; c--) + { + card = fcs_stack_card(state, stack, c); + for(deck=0;deckdecks_num;deck++) + { + if (fcs_foundation_value(state, deck*4+fcs_card_suit(card)) == fcs_card_card_num(card)-1) + { + /* The card is foundation-able. Now let's check if we + * can move the cards above it to the freecells and + * stacks */ + + if ((num_freecells + + ((instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD) ? + num_freestacks : + 0 + )) + >= cards_num-(c+1)) + { + /* We can move it */ + + sfs_check_state_begin() + + my_copy_stack(stack); + + + /* Fill the freecells with the top cards */ + for(a=0 ; afreecells_num; + state_stacks_num = instance->stacks_num; + sequences_are_built_by = instance->sequences_are_built_by; + + /* + * Now let's try to move a stack card to a parent card which is found + * on the same stack. + * */ + for (stack=0;stackempty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD) + { + freestacks_to_fill = min(num_cards_to_relocate, num_freestacks); + + num_cards_to_relocate -= freestacks_to_fill; + } + else + { + freestacks_to_fill = 0; + } + + if (num_cards_to_relocate == 0) + { + /* We can move it */ + + sfs_check_state_begin() + + + { + int i_card_pos; + fcs_card_t moved_card; + int source_type, source_index; + + i_card_pos = fcs_stack_len(new_state,stack)-1; + a = 0; + + my_copy_stack(ds); + while(i_card_pos>c) + { + if (a < freecells_to_fill) + { + for(b=0;bdc) + { + if (a < freecells_to_fill) + { + for(b=0;bfreecells_num; + state_stacks_num = instance->stacks_num; + sequences_are_built_by = instance->sequences_are_built_by; + + /* Now let's try to move a card from one stack to the other * + * Note that it does not involve moving cards lower than king * + * to empty stacks */ + + for (stack=0;stack dc) + { + dest_below_card = fcs_stack_card(state, ds, dc+1); + if (fcs_is_parent_card(dest_below_card, dest_card)) + { + is_seq_in_dest = 1; + } + } + + if (! is_seq_in_dest) + { + num_cards_to_relocate = dest_cards_num - dc - 1 + cards_num - seq_end - 1; + + freecells_to_fill = min(num_cards_to_relocate, num_freecells); + + num_cards_to_relocate -= freecells_to_fill; + + if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD) + { + freestacks_to_fill = min(num_cards_to_relocate, num_freestacks); + + num_cards_to_relocate -= freestacks_to_fill; + } + else + { + freestacks_to_fill = 0; + } + + if ((num_cards_to_relocate == 0) && + (calc_max_sequence_move(num_freecells-freecells_to_fill, num_freestacks-freestacks_to_fill) >= + seq_end - c + 1)) + { + /* We can move it */ + int from_which_stack; + + sfs_check_state_begin() + + + /* Fill the freecells with the top cards */ + + my_copy_stack(stack); + my_copy_stack(ds); + + for(a=0 ; aempty_stacks_fill == FCS_ES_FILLED_BY_NONE) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + state_freecells_num = instance->freecells_num; + state_stacks_num = instance->stacks_num; + sequences_are_built_by = instance->sequences_are_built_by; + + max_sequence_len = calc_max_sequence_move(num_freecells, num_freestacks-1); + + /* Now try to move sequences to empty stacks */ + + if (num_freestacks > 0) + { + for(stack=0;stackempty_stacks_fill == FCS_ES_FILLED_BY_KINGS_ONLY)) + { + continue; + } + + if (seq_end == cards_num -1) + { + /* One stack is the destination stack, so we have one * + * less stack in that case */ + while ((max_sequence_len < cards_num -c) && (c > 0)) + { + c--; + } + + if ( + (c > 0) && + ((instance->empty_stacks_fill == FCS_ES_FILLED_BY_KINGS_ONLY) ? + (fcs_card_card_num(fcs_stack_card(state, stack, c)) == 13) : + 1 + ) + ) + { + sfs_check_state_begin(); + + + for(ds=0;dsempty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD) + { + freestacks_to_fill = min(num_cards_to_relocate, num_freestacks); + + num_cards_to_relocate -= freestacks_to_fill; + } + else + { + freestacks_to_fill = 0; + } + + if ((num_cards_to_relocate == 0) && (num_freestacks-freestacks_to_fill > 0)) + { + /* We can move it */ + int seq_start = c; + while ( + (calc_max_sequence_move( + num_freecells-freecells_to_fill, + num_freestacks-freestacks_to_fill-1) < seq_end-seq_start+1) + && + (seq_start <= seq_end) + ) + { + seq_start++; + } + if ((seq_start <= seq_end) && + ((instance->empty_stacks_fill == FCS_ES_FILLED_BY_KINGS_ONLY) ? + (fcs_card_card_num(fcs_stack_card(state, stack, seq_start)) == 13) : + 1 + ) + ) + { + sfs_check_state_begin(); + + + /* Fill the freecells with the top cards */ + + my_copy_stack(stack); + + for(a=0; aempty_stacks_fill == FCS_ES_FILLED_BY_NONE) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + state_freecells_num = instance->freecells_num; + state_stacks_num = instance->stacks_num; + + for(fc=0;fcempty_stacks_fill == FCS_ES_FILLED_BY_KINGS_ONLY) ? + (fcs_card_card_num(card) == 13) : + (fcs_card_card_num(card) != 0) + ) + { + for(stack=0;stackfreecells_num; + state_stacks_num = instance->stacks_num; + sequences_are_built_by = instance->sequences_are_built_by; + + fcs_move_init(temp_move); + + /* This time try to move cards that are already on top of a parent to a different parent */ + + for (stack=0;stack dc) + { + dest_below_card = fcs_stack_card(state, ds, dc+1); + if (fcs_is_parent_card(dest_below_card,dest_card)) + { + is_seq_in_dest = 1; + } + } + + if (! is_seq_in_dest) + { + if (is_seq_in_src) + { + num_cards_to_relocate = dest_cards_num - dc - 1; + + freecells_to_fill = min(num_cards_to_relocate, num_freecells); + + num_cards_to_relocate -= freecells_to_fill; + + if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD) + { + freestacks_to_fill = min(num_cards_to_relocate, num_freestacks); + + num_cards_to_relocate -= freestacks_to_fill; + } + else + { + freestacks_to_fill = 0; + } + + if ((num_cards_to_relocate == 0) && + (calc_max_sequence_move(num_freecells-freecells_to_fill, num_freestacks-freestacks_to_fill) >= + cards_num - c)) + { + /* We can move it */ + + sfs_check_state_begin() + + + /* Fill the freecells with the top cards */ + + my_copy_stack(ds); + for(a=0 ; aempty_stacks_fill == FCS_ES_FILLED_BY_NONE) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + state_stacks_num = instance->stacks_num; + state_freecells_num = instance->freecells_num; + + + /* Now, let's try to empty an entire stack into the freecells, so other cards can + * inhabit it */ + + if (num_freestacks == 0) + { + for(stack=0;stackstacks_num; + sequences_are_built_by = instance->sequences_are_built_by; + + for( ds=0 ; ds < state_stacks_num ; ds++ ) + { + dest_cards_num = fcs_stack_len(state, ds); + if (dest_cards_num > 0) + { + dest_card = fcs_stack_card(state, ds, dest_cards_num-1); + for( stack=0 ; stack < state_stacks_num ; stack++) + { + if (stack == ds) + { + continue; + } + cards_num = fcs_stack_len(state, stack); + for( c=cards_num-1 ; c >= 0 ; c--) + { + card = fcs_stack_card(state, stack, c); + if (fcs_card_get_flipped(card)) + { + break; + } + if (fcs_is_parent_card(card, dest_card)) + { + /* We can move it there - now let's check to see + * if it is already above a suitable parent. */ + if ((c == 0) || + (! fcs_is_parent_card(card, fcs_stack_card(state, stack, c-1)))) + { + /* Let's move it */ + sfs_check_state_begin(); + + my_copy_stack(stack); + my_copy_stack(ds); + + fcs_move_sequence(ds, stack, c, cards_num-1, a); + + fcs_flip_top_card(stack); + + sfs_check_state_end(); + } + + } + } + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + +int freecell_solver_sfs_yukon_move_kings_to_empty_stack( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + + int check; + + int stack, cards_num, c, a, ds; + fcs_card_t card, temp_card; + + int state_stacks_num; + + fcs_move_t temp_move; + + tests_define_accessors(); + + if (num_freestacks == 0) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + state_stacks_num = instance->stacks_num; + + for( stack=0 ; stack < state_stacks_num ; stack++) + { + cards_num = fcs_stack_len(state, stack); + for( c=cards_num-1 ; c >= 1 ; c--) + { + card = fcs_stack_card(state, stack, c); + if (fcs_card_get_flipped(card)) + { + break; + } + if (fcs_card_card_num(card) == 13) + { + /* It's a King - so let's move it */ + sfs_check_state_begin(); + + + for( ds=0 ; ds < state_stacks_num ; ds++) + { + if (fcs_stack_len(state, ds) == 0) + { + break; + } + } + my_copy_stack(stack); + my_copy_stack(ds); + fcs_move_sequence(ds, stack, c, cards_num-1, a); + + + fcs_flip_top_card(stack); + + sfs_check_state_end(); + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + + + +#ifdef FCS_WITH_TALONS +/* + Let's try to deal the Gypsy-type Talon. + + */ +int freecell_solver_sfs_deal_gypsy_talon( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + + int check; + + fcs_card_t temp_card; + int a; + + fcs_move_t temp_move; + + tests_define_accessors(); + + if (instance->talon_type != FCS_TALON_GYPSY) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + moves = hard_thread->reusable_move_stack; + indirect_stacks_buffer = hard_thread->indirect_stacks_buffer; + + if (fcs_talon_pos(state) < fcs_talon_len(state)) + { + sfs_check_state_begin() + for(a=0;atalon_type != FCS_TALON_KLONDIKE) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + /* Duplicate the talon and its parameters into talon_temp */ + talon_temp = malloc(sizeof(fcs_state_with_locations_t)); + talon_temp->s.talon = malloc(fcs_klondike_talon_len(state)+1); + memcpy( + talon_temp->s.talon, + ptr_state_with_locations->s.talon, + fcs_klondike_talon_len(state)+1 + ); + memcpy( + talon_temp->s.talon_params, + ptr_state_with_locations->s.talon_params, + sizeof(ptr_state_with_locations->s.talon_params) + ); + + /* Make sure we redeal the talon only once */ + num_redeals_left = fcs_klondike_talon_num_redeals_left(state); + if ((num_redeals_left > 0) || (num_redeals_left < 0)) + { + num_redeals_left = 1; + } + num_redeals_done = 0; + num_cards_moved[0] = 0; + num_cards_moved[1] = 0; + + first_iter = 1; + while (num_redeals_left >= 0) + { + if ((fcs_klondike_talon_stack_pos(talon_temp->s) == -1) && + (fcs_klondike_talon_queue_pos(talon_temp->s) == fcs_klondike_talon_len(talon_temp->s))) + { + break; + } + if ((!first_iter) || (fcs_klondike_talon_stack_pos(talon_temp->s) == -1)) + { + if (fcs_klondike_talon_queue_pos(talon_temp->s) == fcs_klondike_talon_len(talon_temp->s)) + { + if (num_redeals_left > 0) + { + fcs_klondike_talon_len(talon_temp->s) = fcs_klondike_talon_stack_pos(talon_temp->s); + fcs_klondike_talon_redeal_bare(talon_temp->s); + + num_redeals_left--; + num_redeals_done++; + } + else + { + break; + } + } + fcs_klondike_talon_queue_to_stack(talon_temp->s); + num_cards_moved[num_redeals_done]++; + } + first_iter = 0; + + card_to_check = fcs_klondike_talon_get_top_card(talon_temp->s); + for(s=0 ; ss)+1); + memcpy( + new_state.talon, + talon_temp->s.talon, + fcs_klondike_talon_len(talon_temp->s)+1 + ); + + memcpy( + ptr_new_state_with_locations->s.talon_params, + talon_temp->s.talon_params, + sizeof(ptr_state_with_locations->s.talon_params) + ); + + for(a=0;a<=num_redeals_done;a++) + { + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_KLONDIKE_FLIP_TALON); + fcs_move_set_num_cards_flipped(temp_move, num_cards_moved[a]); + fcs_move_stack_push(moves, temp_move); + if (a != num_redeals_done) + { + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_KLONDIKE_REDEAL_TALON); + fcs_move_stack_push(moves,temp_move); + } + } + fcs_push_card_into_stack(new_state, s, fcs_klondike_talon_get_top_card(new_state)); + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_KLONDIKE_TALON_TO_STACK); + fcs_move_set_dest_stack(temp_move, s); + fcs_klondike_talon_decrement_stack(new_state); + + sfs_check_state_end() + } + } + } + + + +#if 0 + cleanup: +#endif + free(talon_temp->s.talon); + free(talon_temp); + + return FCS_STATE_IS_NOT_SOLVEABLE; + +} + +#endif + +int freecell_solver_sfs_atomic_move_card_to_empty_stack( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + int empty_stacks_filled_by, state_stacks_num; + int stack, cards_num; + fcs_card_t card, temp_card; + fcs_move_t temp_move; + int check; + int empty_stack_idx; + + tests_define_accessors(); + + if (num_freestacks == 0) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + state_stacks_num = instance->stacks_num; + + for(empty_stack_idx=0;empty_stack_idxempty_stacks_fill; + + if (empty_stacks_filled_by == FCS_ES_FILLED_BY_NONE) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + + + for(stack=0;stack 0) + { + card = fcs_stack_card(state, stack, cards_num-1); + if ((empty_stacks_filled_by == FCS_ES_FILLED_BY_KINGS_ONLY) && + (fcs_card_card_num(card) != 13)) + { + continue; + } + /* Let's move it */ + { + sfs_check_state_begin(); + + my_copy_stack(stack); + + fcs_pop_stack_card(new_state, stack, temp_card); + + + my_copy_stack(empty_stack_idx); + + fcs_push_card_into_stack(new_state, empty_stack_idx, card); + + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_STACK_TO_STACK); + fcs_move_set_src_stack(temp_move, stack); + fcs_move_set_dest_stack(temp_move, empty_stack_idx); + fcs_move_set_num_cards_in_seq(temp_move, 1); + + fcs_move_stack_push(moves, temp_move); + + fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE); + fcs_move_stack_push(moves, temp_move); + + sfs_check_state_end() + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + +int freecell_solver_sfs_atomic_move_card_to_parent( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + int state_stacks_num; + int stack, cards_num, ds, ds_cards_num; + fcs_card_t card, dest_card, temp_card; + fcs_move_t temp_move; + int check; + int sequences_are_built_by; + + tests_define_accessors(); + + state_stacks_num = instance->stacks_num; + + sequences_are_built_by = instance->sequences_are_built_by; + + + for(stack=0;stack 0) + { + card = fcs_stack_card(state, stack, cards_num-1); + + for(ds=0;ds 0) + { + dest_card = fcs_stack_card(state, ds, ds_cards_num-1); + if (fcs_is_parent_card(card, dest_card)) + { + /* Let's move it */ + { + sfs_check_state_begin(); + + my_copy_stack(stack); + my_copy_stack(ds); + + fcs_pop_stack_card(new_state, stack, temp_card); + + fcs_push_card_into_stack(new_state, ds, card); + + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_STACK_TO_STACK); + fcs_move_set_src_stack(temp_move, stack); + fcs_move_set_dest_stack(temp_move, ds); + fcs_move_set_num_cards_in_seq(temp_move, 1); + + fcs_move_stack_push(moves, temp_move); + + fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE); + fcs_move_stack_push(moves, temp_move); + + sfs_check_state_end() + } + } + } + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + +int freecell_solver_sfs_atomic_move_card_to_freecell( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + int state_stacks_num; + int state_freecells_num; + int stack, cards_num, ds; + fcs_card_t card, temp_card; + fcs_move_t temp_move; + int check; + int sequences_are_built_by; + + tests_define_accessors(); + + state_stacks_num = instance->stacks_num; + state_freecells_num = instance->freecells_num; + + sequences_are_built_by = instance->sequences_are_built_by; + + if (num_freecells == 0) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + for(ds=0;ds 0) + { + card = fcs_stack_card(state, stack, cards_num-1); + + /* Let's move it */ + { + sfs_check_state_begin(); + + my_copy_stack(stack); + + fcs_pop_stack_card(new_state, stack, temp_card); + + fcs_put_card_in_freecell(new_state, ds, card); + + fcs_move_init(temp_move); + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_STACK_TO_FREECELL); + fcs_move_set_src_stack(temp_move, stack); + fcs_move_set_dest_freecell(temp_move, ds); + + fcs_move_stack_push(moves, temp_move); + + fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE); + fcs_move_stack_push(moves, temp_move); + + sfs_check_state_end() + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + +int freecell_solver_sfs_atomic_move_freecell_card_to_parent( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + int state_stacks_num, state_freecells_num; + int fc, ds, ds_cards_num; + fcs_card_t card, dest_card; + fcs_move_t temp_move; + int check; + int sequences_are_built_by; + + tests_define_accessors(); + + state_stacks_num = instance->stacks_num; + state_freecells_num = instance->freecells_num; + + sequences_are_built_by = instance->sequences_are_built_by; + + + + + for(fc=0;fc 0) + { + dest_card = fcs_stack_card(state, ds, ds_cards_num-1); + if (fcs_is_parent_card(card, dest_card)) + { + /* Let's move it */ + { + sfs_check_state_begin(); + + my_copy_stack(ds); + + fcs_empty_freecell(new_state, fc); + + fcs_push_card_into_stack(new_state, ds, card); + + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_FREECELL_TO_STACK); + fcs_move_set_src_freecell(temp_move, fc); + fcs_move_set_dest_stack(temp_move, ds); + fcs_move_set_num_cards_in_seq(temp_move, 1); + + fcs_move_stack_push(moves, temp_move); + + fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE); + fcs_move_stack_push(moves, temp_move); + + sfs_check_state_end() + } + } + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + +int freecell_solver_sfs_atomic_move_freecell_card_to_empty_stack( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + int state_stacks_num, state_freecells_num; + int fc, ds; + fcs_card_t card; + fcs_move_t temp_move; + int check; + int sequences_are_built_by, empty_stacks_filled_by; + + tests_define_accessors(); + + moves = hard_thread->reusable_move_stack; + indirect_stacks_buffer = hard_thread->indirect_stacks_buffer; + + state_stacks_num = instance->stacks_num; + state_freecells_num = instance->freecells_num; + + sequences_are_built_by = instance->sequences_are_built_by; + + if (num_freestacks == 0) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + empty_stacks_filled_by = instance->empty_stacks_fill; + + if (empty_stacks_filled_by == FCS_ES_FILLED_BY_NONE) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + for(ds=0;ds +#include +#include +#include +#include +#include + +#include +#include +#include + +#define NUM_TIMES_STEP 50 + +#include "fcs_config.h" + +/* So the FCS_STATE_STORAGE macros would be defined */ +#if FCS_STATE_STORAGE==FCS_STATE_STORAGE_LIBREDBLACK_TREE +#include +#endif + +#include "state.h" +#include "card.h" +#include "fcs_dm.h" +#include "fcs.h" + +#include "fcs_isa.h" + +#include "caas.h" + +#include "preset.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +/* + General use of this interface: + 1. freecell_solver_alloc_instance() + 2. Set the parameters of the game + 3. If you wish to revert, go to step #11. + 4. freecell_solver_init_instance() + 5. Call freecell_solver_solve_instance() with the initial board. + 6. If it returns FCS_STATE_SUSPEND_PROCESS and you wish to proceed, + then increase the iteration limit and call + freecell_solver_resume_instance(). + 7. Repeat Step #6 zero or more times. + 8. If the last call to solve_instance() or resume_instance() returned + FCS_STATE_SUSPEND_PROCESS then call + freecell_solver_unresume_instance(). + 9. If the solving was successful you can use the move stacks or the + intermediate stacks. (Just don't destory them in any way). + 10. Call freecell_solver_finish_instance(). + 11. Call freecell_solver_free_instance(). + + The library functions inside lib.c (a.k.a fcs_user()) give an + easier approach for embedding Freecell Solver into your library. The + intent of this comment is to document the code, rather than to be + a guideline for the user. +*/ + +#if 0 +static const double freecell_solver_a_star_default_weights[5] = {0.5,0,0.5,0,0}; +#else +static const double freecell_solver_a_star_default_weights[5] = {0.5,0,0.3,0,0.2}; +#endif + + + + + + + +static void freecell_solver_initialize_bfs_queue(freecell_solver_soft_thread_t * soft_thread) +{ + /* Initialize the BFS queue. We have one dummy element at the beginning + in order to make operations simpler. */ + soft_thread->bfs_queue = (fcs_states_linked_list_item_t*)malloc(sizeof(fcs_states_linked_list_item_t)); + soft_thread->bfs_queue->next = (fcs_states_linked_list_item_t*)malloc(sizeof(fcs_states_linked_list_item_t)); + soft_thread->bfs_queue_last_item = soft_thread->bfs_queue->next; + soft_thread->bfs_queue_last_item->next = NULL; +} + +static void foreach_soft_thread( + freecell_solver_instance_t * instance, + void (*soft_thread_callback)( + freecell_solver_soft_thread_t * soft_thread, + void * context + ), + void * context + ) + +{ + int ht_idx, st_idx; + freecell_solver_hard_thread_t * hard_thread; + int num_soft_threads; + freecell_solver_soft_thread_t * * ht_soft_threads; + for(ht_idx = 0 ; ht_idxnum_hard_threads; ht_idx++) + { + hard_thread = instance->hard_threads[ht_idx]; + num_soft_threads = hard_thread->num_soft_threads; + ht_soft_threads = hard_thread->soft_threads; + for(st_idx = 0 ; st_idx < num_soft_threads; st_idx++) + { + soft_thread_callback(ht_soft_threads[st_idx], context); + } + } + + if (instance->optimization_thread) + { + soft_thread_callback(instance->optimization_thread->soft_threads[0], context); + } +} + + + +static void soft_thread_clean_soft_dfs( + freecell_solver_soft_thread_t * soft_thread, + void * context + ) +{ + int num_solution_states; + int dfs_max_depth; + fcs_soft_dfs_stack_item_t * soft_dfs_info, * info_ptr; + /* Check if a Soft-DFS-type scan was called in the first place */ + if (soft_thread->soft_dfs_info == NULL) + { + /* If not - do nothing */ + return; + } + + (void)context; + soft_dfs_info = soft_thread->soft_dfs_info; + num_solution_states = soft_thread->num_solution_states; + dfs_max_depth = soft_thread->dfs_max_depth; + /* De-allocate the Soft-DFS specific stacks */ + { + int depth; + info_ptr = soft_dfs_info; + for(depth=0;depthderived_states_list.states); + free(info_ptr->derived_states_random_indexes); + info_ptr++; + } + for(;depthderived_states_list.max_num_states) + { + free(info_ptr->derived_states_list.states); + free(info_ptr->derived_states_random_indexes); + } + info_ptr++; + } + + free(soft_dfs_info); + + soft_thread->soft_dfs_info = NULL; + + soft_thread->dfs_max_depth = 0; + + } +} + +static void clean_soft_dfs( + freecell_solver_instance_t * instance + ) +{ + foreach_soft_thread(instance, soft_thread_clean_soft_dfs, NULL); +} + +static freecell_solver_soft_thread_t * alloc_soft_thread( + freecell_solver_hard_thread_t * hard_thread + ) +{ + freecell_solver_soft_thread_t * soft_thread; + unsigned int a; + + /* Make sure we are not exceeding the maximal number of soft threads + * for an instance. */ + if (hard_thread->instance->next_soft_thread_id == MAX_NUM_SCANS) + { + return NULL; + } + + soft_thread = malloc(sizeof(freecell_solver_soft_thread_t)); + + soft_thread->hard_thread = hard_thread; + + soft_thread->id = (hard_thread->instance->next_soft_thread_id)++; + + soft_thread->dfs_max_depth = 0; + + soft_thread->tests_order.num = 0; + soft_thread->tests_order.tests = NULL; + soft_thread->tests_order.max_num = 0; + + + /* Initialize all the Soft-DFS stacks to NULL */ + soft_thread->soft_dfs_info = NULL; + + /* The default solving method */ + soft_thread->method = FCS_METHOD_SOFT_DFS; + + soft_thread->orig_method = FCS_METHOD_NONE; + + freecell_solver_initialize_bfs_queue(soft_thread); + + /* Initialize the priotity queue of the A* scan */ + soft_thread->a_star_pqueue = malloc(sizeof(PQUEUE)); + freecell_solver_PQueueInitialise( + soft_thread->a_star_pqueue, + 1024 + ); + + /* Set the default A* weigths */ + for(a=0;a<(sizeof(soft_thread->a_star_weights)/sizeof(soft_thread->a_star_weights[0]));a++) + { + soft_thread->a_star_weights[a] = freecell_solver_a_star_default_weights[a]; + } + + soft_thread->rand_gen = freecell_solver_rand_alloc(soft_thread->rand_seed = 24); + + soft_thread->initialized = 0; + + soft_thread->num_times_step = NUM_TIMES_STEP; + +#if 0 + { + char * no_use; + freecell_solver_apply_tests_order(soft_thread, "[01][23456789]", &no_use); + } +#else + soft_thread->tests_order.num = soft_thread->hard_thread->instance->instance_tests_order.num; + soft_thread->tests_order.tests = + malloc(sizeof(soft_thread->tests_order.tests[0]) * soft_thread->tests_order.num); + memcpy(soft_thread->tests_order.tests, + soft_thread->hard_thread->instance->instance_tests_order.tests, + sizeof(soft_thread->tests_order.tests[0]) * soft_thread->tests_order.num + ); + soft_thread->tests_order.max_num = soft_thread->tests_order.num; +#endif + + soft_thread->is_finished = 0; + + soft_thread->name = NULL; + + return soft_thread; +} + +static freecell_solver_hard_thread_t * alloc_hard_thread( + freecell_solver_instance_t * instance + ) +{ + freecell_solver_hard_thread_t * hard_thread; + + /* Make sure we are not exceeding the maximal number of soft threads + * for an instance. */ + if (instance->next_soft_thread_id == MAX_NUM_SCANS) + { + return NULL; + } + + hard_thread = malloc(sizeof(freecell_solver_hard_thread_t)); + + hard_thread->instance = instance; + + hard_thread->num_times = 0; + + hard_thread->num_soft_threads = 1; + + hard_thread->soft_threads = + malloc(sizeof(hard_thread->soft_threads[0]) * + hard_thread->num_soft_threads + ); + + hard_thread->soft_threads[0] = alloc_soft_thread(hard_thread); + + /* Set a limit on the Hard-Thread's scan. */ + hard_thread->num_times_step = NUM_TIMES_STEP; + + hard_thread->ht_max_num_times = hard_thread->num_times_step; + + hard_thread->max_num_times = -1; + + hard_thread->num_soft_threads_finished = 0; + +#ifdef INDIRECT_STACK_STATES + hard_thread->stacks_allocator = + freecell_solver_compact_allocator_new(); +#endif + hard_thread->move_stacks_allocator = + freecell_solver_compact_allocator_new(); + + fcs_move_stack_alloc_into_var(hard_thread->reusable_move_stack); + + hard_thread->prelude_as_string = NULL; + hard_thread->prelude = NULL; + hard_thread->prelude_num_items = 0; + hard_thread->prelude_idx = 0; + + return hard_thread; +} + + +/* + This function allocates a Freecell Solver instance struct and set the + default values in it. After the call to this function, the program can + set parameters in it which are different from the default. + + Afterwards freecell_solver_init_instance() should be called in order + to really prepare it for solving. + */ +freecell_solver_instance_t * freecell_solver_alloc_instance(void) +{ + freecell_solver_instance_t * instance; + + instance = malloc(sizeof(freecell_solver_instance_t)); + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT) + instance->num_indirect_prev_states = 0; + instance->max_num_indirect_prev_states = 0; +#endif + + instance->num_times = 0; + + instance->num_states_in_collection = 0; + + instance->max_num_times = -1; + instance->max_depth = -1; + instance->max_num_states_in_collection = -1; + + instance->instance_tests_order.num = 0; + instance->instance_tests_order.tests = NULL; + instance->instance_tests_order.max_num = 0; + + instance->opt_tests_order_set = 0; + + instance->opt_tests_order.num = 0; + instance->opt_tests_order.tests = NULL; + instance->opt_tests_order.max_num = 0; + + + +#ifdef FCS_WITH_TALONS + instance->talon_type = FCS_TALON_NONE; +#endif + + instance->num_hard_threads = 0; + + freecell_solver_apply_preset_by_name(instance, "freecell"); + + /****************************************/ + + instance->debug_iter_output = 0; + + instance->next_soft_thread_id = 0; + + instance->num_hard_threads = 1; + + instance->hard_threads = malloc(sizeof(instance->hard_threads[0]) * instance->num_hard_threads); + + instance->hard_threads[0] = alloc_hard_thread(instance); + + instance->solution_moves = NULL; + + instance->optimize_solution_path = 0; + +#ifdef FCS_WITH_MHASH + instance->mhash_type = MHASH_MD5; +#endif + + instance->optimization_thread = NULL; + + instance->num_hard_threads_finished = 0; + + instance->calc_real_depth = 0; + + instance->to_reparent_states = 0; + + /* Make the 1 the default, because otherwise scans will not cooperate + * with one another. */ + instance->scans_synergy = 1; + + return instance; +} + + + + + +static void free_bfs_queue(freecell_solver_soft_thread_t * soft_thread) +{ + /* Free the BFS linked list */ + fcs_states_linked_list_item_t * item, * next_item; + item = soft_thread->bfs_queue; + while (item != NULL) + { + next_item = item->next; + free(item); + item = next_item; + } +} + +static void free_instance_soft_thread_callback(freecell_solver_soft_thread_t * soft_thread, void * context) +{ + (void)context; + free_bfs_queue(soft_thread); + freecell_solver_rand_free(soft_thread->rand_gen); + + freecell_solver_PQueueFree(soft_thread->a_star_pqueue); + free(soft_thread->a_star_pqueue); + + free(soft_thread->tests_order.tests); + + if (soft_thread->name != NULL) + { + free(soft_thread->name); + } + /* The data-structure itself was allocated */ + free(soft_thread); +} + +static void free_instance_hard_thread_callback(freecell_solver_hard_thread_t * hard_thread) +{ + if (hard_thread->prelude_as_string) + { + free (hard_thread->prelude_as_string); + } + if (hard_thread->prelude) + { + free (hard_thread->prelude); + } + fcs_move_stack_destroy(hard_thread->reusable_move_stack); + + free(hard_thread->soft_threads); + + if (hard_thread->move_stacks_allocator) + { + freecell_solver_compact_allocator_finish(hard_thread->move_stacks_allocator); + } +#ifdef INDIRECT_STACK_STATES + if (hard_thread->stacks_allocator) + { + freecell_solver_compact_allocator_finish(hard_thread->stacks_allocator); + } +#endif + free(hard_thread); +} + +/* + This function is the last function that should be called in the + sequence of operations on instance, and it is meant for de-allocating + whatever memory was allocated by alloc_instance(). + */ +void freecell_solver_free_instance(freecell_solver_instance_t * instance) +{ + int ht_idx; + + foreach_soft_thread(instance, free_instance_soft_thread_callback, NULL); + + for(ht_idx=0; ht_idx < instance->num_hard_threads; ht_idx++) + { + free_instance_hard_thread_callback(instance->hard_threads[ht_idx]); + } + free(instance->hard_threads); + if (instance->optimization_thread) + { + free_instance_hard_thread_callback(instance->optimization_thread); + } + + free(instance->instance_tests_order.tests); + + if (instance->opt_tests_order_set) + { + free(instance->opt_tests_order.tests); + } + + free(instance); +} + + +static void normalize_a_star_weights( + freecell_solver_soft_thread_t * soft_thread, + void * context + ) +{ + /* Normalize the A* Weights, so the sum of all of them would be 1. */ + double sum; + unsigned int a; + sum = 0; + for(a=0;a<(sizeof(soft_thread->a_star_weights)/sizeof(soft_thread->a_star_weights[0]));a++) + { + if (soft_thread->a_star_weights[a] < 0) + { + soft_thread->a_star_weights[a] = freecell_solver_a_star_default_weights[a]; + } + sum += soft_thread->a_star_weights[a]; + } + if (sum == 0) + { + sum = 1; + } + for(a=0;a<(sizeof(soft_thread->a_star_weights)/sizeof(soft_thread->a_star_weights[0]));a++) + { + soft_thread->a_star_weights[a] /= sum; + } + (void)context; +} + +static void accumulate_tests_order( + freecell_solver_soft_thread_t * soft_thread, + void * context + ) +{ + int * tests_order = (int *)context; + int a; + for(a=0;atests_order.num;a++) + { + *tests_order |= (1 << (soft_thread->tests_order.tests[a] & FCS_TEST_ORDER_NO_FLAGS_MASK)); + } +} + +static void determine_scan_completeness( + freecell_solver_soft_thread_t * soft_thread, + void * context + ) +{ + int global_tests_order = *(int *)context; + int tests_order = 0; + int a; + for(a=0;atests_order.num;a++) + { + tests_order |= (1 << (soft_thread->tests_order.tests[a] & FCS_TEST_ORDER_NO_FLAGS_MASK)); + } + soft_thread->is_a_complete_scan = (tests_order == global_tests_order); +} + +enum FCS_COMPILE_PRELUDE_ERRORS_T +{ + FCS_COMPILE_PRELUDE_OK, + FCS_COMPILE_PRELUDE_NO_AT_SIGN, + FCS_COMPILE_PRELUDE_UNKNOWN_SCAN_ID +}; + +static int compile_prelude( + freecell_solver_hard_thread_t * hard_thread + ) +{ + char * p_quota, * p_scan, * p; + char * string; + int last_one = 0; + int num_items = 0; + int max_num_items = 16; + fcs_prelude_item_t * prelude; + int st_idx; + + prelude = malloc(sizeof(prelude[0]) * max_num_items); + string = hard_thread->prelude_as_string; + + p = string; + + while (! last_one) + { + p_quota = p; + while((*p) && isdigit(*p)) + { + p++; + } + if (*p != '@') + { + free(prelude); + return FCS_COMPILE_PRELUDE_NO_AT_SIGN; + } + *p = '\0'; + p++; + p_scan = p; + while((*p) && ((*p) != ',')) + { + p++; + } + if ((*p) == '\0') + { + last_one = 1; + } + *p = '\0'; + p++; + + for(st_idx = 0; st_idx < hard_thread->num_soft_threads ; st_idx++) + { + if (!strcmp(hard_thread->soft_threads[st_idx]->name, p_scan)) + { + break; + } + } + if (st_idx == hard_thread->num_soft_threads) + { + free(prelude); + return FCS_COMPILE_PRELUDE_UNKNOWN_SCAN_ID; + } + prelude[num_items].scan_idx = st_idx; + prelude[num_items].quota = atoi(p_quota); + num_items++; + if (num_items == max_num_items) + { + max_num_items += 16; + prelude = realloc(prelude, sizeof(prelude[0]) * max_num_items); + } + } + + hard_thread->prelude = prelude; + hard_thread->prelude_num_items = num_items; + hard_thread->prelude_idx = 0; + + return FCS_COMPILE_PRELUDE_OK; +} + + +void freecell_solver_init_instance(freecell_solver_instance_t * instance) +{ + int ht_idx; + freecell_solver_hard_thread_t * hard_thread; +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT) + instance->num_prev_states_margin = 0; + + instance->max_num_indirect_prev_states = PREV_STATES_GROW_BY; + + instance->indirect_prev_states = (fcs_state_with_locations_t * *)malloc(sizeof(fcs_state_with_locations_t *) * instance->max_num_indirect_prev_states); +#endif + + /* Initialize the state packs */ + for(ht_idx=0;ht_idxnum_hard_threads;ht_idx++) + { + hard_thread = instance->hard_threads[ht_idx]; + if (hard_thread->prelude_as_string) + { + compile_prelude(hard_thread); + } + hard_thread->num_times_left_for_soft_thread = + hard_thread->soft_threads[0]->num_times_step; + freecell_solver_state_ia_init(hard_thread); + } + + /* Normalize the A* Weights, so the sum of all of them would be 1. */ + foreach_soft_thread(instance, normalize_a_star_weights, NULL); + + { + int total_tests = 0; + foreach_soft_thread(instance, accumulate_tests_order, &total_tests); + foreach_soft_thread(instance, determine_scan_completeness, &total_tests); + if (instance->opt_tests_order_set == 0) + { + /* + * + * What this code does is convert the bit map of total_tests + * to a valid tests order. + * + * */ + int bit_idx, num_tests = 0; + int * tests = malloc(sizeof(total_tests)*8*sizeof(tests[0])); + + for(bit_idx=0; total_tests != 0; bit_idx++, total_tests >>= 1) + { + if ((total_tests & 0x1) != 0) + { + tests[num_tests++] = bit_idx; + } + } + tests = realloc(tests, num_tests*sizeof(tests[0])); + instance->opt_tests_order.tests = tests; + instance->opt_tests_order.num = + instance->opt_tests_order.max_num = + num_tests; + instance->opt_tests_order_set = 1; + } + } + + +} + + + + +/* These are all stack comparison functions to be used for the stacks + cache when using INDIRECT_STACK_STATES +*/ +#if defined(INDIRECT_STACK_STATES) + +extern int freecell_solver_stack_compare_for_comparison(const void * v_s1, const void * v_s2); + +#if ((FCS_STACK_STORAGE != FCS_STACK_STORAGE_GLIB_TREE) && (FCS_STACK_STORAGE != FCS_STACK_STORAGE_GLIB_HASH)) +static int fcs_stack_compare_for_comparison_with_context( + const void * v_s1, + const void * v_s2, +#if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE) + const +#endif + void * context + + ) +{ + (void)context; + return freecell_solver_stack_compare_for_comparison(v_s1, v_s2); +} +#endif + + + + + +#if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH) +/* A hash calculation function for use in glib's hash */ +static guint freecell_solver_glib_hash_stack_hash_function ( + gconstpointer key + ) +{ + guint hash_value_int; + /* Calculate the hash value for the stack */ + /* This hash function was ripped from the Perl source code. + * (It is not derived work however). */ + const char * s_ptr = (char*)key; + const char * s_end = s_ptr+fcs_standalone_stack_len((fcs_card_t *)key)+1; + hash_value_int = 0; + while (s_ptr < s_end) + { + hash_value_int += (hash_value_int << 5) + *(s_ptr++); + } + hash_value_int += (hash_value_int >> 5); + +} + + + + + +static gint freecell_solver_glib_hash_stack_compare ( + gconstpointer a, + gconstpointer b +) +{ + return !(fcs_stack_compare_for_comparison(a,b)); +} +#endif /* (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH) */ + + + + + +#endif /* defined(INDIRECT_STACK_STATES) */ + + + + + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH) +/* + * This hash function is defined in caas.c + * + * */ +extern guint freecell_solver_hash_function(gconstpointer key); +#endif + +/* + * This function traces the solution from the final state down + * to the initial state + * */ +static void trace_solution( + freecell_solver_instance_t * instance + ) +{ + /* + Trace the solution. + */ + fcs_state_with_locations_t * s1; + fcs_move_stack_t * solution_moves; + int move_idx; + fcs_move_stack_t * stack; + fcs_move_t * moves; + + if (instance->solution_moves != NULL) + { + fcs_move_stack_destroy(instance->solution_moves); + instance->solution_moves = NULL; + } + + fcs_move_stack_alloc_into_var(solution_moves); + instance->solution_moves = solution_moves; + + s1 = instance->final_state; + + /* Retrace the step from the current state to its parents */ + while (s1->parent != NULL) + { + /* Mark the state as part of the non-optimized solution */ + s1->visited |= FCS_VISITED_IN_SOLUTION_PATH; + /* Duplicate the move stack */ + { + stack = s1->moves_to_parent; + moves = stack->moves; + for(move_idx=stack->num_moves-1;move_idx>=0;move_idx--) + { + fcs_move_stack_push(solution_moves, moves[move_idx]); + } + } + /* Duplicate the state to a freshly malloced memory */ + + /* Move to the parent state */ + s1 = s1->parent; + } + /* There's one more state than there are move stacks */ + s1->visited |= FCS_VISITED_IN_SOLUTION_PATH; +} + + +static fcs_tests_order_t tests_order_dup(fcs_tests_order_t * orig) +{ + fcs_tests_order_t ret; + + ret.max_num = ret.num = orig->num; + ret.tests = malloc(sizeof(ret.tests[0]) * ret.num); + memcpy(ret.tests, orig->tests, sizeof(ret.tests[0]) * ret.num); + + return ret; +} + +/* + This function optimizes the solution path using a BFS scan on the + states in the solution path. +*/ +static int freecell_solver_optimize_solution( + freecell_solver_instance_t * instance + ) +{ + freecell_solver_hard_thread_t * optimization_thread; + freecell_solver_soft_thread_t * soft_thread; + + optimization_thread = alloc_hard_thread(instance); + instance->optimization_thread = optimization_thread; + + soft_thread = optimization_thread->soft_threads[0]; + + if (instance->opt_tests_order_set) + { + if (soft_thread->tests_order.tests != NULL) + { + free(soft_thread->tests_order.tests); + } + + soft_thread->tests_order = + tests_order_dup(&(instance->opt_tests_order)); + } + + soft_thread->method = FCS_METHOD_OPTIMIZE; + + soft_thread->is_a_complete_scan = 1; + + /* Initialize the optimization hard-thread and soft-thread */ + optimization_thread->num_times_left_for_soft_thread = 1000000; + freecell_solver_state_ia_init(optimization_thread); + + /* Instruct the optimization hard thread to run indefinitely AFA it + * is concerned */ + optimization_thread->max_num_times = -1; + optimization_thread->ht_max_num_times = -1; + + return + freecell_solver_a_star_or_bfs_do_solve_or_resume( + optimization_thread->soft_threads[0], + instance->state_copy_ptr, + 0 + ); + +} + + +extern void freecell_solver_cache_talon( + freecell_solver_instance_t * instance, + fcs_state_with_locations_t * new_state + ); + +/* + This function starts the solution process _for the first time_. If one + wishes to proceed after the iterations limit was reached, one should + use freecell_solver_resume_instance. + + */ +int freecell_solver_solve_instance( + freecell_solver_instance_t * instance, + fcs_state_with_locations_t * init_state + ) +{ + fcs_state_with_locations_t * state_copy_ptr; + + /* Allocate the first state and initialize it to init_state */ + fcs_state_ia_alloc_into_var(state_copy_ptr, instance->hard_threads[0]); + + fcs_duplicate_state(*state_copy_ptr, *init_state); + + { + int a; + for(a=0;astacks_num;a++) + { + fcs_copy_stack(*state_copy_ptr, a, instance->hard_threads[0]->indirect_stacks_buffer); + } + } + + /* Initialize the state to be a base state for the game tree */ + state_copy_ptr->depth = 0; + state_copy_ptr->moves_to_parent = NULL; + state_copy_ptr->visited = 0; + state_copy_ptr->parent = NULL; + memset(&(state_copy_ptr->scan_visited), '\0', sizeof(state_copy_ptr->scan_visited)); + + instance->state_copy_ptr = state_copy_ptr; + + /* Initialize the data structure that will manage the state collection */ +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBREDBLACK_TREE) + instance->tree = rbinit(freecell_solver_state_compare_with_context, NULL); +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE) + instance->tree = avl_create(freecell_solver_state_compare_with_context, NULL); +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE) + instance->tree = rb_create(freecell_solver_state_compare_with_context, NULL); +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_TREE) + instance->tree = g_tree_new(freecell_solver_state_compare); +#endif + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH) + instance->hash = g_hash_table_new( + freecell_solver_hash_function, + freecell_solver_state_compare_equal + ); +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) + instance->hash = freecell_solver_hash_init( + 2048, + freecell_solver_state_compare_with_context, + NULL + ); +#endif + + /****************************************************/ + +#ifdef INDIRECT_STACK_STATES + /* Initialize the data structure that will manage the stack + collection */ +#if FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH + instance->stacks_hash = freecell_solver_hash_init( + 2048, + fcs_stack_compare_for_comparison_with_context, + NULL + ); +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE) + instance->stacks_tree = avl_create( + fcs_stack_compare_for_comparison_with_context, + NULL + ); +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE) + instance->stacks_tree = rb_create( + fcs_stack_compare_for_comparison_with_context, + NULL + ); +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE) + instance->stacks_tree = rbinit( + fcs_stack_compare_for_comparison_with_context, + NULL + ); +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE) + instance->stacks_tree = g_tree_new(fcs_stack_compare_for_comparison); +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH) + instance->stacks_hash = g_hash_table_new( + freecell_solver_glib_hash_stack_hash_function, + freecell_solver_glib_hash_stack_compare + ); +#endif +#endif + + /***********************************************/ + +#ifdef FCS_WITH_TALONS + /* Initialize the Talon's Cache */ + if (instance->talon_type == FCS_TALON_KLONDIKE) + { + instance->talons_hash = freecell_solver_hash_init( + 512, + fcs_talon_compare_with_context, + NULL + ); + + freecell_solver_cache_talon(instance, instance->state_copy_ptr); + } +#endif + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_DB_FILE) + /* Not working - ignore */ + db_open( + NULL, + DB_BTREE, + O_CREAT|O_RDWR, + 0777, + NULL, + NULL, + &(instance->db) + ); +#endif + + { + fcs_state_with_locations_t * no_use; + + freecell_solver_check_and_add_state( + instance->hard_threads[0]->soft_threads[0], + state_copy_ptr, + &no_use + ); + + } + + instance->ht_idx = 0; + { + int ht_idx; + for(ht_idx=0; ht_idx < instance->num_hard_threads ; ht_idx++) + { + freecell_solver_hard_thread_t * hard_thread; + hard_thread = instance->hard_threads[ht_idx]; + + if (hard_thread->prelude != NULL) + { + hard_thread->prelude_idx = 0; + hard_thread->st_idx = hard_thread->prelude[hard_thread->prelude_idx].scan_idx; + hard_thread->num_times_left_for_soft_thread = hard_thread->prelude[hard_thread->prelude_idx].quota; + hard_thread->prelude_idx++; + } + else + { + hard_thread->st_idx = 0; + } + } + } + + return freecell_solver_resume_instance(instance); +} + + +static int run_hard_thread(freecell_solver_hard_thread_t * hard_thread) +{ + freecell_solver_soft_thread_t * soft_thread; + int num_times_started_at; + int ret; + freecell_solver_instance_t * instance = hard_thread->instance; + /* + * Again, making sure that not all of the soft_threads in this + * hard thread are finished. + * */ + + ret = FCS_STATE_SUSPEND_PROCESS; + while(hard_thread->num_soft_threads_finished < hard_thread->num_soft_threads) + { + soft_thread = hard_thread->soft_threads[hard_thread->st_idx]; + /* + * Move to the next thread if it's already finished + * */ + if (soft_thread->is_finished) + { + /* + * Hmmpf - duplicate code. That's ANSI C for you. + * A macro, anyone? + * */ + +#define switch_to_next_soft_thread() \ + /* \ + * Switch to the next soft thread in the hard thread, \ + * since we are going to call continue and this is \ + * a while loop \ + * */ \ + if ((hard_thread->prelude != NULL) && \ + (hard_thread->prelude_idx < hard_thread->prelude_num_items)) \ + { \ + hard_thread->st_idx = hard_thread->prelude[hard_thread->prelude_idx].scan_idx; \ + hard_thread->num_times_left_for_soft_thread = hard_thread->prelude[hard_thread->prelude_idx].quota; \ + hard_thread->prelude_idx++; \ + } \ + else \ + { \ + hard_thread->st_idx++; \ + if (hard_thread->st_idx == hard_thread->num_soft_threads) \ + { \ + hard_thread->st_idx = 0; \ + } \ + hard_thread->num_times_left_for_soft_thread = hard_thread->soft_threads[hard_thread->st_idx]->num_times_step; \ + } + + + + switch_to_next_soft_thread(); + + continue; + } + + /* + * Keep record of the number of iterations since this + * thread started. + * */ + num_times_started_at = hard_thread->num_times; + /* + * Calculate a soft thread-wise limit for this hard + * thread to run. + * */ + hard_thread->max_num_times = hard_thread->num_times + hard_thread->num_times_left_for_soft_thread; + + + + /* + * Call the resume or solving function that is specific + * to each scan + * + * This switch-like construct calls for declaring a class + * that will abstract a scan. But it's not critical since + * I don't support user-defined scans. + * */ + switch(soft_thread->method) + { + case FCS_METHOD_HARD_DFS: + + if (! soft_thread->initialized) + { + ret = freecell_solver_hard_dfs_solve_for_state( + soft_thread, + instance->state_copy_ptr, + 0, + 0); + + soft_thread->initialized = 1; + } + else + { + ret = freecell_solver_hard_dfs_resume_solution(soft_thread, 0); + } + break; + + case FCS_METHOD_SOFT_DFS: + + if (! soft_thread->initialized) + { + ret = + freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume( + soft_thread, + instance->state_copy_ptr, + 0, + 0 + ); + soft_thread->initialized = 1; + } + else + { + ret = + freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume( + soft_thread, + NULL, + 1, + 0 + ); + } + break; + + case FCS_METHOD_RANDOM_DFS: + + if (! soft_thread->initialized) + { + ret = + freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume( + soft_thread, + instance->state_copy_ptr, + 0, + 1 + ); + + soft_thread->initialized = 1; + } + else + { + ret = + freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume( + soft_thread, + NULL, + 1, + 1 + ); + } + break; + + case FCS_METHOD_BFS: + case FCS_METHOD_A_STAR: + case FCS_METHOD_OPTIMIZE: + if (! soft_thread->initialized) + { + if (soft_thread->method == FCS_METHOD_A_STAR) + { + freecell_solver_a_star_initialize_rater( + soft_thread, + instance->state_copy_ptr + ); + } + + ret = freecell_solver_a_star_or_bfs_do_solve_or_resume( + soft_thread, + instance->state_copy_ptr, + 0 + ); + + soft_thread->initialized = 1; + } + else + { + ret = + freecell_solver_a_star_or_bfs_do_solve_or_resume( + soft_thread, + soft_thread->first_state_to_check, + 1 + ); + } + break; + + default: + ret = FCS_STATE_IS_NOT_SOLVEABLE; + break; + } + /* + * Determine how much iterations we still have left + * */ + hard_thread->num_times_left_for_soft_thread -= (hard_thread->num_times - num_times_started_at); + + /* + * I use <= instead of == because it is possible that + * there will be a few more iterations than what this + * thread was allocated, due to the fact that + * check_and_add_state is only called by the test + * functions. + * + * It's a kludge, but it works. + * */ + if (hard_thread->num_times_left_for_soft_thread <= 0) + { + switch_to_next_soft_thread(); + /* + * Reset num_times_left_for_soft_thread + * */ + + } + + /* + * It this thread indicated that the scan was finished, + * disable the thread or even stop searching altogether. + * */ + if (ret == FCS_STATE_IS_NOT_SOLVEABLE) + { + soft_thread->is_finished = 1; + hard_thread->num_soft_threads_finished++; + if (hard_thread->num_soft_threads_finished == hard_thread->num_soft_threads) + { + instance->num_hard_threads_finished++; + } + /* + * Check if this thread is a complete scan and if so, + * terminate the search + * */ + if (soft_thread->is_a_complete_scan) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + else + { + /* + * Else, make sure ret is something more sensible + * */ + ret = FCS_STATE_SUSPEND_PROCESS; + } + } + + if ((ret == FCS_STATE_WAS_SOLVED) || + ( + (ret == FCS_STATE_SUSPEND_PROCESS) && + /* There's a limit to the scan only + * if max_num_times is greater than 0 */ + ( + ( + (instance->max_num_times > 0) && + (instance->num_times >= instance->max_num_times) + ) || + ( + (instance->max_num_states_in_collection > 0) && + (instance->num_states_in_collection >= instance->max_num_states_in_collection) + + ) + ) + ) + ) + { + return ret; + } + else if ((ret == FCS_STATE_SUSPEND_PROCESS) && + (hard_thread->num_times >= hard_thread->ht_max_num_times)) + { + hard_thread->ht_max_num_times += hard_thread->num_times_step; + break; + } + } + + return ret; +} + + +/* Resume a solution process that was stopped in the middle */ +int freecell_solver_resume_instance( + freecell_solver_instance_t * instance + ) +{ + int ret = FCS_STATE_SUSPEND_PROCESS; + freecell_solver_hard_thread_t * hard_thread; + + /* + * If the optimization thread is defined, it means we are in the + * optimization phase of the total scan. In that case, just call + * its scanning function. + * + * Else, proceed with the normal total scan. + * */ + if (instance->optimization_thread) + { + ret = + freecell_solver_a_star_or_bfs_do_solve_or_resume( + instance->optimization_thread->soft_threads[0], + instance->optimization_thread->soft_threads[0]->first_state_to_check, + 1 + ); + } + else + { + /* + * instance->num_hard_threads_finished signals to us that + * all the incomplete soft threads terminated. It is necessary + * in case the scan only contains incomplete threads. + * + * I.e: 01235 and 01246, where no thread contains all tests. + * */ + while(instance->num_hard_threads_finished < instance->num_hard_threads) + { + /* + * A loop on the hard threads. + * Note that we do not initialize instance->ht_idx because: + * 1. It is initialized before the first call to this function. + * 2. It is reset to zero below. + * */ + for(; + instance->ht_idx < instance->num_hard_threads ; + instance->ht_idx++) + { + hard_thread = instance->hard_threads[instance->ht_idx]; + + ret = run_hard_thread(hard_thread); + if ((ret == FCS_STATE_IS_NOT_SOLVEABLE) || + (ret == FCS_STATE_WAS_SOLVED) || + ( + (ret == FCS_STATE_SUSPEND_PROCESS) && + /* There's a limit to the scan only + * if max_num_times is greater than 0 */ + ( + ( + (instance->max_num_times > 0) && + (instance->num_times >= instance->max_num_times) + ) || + ( + (instance->max_num_states_in_collection > 0) && + (instance->num_states_in_collection >= instance->max_num_states_in_collection) + + ) + ) + ) + + ) + { + goto end_of_hard_threads_loop; + } + } + /* + * Avoid over-flow + * */ + if (instance->ht_idx == instance->num_hard_threads) + { + instance->ht_idx = 0; + } + } + + end_of_hard_threads_loop: + + /* + * If all the incomplete scans finished, then terminate. + * */ + if (instance->num_hard_threads_finished == instance->num_hard_threads) + { + ret = FCS_STATE_IS_NOT_SOLVEABLE; + } + + if (ret == FCS_STATE_WAS_SOLVED) + { + /* Create solution_moves in the first place */ + trace_solution(instance); + } + } + + + if (ret == FCS_STATE_WAS_SOLVED) + { + if (instance->optimize_solution_path) + { + /* Call optimize_solution only once. Make sure that if + * it has already run - we retain the old ret. */ + if (! instance->optimization_thread) + { + ret = freecell_solver_optimize_solution(instance); + } + if (ret == FCS_STATE_WAS_SOLVED) + { + /* Create the solution_moves in the first place */ + trace_solution(instance); + } + } + } + + return ret; +} + + + +/* + Clean up a solving process that was terminated in the middle. + This function does not substitute for later calling + finish_instance() and free_instance(). + */ +void freecell_solver_unresume_instance( + freecell_solver_instance_t * instance + ) +{ + /* + * Do nothing - since finish_instance() can take care of solution_states + * and proto_solution_moves as they were created by these scans, then + * I don't need to do it here, too + * + * */ + (void)instance; +} + + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE) || (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE) + +static void freecell_solver_tree_do_nothing(void * data, void * context) +{ +} + +#endif + + +/* A function for freeing a stack for the cleanup of the + stacks collection +*/ +#ifdef INDIRECT_STACK_STATES +#if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH) || (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE) || (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE) +#if 0 +static void freecell_solver_stack_free(void * key, void * context) +{ + free(key); +} +#endif + +#elif FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE +static void freecell_solver_libredblack_walk_destroy_stack_action +( + const void * nodep, + const VISIT which, + const int depth, + void * arg + ) +{ + if ((which == leaf) || (which == preorder)) + { + free((void*)nodep); + } +} +#elif FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE +static gint freecell_solver_glib_tree_walk_destroy_stack_action +( + gpointer key, + gpointer value, + gpointer data +) +{ + free(key); + + return 0; +} + +#elif FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH +static void freecell_solver_glib_hash_foreach_destroy_stack_action +( + gpointer key, + gpointer value, + gpointer data +) +{ + free(key); +} +#endif + +#endif + +/***********************************************************/ + + + + +void freecell_solver_destroy_move_stack_of_state( + fcs_state_with_locations_t * ptr_state_with_locations, + void * context + ) +{ + (void)context; + if (ptr_state_with_locations->moves_to_parent != NULL) + { + fcs_move_stack_destroy(ptr_state_with_locations->moves_to_parent); + } +} + +/* + This function should be called after the user has retrieved the + results generated by the scan as it will destroy them. + */ +void freecell_solver_finish_instance( + freecell_solver_instance_t * instance + ) +{ + int ht_idx; + freecell_solver_hard_thread_t * hard_thread; + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT) + free(instance->indirect_prev_states); +#endif + + /* De-allocate the state packs */ + for(ht_idx=0;ht_idxnum_hard_threads;ht_idx++) + { + hard_thread = instance->hard_threads[ht_idx]; + freecell_solver_state_ia_finish(hard_thread); + +#ifdef INDIRECT_STACK_STATES + freecell_solver_compact_allocator_finish(hard_thread->stacks_allocator); + hard_thread->stacks_allocator = NULL; +#endif + freecell_solver_compact_allocator_finish(hard_thread->move_stacks_allocator); + hard_thread->move_stacks_allocator = NULL; + + } + + if (instance->optimization_thread) + { + freecell_solver_state_ia_finish(instance->optimization_thread); + } + + + /* De-allocate the state collection */ +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBREDBLACK_TREE) + rbdestroy(instance->tree); +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE) + avl_destroy(instance->tree, freecell_solver_tree_do_nothing); +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE) + rb_destroy(instance->tree, freecell_solver_tree_do_nothing); +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_TREE) + g_tree_destroy(instance->tree); +#endif + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH) + g_hash_table_destroy(instance->hash); +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) + freecell_solver_hash_free(instance->hash); +#endif + + + + /* De-allocate the stack collection while free()'ing the stacks + in the process */ +#ifdef INDIRECT_STACK_STATES +#if FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH +#if 0 + freecell_solver_hash_free_with_callback(instance->stacks_hash, freecell_solver_stack_free); +#else + freecell_solver_hash_free(instance->stacks_hash); +#endif +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE) +#if 0 + avl_destroy(instance->stacks_tree, freecell_solver_stack_free); +#else + avl_destroy(instance->stacks_tree, NULL); +#endif +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE) +#if 0 + rb_destroy(instance->stacks_tree, freecell_solver_stack_free); +#else + rb_destroy(instance->stacks_tree, NULL); +#endif +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE) +#if 0 + rbwalk(instance->stacks_tree, + freecell_solver_libredblack_walk_destroy_stack_action, + NULL + ); +#endif + rbdestroy(instance->stacks_tree); +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE) +#if 0 + g_tree_traverse( + instance->stacks_tree, + freecell_solver_glib_tree_walk_destroy_stack_action, + G_IN_ORDER, + NULL + ); +#endif + g_tree_destroy(instance->stacks_tree); +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH) +#if 0 + g_hash_table_foreach( + instance->stacks_hash, + freecell_solver_glib_hash_foreach_destroy_stack_action, + NULL + ); +#endif + g_hash_table_destroy(instance->stacks_hash); +#endif +#endif + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_DB_FILE) + instance->db->close(instance->db,0); +#endif + + + clean_soft_dfs(instance); +} + +freecell_solver_soft_thread_t * freecell_solver_instance_get_soft_thread( + freecell_solver_instance_t * instance, + int ht_idx, + int st_idx + ) +{ + if (ht_idx >= instance->num_hard_threads) + { + return NULL; + } + else + { + freecell_solver_hard_thread_t * hard_thread; + hard_thread = instance->hard_threads[ht_idx]; + if (st_idx >= hard_thread->num_soft_threads) + { + return NULL; + } + else + { + return hard_thread->soft_threads[st_idx]; + } + } +} + +freecell_solver_soft_thread_t * freecell_solver_new_soft_thread( + freecell_solver_soft_thread_t * soft_thread + ) +{ + freecell_solver_soft_thread_t * ret; + freecell_solver_hard_thread_t * hard_thread; + + hard_thread = soft_thread->hard_thread; + ret = alloc_soft_thread(hard_thread); + + /* Exceeded the maximal number of Soft-Threads in an instance */ + if (ret == NULL) + { + return NULL; + } + + hard_thread->soft_threads = realloc(hard_thread->soft_threads, sizeof(hard_thread->soft_threads[0])*(hard_thread->num_soft_threads+1)); + hard_thread->soft_threads[hard_thread->num_soft_threads] = ret; + hard_thread->num_soft_threads++; + + return ret; +} + +freecell_solver_soft_thread_t * freecell_solver_new_hard_thread( + freecell_solver_instance_t * instance + ) +{ + freecell_solver_hard_thread_t * ret; + + /* Exceeded the maximal number of Soft-Threads in an instance */ + ret = alloc_hard_thread(instance); + + if (ret == NULL) + { + return NULL; + } + + instance->hard_threads = + realloc( + instance->hard_threads, + (sizeof(instance->hard_threads[0]) * (instance->num_hard_threads+1)) + ); + + instance->hard_threads[instance->num_hard_threads] = ret; + + instance->num_hard_threads++; + + return ret->soft_threads[0]; +} + +void freecell_solver_recycle_instance( + freecell_solver_instance_t * instance + ) +{ + int ht_idx, st_idx; + freecell_solver_hard_thread_t * hard_thread; + freecell_solver_soft_thread_t * soft_thread; + + freecell_solver_finish_instance(instance); + + instance->num_times = 0; + + instance->num_hard_threads_finished = 0; + + for(ht_idx = 0; ht_idx < instance->num_hard_threads; ht_idx++) + { + hard_thread = instance->hard_threads[ht_idx]; + hard_thread->num_times = 0; + hard_thread->ht_max_num_times = hard_thread->num_times_step; + hard_thread->max_num_times = -1; + hard_thread->num_soft_threads_finished = 0; + hard_thread->move_stacks_allocator = + freecell_solver_compact_allocator_new(); +#ifdef INDIRECT_STACK_STATES + hard_thread->stacks_allocator = + freecell_solver_compact_allocator_new(); +#endif + for(st_idx = 0; st_idx < hard_thread->num_soft_threads ; st_idx++) + { + soft_thread = hard_thread->soft_threads[st_idx]; + soft_thread->is_finished = 0; + soft_thread->initialized = 0; + + freecell_solver_rand_srand(soft_thread->rand_gen, soft_thread->rand_seed); + /* Reset the priority queue */ + soft_thread->a_star_pqueue->CurrentSize = 0; + } + } +} diff --git a/kpat/freecell-solver/jhjtypes.h b/kpat/freecell-solver/jhjtypes.h new file mode 100644 index 00000000..5a98f4c2 --- /dev/null +++ b/kpat/freecell-solver/jhjtypes.h @@ -0,0 +1,25 @@ +/* + jhjtypes.h - header file for Justin-Heyes Jones' defined types + + Written by Justin-Heyes Jones + + This file is in the public domain (it's uncopyrighted). + + Check out Justin-Heyes Jones' A* page from which this code has + originated: + http://www.geocities.com/jheyesjones/astar.html +*/ + +#ifndef FC_SOLVE__JHJTYPES_H +#define FC_SOLVE__JHJTYPES_H + +/* Data types used in JHeyes-Jones sample code */ + +typedef int int32; +typedef unsigned int uint32; +typedef short int16; +typedef unsigned short uint16; +typedef signed char int8; +typedef unsigned char uint8; + +#endif /* #ifdef FC_SOLVE__JHJTYPES_H */ diff --git a/kpat/freecell-solver/lib.c b/kpat/freecell-solver/lib.c new file mode 100644 index 00000000..1839614b --- /dev/null +++ b/kpat/freecell-solver/lib.c @@ -0,0 +1,1244 @@ +/* + * lib.c - library interface functions of Freecell Solver. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ +#include +#include +#include + +#include "card.h" +#include "fcs.h" +#include "preset.h" +#include "fcs_user.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +struct fcs_instance_item_struct +{ + freecell_solver_instance_t * instance; + int ret; + int limit; +}; + +typedef struct fcs_instance_item_struct fcs_instance_item_t; + +struct fcs_user_struct +{ + /* + * This is a list of several consecutive instances that are run + * one after the other in case the previous ones could not solve + * the board + * */ + fcs_instance_item_t * instances_list; + int num_instances; + int max_num_instances; + + int current_instance_idx; + /* + * The global (sequence-wide) limit of the iterations. Used + * by limit_iterations() and friends + * */ + int current_iterations_limit; + /* + * The number of iterations this board started at. + * */ + int iterations_board_started_at; + /* + * The number of iterations that the current instance started solving from. + * */ + int init_num_times; + /* + * A pointer to the currently active instance out of the sequence + * */ + freecell_solver_instance_t * instance; + fcs_state_with_locations_t state; + fcs_state_with_locations_t running_state; + int ret; + int state_validity_ret; + fcs_card_t state_validity_card; + freecell_solver_user_iter_handler_t iter_handler; + void * iter_handler_context; + + freecell_solver_soft_thread_t * soft_thread; + +#ifdef INDIRECT_STACK_STATES + fcs_card_t indirect_stacks_buffer[MAX_NUM_STACKS << 7]; +#endif + char * state_string_copy; + + fcs_preset_t common_preset; +}; + +typedef struct fcs_user_struct fcs_user_t; + +static void user_initialize( + fcs_user_t * ret + ) +{ + const fcs_preset_t * freecell_preset; + + freecell_solver_get_preset_by_name( + "freecell", + &freecell_preset + ); + + fcs_duplicate_preset(ret->common_preset, *freecell_preset); + + ret->max_num_instances = 10; + ret->instances_list = malloc(sizeof(ret->instances_list[0]) * ret->max_num_instances); + ret->num_instances = 1; + ret->current_instance_idx = 0; + ret->instance = freecell_solver_alloc_instance(); + freecell_solver_apply_preset_by_ptr(ret->instance, &(ret->common_preset)); + ret->instances_list[ret->current_instance_idx].instance = ret->instance; + ret->instances_list[ret->current_instance_idx].ret = ret->ret = FCS_STATE_NOT_BEGAN_YET; + ret->instances_list[ret->current_instance_idx].limit = -1; + ret->current_iterations_limit = -1; + + ret->soft_thread = + freecell_solver_instance_get_soft_thread( + ret->instance, 0,0 + ); + + ret->state_string_copy = NULL; + ret->iterations_board_started_at = 0; +} + +void * freecell_solver_user_alloc(void) +{ + fcs_user_t * ret; + + ret = (fcs_user_t *)malloc(sizeof(fcs_user_t)); + + user_initialize(ret); + + return (void*)ret; +} + +int freecell_solver_user_apply_preset( + void * user_instance, + const char * preset_name) +{ + const fcs_preset_t * new_preset_ptr; + fcs_user_t * user; + int status; + int i; + + user = (fcs_user_t*)user_instance; + + status = + freecell_solver_get_preset_by_name( + preset_name, + &new_preset_ptr + ); + + if (status != FCS_PRESET_CODE_OK) + { + return status; + } + + for(i = 0 ; i < user->num_instances ; i++) + { + status = freecell_solver_apply_preset_by_ptr( + user->instances_list[i].instance, + new_preset_ptr + ); + + if (status != FCS_PRESET_CODE_OK) + { + return status; + } + } + + fcs_duplicate_preset(user->common_preset, *new_preset_ptr); + + return FCS_PRESET_CODE_OK; +} + +void freecell_solver_user_limit_iterations( + void * user_instance, + int max_iters + ) +{ + fcs_user_t * user; + + user = (fcs_user_t*)user_instance; + + user->current_iterations_limit = max_iters; +} + +void freecell_solver_user_limit_current_instance_iterations( + void * user_instance, + int max_iters + ) +{ + fcs_user_t * user; + + user = (fcs_user_t*)user_instance; + + user->instances_list[user->current_instance_idx].limit = max_iters; +} + +#ifndef min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif + +int freecell_solver_user_set_tests_order( + void * user_instance, + const char * tests_order, + char * * error_string + ) +{ + fcs_user_t * user; + + user = (fcs_user_t*)user_instance; + + return + freecell_solver_apply_tests_order( + &(user->soft_thread->tests_order), + tests_order, + error_string + ); +} + +int freecell_solver_user_solve_board( + void * user_instance, + const char * state_as_string + ) +{ + fcs_user_t * user; + + user = (fcs_user_t*)user_instance; + + user->state_string_copy = strdup(state_as_string); + + user->current_instance_idx = 0; + + return freecell_solver_user_resume_solution(user_instance); +} + +static void recycle_instance( + fcs_user_t * user, + int i + ) +{ + if (user->instances_list[i].ret == FCS_STATE_WAS_SOLVED) + { + fcs_move_stack_destroy(user->instance->solution_moves); + user->instance->solution_moves = NULL; + } + else if (user->instances_list[i].ret == FCS_STATE_SUSPEND_PROCESS) + { + freecell_solver_unresume_instance(user->instances_list[i].instance); + } + + if (user->instances_list[i].ret != FCS_STATE_NOT_BEGAN_YET) + { + freecell_solver_recycle_instance(user->instances_list[i].instance); + /* + * We have to initialize init_num_times to 0 here, because it may not + * get initialized again, and now the num_times of the instance + * is equal to 0. + * */ + user->init_num_times = 0; + } + + user->instances_list[i].ret = FCS_STATE_NOT_BEGAN_YET; +} + +int freecell_solver_user_resume_solution( + void * user_instance + ) +{ + int init_num_times; + int run_for_first_iteration = 1; + int ret; + fcs_user_t * user; + + user = (fcs_user_t*)user_instance; + + /* + * I expect user->current_instance_idx to be initialized at some value. + * */ + for( ; + run_for_first_iteration || ((user->current_instance_idx < user->num_instances) && (ret == FCS_STATE_IS_NOT_SOLVEABLE)) ; + recycle_instance(user, user->current_instance_idx), user->current_instance_idx++ + ) + { + run_for_first_iteration = 0; + + user->instance = user->instances_list[user->current_instance_idx].instance; + + if (user->instances_list[user->current_instance_idx].ret == FCS_STATE_NOT_BEGAN_YET) + { + int status; + status = freecell_solver_initial_user_state_to_c( + user->state_string_copy, + &(user->state), + user->instance->freecells_num, + user->instance->stacks_num, + user->instance->decks_num +#ifdef FCS_WITH_TALONS + ,user->instance->talon_type +#endif +#ifdef INDIRECT_STACK_STATES + ,user->indirect_stacks_buffer +#endif + ); + + if (status != FCS_USER_STATE_TO_C__SUCCESS) + { + user->ret = FCS_STATE_INVALID_STATE; + user->state_validity_ret = FCS_STATE_VALIDITY__PREMATURE_END_OF_INPUT; + return user->ret; + } + + user->state_validity_ret = freecell_solver_check_state_validity( + &user->state, + user->instance->freecells_num, + user->instance->stacks_num, + user->instance->decks_num, +#ifdef FCS_WITH_TALONS + FCS_TALON_NONE, +#endif + &(user->state_validity_card)); + + if (user->state_validity_ret != 0) + { + user->ret = FCS_STATE_INVALID_STATE; + return user->ret; + } + + + /* running_state is a normalized state. So I'm duplicating + * state to it before state is canonized + * */ + fcs_duplicate_state(user->running_state, user->state); + + fcs_canonize_state( + &user->state, + user->instance->freecells_num, + user->instance->stacks_num + ); + + freecell_solver_init_instance(user->instance); + +#define global_limit() \ + (user->instance->num_times + user->current_iterations_limit - user->iterations_board_started_at) +#define local_limit() \ + (user->instances_list[user->current_instance_idx].limit) +#define min(a,b) (((a)<(b))?(a):(b)) +#define calc_max_iters() \ + { \ + if (user->instances_list[user->current_instance_idx].limit < 0) \ + {\ + if (user->current_iterations_limit < 0)\ + {\ + user->instance->max_num_times = -1;\ + }\ + else\ + {\ + user->instance->max_num_times = global_limit();\ + }\ + }\ + else\ + {\ + if (user->current_iterations_limit < 0)\ + {\ + user->instance->max_num_times = local_limit();\ + }\ + else\ + {\ + int a, b;\ + \ + a = global_limit();\ + b = local_limit();\ + \ + user->instance->max_num_times = min(a,b);\ + }\ + }\ + } + + + calc_max_iters(); + + user->init_num_times = init_num_times = user->instance->num_times; + + ret = user->ret = + user->instances_list[user->current_instance_idx].ret = + freecell_solver_solve_instance(user->instance, &user->state); + } + else + { + + calc_max_iters(); + + user->init_num_times = init_num_times = user->instance->num_times; + + ret = user->ret = + user->instances_list[user->current_instance_idx].ret = + freecell_solver_resume_instance(user->instance); + } + + user->iterations_board_started_at += user->instance->num_times - init_num_times; + user->init_num_times = user->instance->num_times; + + if (user->ret == FCS_STATE_WAS_SOLVED) + { + freecell_solver_move_stack_normalize( + user->instance->solution_moves, + &(user->state), + user->instance->freecells_num, + user->instance->stacks_num, + user->instance->decks_num + ); + + break; + } + else if (user->ret == FCS_STATE_SUSPEND_PROCESS) + { + /* + * First - check if we exceeded our limit. If so - we must terminate + * and return now. + * */ + if ((user->current_iterations_limit >= 0) && + (user->iterations_board_started_at >= user->current_iterations_limit)) + { + break; + } + + /* + * Determine if we exceeded the instance-specific quota and if + * so, designate it as unsolvable. + * */ + if ((local_limit() >= 0) && + (user->instance->num_times >= local_limit()) + ) + { + ret = FCS_STATE_IS_NOT_SOLVEABLE; + } + } + } + + return ret; +} + +int freecell_solver_user_get_next_move( + void * user_instance, + fcs_move_t * move + ) +{ + fcs_user_t * user; + + user = (fcs_user_t*)user_instance; + if (user->ret == FCS_STATE_WAS_SOLVED) + { + int ret; + + ret = fcs_move_stack_pop( + user->instance->solution_moves, + move + ); + + if (ret == 0) + { + freecell_solver_apply_move( + &(user->running_state), + *move, + user->instance->freecells_num, + user->instance->stacks_num, + user->instance->decks_num + ); + } + return ret; + } + else + { + return 1; + } +} + +char * freecell_solver_user_current_state_as_string( + void * user_instance, + int parseable_output, + int canonized_order_output, + int display_10_as_t + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + return + freecell_solver_state_as_string( + &(user->running_state), + user->instance->freecells_num, + user->instance->stacks_num, + user->instance->decks_num, + parseable_output, + canonized_order_output, + display_10_as_t + ); +} + +static void user_free_resources( + fcs_user_t * user + ) +{ + int i; + + for(i=0;inum_instances;i++) + { + int ret_code = user->instances_list[i].ret; + + if (ret_code == FCS_STATE_WAS_SOLVED) + { + fcs_move_stack_destroy(user->instance->solution_moves); + user->instance->solution_moves = NULL; + } + else if (ret_code == FCS_STATE_SUSPEND_PROCESS) + { + freecell_solver_unresume_instance(user->instances_list[i].instance); + } + + if (ret_code != FCS_STATE_NOT_BEGAN_YET) + { + if (ret_code != FCS_STATE_INVALID_STATE) + { + freecell_solver_finish_instance(user->instances_list[i].instance); + } + } + + freecell_solver_free_instance(user->instances_list[i].instance); + } + + free(user->instances_list); + + if (user->state_string_copy != NULL) + { + free(user->state_string_copy); + user->state_string_copy = NULL; + } +} + +void freecell_solver_user_free( + void * user_instance + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user_free_resources(user); + + free(user); +} + +int freecell_solver_user_get_current_depth( + void * user_instance + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + return (user->soft_thread->num_solution_states - 1); +} + +void freecell_solver_user_set_solving_method( + void * user_instance, + int method + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user->soft_thread->method = method; +} + +#define set_for_all_instances(what) \ + { \ + for(i = 0 ; i < user->num_instances ; i++) \ + { \ + user->instances_list[i].instance->what = what; \ + } \ + user->common_preset.what = what; \ + } + +int freecell_solver_user_set_num_freecells( + void * user_instance, + int freecells_num + ) +{ + fcs_user_t * user; + int i; + + user = (fcs_user_t *)user_instance; + + if ((freecells_num < 0) || (freecells_num > MAX_NUM_FREECELLS)) + { + return 1; + } + + set_for_all_instances(freecells_num); + + return 0; +} + +int freecell_solver_user_set_num_stacks( + void * user_instance, + int stacks_num + ) +{ + fcs_user_t * user; + int i; + + user = (fcs_user_t *)user_instance; + + if ((stacks_num < 0) || (stacks_num > MAX_NUM_STACKS)) + { + return 1; + } + set_for_all_instances(stacks_num); + + return 0; +} + +int freecell_solver_user_set_num_decks( + void * user_instance, + int decks_num + ) +{ + fcs_user_t * user; + int i; + + user = (fcs_user_t *)user_instance; + + if ((decks_num < 0) || (decks_num > MAX_NUM_DECKS)) + { + return 1; + } + set_for_all_instances(decks_num); + + return 0; +} + + +int freecell_solver_user_set_game( + void * user_instance, + int freecells_num, + int stacks_num, + int decks_num, + int sequences_are_built_by, + int unlimited_sequence_move, + int empty_stacks_fill + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + if (freecell_solver_user_set_num_freecells(user_instance, freecells_num)) + { + return 1; + } + if (freecell_solver_user_set_num_stacks(user_instance, stacks_num)) + { + return 2; + } + if (freecell_solver_user_set_num_decks(user_instance, decks_num)) + { + return 3; + } + if (freecell_solver_user_set_sequences_are_built_by_type(user_instance, sequences_are_built_by)) + { + return 4; + } + if (freecell_solver_user_set_sequence_move(user_instance, unlimited_sequence_move)) + { + return 5; + } + if (freecell_solver_user_set_empty_stacks_filled_by(user_instance, empty_stacks_fill)) + { + return 6; + } + + return 0; +} + +int freecell_solver_user_get_num_times(void * user_instance) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + return user->iterations_board_started_at + user->instance->num_times - user->init_num_times; +} + +int freecell_solver_user_get_limit_iterations(void * user_instance) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + return user->instance->max_num_times; +} + +int freecell_solver_user_get_moves_left(void * user_instance) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + if (user->ret == FCS_STATE_WAS_SOLVED) + return user->instance->solution_moves->num_moves; + else + return 0; +} + +void freecell_solver_user_set_solution_optimization( + void * user_instance, + int optimize +) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user->instance->optimize_solution_path = optimize; +} + +char * freecell_solver_user_move_to_string( + fcs_move_t move, + int standard_notation + ) +{ + return freecell_solver_move_to_string(move, standard_notation); +} + +char * freecell_solver_user_move_to_string_w_state( + void * user_instance, + fcs_move_t move, + int standard_notation + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + return + freecell_solver_move_to_string_w_state( + &(user->running_state), + user->instance->freecells_num, + user->instance->stacks_num, + user->instance->decks_num, + move, + standard_notation + ); +} + +void freecell_solver_user_limit_depth( + void * user_instance, + int max_depth +) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user->instance->max_depth = max_depth; +} + +int freecell_solver_user_get_max_num_freecells(void) +{ + return MAX_NUM_FREECELLS; +} + +int freecell_solver_user_get_max_num_stacks(void) +{ + return MAX_NUM_STACKS; +} + +int freecell_solver_user_get_max_num_decks(void) +{ + return MAX_NUM_DECKS; +} + + +char * freecell_solver_user_get_invalid_state_error_string( + void * user_instance, + int print_ts + ) +{ + fcs_user_t * user; + char string[80], card_str[10]; + + user = (fcs_user_t *)user_instance; + + if (user->state_validity_ret == FCS_STATE_VALIDITY__OK) + { + return strdup(""); + } + fcs_card_perl2user(user->state_validity_card, card_str, print_ts); + + if (user->state_validity_ret == FCS_STATE_VALIDITY__EMPTY_SLOT) + { + sprintf(string, "%s", + "There's an empty slot in one of the stacks." + ); + } + else if ((user->state_validity_ret == FCS_STATE_VALIDITY__EXTRA_CARD) || + (user->state_validity_ret == FCS_STATE_VALIDITY__MISSING_CARD) + ) + { + sprintf(string, "%s%s.", + ((user->state_validity_ret == FCS_STATE_VALIDITY__EXTRA_CARD)? "There's an extra card: " : "There's a missing card: "), + card_str + ); + } + else if (user->state_validity_ret == FCS_STATE_VALIDITY__PREMATURE_END_OF_INPUT) + { + sprintf(string, "%s.", "Not enough input"); + } + return strdup(string); +} + +int freecell_solver_user_set_sequences_are_built_by_type( + void * user_instance, + int sequences_are_built_by + ) +{ + fcs_user_t * user; + int i; + + user = (fcs_user_t *)user_instance; + + if ((sequences_are_built_by < 0) || (sequences_are_built_by > 2)) + { + return 1; + } + set_for_all_instances(sequences_are_built_by) + + return 0; +} + +int freecell_solver_user_set_sequence_move( + void * user_instance, + int unlimited_sequence_move + ) +{ + fcs_user_t * user; + int i; + + user = (fcs_user_t *)user_instance; + + set_for_all_instances(unlimited_sequence_move); + + return 0; +} + +int freecell_solver_user_set_empty_stacks_filled_by( + void * user_instance, + int empty_stacks_fill + ) +{ + fcs_user_t * user; + int i; + + user = (fcs_user_t *)user_instance; + + if ((empty_stacks_fill < 0) || (empty_stacks_fill > 2)) + { + return 1; + } + set_for_all_instances(empty_stacks_fill); + + return 0; +} + +int freecell_solver_user_set_a_star_weight( + void * user_instance, + int index, + double weight + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + if ((index < 0) || (index >= (int)(sizeof(user->soft_thread->a_star_weights)/sizeof(user->soft_thread->a_star_weights[0])))) + { + return 1; + } + if (weight < 0) + { + return 2; + } + + user->soft_thread->a_star_weights[index] = weight; + + return 0; + +} + +static void freecell_solver_user_iter_handler_wrapper( + void * user_instance, + int iter_num, + int depth, + void * lp_instance, + fcs_state_with_locations_t * ptr_state_with_locations, + int parent_iter_num + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user->iter_handler( + user_instance, + iter_num, + depth, + (void *)ptr_state_with_locations, + parent_iter_num, + user->iter_handler_context + ); + + (void)lp_instance; + return; +} + +void freecell_solver_user_set_iter_handler( +void * user_instance, +freecell_solver_user_iter_handler_t iter_handler, +void * iter_handler_context +) +{ +fcs_user_t * user; + +user = (fcs_user_t *)user_instance; + +if (iter_handler == NULL) +{ + user->instance->debug_iter_output = 0; +} +else +{ + /* Disable it temporarily while we change the settings */ + user->instance->debug_iter_output = 0; + user->iter_handler = iter_handler; + user->iter_handler_context = iter_handler_context; + user->instance->debug_iter_output_context = user; + user->instance->debug_iter_output_func = freecell_solver_user_iter_handler_wrapper; + user->instance->debug_iter_output = 1; +} +} + +char * freecell_solver_user_iter_state_as_string( +void * user_instance, +void * ptr_state, +int parseable_output, +int canonized_order_output, +int display_10_as_t +) +{ +fcs_user_t * user; + +user = (fcs_user_t *)user_instance; + +return + freecell_solver_state_as_string( + ptr_state, + user->instance->freecells_num, + user->instance->stacks_num, + user->instance->decks_num, + parseable_output, + canonized_order_output, + display_10_as_t + ); +} + +void freecell_solver_user_set_random_seed( +void * user_instance, +int seed +) +{ +fcs_user_t * user; + +user = (fcs_user_t *)user_instance; + +freecell_solver_rand_srand(user->soft_thread->rand_gen, (user->soft_thread->rand_seed = seed)); +} + +int freecell_solver_user_get_num_states_in_collection(void * user_instance) +{ +fcs_user_t * user; + +user = (fcs_user_t *)user_instance; + +return user->instance->num_states_in_collection; +} + +void freecell_solver_user_limit_num_states_in_collection( +void * user_instance, +int max_num_states + ) +{ + fcs_user_t * user; + + user = (fcs_user_t*)user_instance; + + user->instance->max_num_states_in_collection = max_num_states; +} + +int freecell_solver_user_next_soft_thread( + void * user_instance + ) +{ + fcs_user_t * user; + freecell_solver_soft_thread_t * soft_thread; + + user = (fcs_user_t *)user_instance; + + soft_thread = freecell_solver_new_soft_thread(user->soft_thread); + + if (soft_thread == NULL) + { + return 1; + } + + user->soft_thread = soft_thread; + + return 0; +} + +extern void freecell_solver_user_set_soft_thread_step( + void * user_instance, + int num_times_step + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user->soft_thread->num_times_step = num_times_step; +} + +int freecell_solver_user_next_hard_thread( + void * user_instance + ) +{ + fcs_user_t * user; + freecell_solver_soft_thread_t * soft_thread; + + user = (fcs_user_t *)user_instance; + + soft_thread = freecell_solver_new_hard_thread(user->instance); + + if (soft_thread == NULL) + { + return 1; + } + + user->soft_thread = soft_thread; + + return 0; +} + +int freecell_solver_user_get_num_soft_threads_in_instance( + void * user_instance + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + return user->instance->next_soft_thread_id; +} + +void freecell_solver_user_set_calc_real_depth( + void * user_instance, + int calc_real_depth +) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user->instance->calc_real_depth = calc_real_depth; +} + +void freecell_solver_user_set_soft_thread_name( + void * user_instance, + char * name + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + if (user->soft_thread->name != NULL) + { + free(user->soft_thread->name); + } + user->soft_thread->name = strdup(name); +} + +int freecell_solver_user_set_hard_thread_prelude( + void * user_instance, + char * prelude + ) +{ + fcs_user_t * user; + freecell_solver_hard_thread_t * hard_thread; + + user = (fcs_user_t *)user_instance; + + hard_thread = user->soft_thread->hard_thread; + + if (hard_thread->prelude_as_string != NULL) + { + free(hard_thread->prelude_as_string); + hard_thread->prelude_as_string = NULL; + } + hard_thread->prelude_as_string = strdup(prelude); + + return 0; +} + +void freecell_solver_user_recycle( + void * user_instance + ) +{ + fcs_user_t * user; + int i; + + user = (fcs_user_t *)user_instance; + + for(i=0;inum_instances;i++) + { + recycle_instance(user, i); + } + user->current_iterations_limit = -1; + user->iterations_board_started_at = 0; + if (user->state_string_copy != NULL) + { + free(user->state_string_copy); + user->state_string_copy = NULL; + } +} + +int freecell_solver_user_set_optimization_scan_tests_order( + void * user_instance, + const char * tests_order, + char * * error_string + ) +{ + fcs_user_t * user; + int ret; + + user = (fcs_user_t*)user_instance; + + if (user->instance->opt_tests_order.tests) + { + free(user->instance->opt_tests_order.tests); + user->instance->opt_tests_order.tests = NULL; + } + + user->instance->opt_tests_order_set = 0; + + ret = + freecell_solver_apply_tests_order( + &(user->instance->opt_tests_order), + tests_order, + error_string + ); + + if (!ret) + { + user->instance->opt_tests_order_set = 1; + } + + return ret; +} + +void freecell_solver_user_set_reparent_states( + void * user_instance, + int to_reparent_states + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user->instance->to_reparent_states = to_reparent_states; +} + +void freecell_solver_user_set_scans_synergy( + void * user_instance, + int synergy + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user->instance->scans_synergy = synergy; +} + +int freecell_solver_user_next_instance( + void * user_instance + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user->num_instances++; + if (user->num_instances == user->max_num_instances) + { + user->max_num_instances += 10; + user->instances_list = + realloc( + user->instances_list, + sizeof(user->instances_list[0])*user->max_num_instances + ); + } + user->current_instance_idx = user->num_instances-1; + user->instance = freecell_solver_alloc_instance(); + + freecell_solver_apply_preset_by_ptr(user->instance, &(user->common_preset)); + + /* + * Switch the soft_thread variable so it won't refer to the old + * instance + * */ + user->soft_thread = + freecell_solver_instance_get_soft_thread( + user->instance, 0, 0 + ); + + user->instances_list[user->current_instance_idx].instance = user->instance; + user->instances_list[user->current_instance_idx].ret = user->ret = FCS_STATE_NOT_BEGAN_YET; + user->instances_list[user->current_instance_idx].limit = -1; + + return 0; +} + +int freecell_solver_user_reset(void * user_instance) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user_free_resources(user); + + user_initialize(user); + + return 0; +} + diff --git a/kpat/freecell-solver/lookup2.c b/kpat/freecell-solver/lookup2.c new file mode 100644 index 00000000..6ab9ae7e --- /dev/null +++ b/kpat/freecell-solver/lookup2.c @@ -0,0 +1,119 @@ +/* +-------------------------------------------------------------------- +lookup2.c, by Bob Jenkins, December 1996, Public Domain. +hash(), hash2(), hash3, and mix() are externally useful functions. +Routines to test the hash are included if SELF_TEST is defined. +You can use this free for any purpose. It has no warranty. +-------------------------------------------------------------------- + +Note: + This code was ripped and modified by Shlomi Fish. The original can + be found at http://burtleburtle.net/bob/c/lookup2.c. +*/ + +#include +#include +#include + +#include "lookup2.h" + + +#define hashsize(n) ((ub4)1<<(n)) +#define hashmask(n) (hashsize(n)-1) + +/* +-------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. +For every delta with one or two bit set, and the deltas of all three + high bits or all three low bits, whether the original value of a,b,c + is almost all zero or is uniformly distributed, +* If mix() is run forward or backward, at least 32 bits in a,b,c + have at least 1/4 probability of changing. +* If mix() is run forward, every bit of c will change between 1/3 and + 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.) +mix() was built out of 36 single-cycle latency instructions in a + structure that could supported 2x parallelism, like so: + a -= b; + a -= c; x = (c>>13); + b -= c; a ^= x; + b -= a; x = (a<<8); + c -= a; b ^= x; + c -= b; x = (b>>13); + ... + Unfortunately, superscalar Pentiums and Sparcs can't take advantage + of that parallelism. They've also turned some of those single-cycle + latency instructions into multi-cycle latency instructions. Still, + this is the fastest good hash I could find. There were about 2^^68 + to choose from. I only looked at a billion or so. +-------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ +} + +/* +-------------------------------------------------------------------- + This works on all machines. hash2() is identical to hash() on + little-endian machines, except that the length has to be measured + in ub4s instead of bytes. It is much faster than hash(). It + requires + -- that the key be an array of ub4's, and + -- that all your machines have the same endianness, and + -- that the length be the number of ub4's in the key +-------------------------------------------------------------------- +*/ + +ub4 freecell_solver_lookup2_hash_function( + register ub1 *k, /* the key */ + register ub4 length, /* the length of the key */ + register ub4 initval /* the previous hash, or an arbitrary value */ + ) +{ + register ub4 a,b,c,len; + + /* Set up the internal state */ + len = length; + a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + c = initval; /* the previous hash value */ + + /*---------------------------------------- handle most of the key */ + while (len >= 12) + { + a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24)); + b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24)); + c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24)); + mix(a,b,c); + k += 12; len -= 12; + } + + /*------------------------------------- handle the last 11 bytes */ + c += length; + switch(len) /* all the case statements fall through */ + { + case 11: c+=((ub4)k[10]<<24); + case 10: c+=((ub4)k[9]<<16); + case 9 : c+=((ub4)k[8]<<8); + /* the first byte of c is reserved for the length */ + case 8 : b+=((ub4)k[7]<<24); + case 7 : b+=((ub4)k[6]<<16); + case 6 : b+=((ub4)k[5]<<8); + case 5 : b+=k[4]; + case 4 : a+=((ub4)k[3]<<24); + case 3 : a+=((ub4)k[2]<<16); + case 2 : a+=((ub4)k[1]<<8); + case 1 : a+=k[0]; + /* case 0: nothing left to add */ + } + mix(a,b,c); + /*-------------------------------------------- report the result */ + return c; +} diff --git a/kpat/freecell-solver/lookup2.h b/kpat/freecell-solver/lookup2.h new file mode 100644 index 00000000..002502ed --- /dev/null +++ b/kpat/freecell-solver/lookup2.h @@ -0,0 +1,13 @@ +#ifndef FC_SOLVE__LOOKUP2_H +#define FC_SOLVE__LOOKUP2_H + +typedef unsigned long int ub4; /* unsigned 4-byte quantities */ +typedef unsigned char ub1; + +ub4 freecell_solver_lookup2_hash_function( + register ub1 *k, /* the key */ + register ub4 length, /* the length of the key */ + register ub4 initval /* the previous hash, or an arbitrary value */ + ); + +#endif /* FC_SOLVE__LOOKUP2_H */ diff --git a/kpat/freecell-solver/main.c b/kpat/freecell-solver/main.c new file mode 100644 index 00000000..d16468c4 --- /dev/null +++ b/kpat/freecell-solver/main.c @@ -0,0 +1,859 @@ +#include +#include +#include +#include + +#include "fcs_cl.h" + +struct freecell_solver_display_information_context_struct +{ + int debug_iter_state_output; + int freecells_num; + int stacks_num; + int decks_num; + int parseable_output; + int canonized_order_output; + int display_10_as_t; + int display_parent_iter_num; + int debug_iter_output_on; + int display_moves; + int display_states; + int standard_notation; +}; + +typedef struct freecell_solver_display_information_context_struct freecell_solver_display_information_context_t; + +static void init_debug_context( + freecell_solver_display_information_context_t * dc + ) +{ + dc->parseable_output = 0; + dc->canonized_order_output = 0; + dc->display_10_as_t = 0; + dc->display_parent_iter_num = 0; + dc->display_moves = 0; + dc->display_states = 1; + dc->standard_notation = 0; +} + + + +static void my_iter_handler( + void * user_instance, + int iter_num, + int depth, + void * ptr_state, + int parent_iter_num, + void * lp_context + ) +{ + freecell_solver_display_information_context_t * context; + context = (freecell_solver_display_information_context_t*)lp_context; + + fprintf(stdout, "Iteration: %i\n", iter_num); + fprintf(stdout, "Depth: %i\n", depth); + fprintf(stdout, "Stored-States: %i\n", + freecell_solver_user_get_num_states_in_collection(user_instance) + ); + if (context->display_parent_iter_num) + { + fprintf(stdout, "Parent Iteration: %i\n", parent_iter_num); + } + fprintf(stdout, "\n"); + + + if (context->debug_iter_state_output) + { + char * state_string = + freecell_solver_user_iter_state_as_string( + user_instance, + ptr_state, + context->parseable_output, + context->canonized_order_output, + context->display_10_as_t + ); + printf("%s\n---------------\n\n\n", state_string); + free((void*)state_string); + } + +#ifdef MYDEBUG + { + fcs_card_t card; + int ret; + char card_str[10]; + + ret = fcs_check_state_validity( + ptr_state_with_locations, + context->freecells_num, + context->stacks_num, + context->decks_num, + &card + ); + + if (ret != 0) + { + + fcs_card_perl2user(card, card_str, context->display_10_as_t); + if (ret == 3) + { + fprintf(stdout, "%s\n", + "There's an empty slot in one of the stacks." + ); + } + else + { + fprintf(stdout, "%s%s.\n", + ((ret == 2)? "There's an extra card: " : "There's a missing card: "), + card_str + ); + } + exit(-1); + } + } +#endif +} + +struct help_screen_struct +{ + char * key; + char * screen; +}; + +typedef struct help_screen_struct help_screen_t; + +help_screen_t help_screens[] = { +{ + "configs", +"These configurations are usually faster than the unmodified run:\n" +"\n" +" fc-solve -l cool-jives\n" +" fc-solve -l john-galt-line\n" +"\n" +"Or if you want an accurate verdict:\n" +"\n" +" fc-solve -l fools-gold\n" +"\n" +"If you want to try constructing your own configurations refer to the\n" +"USAGE file in the Freecell Solver distribution\n" +}, +{ + "options", +"fc-solve [options] board_file\n" +"\n" +"If board_file is - or unspecified reads standard input\n" +"\n" +"Available Options:\n" +"-h --help\n" +" display the default help screen\n" +"--help-summary\n" +" display the summary help screen\n" +"-i --iter-output\n" +" display the iteration number and depth in every state that is checked\n" +"-s --state-output\n" +" also output the state in every state that is checked\n" +"-p --parseable-output\n" +" Output the states in a format that is friendly to perl, grep and\n" +" friends.\n" +"-c --canonized-order-output\n" +" Output the stacks and freecells according to their canonic order.\n" +" (That means that stacks and freecells won't retain their place.)\n" +"-t --display-10-as-t\n" +" Display the card 10 as a capital T instead of \"10\".\n" +"-m --display-moves\n" +" Display the moves instead of the intermediate states.\n" +"-sam --display-states-and-moves \n" +" Display both intermediate states and moves.\n" +"-sn --standard-notation\n" +" Display the moves in standard (non-verbose) notation.\n" +" (Applicable only if -m was specified)\n" +"-snx --standard-notation-extended\n" +" Display the moves in extended standard notation while specifying the\n" +" number of cards moved if applicable\n" +"-pi --display-parent-iter \n" +" Display the index of the parent iteration of each state in the\n" +" run-time dump.\n" +"\n" +"--freecells-num [Freecells\' Number]\n" +" The number of freecells present in the board.\n" +"--stacks-num [Stacks\' Number]\n" +" The number of stacks present in the board.\n" +"--decks-num [Decks\' Number]\n" +" The number of decks in the board.\n" +"\n" +"--sequences-are-built-by {suit|alternate_color|rank}\n" +" Specifies the type of sequence\n" +"--sequence-move {limited|unlimited}\n" +" Specifies whether the sequence move is limited by the number of\n" +" freecells or vacant stacks or not.\n" +"--empty-stacks-filled-by {kings|none|all}\n" +" Specifies which cards can fill empty stacks.\n" +"\n" +"--game [game] --preset [game] -g [game]\n" +" Specifies the type of game. (Implies several of the game settings\n" +" options above.). Available presets:\n" +" bakers_dozen - Baker\'s Dozen\n" +" bakers_game - Baker\'s Game\n" +" beleaguered_castle - Beleaguered Castle\n" +" citadel - Citadel\n" +" cruel - Cruel\n" +" der_katz - Der Katzenschwanz\n" +" die_schlange - Die Schlange\n" +" eight_off - Eight Off\n" +" fan - Fan\n" +" forecell - Forecell\n" +" freecell - Freecell\n" +" good_measure - Good Measure\n" +" ko_bakers_game - Kings\' Only Baker\'s Game\n" +" relaxed_freecell - Relaxed Freecell\n" +" relaxed_seahaven - Relaxed Seahaven Towers\n" +" seahaven - Seahaven Towers\n" +" simple_simon - Simple Simon\n" +" streets_and_alleys - Streets and Alleys\n" +"\n" +"-md [depth] --max-depth [depth] \n" +" Specify a maximal search depth for the solution process.\n" +"-mi [iter_num] --max-iters [iter_num] \n" +" Specify a maximal number of iterations number.\n" +"-mss [states_num] --max-stored-states [states_num] \n" +" Specify the maximal number of states stored in memory.\n" +"\n" +"-to [tests_order] --tests-order [tests_order] \n" +" Specify a test order string. Each test is represented by one character.\n" +" Valid tests:\n" +" Freecell Tests:\n" +"\n" +" '0' - put top stack cards in the foundations.\n" +" '1' - put freecell cards in the foundations.\n" +" '2' - put freecell cards on top of stacks.\n" +" '3' - put non-top stack cards in the foundations.\n" +" '4' - move stack cards to different stacks.\n" +" '5' - move stack cards to a parent card on the same stack.\n" +" '6' - move sequences of cards onto free stacks.\n" +" '7' - put freecell cards on empty stacks.\n" +" '8' - move cards to a different parent.\n" +" '9' - empty an entire stack into the freecells.\n" +"\n" +" Atomic Freecell Tests:\n" +"\n" +" 'A' - move a stack card to an empty stack.\n" +" 'B' - move a stack card to a parent on a different stack.\n" +" 'C' - move a stack card to a freecell.\n" +" 'D' - move a freecel card to a parent.\n" +" 'E' - move a freecel card to an empty stack.\n" +"\n" +" Simple Simon Tests:\n" +"\n" +" 'a' - move a full sequence to the foundations.\n" +" 'b' - move a sequence to a true parent of his.\n" +" 'c' - move a whole stack sequence to a false parent (in order to\n" +" clear the stack)\n" +" 'd' - move a sequence to a true parent that has some cards above it.\n" +" 'e' - move a sequence with some cards above it to a true parent.\n" +" 'f' - move a sequence with a junk sequence above it to a true parent\n" +" that has some cards above it.\n" +" 'g' - move a whole stack sequence to a false parent which has some\n" +" cards above it.\n" +" 'h' - move a sequence to a parent on the same stack.\n" +"\n" +" Tests are grouped with parenthesis or square brackets. Each group\n" +" will be randomized as a whole by the random-dfs scan.\n" +"\n" +"\n" +"-me [solving_method] --method [solving_method]\n" +" Specify a solving method. Available methods are:\n" +" \"a-star\" - A*\n" +" \"bfs\" - Breadth-First Search\n" +" \"dfs\" - Depth-First Search (default)\n" +" \"random-dfs\" - A randomized DFS\n" +" \"soft-dfs\" - \"Soft\" DFS\n" +"\n" +"-asw [A* Weights] --a-star-weight [A* Weights]\n" +" Specify weights for the A* scan, assuming it is used. The parameter\n" +" should be a comma-separated list of numbers, each one is proportional\n" +" to the weight of its corresponding test.\n" +"\n" +" The numbers are, in order:\n" +" 1. The number of cards out.\n" +" 2. The maximal sequence move.\n" +" 3. The number of cards under sequences.\n" +" 4. The length of the sequences which are found over renegade cards.\n" +" 5. The depth of the board in the solution.\n" +"\n" +"-seed [seed_number]\n" +" Set the seed for the random number generator used by the\n" +" \"random-dfs\" scan.\n" +"\n" +"-nst --next-soft-thread\n" +" Move to the next Soft-Thread. I.e: input another scan to run in\n" +" parallel.\n" +"-step [step iterations] --soft-thread-step [step iterations]\n" +" Set the number of iterations in the step of the current soft-thread.\n" +"-nht --next-hard-thread\n" +" Move to the next Hard-Thread. This is a new group of scans to run\n" +" in their own system thread (assuming the executable was compiled with\n" +" support for them.)\n" +"--st-name\n" +" Set the name of the soft-thread.\n" +"\n" +"--prelude [prelude_string]\n" +" Set the prelude string of the hard thread. A prelude is a static\n" +" sequence of iterations quotas that are executed at the beginning of\n" +" the search. The format is a list of [Limit]@[Soft-Thread Name]\n" +" delimited by commas.\n" +"\n" +"-ni --next-instance\n" +" Move to the next distinct solver instance. This is a separate scan\n" +" which would run only if the previous ones returned an unsolvable\n" +" verdict.\n" +"\n" +"-opt --optimize-solution\n" +" Try and optimize the solution for a small number of moves.\n" +"-opt-to --optimization-tests-order\n" +" The test order of the optimization scan.\n" +"\n" +"\n" +"--reparent-states\n" +" Reparent states that have a larger depth than that of the state\n" +" from which they were reached a posteriori.\n" +"--calc-real-depth\n" +" If --reparent-states is enabled, then explictly calculate the real\n" +" depth of a state by tracing its path to the initial state\n" +"--scans-synergy {none|dead-end-marks}\n" +" Specifies the cooperation between the scans.\n" +"\n" +"\n" +"--reset\n" +" Reset the program to its initial, unconfigured state.\n" +"--read-from-file [{num_skip},]filename\n" +" Reads configuration parameter with the file while skipping num_skip\n" +" arguments from the beginning.\n" +"-l [configuration] --load-config [configuration]\n" +" Reads the configuration [configruration] and configures the solver\n" +" accordingly.\n" +"\n" +"\n" +"Signals:\n" +"SIGUSR1 - Prints the number of states that were checked so far to stderr.\n" +"SIGUSR2 SIGUSR1 - Turns iteration output on/off.\n" +"SIGUSR2 SIGUSR2 SIGUSR1 - Turns iteration's state output on/off.\n" +"\n" +"\n" +"Freecell Solver was written by Shlomi Fish.\n" +"Homepage: http://vipe.technion.ac.il/~shlomif/freecell-solver/\n" +"Send comments and suggestions to shlomif@vipe.technion.ac.il\n" +}, +{ + "real-help", +"The environment variable FREECELL_SOLVER_DEFAULT_HELP sets the default help\n" +"screen. The name of the help screen is the same name as its \"--help-\" flag\n" +"but without the preceding \"--help-\". Type:\n" +"\n" +" fc-solve --help-summary\n" +"\n" +"for the available help screens.\n" +"\n" +"Refer to your system's documentation for information on how to set environment\n" +"variables.\n" +}, +{ + "problems", +"To be discussed.\n" +}, +{ + "short-sol", +"The following configurations may produce shorter solutions:\n" +"\n" +" fc-solve -opt\n" +" fc-solve --method a-star -opt\n" +" fc-solve --reparent-states -opt\n" +" fc-solve --method a-star --reparent-states -opt\n" +"\n" +"If \"--method a-star\" is specified you can set the weights with\n" +"-asw {comma separated list of 5 numeric weights}, which may improve\n" +"the length of the solution. (refer to the USAGE file for more information)\n" +}, +{ + "summary", +"fc-solve [flags] [board_file|-]\n" +"\n" +"Reads board from standard input by default or if a \"-\" is specified.\n" +"\n" +"- If it takes too long to finish, type \"fc-solve --help-configs\"\n" +"- If it erroneously reports a board as unsolvable, try adding the\n" +" \"-to 01ABCDE\" flag\n" +"- If the solution is too long type \"fc-solve --help-short-sol\"\n" +"- To present the moves only try adding \"-m\" or \"-m -snx\"\n" +"- For a description of all options type \"fc-solve --help-options\"\n" +"- To deal with other problems type \"fc-solve --help-problems\"\n" +"- To turn --help into something more useful, type\n" +" \"fc-solve --help-real-help\"\n" +"\n" +"Contact Shlomi Fish, shlomif@vipe.technion.ac.il for more information.\n" +}, +{ + NULL, + NULL +} +} +; + +enum MY_FCS_CMD_LINE_RET_VALUES +{ + EXIT_AND_RETURN_0 = FCS_CMD_LINE_USER, + +}; + +static void print_help_string(char * key) +{ + int i; + for(i=0;help_screens[i].key != NULL ; i++) + { + if (!strcmp(key, help_screens[i].key)) + { + printf("%s", help_screens[i].screen); + } + } +} + +static int cmd_line_callback( + void * instance, + int argc, + char * argv[], + int arg, + int * num_to_skip, + int * ret, + void * context + ) +{ + freecell_solver_display_information_context_t * dc; + *num_to_skip = 0; + + dc = (freecell_solver_display_information_context_t * )context; + + if ((!strcmp(argv[arg], "-h")) || (!strcmp(argv[arg], "--help"))) + { + char * help_key; + + help_key = getenv("FREECELL_SOLVER_DEFAULT_HELP"); + if (help_key == NULL) + { + help_key = "summary"; + } + print_help_string(help_key); + *ret = EXIT_AND_RETURN_0; + return FCS_CMD_LINE_STOP; + } + else if (!strncmp(argv[arg], "--help-", 7)) + { + print_help_string(argv[arg]+7); + *ret = EXIT_AND_RETURN_0; + return FCS_CMD_LINE_STOP; + } + else if ((!strcmp(argv[arg], "-i")) || (!strcmp(argv[arg], "--iter-output"))) + { +#define set_iter_handler() \ + freecell_solver_user_set_iter_handler( \ + instance, \ + my_iter_handler, \ + dc \ + ); \ + dc->debug_iter_output_on = 1; + + set_iter_handler(); + } + else if ((!strcmp(argv[arg], "-s")) || (!strcmp(argv[arg], "--state-output"))) + { + set_iter_handler(); + dc->debug_iter_state_output = 1; +#undef set_iter_handler + } + else if ((!strcmp(argv[arg], "-p")) || (!strcmp(argv[arg], "--parseable-output"))) + { + dc->parseable_output = 1; + } + else if ((!strcmp(argv[arg], "-c")) || (!strcmp(argv[arg], "--canonized-order-output"))) + { + dc->canonized_order_output = 1; + } + else if ((!strcmp(argv[arg], "-t")) || (!strcmp(argv[arg], "--display-10-as-t"))) + { + dc->display_10_as_t = 1; + } + else if ((!strcmp(argv[arg], "-m")) || (!strcmp(argv[arg], "--display-moves"))) + { + dc->display_moves = 1; + dc->display_states = 0; + } + else if ((!strcmp(argv[arg], "-sn")) || (!strcmp(argv[arg], "--standard-notation"))) + { + dc->standard_notation = 1; + + } + else if ((!strcmp(argv[arg], "-snx")) || (!strcmp(argv[arg], "--standard-notation-extended"))) + { + dc->standard_notation = 2; + } + else if ((!strcmp(argv[arg], "-sam")) || (!strcmp(argv[arg], "--display-states-and-moves"))) + { + dc->display_moves = 1; + dc->display_states = 1; + } + else if ((!strcmp(argv[arg], "-pi")) || (!strcmp(argv[arg], "--display-parent-iter"))) + { + dc->display_parent_iter_num = 1; + } + else if ((!strcmp(argv[arg], "--reset"))) + { + init_debug_context(dc); + freecell_solver_user_set_iter_handler( + instance, + NULL, + NULL + ); + *num_to_skip = 0; + return FCS_CMD_LINE_OK; + } + else + { + printf("Unimplemented option - \"%s\"!", argv[arg]); + exit(-1); + } + *num_to_skip = 1; + return FCS_CMD_LINE_SKIP; +} + + +static int command_num = 0; +static int debug_iter_output_on = 0; + +static void select_signal_handler(int signal_num) +{ + command_num = (command_num+1)%3; +} + +static void * current_instance; +static freecell_solver_display_information_context_t * dc; + + +static void command_signal_handler(int signal_num) +{ + if (command_num == 0) + { + fprintf( + stderr, + "The number of iterations is %i\n", + freecell_solver_user_get_num_times(current_instance) + ); + } + else if (command_num == 1) + { + if (debug_iter_output_on) + { + freecell_solver_user_set_iter_handler( + current_instance, + NULL, + NULL + ); + debug_iter_output_on = 0; + } + else + { + freecell_solver_user_set_iter_handler( + current_instance, + my_iter_handler, + dc + ); + debug_iter_output_on = 1; + } + } + else if (command_num == 2) + { + dc->debug_iter_state_output = ! dc->debug_iter_state_output; + } + + command_num = 0; +} + + +static char * known_parameters[] = { + "-h", "--help", + "--help-configs", "--help-options", "--help-problems", + "--help-real-help", "--help-short-sol", "--help-summary", + "-i", "--iter-output", + "-s", "--state-output", + "-p", "--parseable-output", + "-c", "--canonized-order-output", + "-t", "--display-10-as-t", + "-m", "--display-moves", + "-sn", "--standard-notation", + "-snx", "--standard-notation-extended", + "-sam", "--display-states-and-moves", + "-pi", "--display-parent-iter", + "--reset", + NULL + }; + +#define USER_STATE_SIZE 1024 + +int main(int argc, char * argv[]) +{ + int parser_ret; + void * instance; + char * error_string; + int arg; + FILE * file; + char user_state[USER_STATE_SIZE]; + int ret; + + freecell_solver_display_information_context_t debug_context; + + init_debug_context(&debug_context); + + dc = &debug_context; + + instance = freecell_solver_user_alloc(); + + current_instance = instance; + + + parser_ret = + freecell_solver_user_cmd_line_parse_args( + instance, + argc, + argv, + 1, + known_parameters, + cmd_line_callback, + &debug_context, + &error_string, + &arg + ); + + if (parser_ret == EXIT_AND_RETURN_0) + { + freecell_solver_user_free(instance); + return 0; + } + else if ( + (parser_ret == FCS_CMD_LINE_PARAM_WITH_NO_ARG) + ) + { + fprintf(stderr, "The command line parameter \"%s\" requires an argument" + " and was not supplied with one.\n", argv[arg]); + return (-1); + } + else if ( + (parser_ret == FCS_CMD_LINE_ERROR_IN_ARG) + ) + { + if (error_string != NULL) + { + fprintf(stderr, "%s", error_string); + free(error_string); + } + freecell_solver_user_free(instance); + return -1; + } + + if ((arg == argc) || (!strcmp(argv[arg], "-"))) + { + file = stdin; + if (!getenv("FREECELL_SOLVER_QUIET")) + { + fprintf(stderr, "%s", + "Reading the board from the standard input.\n" + "Type \"fc-solve --help\" for more usage information.\n" + "To cancel this message set the FREECELL_SOLVER_QUIET environment variable.\n" + ); + } + } + else if (argv[arg][0] == '-') + { + fprintf(stderr, + "Unknown option \"%s\". " + "Type \"%s --help\" for usage information.\n", + argv[arg], + argv[0] + ); + freecell_solver_user_free(instance); + + return -1; + } + else + { + file = fopen(argv[arg], "r"); + if (file == NULL) + { + fprintf(stderr, + "Could not open file \"%s\" for input. Exiting.\n", + argv[arg] + ); + freecell_solver_user_free(instance); + + return -1; + } + } + memset(user_state, '\0', sizeof(user_state)); + fread(user_state, sizeof(user_state[0]), USER_STATE_SIZE-1, file); + fclose(file); + + /* Win32 Does not have those signals */ +#ifndef WIN32 + signal(SIGUSR1, command_signal_handler); + signal(SIGUSR2, select_signal_handler); +#endif + +#if 0 + { + int limit = 500; + freecell_solver_user_limit_iterations(instance, limit); + ret = freecell_solver_user_solve_board(instance, user_state); + while (ret == FCS_STATE_SUSPEND_PROCESS) + { + limit += 500; + freecell_solver_user_limit_iterations(instance, limit); + ret = freecell_solver_user_resume_solution(instance); + } + } +#else + ret = freecell_solver_user_solve_board(instance, user_state); +#endif + + if (ret == FCS_STATE_INVALID_STATE) + { + char * error_string; + error_string = + freecell_solver_user_get_invalid_state_error_string( + instance, + debug_context.display_10_as_t + ); + printf("%s\n", error_string); + free(error_string); + } + else + { + if (ret == FCS_STATE_WAS_SOLVED) + { + printf("-=-=-=-=-=-=-=-=-=-=-=-\n\n"); + { + fcs_move_t move; + FILE * move_dump; + char * as_string; + int move_num = 0; + + move_dump = stdout; + + + if (debug_context.display_states) + { + as_string = + freecell_solver_user_current_state_as_string( + instance, + debug_context.parseable_output, + debug_context.canonized_order_output, + debug_context.display_10_as_t + ); + + fprintf(move_dump, "%s\n", as_string); + + free(as_string); + + fprintf(move_dump, "%s", "\n====================\n\n"); + } + + while ( + freecell_solver_user_get_next_move( + instance, + &move + ) == 0 + ) + { + if (debug_context.display_moves) + { + as_string = + freecell_solver_user_move_to_string_w_state( + instance, + move, + debug_context.standard_notation + ); + + if (debug_context.display_states && debug_context.standard_notation) + { + fprintf(move_dump, "Move: "); + } + + fprintf( + move_dump, + (debug_context.standard_notation ? + "%s " : + "%s\n" + ), + as_string + ); + move_num++; + if (debug_context.standard_notation) + { + if ((move_num % 10 == 0) || debug_context.display_states) + { + fprintf(move_dump, "\n"); + } + } + if (debug_context.display_states) + { + fprintf(move_dump, "\n"); + } + fflush(move_dump); + free(as_string); + } + + if (debug_context.display_states) + { + as_string = + freecell_solver_user_current_state_as_string( + instance, + debug_context.parseable_output, + debug_context.canonized_order_output, + debug_context.display_10_as_t + ); + + fprintf(move_dump, "%s\n", as_string); + + free(as_string); + } + + if (debug_context.display_states || (!debug_context.standard_notation)) + { + fprintf(move_dump, "%s", "\n====================\n\n"); + } + } + + if (debug_context.standard_notation && (!debug_context.display_states)) + { + fprintf(move_dump, "\n\n"); + } + } + + printf("This game is solveable.\n"); + } + else + { + printf ("I could not solve this game.\n"); + } + + printf( + "Total number of states checked is %i.\n", + freecell_solver_user_get_num_times(instance) + ); +#if 1 + printf( + "This scan generated %i states.\n", + freecell_solver_user_get_num_states_in_collection(instance) + ); +#endif + } + + freecell_solver_user_free(instance); + + return 0; +} + diff --git a/kpat/freecell-solver/move.c b/kpat/freecell-solver/move.c new file mode 100644 index 00000000..aa8ed560 --- /dev/null +++ b/kpat/freecell-solver/move.c @@ -0,0 +1,531 @@ +/* + * move.c - move and move stacks routines for Freecell Solver + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ +#include +#include +#include + +#include "move.h" +#include "state.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#include "inline.h" + +#if 0 +/* This variable was used for debugging. */ +int msc_counter=0; +#endif + +#if 0 +/* This function allocates an empty move stack */ +fcs_move_stack_t * fcs_move_stack_create(void) +{ + fcs_move_stack_t * ret; + + /* Allocate the data structure itself */ + ret = (fcs_move_stack_t *)malloc(sizeof(fcs_move_stack_t)); + + ret->max_num_moves = FCS_MOVE_STACK_GROW_BY; + ret->num_moves = 0; + /* Allocate some space for the moves */ + ret->moves = (fcs_move_t *)malloc(sizeof(fcs_move_t)*ret->max_num_moves); + + return ret; +} +#endif + +#if 0 +int fcs_move_stack_push(fcs_move_stack_t * stack, fcs_move_t move) +{ + /* If all the moves inside the stack are taken then + resize the move vector */ + + if (stack->num_moves == stack->max_num_moves) + { + int a, b; + a = (stack->max_num_moves >> 3); + b = FCS_MOVE_STACK_GROW_BY; + stack->max_num_moves += max(a,b); + stack->moves = realloc( + stack->moves, + stack->max_num_moves * sizeof(fcs_move_t) + ); + } + stack->moves[stack->num_moves++] = move; + + return 0; +} +#endif + +int freecell_solver_move_stack_pop(fcs_move_stack_t * stack, fcs_move_t * move) +{ + if (stack->num_moves > 0) + { + *move = stack->moves[--stack->num_moves]; + return 0; + } + else + { + return 1; + } +} + +#if 0 +void fcs_move_stack_destroy(fcs_move_stack_t * stack) +{ + free(stack->moves); + free(stack); +} +#endif + +void freecell_solver_move_stack_swallow_stack( + fcs_move_stack_t * stack, + fcs_move_stack_t * src_stack + ) +{ + fcs_move_t move; + while (!fcs_move_stack_pop(src_stack, &move)) + { + fcs_move_stack_push(stack, move); + } + fcs_move_stack_destroy(src_stack); +} + +#if 0 +void fcs_move_stack_reset( + fcs_move_stack_t * stack + ) +{ + stack->num_moves = 0; +} +#endif + +int freecell_solver_move_stack_get_num_moves( + fcs_move_stack_t * stack + ) +{ + return stack->num_moves; +} + +#if 0 +/* + This function duplicates a move stack +*/ +fcs_move_stack_t * fcs_move_stack_duplicate( + fcs_move_stack_t * stack + ) +{ + fcs_move_stack_t * ret; + + ret = (fcs_move_stack_t *)malloc(sizeof(fcs_move_stack_t)); + + ret->max_num_moves = stack->max_num_moves; + ret->num_moves = stack->num_moves; + ret->moves = (fcs_move_t *)malloc(sizeof(fcs_move_t) * ret->max_num_moves); + memcpy(ret->moves, stack->moves, sizeof(fcs_move_t) * ret->max_num_moves); + + return ret; +} +#endif + +#if 0 +extern void fcs_derived_states_list_add_state( + fcs_derived_states_list_t * list, + fcs_state_with_locations_t * state, + fcs_move_stack_t * move_stack + ) +{ + if (list->num_states == list->max_num_states) + { + list->max_num_states += 16; + list->states = realloc(list->states, sizeof(list->states[0]) * list->max_num_states); + list->move_stacks = realloc(list->move_stacks, sizeof(list->move_stacks[0]) * list->max_num_states); + } + list->states[list->num_states] = state; + list->move_stacks[list->num_states] = move_stack; + list->num_states++; +} +#endif +/* + This function performs a given move on a state + + */ +void freecell_solver_apply_move(fcs_state_with_locations_t * state_with_locations, fcs_move_t move, int freecells_num, int stacks_num, int decks_num) +{ + fcs_state_t * state; + fcs_card_t temp_card; + int a; + int src_stack, dest_stack; + int src_freecell, dest_freecell; + int src_stack_len; + + state = (&(state_with_locations->s)); + + dest_stack = fcs_move_get_dest_stack(move); + src_stack = fcs_move_get_src_stack(move); + dest_freecell = fcs_move_get_dest_freecell(move); + src_freecell = fcs_move_get_src_freecell(move); + + + switch(fcs_move_get_type(move)) + { + case FCS_MOVE_TYPE_STACK_TO_STACK: + { + src_stack_len = fcs_stack_len(*state, src_stack); + for(a=0 ; a= 7) + return (fcn+3); + else + return fcn; +} + +char * freecell_solver_move_to_string(fcs_move_t move, int standard_notation) +{ + return + freecell_solver_move_to_string_w_state( + NULL, 4, 8, 1, + move, + (standard_notation == 2)?1:standard_notation + ); +} + +char * freecell_solver_move_to_string_w_state(fcs_state_with_locations_t * state, int freecells_num, int stacks_num, int decks_num, fcs_move_t move, int standard_notation) +{ + char string[256]; + switch(fcs_move_get_type(move)) + { + case FCS_MOVE_TYPE_STACK_TO_STACK: + if ((standard_notation == 2) && + /* More than one card was moved */ + (fcs_move_get_num_cards_in_seq(move) > 1) && + /* It was a move to an empty stack */ + (fcs_stack_len(state->s, fcs_move_get_dest_stack(move)) == + fcs_move_get_num_cards_in_seq(move)) + ) + { + sprintf(string, "%i%iv%x", + 1+fcs_move_get_src_stack(move), + 1+fcs_move_get_dest_stack(move), + fcs_move_get_num_cards_in_seq(move) + ); + } + else if (standard_notation) + { + sprintf(string, "%i%i", + 1+fcs_move_get_src_stack(move), + 1+fcs_move_get_dest_stack(move) + ); + } + else + { + sprintf(string, "Move %i cards from stack %i to stack %i", + fcs_move_get_num_cards_in_seq(move), + fcs_move_get_src_stack(move), + fcs_move_get_dest_stack(move) + ); + } + break; + + case FCS_MOVE_TYPE_FREECELL_TO_STACK: + if (standard_notation) + { + sprintf(string, "%c%i", + ('a'+convert_freecell_num(fcs_move_get_src_freecell(move))), + 1+fcs_move_get_dest_stack(move) + ); + } + else + { + sprintf(string, "Move a card from freecell %i to stack %i", + fcs_move_get_src_freecell(move), + fcs_move_get_dest_stack(move) + ); + } + + break; + + case FCS_MOVE_TYPE_FREECELL_TO_FREECELL: + if (standard_notation) + { + sprintf(string, "%c%c", + ('a'+convert_freecell_num(fcs_move_get_src_freecell(move))), + ('a'+convert_freecell_num(fcs_move_get_dest_freecell(move))) + ); + } + else + { + sprintf(string, "Move a card from freecell %i to freecell %i", + fcs_move_get_src_freecell(move), + fcs_move_get_dest_freecell(move) + ); + } + + break; + + case FCS_MOVE_TYPE_STACK_TO_FREECELL: + if (standard_notation) + { + sprintf(string, "%i%c", + 1+fcs_move_get_src_stack(move), + ('a'+convert_freecell_num(fcs_move_get_dest_freecell(move))) + ); + } + else + { + sprintf(string, "Move a card from stack %i to freecell %i", + fcs_move_get_src_stack(move), + fcs_move_get_dest_freecell(move) + ); + } + + break; + + case FCS_MOVE_TYPE_STACK_TO_FOUNDATION: + if (standard_notation) + { + sprintf(string, "%ih", 1+fcs_move_get_src_stack(move)); + } + else + { + sprintf(string, "Move a card from stack %i to the foundations", + fcs_move_get_src_stack(move) + ); + } + + break; + + + case FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION: + if (standard_notation) + { + sprintf(string, "%ch", ('a'+convert_freecell_num(fcs_move_get_src_freecell(move)))); + } + else + { + sprintf(string, + "Move a card from freecell %i to the foundations", + fcs_move_get_src_freecell(move) + ); + } + + break; + + case FCS_MOVE_TYPE_SEQ_TO_FOUNDATION: + if (standard_notation) + { + sprintf(string, "%ih", fcs_move_get_src_stack(move)); + } + else + { + sprintf(string, + "Move the sequence on top of Stack %i to the foundations", + fcs_move_get_src_stack(move) + ); + } + break; + + default: + string[0] = '\0'; + break; + } + + return strdup(string); +} diff --git a/kpat/freecell-solver/move.h b/kpat/freecell-solver/move.h new file mode 100644 index 00000000..a7501788 --- /dev/null +++ b/kpat/freecell-solver/move.h @@ -0,0 +1,172 @@ +/* + * move.h - header file for the move and move stacks functions of + * Freecell Solver + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#ifndef FC_SOLVE__MOVE_H +#define FC_SOLVE__MOVE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This include is done to prevent a warning in case stdlib.h defines + * max. (which is the case for the Microsoft C Compiler) + * */ +#include + +#include "state.h" +#include "fcs_move.h" + + +#if 0 +fcs_move_stack_t * fcs_move_stack_create(void); +int fcs_move_stack_push(fcs_move_stack_t * stack, fcs_move_t move); +#endif + +#define fcs_move_stack_pop(stack,move) (freecell_solver_move_stack_pop(stack,move)) +extern int freecell_solver_move_stack_pop(fcs_move_stack_t * stack, fcs_move_t * move); + +#if 0 +void fcs_move_stack_destroy(fcs_move_stack_t * stack); +#endif + +#define fcs_move_stack_destroy(stack) \ +{ \ + free((stack)->moves); \ + free(stack); \ +} + +extern void freecell_solver_move_stack_swallow_stack(fcs_move_stack_t * stack, fcs_move_stack_t * src_stack); +#if 0 +void fcs_move_stack_reset(fcs_move_stack_t * stack); +#endif +#define fcs_move_stack_reset(stack) \ +{ \ + (stack)->num_moves = 0; \ +} + + + +#define fcs_move_stack_get_num_moves(stack) (freecell_solver_move_stack_get_num_moves(stack)) +extern int freecell_solver_move_stack_get_num_moves(fcs_move_stack_t * stack); + +#if 0 +fcs_move_stack_t * fcs_move_stack_duplicate(fcs_move_stack_t * stack); +#endif +#define fcs_move_stack_duplicate_into_var(final_ret,stack) \ +{ \ + fcs_move_stack_t * ret; \ + fcs_move_stack_t * temp_stack=(stack) ; \ + \ + ret = (fcs_move_stack_t *)malloc(sizeof(fcs_move_stack_t)); \ + \ + ret->max_num_moves = temp_stack->max_num_moves; \ + ret->num_moves = temp_stack->num_moves; \ + ret->moves = (fcs_move_t *)malloc(sizeof(fcs_move_t) * ret->max_num_moves); \ + memcpy(ret->moves, temp_stack->moves, sizeof(fcs_move_t) * ret->max_num_moves); \ + \ + (final_ret) = ret; \ +} + + + +void freecell_solver_apply_move(fcs_state_with_locations_t * state_with_locations, fcs_move_t move, int freecells_num, int stacks_num, int decks_num); + +void freecell_solver_move_stack_normalize( + fcs_move_stack_t * moves, + fcs_state_with_locations_t * init_state, + int freecells_num, + int stacks_num, + int decks_num + ); + +extern char * freecell_solver_move_to_string(fcs_move_t move, int standard_notation); + +extern char * freecell_solver_move_to_string_w_state(fcs_state_with_locations_t * state, int freecells_num, int stacks_num, int decks_num, fcs_move_t move, int standard_notation); + +struct fcs_derived_states_list_struct +{ + int num_states; + int max_num_states; + fcs_state_with_locations_t * * states; +}; + +typedef struct fcs_derived_states_list_struct fcs_derived_states_list_t; + +#if 0 +extern void fcs_derived_states_list_add_state( + fcs_derived_states_list_t * list, + fcs_state_with_locations_t * state, + fcs_move_stack_t * move_stack + ); +#endif + +#ifndef max +#define max(a,b) (((a)>(b))?(a):(b)) +#endif + +#define FCS_MOVE_STACK_GROW_BY 16 + +/* This macro allocates an empty move stack */ +#define fcs_move_stack_alloc_into_var(final_ret) \ +{ \ + fcs_move_stack_t * ret; \ + \ + /* Allocate the data structure itself */ \ + ret = (fcs_move_stack_t *)malloc(sizeof(fcs_move_stack_t)); \ + \ + ret->max_num_moves = FCS_MOVE_STACK_GROW_BY; \ + ret->num_moves = 0; \ + /* Allocate some space for the moves */ \ + ret->moves = (fcs_move_t *)malloc(sizeof(fcs_move_t)*ret->max_num_moves); \ + \ + (final_ret) = ret; \ +} + + +#define fcs_move_stack_push(stack, move) \ +{ \ + /* If all the moves inside the stack are taken then \ + resize the move vector */ \ + \ + if (stack->num_moves == stack->max_num_moves) \ + { \ + int a, b; \ + a = (stack->max_num_moves >> 3); \ + b = FCS_MOVE_STACK_GROW_BY; \ + stack->max_num_moves += max(a,b); \ + stack->moves = realloc( \ + stack->moves, \ + stack->max_num_moves * sizeof(fcs_move_t) \ + ); \ + } \ + stack->moves[stack->num_moves++] = move; \ + \ +} + +#define fcs_derived_states_list_add_state(list,state) \ + \ +{ \ + if ((list)->num_states == (list)->max_num_states) \ + { \ + (list)->max_num_states += 16; \ + (list)->states = realloc((list)->states, sizeof((list)->states[0]) * (list)->max_num_states); \ + } \ + (list)->states[(list)->num_states] = (state); \ + (list)->num_states++; \ +} + + + + +#ifdef __cplusplus +} +#endif + +#endif /* FC_SOLVE__MOVE_H */ diff --git a/kpat/freecell-solver/ms_ca.h b/kpat/freecell-solver/ms_ca.h new file mode 100644 index 00000000..5c1b44ec --- /dev/null +++ b/kpat/freecell-solver/ms_ca.h @@ -0,0 +1,33 @@ +/* + * ms_ca.h - A header file for a (possibly inline) function that compactly + * allocates a move stack. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2002 + * + * This file is in the public domain (it's uncopyrighted). + * */ + +#include "inline.h" + +static GCC_INLINE fcs_move_stack_t * freecell_solver_move_stack_compact_allocate(freecell_solver_hard_thread_t * hard_thread, fcs_move_stack_t * old_move_stack_to_parent) +{ + char * ptr; + fcs_move_stack_t * new_move_stack_to_parent; + fcs_move_t * new_moves_to_parent; + + fcs_compact_alloc_typed_ptr_into_var( + ptr, + char, + hard_thread->move_stacks_allocator, + (sizeof(fcs_move_stack_t) + sizeof(fcs_move_t)*old_move_stack_to_parent->num_moves) + ); + new_move_stack_to_parent = (fcs_move_stack_t *)ptr; + new_moves_to_parent = (fcs_move_t *)(ptr+sizeof(fcs_move_stack_t)); + new_move_stack_to_parent->moves = new_moves_to_parent; + new_move_stack_to_parent->num_moves = + new_move_stack_to_parent->max_num_moves = + old_move_stack_to_parent->num_moves; + memcpy(new_moves_to_parent, old_move_stack_to_parent->moves, sizeof(fcs_move_t)*old_move_stack_to_parent->num_moves); + return new_move_stack_to_parent; +} + diff --git a/kpat/freecell-solver/pqueue.c b/kpat/freecell-solver/pqueue.c new file mode 100644 index 00000000..086cce96 --- /dev/null +++ b/kpat/freecell-solver/pqueue.c @@ -0,0 +1,173 @@ +/* + pqueue.c - implementation of a priority queue by using a binary heap. + + Originally written by Justin-Heyes Jones + Modified by Shlomi Fish, 2000 + + This file is in the public domain (it's uncopyrighted). + + Check out Justin-Heyes Jones' A* page from which this code has + originated: + http://www.geocities.com/jheyesjones/astar.html + */ + +/* manage a priority queue as a heap + the heap is implemented as a fixed size array of pointers to your data */ + +#include +#include + +#include "jhjtypes.h" + +#include "pqueue.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#define TRUE 1 +#define FALSE 0 + +/* initialise the priority queue with a maximum size of maxelements. maxrating is the highest or lowest value of an + entry in the pqueue depending on whether it is ascending or descending respectively. Finally the bool32 tells you whether + the list is sorted ascending or descending... */ + +void freecell_solver_PQueueInitialise( + PQUEUE *pq, + int32 MaxElements + ) +{ + pq->MaxSize = MaxElements; + + pq->CurrentSize = 0; + + pq->Elements = (pq_element_t*) malloc( sizeof( pq_element_t ) * (MaxElements + 1) ); + + if( pq->Elements == NULL ) + { + printf( "Memory alloc failed!\n" ); + } +} + +/* join a priority queue + returns TRUE if successful, FALSE if fails. (You fail by filling the pqueue.) + PGetRating is a function which returns the rating of the item you're adding for sorting purposes */ + +int freecell_solver_PQueuePush( PQUEUE *pq, void *item, pq_rating_t r) +{ + uint32 i; + pq_element_t * Elements = pq->Elements; + + int32 CurrentSize = pq->CurrentSize; + + if (CurrentSize == pq->MaxSize ) + { + int new_size; + new_size = pq->MaxSize + 256; + pq->Elements = Elements = (pq_element_t *)realloc( Elements, sizeof(pq_element_t) * (new_size+1)); + pq->MaxSize = new_size; + } + + { + /* set i to the first unused element and increment CurrentSize */ + + i = (++CurrentSize); + + /* while the parent of the space we're putting the new node into is worse than + our new node, swap the space with the worse node. We keep doing that until we + get to a worse node or until we get to the top + + note that we also can sort so that the minimum elements bubble up so we need to loops + with the comparison operator flipped... */ + + { + + while( ( i==PQ_FIRST_ENTRY ? + (PQUEUE_MaxRating) /* return biggest possible rating if first element */ + : + (PGetRating(Elements[ PQ_PARENT_INDEX(i) ]) ) + ) + < r + ) + { + Elements[ i ] = Elements[ PQ_PARENT_INDEX(i) ]; + + i = PQ_PARENT_INDEX(i); + } + } + + /* then add the element at the space we created. */ + Elements[i].item = item; + Elements[i].rating = r; + } + + pq->CurrentSize = CurrentSize; + + return TRUE; + +} + +#define PQueueIsEmpty(pq) ((pq)->CurrentSize == 0) + +/* free up memory for pqueue */ +void freecell_solver_PQueueFree( PQUEUE *pq ) +{ + free( pq->Elements ); +} + +/* remove the first node from the pqueue and provide a pointer to it */ + +void *freecell_solver_PQueuePop( PQUEUE *pq) +{ + int32 i; + int32 child; + pq_element_t * Elements = pq->Elements; + int32 CurrentSize = pq->CurrentSize; + + pq_element_t pMaxElement; + pq_element_t pLastElement; + + if( PQueueIsEmpty( pq ) ) + { + return NULL; + } + + pMaxElement = Elements[PQ_FIRST_ENTRY]; + + /* get pointer to last element in tree */ + pLastElement = Elements[ CurrentSize-- ]; + + { + + /* code to pop an element from an ascending (top to bottom) pqueue */ + + /* UNTESTED */ + + for( i=PQ_FIRST_ENTRY; (child = PQ_LEFT_CHILD_INDEX(i)) <= CurrentSize; i=child ) + { + /* set child to the smaller of the two children... */ + + if( (child != CurrentSize) && + (PGetRating(Elements[child + 1]) > PGetRating(Elements[child])) ) + { + child ++; + } + + if( PGetRating( pLastElement ) < PGetRating( Elements[ child ] ) ) + { + Elements[ i ] = Elements[ child ]; + } + else + { + break; + } + } + } + + Elements[i] = pLastElement; + pq->CurrentSize = CurrentSize; + + return pMaxElement.item; +} + + diff --git a/kpat/freecell-solver/pqueue.h b/kpat/freecell-solver/pqueue.h new file mode 100644 index 00000000..cf5f5372 --- /dev/null +++ b/kpat/freecell-solver/pqueue.h @@ -0,0 +1,71 @@ +/* + pqueue.h - header file for the priority queue implementation. + + Originally written by Justin-Heyes Jones + Modified by Shlomi Fish, 2000 + + This file is in the public domain (it's uncopyrighted). + + Check out Justin-Heyes Jones' A* page from which this code has + originated: + http://www.geocities.com/jheyesjones/astar.html +*/ + +#ifndef FC_SOLVE__PQUEUE_H +#define FC_SOLVE__PQUEUE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "jhjtypes.h" + +#define PQUEUE_MaxRating INT_MAX + +typedef int32 pq_rating_t; + +typedef struct struct_pq_element_t +{ + void * item; + pq_rating_t rating; +} pq_element_t; + +typedef struct _PQUEUE +{ + int32 MaxSize; + int32 CurrentSize; + pq_element_t * Elements; /* pointer to void pointers */ + pq_rating_t MaxRating; /* biggest element possible */ +} PQUEUE; + +/* given an index to any element in a binary tree stored in a linear array with the root at 1 and + a "sentinel" value at 0 these macros are useful in making the code clearer */ + +/* the parent is always given by index/2 */ +#define PQ_PARENT_INDEX(i) ((i)>>1) +#define PQ_FIRST_ENTRY (1) + +/* left and right children are index * 2 and (index * 2) +1 respectively */ +#define PQ_LEFT_CHILD_INDEX(i) ((i)<<1) +#define PQ_RIGHT_CHILD_INDEX(i) (((i)<<1)+1) + +void freecell_solver_PQueueInitialise( + PQUEUE *pq, + int32 MaxElements + ); + +void freecell_solver_PQueueFree( PQUEUE *pq ); + +int freecell_solver_PQueuePush( PQUEUE *pq, void *item, pq_rating_t); + +void *freecell_solver_PQueuePop( PQUEUE *pq); + +#define PGetRating(elem) ((elem).rating) + +#ifdef __cplusplus +} +#endif + +#endif /* #ifdef FC_SOLVE__PQUEUE_H */ diff --git a/kpat/freecell-solver/prefix.h b/kpat/freecell-solver/prefix.h new file mode 100644 index 00000000..b3b5094e --- /dev/null +++ b/kpat/freecell-solver/prefix.h @@ -0,0 +1,4 @@ +#define FREECELL_SOLVER_PREFIX "/usr/local" + +#define FREECELL_SOLVER_PKG_DATA_DIR "/usr/local/share/freecell-solver/" + diff --git a/kpat/freecell-solver/preset.c b/kpat/freecell-solver/preset.c new file mode 100644 index 00000000..16a02f1d --- /dev/null +++ b/kpat/freecell-solver/preset.c @@ -0,0 +1,637 @@ +/* + * preset.c - game presets management for Freecell Solver + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + * + */ + + +#include +#include + +#include "fcs.h" +#include "preset.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +enum fcs_presets_ids +{ + FCS_PRESET_BAKERS_DOZEN, + FCS_PRESET_BAKERS_GAME, + FCS_PRESET_CRUEL, + FCS_PRESET_DER_KATZENSCHWANZ, + FCS_PRESET_DIE_SCHLANGE, + FCS_PRESET_EIGHT_OFF, + FCS_PRESET_FAN, + FCS_PRESET_FORECELL, + FCS_PRESET_FREECELL, + FCS_PRESET_GOOD_MEASURE, + FCS_PRESET_KINGS_ONLY_BAKERS_GAME, + FCS_PRESET_RELAXED_FREECELL, + FCS_PRESET_RELAXED_SEAHAVEN_TOWERS, + FCS_PRESET_SEAHAVEN_TOWERS, + FCS_PRESET_SIMPLE_SIMON, + FCS_PRESET_YUKON, + FCS_PRESET_BELEAGUERED_CASTLE +}; + +static const fcs_preset_t fcs_presets[16] = +{ + { + FCS_PRESET_BAKERS_DOZEN, + 0, + 13, + 1, + + FCS_SEQ_BUILT_BY_RANK, + 0, + FCS_ES_FILLED_BY_NONE, + + "0123456789", + "0123456789", + }, + { + FCS_PRESET_BAKERS_GAME, + 4, + 8, + 1, + + FCS_SEQ_BUILT_BY_SUIT, + 0, + FCS_ES_FILLED_BY_ANY_CARD, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_BELEAGUERED_CASTLE, + 0, + 8, + 1, + + FCS_SEQ_BUILT_BY_RANK, + 0, + FCS_ES_FILLED_BY_ANY_CARD, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_CRUEL, + 0, + 12, + 1, + + FCS_SEQ_BUILT_BY_SUIT, + 0, + FCS_ES_FILLED_BY_NONE, + + "0123456789", + "0123456789", + }, + { + FCS_PRESET_DER_KATZENSCHWANZ, + 8, + 9, + 2, + + FCS_SEQ_BUILT_BY_ALTERNATE_COLOR, + 1, + FCS_ES_FILLED_BY_NONE, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_DIE_SCHLANGE, + 8, + 9, + 2, + + FCS_SEQ_BUILT_BY_ALTERNATE_COLOR, + 0, + FCS_ES_FILLED_BY_NONE, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_EIGHT_OFF, + 8, + 8, + 1, + + FCS_SEQ_BUILT_BY_SUIT, + 0, + FCS_ES_FILLED_BY_KINGS_ONLY, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_FAN, + 0, + 18, + 1, + + FCS_SEQ_BUILT_BY_SUIT, + 0, + FCS_ES_FILLED_BY_KINGS_ONLY, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_FORECELL, + 4, + 8, + 1, + + FCS_SEQ_BUILT_BY_ALTERNATE_COLOR, + 0, + FCS_ES_FILLED_BY_KINGS_ONLY, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_FREECELL, + 4, + 8, + 1, + + FCS_SEQ_BUILT_BY_ALTERNATE_COLOR, + 0, + FCS_ES_FILLED_BY_ANY_CARD, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_GOOD_MEASURE, + 0, + 10, + 1, + + FCS_SEQ_BUILT_BY_RANK, + 0, + FCS_ES_FILLED_BY_NONE, + + "0123456789", + "0123456789", + }, + { + FCS_PRESET_KINGS_ONLY_BAKERS_GAME, + 4, + 8, + 1, + + FCS_SEQ_BUILT_BY_SUIT, + 0, + FCS_ES_FILLED_BY_KINGS_ONLY, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_RELAXED_FREECELL, + 4, + 8, + 1, + + FCS_SEQ_BUILT_BY_ALTERNATE_COLOR, + 1, + FCS_ES_FILLED_BY_ANY_CARD, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_RELAXED_SEAHAVEN_TOWERS, + 4, + 10, + 1, + + FCS_SEQ_BUILT_BY_SUIT, + 1, + FCS_ES_FILLED_BY_KINGS_ONLY, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_SEAHAVEN_TOWERS, + 4, + 10, + 1, + + FCS_SEQ_BUILT_BY_SUIT, + 0, + FCS_ES_FILLED_BY_KINGS_ONLY, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_SIMPLE_SIMON, + 0, + 10, + 1, + + FCS_SEQ_BUILT_BY_SUIT, + 0, + FCS_ES_FILLED_BY_ANY_CARD, + + "abcdefgh", + "abcdefgh", + }, +}; + +struct fcs_preset_name_struct +{ + const char name[32]; + int preset_id; +}; + +typedef struct fcs_preset_name_struct fcs_preset_name_t; + +static const fcs_preset_name_t fcs_preset_names[23] = +{ + { + "bakers_dozen", + FCS_PRESET_BAKERS_DOZEN, + }, + { + "bakers_game", + FCS_PRESET_BAKERS_GAME, + }, + { + "beleaguered_castle", + FCS_PRESET_BELEAGUERED_CASTLE, + }, + { + "citadel", + FCS_PRESET_BELEAGUERED_CASTLE, + }, + { + "cruel", + FCS_PRESET_CRUEL, + }, + { + "der_katzenschwanz", + FCS_PRESET_DER_KATZENSCHWANZ, + }, + { + "der_katz", + FCS_PRESET_DER_KATZENSCHWANZ, + }, + { + "die_schlange", + FCS_PRESET_DIE_SCHLANGE, + }, + { + "eight_off", + FCS_PRESET_EIGHT_OFF, + }, + { + "fan", + FCS_PRESET_FAN, + }, + { + "forecell", + FCS_PRESET_FORECELL, + }, + { + "freecell", + FCS_PRESET_FREECELL, + }, + { + "good_measure", + FCS_PRESET_GOOD_MEASURE, + }, + { + "ko_bakers_game", + FCS_PRESET_KINGS_ONLY_BAKERS_GAME, + }, + { + "kings_only_bakers_game", + FCS_PRESET_KINGS_ONLY_BAKERS_GAME, + }, + { + "relaxed_freecell", + FCS_PRESET_RELAXED_FREECELL, + }, + { + "relaxed_seahaven_towers", + FCS_PRESET_RELAXED_SEAHAVEN_TOWERS, + }, + { + "relaxed_seahaven", + FCS_PRESET_RELAXED_SEAHAVEN_TOWERS, + }, + { + "seahaven_towers", + FCS_PRESET_SEAHAVEN_TOWERS, + }, + { + "seahaven", + FCS_PRESET_SEAHAVEN_TOWERS, + }, + { + "simple_simon", + FCS_PRESET_SIMPLE_SIMON, + }, + { + "streets_and_alleys", + FCS_PRESET_BELEAGUERED_CASTLE, + }, + { + "yukon", + FCS_PRESET_YUKON, + }, +}; + +static int fcs_get_preset_id_by_name( + const char * name +) +{ + int a; + int ret = -1; + int num_elems; + + num_elems = ( (int) (sizeof(fcs_preset_names)/sizeof(fcs_preset_names[0]))); + for(a=0;a= '0') && (c <= '9')) + { + return c-'0'; + } + else if ((c >= 'a') && (c <= 'h')) + { + return c-'a'+10; + } + else if ((c >= 'A') && (c <= 'Z')) + { + return c-'A'+18; + } + else + { + return 0; + } +} + +#ifndef min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif + +struct internal_tests_order_struct +{ + int tests_order_num; + int tests_order[FCS_TESTS_NUM]; +}; + +typedef struct internal_tests_order_struct internal_tests_order_t; + +int freecell_solver_apply_tests_order( + fcs_tests_order_t * tests_order, + const char * string, + char * * error_string + ) + +{ + int a; + int len; + int test_index; + int is_group, is_start_group; + if (tests_order->tests) + { + free(tests_order->tests); + tests_order->max_num = 10; + tests_order->num = 0; + tests_order->tests = malloc(sizeof(tests_order->tests[0])*tests_order->max_num ); + } + +#if 0 + instance->tests_order_num = min(strlen(string), FCS_TESTS_NUM); +#endif + len = strlen(string); + test_index = 0; + is_group = 0; + is_start_group = 0; + for(a=0;(amax_num) + { + tests_order->max_num += 10; + tests_order->tests = realloc(tests_order->tests, sizeof(tests_order->tests[0]) * tests_order->max_num); + } + tests_order->tests[test_index] = (freecell_solver_char_to_test_num(string[a])%FCS_TESTS_NUM) | (is_group ? FCS_TEST_ORDER_FLAG_RANDOM : 0) | (is_start_group ? FCS_TEST_ORDER_FLAG_START_RANDOM_GROUP : 0); + + test_index++; + is_start_group = 0; + } + if (a != len) + { + *error_string = strdup("The Input string is too long."); + return 4; + } + + tests_order->num = test_index; + *error_string = NULL; + + return 0; +} + +int freecell_solver_apply_preset_by_ptr( + freecell_solver_instance_t * instance, + const fcs_preset_t * preset_ptr + ) +{ + char * no_use; + +#define preset (*preset_ptr) + if (preset.freecells_num > MAX_NUM_FREECELLS) + { + return FCS_PRESET_CODE_FREECELLS_EXCEED_MAX; + } + if (preset.stacks_num > MAX_NUM_STACKS) + { + return FCS_PRESET_CODE_STACKS_EXCEED_MAX; + } + if (preset.decks_num > MAX_NUM_DECKS) + { + return FCS_PRESET_CODE_DECKS_EXCEED_MAX; + } + instance->freecells_num = preset.freecells_num; + instance->stacks_num = preset.stacks_num; + instance->decks_num = preset.decks_num; + + instance->sequences_are_built_by = preset.sequences_are_built_by; + instance->unlimited_sequence_move = preset.unlimited_sequence_move; + instance->empty_stacks_fill = preset.empty_stacks_fill; + + /* + * This code makes sure that all the tests in all the existing + * soft threads are acceptable by the new preset. + * */ + + { + int ht_idx, st_idx; + for(ht_idx = 0; ht_idx < instance->num_hard_threads ; ht_idx++) + { + for(st_idx = 0; st_idx < instance->hard_threads[ht_idx]->num_soft_threads; st_idx++) + { + freecell_solver_soft_thread_t * soft_thread = instance->hard_threads[ht_idx]->soft_threads[st_idx]; + + int num_valid_tests; + const char * s; + + /* Check every test */ + + for(num_valid_tests=0;num_valid_tests < soft_thread->tests_order.num; num_valid_tests++) + { + for(s = preset.allowed_tests;*s != '\0';s++) + { + /* Check if this test corresponds to this character */ + if ((soft_thread->tests_order.tests[num_valid_tests] & FCS_TEST_ORDER_NO_FLAGS_MASK) == ((freecell_solver_char_to_test_num(*s)%FCS_TESTS_NUM))) + { + break; + } + } + /* If the end of the string was reached, it means + * this test is unacceptable by this preset. */ + if (*s == '\0') + { + break; + } + } + if (num_valid_tests < soft_thread->tests_order.num) + { + freecell_solver_apply_tests_order( + &(soft_thread->tests_order), + preset.tests_order, + &no_use); + } + } + } + } + + /* Assign the master tests order */ + + { + freecell_solver_apply_tests_order( + &(instance->instance_tests_order), + preset.tests_order, + &no_use); + } +#undef preset + return FCS_PRESET_CODE_OK; +} + +static int fcs_get_preset_by_id( + int preset_id, + const fcs_preset_t * * preset_ptr + ) +{ + int preset_index; + int num_elems; + + num_elems = ( (int) (sizeof(fcs_presets)/sizeof(fcs_presets[0]))); + + for(preset_index=0 ; preset_index < num_elems ; preset_index++) + { + if (fcs_presets[preset_index].preset_id == preset_id) + { + *preset_ptr = &(fcs_presets[preset_index]); + return FCS_PRESET_CODE_OK; + } + } + + return FCS_PRESET_CODE_NOT_FOUND; +} + +int freecell_solver_get_preset_by_name( + const char * name, + const fcs_preset_t * * preset_ptr + ) +{ + int preset_id; + + preset_id = fcs_get_preset_id_by_name(name); + if (preset_id >= 0) + { + return fcs_get_preset_by_id( + preset_id, + preset_ptr + ); + } + else + { + return FCS_PRESET_CODE_NOT_FOUND; + } +} + +int freecell_solver_apply_preset_by_name( + freecell_solver_instance_t * instance, + const char * name + ) +{ + int ret; + const fcs_preset_t * preset_ptr; + + ret = freecell_solver_get_preset_by_name( + name, + &preset_ptr + ); + + if (ret != FCS_PRESET_CODE_OK) + { + return ret; + } + + return freecell_solver_apply_preset_by_ptr(instance, preset_ptr); +} diff --git a/kpat/freecell-solver/preset.h b/kpat/freecell-solver/preset.h new file mode 100644 index 00000000..553e9d07 --- /dev/null +++ b/kpat/freecell-solver/preset.h @@ -0,0 +1,62 @@ +/* + * fcs.h - header file of the preset management functions for Freecell Solver. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#ifndef FC_SOLVE__PRESET_H +#define FC_SOLVE__PRESET_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "fcs.h" + +struct fcs_preset_struct +{ + int preset_id; + int freecells_num; + int stacks_num; + int decks_num; + + int sequences_are_built_by; + int unlimited_sequence_move; + int empty_stacks_fill; + + char tests_order[FCS_TESTS_NUM*3+1]; + char allowed_tests[FCS_TESTS_NUM*3+1]; +}; + +typedef struct fcs_preset_struct fcs_preset_t; + +extern int freecell_solver_apply_preset_by_ptr( + freecell_solver_instance_t * instance, + const fcs_preset_t * preset_ptr + ); + +extern int freecell_solver_apply_preset_by_name( + freecell_solver_instance_t * instance, + const char * name + ); + +extern int freecell_solver_apply_tests_order( + fcs_tests_order_t * tests_order, + const char * string, + char * * error_string + ); + +extern int freecell_solver_get_preset_by_name( + const char * name, + const fcs_preset_t * * preset_ptr + ); + +#define fcs_duplicate_preset(d,s) ((d) = (s)) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/kpat/freecell-solver/rand.c b/kpat/freecell-solver/rand.c new file mode 100644 index 00000000..d5151e8e --- /dev/null +++ b/kpat/freecell-solver/rand.c @@ -0,0 +1,30 @@ +#include + +#ifdef DMALLOC +#include +#endif + +#include "rand.h" + +fcs_rand_t * freecell_solver_rand_alloc(unsigned int seed) +{ + fcs_rand_t * ret; + + ret = malloc(sizeof(fcs_rand_t)); + ret->seed = (long)seed; + + return ret; +} + +void freecell_solver_rand_free(fcs_rand_t * rand) +{ + free(rand); +} + + +void freecell_solver_rand_srand(fcs_rand_t * rand, unsigned int seed) +{ + rand->seed = seed; +} + + diff --git a/kpat/freecell-solver/rand.h b/kpat/freecell-solver/rand.h new file mode 100644 index 00000000..0cecfafd --- /dev/null +++ b/kpat/freecell-solver/rand.h @@ -0,0 +1,49 @@ + +#ifndef FC_SOLVE__RAND_H +#define FC_SOLVE__RAND_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "inline.h" + +struct fcs_rand_struct +{ + unsigned long seed; +}; + +typedef struct fcs_rand_struct fcs_rand_t; + +extern fcs_rand_t * freecell_solver_rand_alloc(unsigned int seed); +extern void freecell_solver_rand_free(fcs_rand_t * rand); + +extern void freecell_solver_rand_srand(fcs_rand_t * rand, unsigned int seed); + +static GCC_INLINE int freecell_solver_rand_rand15(fcs_rand_t * rand) +{ + rand->seed = (rand->seed * 214013 + 2531011); + return (rand->seed >> 16) & 0x7fff; +} + +/* + * + * This function constructs a larger integral number of out of two + * 15-bit ones. + * + * */ +static GCC_INLINE int freecell_solver_rand_get_random_number(fcs_rand_t * rand) +{ + int one, two; + one = freecell_solver_rand_rand15(rand); + two = freecell_solver_rand_rand15(rand); + + return (one | (two << 15)); +} + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/kpat/freecell-solver/scans.c b/kpat/freecell-solver/scans.c new file mode 100644 index 00000000..5c579739 --- /dev/null +++ b/kpat/freecell-solver/scans.c @@ -0,0 +1,1170 @@ +/* + * scans.c - The code that relates to the various scans. + * Currently Hard DFS, Soft-DFS, Random-DFS, A* and BFS are implemented. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000-2001 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#include +#include +#include +#include +#include + +#include "fcs_config.h" + +/* So FCS_STATE_STORAGE and friends would be defined */ +#if FCS_STATE_STORAGE==FCS_STATE_STORAGE_LIBREDBLACK_TREE +#include +#endif + +#include "state.h" +#include "card.h" +#include "fcs_dm.h" +#include "fcs.h" + +#include "fcs_isa.h" + +#include "test_arr.h" +#include "caas.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +static pq_rating_t freecell_solver_a_star_rate_state( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations); + +#define freecell_solver_a_star_enqueue_state(soft_thread,ptr_state_with_locations) \ + { \ + freecell_solver_PQueuePush( \ + a_star_pqueue, \ + ptr_state_with_locations, \ + freecell_solver_a_star_rate_state(soft_thread, ptr_state_with_locations) \ + ); \ + } + + +#define freecell_solver_bfs_enqueue_state(soft_thread, state) \ + { \ + fcs_states_linked_list_item_t * last_item_next; \ + last_item_next = bfs_queue_last_item->next = (fcs_states_linked_list_item_t*)malloc(sizeof(fcs_states_linked_list_item_t)); \ + bfs_queue_last_item->s = state; \ + last_item_next->next = NULL; \ + bfs_queue_last_item = last_item_next; \ + } + +#define the_state (ptr_state_with_locations->s) + +int freecell_solver_hard_dfs_solve_for_state( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int depth, + int ignore_osins + ) + +{ + freecell_solver_hard_thread_t * hard_thread = soft_thread->hard_thread; + freecell_solver_instance_t * instance = hard_thread->instance; + + int a; + int check; + + int num_freestacks, num_freecells; + + int iter_num = instance->num_times; + + fcs_derived_states_list_t derived; + + int derived_state_index; + + int ret_value; + + int freecells_num, stacks_num; + + int calc_real_depth, scans_synergy; + + freecells_num = instance->freecells_num; + stacks_num = instance->stacks_num; + + derived.num_states = derived.max_num_states = 0; + derived.states = NULL; + + calc_real_depth = instance->calc_real_depth; + scans_synergy = instance->scans_synergy; + + /* + * If this state has not been visited before - increase the number of + * iterations this program has seen, and output this state again. + * + * I'm doing this in order to make the output of a stopped and + * resumed run consistent with the output of a normal (all-in-one-time) + * run. + * */ + if (!is_scan_visited(ptr_state_with_locations, soft_thread->id)) + { + if (instance->debug_iter_output) + { + instance->debug_iter_output_func( + (void*)instance->debug_iter_output_context, + iter_num, + depth, + (void*)instance, + ptr_state_with_locations, + 0 /* It's a temporary kludge */ + ); + } + /* Increase the number of iterations */ + instance->num_times++; + hard_thread->num_times++; + ptr_state_with_locations->visited_iter = iter_num; + } + + /* Mark this state as visited, so it won't be recursed into again. */ + set_scan_visited(ptr_state_with_locations, soft_thread->id); + + /* Count the free-cells */ + num_freecells = 0; + for(a=0;afinal_state = ptr_state_with_locations; + + ret_value = FCS_STATE_WAS_SOLVED; + goto free_derived; + } + + calculate_real_depth(ptr_state_with_locations); + + for(a=0 ; + a < soft_thread->tests_order.num; + a++) + { + derived.num_states = 0; + + check = + freecell_solver_sfs_tests[soft_thread->tests_order.tests[a] & FCS_TEST_ORDER_NO_FLAGS_MASK ] ( + soft_thread, + ptr_state_with_locations, + num_freestacks, + num_freecells, + &derived, + 0 + ); + + if ((check == FCS_STATE_BEGIN_SUSPEND_PROCESS) || + (check == FCS_STATE_SUSPEND_PROCESS)) + { + if (check == FCS_STATE_BEGIN_SUSPEND_PROCESS) + { + soft_thread->num_solution_states = depth+1; + + soft_thread->soft_dfs_info = malloc(sizeof(soft_thread->soft_dfs_info[0]) * soft_thread->num_solution_states); + } + + soft_thread->soft_dfs_info[depth].state = ptr_state_with_locations; + + ret_value = FCS_STATE_SUSPEND_PROCESS; + + goto free_derived; + } + + for(derived_state_index=0;derived_state_indexvisited & + FCS_VISITED_DEAD_END) + ) && + (! is_scan_visited( + derived.states[derived_state_index], + soft_thread->id) + ) + ) + { + check = + freecell_solver_hard_dfs_solve_for_state( + soft_thread, + derived.states[derived_state_index], + depth+1, + ignore_osins + ); + + if ((check == FCS_STATE_SUSPEND_PROCESS) || + (check == FCS_STATE_BEGIN_SUSPEND_PROCESS)) + { + + soft_thread->soft_dfs_info[depth].state = ptr_state_with_locations; + + ret_value = FCS_STATE_SUSPEND_PROCESS; + + goto free_derived; + } + + if (check == FCS_STATE_WAS_SOLVED) + { + ret_value = FCS_STATE_WAS_SOLVED; + + goto free_derived; + } + } + } + } + + if (check_if_limits_exceeded()) + { + soft_thread->num_solution_states = depth+1; + + soft_thread->soft_dfs_info = malloc(sizeof(soft_thread->soft_dfs_info[0]) * soft_thread->num_solution_states); + + + soft_thread->soft_dfs_info[depth].state = ptr_state_with_locations; + + ret_value = FCS_STATE_SUSPEND_PROCESS; + + goto free_derived; + } + + ret_value = FCS_STATE_IS_NOT_SOLVEABLE; + + if (soft_thread->is_a_complete_scan) + { + mark_as_dead_end(ptr_state_with_locations); + } + + +free_derived: + if (derived.states != NULL) + { + free(derived.states); + } + + return ret_value; +} + + +int freecell_solver_hard_dfs_resume_solution( + freecell_solver_soft_thread_t * soft_thread, + int depth + ) +{ + fcs_state_with_locations_t * ptr_state_with_locations; + int check; + + ptr_state_with_locations = soft_thread->soft_dfs_info[depth].state; + + if (depth < soft_thread->num_solution_states-1) + { + check = freecell_solver_hard_dfs_resume_solution( + soft_thread, + depth+1 + ); + } + else + { + free(soft_thread->soft_dfs_info); + soft_thread->soft_dfs_info = NULL; + check = FCS_STATE_IS_NOT_SOLVEABLE; + } + + if (check == FCS_STATE_IS_NOT_SOLVEABLE) + { + check = freecell_solver_hard_dfs_solve_for_state( + soft_thread, + ptr_state_with_locations, + depth, + 1); + } + else if (check == FCS_STATE_WAS_SOLVED) + { + /* Do nothing - fall back to return check. */ + } + else + { + if ((check == FCS_STATE_SUSPEND_PROCESS) || (check == FCS_STATE_WAS_SOLVED)) + { + + soft_thread->soft_dfs_info[depth].state = ptr_state_with_locations; + } + } + + return check; +} + +#undef state + + + + + +static void freecell_solver_increase_dfs_max_depth( + freecell_solver_soft_thread_t * soft_thread + ) +{ + int new_dfs_max_depth = soft_thread->dfs_max_depth + 16; + int d; + +#define MYREALLOC(what) \ + soft_thread->what = realloc( \ + soft_thread->what, \ + sizeof(soft_thread->what[0])*new_dfs_max_depth \ + ); \ + + MYREALLOC(soft_dfs_info); +#undef MYREALLOC + + for(d=soft_thread->dfs_max_depth ; dsoft_dfs_info[d].state = NULL; + soft_thread->soft_dfs_info[d].derived_states_list.max_num_states = 0; + soft_thread->soft_dfs_info[d].test_index = 0; + soft_thread->soft_dfs_info[d].current_state_index = 0; + soft_thread->soft_dfs_info[d].derived_states_list.num_states = 0; + soft_thread->soft_dfs_info[d].derived_states_list.states = NULL; + soft_thread->soft_dfs_info[d].derived_states_random_indexes = NULL; + soft_thread->soft_dfs_info[d].derived_states_random_indexes_max_size = 0; + } + + soft_thread->dfs_max_depth = new_dfs_max_depth; +} + +/* + freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume is the event loop of the + Random-DFS scan. DFS which is recursive in nature is handled here + without procedural recursion + by using some dedicated stacks for the traversal. + */ +#define the_state (ptr_state_with_locations->s) + +#define myreturn(ret_value) \ + soft_thread->num_solution_states = depth+1; \ + return (ret_value); + +int freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations_orig, + int resume, + int to_randomize + ) +{ + freecell_solver_hard_thread_t * hard_thread = soft_thread->hard_thread; + freecell_solver_instance_t * instance = hard_thread->instance; + + int depth; + fcs_state_with_locations_t * ptr_state_with_locations, + * ptr_recurse_into_state_with_locations; + int a; + int check; + int do_first_iteration; + fcs_soft_dfs_stack_item_t * the_soft_dfs_info; + int freecells_num, stacks_num; + int dfs_max_depth; + + int tests_order_num = soft_thread->tests_order.num; + int * tests_order_tests = soft_thread->tests_order.tests; + int calc_real_depth = instance->calc_real_depth; + int is_a_complete_scan = soft_thread->is_a_complete_scan; + int soft_thread_id = soft_thread->id; + int test_index, current_state_index; + fcs_derived_states_list_t * derived_states_list; + int to_reparent_states, scans_synergy; + + freecells_num = instance->freecells_num; + stacks_num = instance->stacks_num; + to_reparent_states = instance->to_reparent_states; + scans_synergy = instance->scans_synergy; + + if (!resume) + { + /* + Allocate some space for the states at depth 0. + */ + depth=0; + + freecell_solver_increase_dfs_max_depth(soft_thread); + + /* Initialize the initial state to indicate it is the first */ + ptr_state_with_locations_orig->parent = NULL; + ptr_state_with_locations_orig->moves_to_parent = NULL; + ptr_state_with_locations_orig->depth = 0; + + soft_thread->soft_dfs_info[0].state = ptr_state_with_locations_orig; + } + else + { + /* + Set the initial depth to that of the last state encountered. + */ + depth = soft_thread->num_solution_states - 1; + } + + the_soft_dfs_info = &(soft_thread->soft_dfs_info[depth]); + + + dfs_max_depth = soft_thread->dfs_max_depth; + test_index = the_soft_dfs_info->test_index; + current_state_index = the_soft_dfs_info->current_state_index; + ptr_state_with_locations = the_soft_dfs_info->state; + derived_states_list = &(the_soft_dfs_info->derived_states_list); + + calculate_real_depth(ptr_state_with_locations); + + /* + The main loop. + */ + while (depth >= 0) + { + /* + Increase the "maximal" depth if it about to be exceeded. + */ + if (depth+1 >= dfs_max_depth) + { + freecell_solver_increase_dfs_max_depth(soft_thread); + + /* Because the address of soft_thread->soft_dfs_info may + * be changed + * */ + the_soft_dfs_info = &(soft_thread->soft_dfs_info[depth]); + dfs_max_depth = soft_thread->dfs_max_depth; + /* This too has to be re-synced */ + derived_states_list = &(the_soft_dfs_info->derived_states_list); + } + + /* All the resultant states in the last test conducted were covered */ + if (current_state_index == derived_states_list->num_states) + { + if (test_index >= tests_order_num) + { + /* Backtrack to the previous depth. */ + + if (is_a_complete_scan) + { + ptr_state_with_locations->visited |= FCS_VISITED_ALL_TESTS_DONE; + mark_as_dead_end(ptr_state_with_locations); + } + + depth--; + + if (check_if_limits_exceeded()) + { + the_soft_dfs_info->test_index = test_index; + the_soft_dfs_info->current_state_index = current_state_index; + myreturn(FCS_STATE_SUSPEND_PROCESS); + } + + the_soft_dfs_info--; + /* + * depth (and evidently the_soft_dfs_info) might be invalid + * now, so we should check before we assign. + * */ + if (depth >= 0) + { + test_index = the_soft_dfs_info->test_index; + current_state_index = the_soft_dfs_info->current_state_index; + derived_states_list = &(the_soft_dfs_info->derived_states_list); + ptr_state_with_locations = the_soft_dfs_info->state; + } + continue; /* Just to make sure depth is not -1 now */ + } + + derived_states_list->num_states = 0; + + /* If this is the first test, then count the number of unoccupied + freeceels and stacks and check if we are done. */ + if (test_index == 0) + { + int num_freestacks, num_freecells; + + if (instance->debug_iter_output) + { +#ifdef DEBUG + printf("ST Name: %s\n", soft_thread->name); +#endif + instance->debug_iter_output_func( + (void*)instance->debug_iter_output_context, + instance->num_times, + depth, + (void*)instance, + ptr_state_with_locations, + ((depth == 0) ? + 0 : + soft_thread->soft_dfs_info[depth-1].state->visited_iter + ) + ); + } + + /* Count the free-cells */ + num_freecells = 0; + for(a=0;afinal_state = ptr_state_with_locations; + + myreturn(FCS_STATE_WAS_SOLVED); + } + /* + Cache num_freecells and num_freestacks in their + appropriate stacks, so they won't be calculated over and over + again. + */ + the_soft_dfs_info->num_freecells = num_freecells; + the_soft_dfs_info->num_freestacks = num_freestacks; + } + + /* Always do the first test */ + do_first_iteration = 1; + + while ( + /* Make sure we do not exceed the number of tests */ + (test_index < tests_order_num) && + ( + /* Always do the first test */ + do_first_iteration || + ( + /* This is a randomized scan. Else - quit after the first iteration */ + to_randomize && + /* We are still on a random group */ + (tests_order_tests[ test_index ] & FCS_TEST_ORDER_FLAG_RANDOM) && + /* A new random group did not start */ + (! (tests_order_tests[ test_index ] & FCS_TEST_ORDER_FLAG_START_RANDOM_GROUP)) + ) + ) + ) + { + do_first_iteration = 0; + + check = freecell_solver_sfs_tests[tests_order_tests[ + test_index + ] & FCS_TEST_ORDER_NO_FLAGS_MASK] ( + soft_thread, + ptr_state_with_locations, + the_soft_dfs_info->num_freestacks, + the_soft_dfs_info->num_freecells, + derived_states_list, + to_reparent_states + ); + + if ((check == FCS_STATE_BEGIN_SUSPEND_PROCESS) || + (check == FCS_STATE_EXCEEDS_MAX_NUM_TIMES) || + (check == FCS_STATE_SUSPEND_PROCESS)) + { + /* Have this test be re-performed */ + derived_states_list->num_states = 0; + the_soft_dfs_info->current_state_index = 0; + the_soft_dfs_info->test_index = test_index; + myreturn(FCS_STATE_SUSPEND_PROCESS); + } + + /* Move the counter to the next test */ + test_index++; + } + + + { + int a, j; + int swap_save; + int * rand_array, * ra_ptr; + int num_states = derived_states_list->num_states; + + if (num_states > + the_soft_dfs_info->derived_states_random_indexes_max_size) + { + the_soft_dfs_info->derived_states_random_indexes_max_size = + num_states; + the_soft_dfs_info->derived_states_random_indexes = + realloc( + the_soft_dfs_info->derived_states_random_indexes, + sizeof(the_soft_dfs_info->derived_states_random_indexes[0]) * the_soft_dfs_info->derived_states_random_indexes_max_size + ); + } + rand_array = the_soft_dfs_info->derived_states_random_indexes; + + for(a=0, ra_ptr = rand_array; a < num_states ; a++) + { + *(ra_ptr++) = a; + } + /* If we just conducted the tests for a random group - + * randomize. Else - keep those indexes as the unity vector. + * + * Also, do not randomize if this is a pure soft-DFS scan. + * */ + if (to_randomize && tests_order_tests[ test_index-1 ] & FCS_TEST_ORDER_FLAG_RANDOM) + { + a = num_states-1; + while (a > 0) + { + j = + ( + freecell_solver_rand_get_random_number( + soft_thread->rand_gen + ) + % (a+1) + ); + + swap_save = rand_array[a]; + rand_array[a] = rand_array[j]; + rand_array[j] = swap_save; + a--; + } + } + } + + /* We just performed a test, so the index of the first state that + ought to be checked in this depth is 0. + */ + current_state_index = 0; + } + + { + int num_states = derived_states_list->num_states; + fcs_state_with_locations_t * * derived_states = derived_states_list->states; + int * rand_array = the_soft_dfs_info->derived_states_random_indexes; + + while (current_state_index < + num_states) + { + ptr_recurse_into_state_with_locations = + (derived_states[ + rand_array[ + current_state_index + ] + ]); + + current_state_index++; + if ( + (! (ptr_recurse_into_state_with_locations->visited & + FCS_VISITED_DEAD_END) + ) && + (! is_scan_visited( + ptr_recurse_into_state_with_locations, + soft_thread_id) + ) + ) + { + instance->num_times++; + hard_thread->num_times++; + + the_soft_dfs_info->test_index = test_index; + the_soft_dfs_info->current_state_index = current_state_index; + + set_scan_visited(ptr_recurse_into_state_with_locations, soft_thread_id); + + ptr_recurse_into_state_with_locations->visited_iter = instance->num_times; +#if 0 + ptr_recurse_into_state_with_locations->parent = ptr_state_with_locations; +#endif + + /* + I'm using current_state_indexes[depth]-1 because we already + increased it by one, so now it refers to the next state. + */ + depth++; + the_soft_dfs_info++; + the_soft_dfs_info->state = + ptr_state_with_locations = + ptr_recurse_into_state_with_locations; + test_index = 0; + current_state_index = 0; + derived_states_list = &(the_soft_dfs_info->derived_states_list); + derived_states_list->num_states = 0; + + calculate_real_depth(ptr_recurse_into_state_with_locations); + + break; + } + } + } + } + + soft_thread->num_solution_states = 0; + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + + +#undef state +#undef myreturn + +#define FCS_A_STAR_CARDS_UNDER_SEQUENCES_EXPONENT 1.3 +#define FCS_A_STAR_SEQS_OVER_RENEGADE_CARDS_EXPONENT 1.3 + +#define state (ptr_state_with_locations->s) + +void freecell_solver_a_star_initialize_rater( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations + ) +{ + freecell_solver_hard_thread_t * hard_thread = soft_thread->hard_thread; + freecell_solver_instance_t * instance = hard_thread->instance; + + int a, c, cards_num; + fcs_card_t this_card, prev_card; + double cards_under_sequences; + int sequences_are_built_by = instance->sequences_are_built_by; + + + cards_under_sequences = 0; + for(a=0;astacks_num;a++) + { + cards_num = fcs_stack_len(state, a); + if (cards_num <= 1) + { + continue; + } + + c = cards_num-2; + this_card = fcs_stack_card(state, a, c+1); + prev_card = fcs_stack_card(state, a, c); + while (fcs_is_parent_card(this_card,prev_card) && (c >= 0)) + { + c--; + this_card = prev_card; + if (c>=0) + { + prev_card = fcs_stack_card(state, a, c); + } + } + cards_under_sequences += pow(c+1, FCS_A_STAR_CARDS_UNDER_SEQUENCES_EXPONENT); + } + soft_thread->a_star_initial_cards_under_sequences = cards_under_sequences; +} + + +static pq_rating_t freecell_solver_a_star_rate_state( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations + ) +{ + freecell_solver_hard_thread_t * hard_thread = soft_thread->hard_thread; + freecell_solver_instance_t * instance = hard_thread->instance; + + double ret=0; + int a, c, cards_num, num_cards_in_founds; + int num_freestacks, num_freecells; + fcs_card_t this_card, prev_card; + double cards_under_sequences, temp; + double seqs_over_renegade_cards; + int sequences_are_built_by = instance->sequences_are_built_by; + int freecells_num = instance->freecells_num; + int stacks_num = instance->stacks_num; + double * a_star_weights = soft_thread->a_star_weights; + int unlimited_sequence_move = instance->unlimited_sequence_move; + int decks_num = instance->decks_num; + + cards_under_sequences = 0; + num_freestacks = 0; + seqs_over_renegade_cards = 0; + for(a=0;a= 0) && fcs_is_parent_card(this_card,prev_card)) + { + c--; + this_card = prev_card; + if (c>=0) + { + prev_card = fcs_stack_card(state, a, c); + } + } + cards_under_sequences += pow(c+1, FCS_A_STAR_CARDS_UNDER_SEQUENCES_EXPONENT); + if (c >= 0) + { + seqs_over_renegade_cards += + ((unlimited_sequence_move) ? + 1 : + pow(cards_num-c-1, FCS_A_STAR_SEQS_OVER_RENEGADE_CARDS_EXPONENT) + ); + } + } + + ret += ((soft_thread->a_star_initial_cards_under_sequences - cards_under_sequences) + / soft_thread->a_star_initial_cards_under_sequences) * a_star_weights[FCS_A_STAR_WEIGHT_CARDS_UNDER_SEQUENCES]; + + ret += (seqs_over_renegade_cards / + pow(decks_num*52, FCS_A_STAR_SEQS_OVER_RENEGADE_CARDS_EXPONENT) ) + * a_star_weights[FCS_A_STAR_WEIGHT_SEQS_OVER_RENEGADE_CARDS]; + + num_cards_in_founds = 0; + for(a=0;a<(decks_num<<2);a++) + { + num_cards_in_founds += fcs_foundation_value(state, a); + } + + ret += ((double)num_cards_in_founds/(decks_num*52)) * a_star_weights[FCS_A_STAR_WEIGHT_CARDS_OUT]; + + num_freecells = 0; + for(a=0;aempty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD) + { + if (unlimited_sequence_move) + { + temp = (((double)num_freecells+num_freestacks)/(freecells_num+instance->stacks_num)); + } + else + { + temp = (((double)((num_freecells+1)<stacks_num))); + } + } + else + { + if (unlimited_sequence_move) + { + temp = (((double)num_freecells)/freecells_num); + } + else + { + temp = 0; + } + } + + ret += (temp * a_star_weights[FCS_A_STAR_WEIGHT_MAX_SEQUENCE_MOVE]); + + if (ptr_state_with_locations->depth <= 20000) + { + ret += ((20000 - ptr_state_with_locations->depth)/20000.0) * a_star_weights[FCS_A_STAR_WEIGHT_DEPTH]; + } + + return (int)(ret*INT_MAX); +} + + + + +/* + freecell_solver_a_star_or_bfs_do_solve_or_resume() is the main event + loop of the A* And BFS scans. It is quite simple as all it does is + extract elements out of the queue or priority queue and run all the test + of them. + + It goes on in this fashion until the final state was reached or + there are no more states in the queue. +*/ + +#define myreturn(ret_value) \ + /* Free the memory that was allocated by the \ + * derived states list */ \ + if (derived.states != NULL) \ + { \ + free(derived.states); \ + } \ + \ + soft_thread->bfs_queue_last_item = bfs_queue_last_item; \ + \ + return (ret_value); + + +int freecell_solver_a_star_or_bfs_do_solve_or_resume( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations_orig, + int resume + ) +{ + freecell_solver_hard_thread_t * hard_thread = soft_thread->hard_thread; + freecell_solver_instance_t * instance = hard_thread->instance; + + fcs_state_with_locations_t * ptr_state_with_locations; + int num_freestacks, num_freecells; + fcs_states_linked_list_item_t * save_item; + int a; + int check; + fcs_derived_states_list_t derived; + int derived_index; + + int method; + int freecells_num, stacks_num; + int tests_order_num; + int * tests_order_tests; + int calc_real_depth = instance->calc_real_depth; + int soft_thread_id = soft_thread->id; + int is_a_complete_scan = soft_thread->is_a_complete_scan; + int to_reparent_states = + (instance->to_reparent_states || + (soft_thread->method == FCS_METHOD_OPTIMIZE) + ); + int scans_synergy = instance->scans_synergy; + fcs_states_linked_list_item_t * bfs_queue = soft_thread->bfs_queue; + PQUEUE * a_star_pqueue = soft_thread->a_star_pqueue; + fcs_states_linked_list_item_t * bfs_queue_last_item = soft_thread->bfs_queue_last_item; + + derived.num_states = 0; + derived.max_num_states = 0; + derived.states = NULL; + + tests_order_num = soft_thread->tests_order.num; + tests_order_tests = soft_thread->tests_order.tests; + + if (!resume) + { + /* Initialize the first element to indicate it is the first */ + ptr_state_with_locations_orig->parent = NULL; + ptr_state_with_locations_orig->moves_to_parent = NULL; + ptr_state_with_locations_orig->depth = 0; + } + + ptr_state_with_locations = ptr_state_with_locations_orig; + + method = soft_thread->method; + freecells_num = instance->freecells_num; + stacks_num = instance->stacks_num; + + /* Continue as long as there are states in the queue or + priority queue. */ + while ( ptr_state_with_locations != NULL) + { + /* + * If this is an optimization scan and the state being checked is not + * in the original solution path - move on to the next state + * */ + if ((method == FCS_METHOD_OPTIMIZE) && (!(ptr_state_with_locations->visited & FCS_VISITED_IN_SOLUTION_PATH))) + { + goto label_next_state; + } + + /* + * It the state has already been visited - move on to the next + * state. + * */ + if ((method == FCS_METHOD_OPTIMIZE) ? + (ptr_state_with_locations->visited & FCS_VISITED_IN_OPTIMIZED_PATH) : + ((ptr_state_with_locations->visited & FCS_VISITED_DEAD_END) || + (is_scan_visited(ptr_state_with_locations, soft_thread_id))) + ) + { + goto label_next_state; + } + + /* Count the free-cells */ + num_freecells = 0; + for(a=0;adebug_iter_output) && (!resume)) + { +#ifdef DEBUG + printf("ST Name: %s\n", soft_thread->name); +#endif + instance->debug_iter_output_func( + (void*)instance->debug_iter_output_context, + instance->num_times, + ptr_state_with_locations->depth, + (void*)instance, + ptr_state_with_locations, + ((ptr_state_with_locations->parent == NULL) ? + 0 : + ptr_state_with_locations->parent->visited_iter + ) + ); + } + + + if ((num_freestacks == stacks_num) && (num_freecells == freecells_num)) + { + instance->final_state = ptr_state_with_locations; + + myreturn(FCS_STATE_WAS_SOLVED); + } + + calculate_real_depth(ptr_state_with_locations); + + /* Do all the tests at one go, because that the way it should be + done for BFS and A* + */ + derived.num_states = 0; + for(a=0 ; + a < tests_order_num; + a++) + { + check = freecell_solver_sfs_tests[tests_order_tests[a] & FCS_TEST_ORDER_NO_FLAGS_MASK] ( + soft_thread, + ptr_state_with_locations, + num_freestacks, + num_freecells, + &derived, + /* + * We want to reparent the new states, only if this + * is an optimization scan. + * */ + to_reparent_states + ); + if ((check == FCS_STATE_BEGIN_SUSPEND_PROCESS) || + (check == FCS_STATE_EXCEEDS_MAX_NUM_TIMES) || + (check == FCS_STATE_SUSPEND_PROCESS)) + { + /* Save the current position in the scan */ + soft_thread->first_state_to_check = ptr_state_with_locations; + + myreturn(FCS_STATE_SUSPEND_PROCESS); + } + } + + if (check_if_limits_exceeded()) + + { + soft_thread->first_state_to_check = ptr_state_with_locations; + + myreturn(FCS_STATE_SUSPEND_PROCESS); + } + + + if (is_a_complete_scan) + { + ptr_state_with_locations->visited |= FCS_VISITED_ALL_TESTS_DONE; + } + + /* Increase the number of iterations by one . + * */ + { + instance->num_times++; + hard_thread->num_times++; + } + + /* Insert all the derived states into the PQ or Queue */ + + for(derived_index = 0 ; derived_index < derived.num_states ; derived_index++) + { + if (method == FCS_METHOD_A_STAR) + { + freecell_solver_a_star_enqueue_state( + soft_thread, + derived.states[derived_index] + ); + } + else + { + freecell_solver_bfs_enqueue_state( + soft_thread, + derived.states[derived_index] + ); + } + } + + if (method == FCS_METHOD_OPTIMIZE) + { + ptr_state_with_locations->visited |= FCS_VISITED_IN_OPTIMIZED_PATH; + } + else + { + set_scan_visited(ptr_state_with_locations, soft_thread_id); + + if (derived.num_states == 0) + { + if (is_a_complete_scan) + { + mark_as_dead_end(ptr_state_with_locations); + } + } + } + + ptr_state_with_locations->visited_iter = instance->num_times-1; + +label_next_state: + + /* + Extract the next item in the queue/priority queue. + */ + if ((method == FCS_METHOD_BFS) || (method == FCS_METHOD_OPTIMIZE)) + { + save_item = bfs_queue->next; + if (save_item != bfs_queue_last_item) + { + ptr_state_with_locations = save_item->s; + bfs_queue->next = save_item->next; + free(save_item); + } + else + { + ptr_state_with_locations = NULL; + } + } + else + { + /* It is an A* scan */ + ptr_state_with_locations = freecell_solver_PQueuePop(a_star_pqueue); + } + resume = 0; + } + + myreturn(FCS_STATE_IS_NOT_SOLVEABLE); +} + +#undef myreturn + +#undef state diff --git a/kpat/freecell-solver/simpsim.c b/kpat/freecell-solver/simpsim.c new file mode 100644 index 00000000..f603ba39 --- /dev/null +++ b/kpat/freecell-solver/simpsim.c @@ -0,0 +1,1716 @@ +/* + * simpsim.c - a module that contains Simple Simon Moves. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2001 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#include + +#include "fcs.h" + +#include "tests.h" + +#include "ms_ca.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + + +#define fcs_is_ss_false_parent(parent, child) \ + (fcs_card_card_num(parent) == fcs_card_card_num(child)+1) + +#define fcs_suit_is_ss_true_parent(parent_suit, child_suit) \ + ((parent_suit) == (child_suit)) + +#define fcs_is_ss_true_parent(parent, child) \ + ( \ + fcs_is_ss_false_parent(parent,child) && \ + (fcs_suit_is_ss_true_parent(fcs_card_suit(parent),fcs_card_suit(child))) \ + ) + +/* + * Those are some macros to make it easier for the programmer. + * */ +#define state_with_locations (*ptr_state_with_locations) +#define state (ptr_state_with_locations->s) +#define new_state_with_locations (*ptr_new_state_with_locations) +#define new_state (ptr_new_state_with_locations->s) + + + +int freecell_solver_sfs_simple_simon_move_sequence_to_founds( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + + + fcs_move_t temp_move; + + int check; + + fcs_card_t temp_card; + + /* + * stack - the stack index from which to move cards to the founds. + * cards_num - the number of cards in "stack" + * suit - the suit of the complete sequence + * a - the height of the card + * */ + int stack, cards_num, suit, a; + /* + * card - the current card (at height a) + * above_card - the card above it. + * */ + fcs_card_t card, above_card; + + int state_stacks_num; + tests_define_accessors(); + + state_stacks_num = instance->stacks_num; + + + for(stack=0;stack= 13) + { + card = fcs_stack_card(state,stack,cards_num-1); + + /* Check if the top 13 cards are a sequence */ + + for(a=2;a<=13;a++) + { + above_card = fcs_stack_card(state,stack,cards_num-a); + if (fcs_is_ss_true_parent(above_card, card)) + { + /* Do nothing - the card is OK for a propert sequence*/ + } + else + { + break; + } + card = above_card; + } + if (a == 14) + { + /* We can move this sequence up there */ + + sfs_check_state_begin(); + + my_copy_stack(stack); + + suit = fcs_card_suit(card); + for(a=0;a<13;a++) + { + fcs_pop_stack_card(new_state, stack, temp_card); + fcs_increment_foundation(new_state, suit); + } + + + fcs_move_init(temp_move); + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_SEQ_TO_FOUNDATION); + fcs_move_set_src_stack(temp_move, stack); + fcs_move_set_foundation(temp_move,suit); + fcs_move_stack_push(moves,temp_move); + + sfs_check_state_end(); + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + +int freecell_solver_sfs_simple_simon_move_sequence_to_true_parent( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + + + fcs_move_t temp_move; + + int check; + + /* + * stack - the source stack index on which the sequence currently resides. + * cards_num - the number of cards in "stack". + * suit - the suit of the current card + * a - a temporary variable that designates a card height + * */ + int stack, cards_num, suit, a; + /* + * h - the current height in stack + * */ + int h; + /* + * card - the current card (at height h) + * above_card - the card above it. + * dest_card - the destination card on which to put the sequence + * */ + fcs_card_t card, temp_card, dest_card; + /* + * card_num - the card number (i.e: A, 2 ,3 ... K) of the card, or + * its previous one. + * num_true_seqs - the number of true sequences (i.e: sequences of a + * unified suit) in the source sequence. + * ds - the destination stack index. + * dest_cards_num - the number of cards in "ds". + * */ + int card_num, num_true_seqs, ds, dest_cards_num ; + + int state_stacks_num; + tests_define_accessors(); + + state_stacks_num = instance->stacks_num; + + + for(stack=0;stack 0) + { + /* Loop on the cards in the stack and try to look for a true + * parent on top one of the stacks */ + card = fcs_stack_card(state,stack,cards_num-1); + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + num_true_seqs = 1; + + for(h=cards_num-2;h>=-1;h--) + { + for(ds=0;ds 0) + { + dest_card = fcs_stack_card(state, ds, dest_cards_num-1); + if ((fcs_card_suit(dest_card) == suit) && + (fcs_card_card_num(dest_card) == (card_num+1)) + ) + { + /* This is a suitable parent - let's check if we + * have enough empty stacks to make the move feasible */ + if (calc_max_sequence_move(0, num_freestacks) >= num_true_seqs) + { + /* We can do it - so let's move */ + + sfs_check_state_begin(); + + my_copy_stack(stack); + my_copy_stack(ds); + + + fcs_move_sequence(ds, stack, h+1, cards_num-1, a); + sfs_check_state_end(); + + } + } + } + } + + /* Stop if we reached the bottom of the stack */ + if (h == -1) + { + break; + } + + card = fcs_stack_card(state,stack,h); + /* If this is no longer a sequence - move to the next stack */ + if (fcs_card_card_num(card) != card_num+1) + { + break; + } + if (! fcs_suit_is_ss_true_parent(suit, fcs_card_suit(card))) + { + num_true_seqs++; + } + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + +int freecell_solver_sfs_simple_simon_move_whole_stack_sequence_to_false_parent( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + + + fcs_move_t temp_move; + + int check; + + /* + * stack - the source stack index + * cards_num - number of cards in "stack" + * ds - the dest stack index + * dest_cards_num - number of cards in "ds". + * card - the current card + * card_num - its card number + * suit - its suit + * dest_card - the card at the top of "ds". + * h - the height of the current card on "stack" + * num_true_seqs - the number of true sequences on the current + * false sequence + * */ + int stack, cards_num, suit, a; + fcs_card_t card, temp_card, dest_card; + int card_num, num_true_seqs, h, ds, dest_cards_num ; + + int state_stacks_num; + tests_define_accessors(); + + state_stacks_num = instance->stacks_num; + + for(stack=0;stack 0) + { + card = fcs_stack_card(state,stack,cards_num-1); + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + num_true_seqs = 1; + + /* Stop if we reached the bottom of the stack */ + for(h=cards_num-2;h>-1;h--) + { + card = fcs_stack_card(state,stack,h); + /* If this is no longer a sequence - move to the next stack */ + if (fcs_card_card_num(card) != card_num+1) + { + break; + } + if (fcs_card_suit(card) != suit) + { + num_true_seqs++; + } + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + } + /* This means that the loop exited prematurely and the stack does + * not contain a sequence. */ + if (h != -1) + { + continue; + } + + for(ds=0;ds 0) + { + dest_card = fcs_stack_card(state, ds, dest_cards_num-1); + if ( + (fcs_is_ss_false_parent(dest_card, card)) + ) + { + /* This is a suitable parent - let's check if we + * have enough empty stacks to make the move feasible */ + if (calc_max_sequence_move(0, num_freestacks) >= num_true_seqs) + { + /* We can do it - so let's move */ + + sfs_check_state_begin(); + + my_copy_stack(stack); + my_copy_stack(ds); + + + fcs_move_sequence(ds, stack, h+1, cards_num-1, a); + sfs_check_state_end(); + + } + } + } + } + + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + + +int freecell_solver_sfs_simple_simon_move_sequence_to_true_parent_with_some_cards_above( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + + + fcs_move_t temp_move; + int check; + + /* + * stack - the source stack index + * cards_num - the number of cards in "stack" + * h - the height of the current card in "stack" + * card - the card in height "h" + * suit - its suit + * card_num - its card number + * ds - the destionation stack index + * dest_cards_num - the number of cards in "ds" + * dc - the index of the current card in "ds". + * num_separate_false_seqs - this variable tells how many distinct false + * sequences exist above the true parent + * above_num_true_seqs[] - the number of true sequences in each false + * sequence + * seq_points[] - the separation points of the false sequences (i.e: where + * they begin and end) + * stacks_map[] - a boolean map that indicates if one can place a card + * on this stack or is it already taken. + * junk_move_to_stacks[] - the stacks to move each false sequence of the + * junk to. + * false_seq_index - an iterator to hold the index of the current false + * sequence. + * after_junk_num_freestacks - this variable holds the number of stacks + * that remained unoccupied during and after the process of moving + * the junk sequences to different stacks. + * + * */ + int stack, cards_num, suit, a; + fcs_card_t card, temp_card, dest_card; + int card_num, above_num_true_seqs[MAX_NUM_CARDS_IN_A_STACK], h, ds, dest_cards_num ; + int dc; + int seq_points[MAX_NUM_CARDS_IN_A_STACK]; + int num_separate_false_seqs; + int false_seq_index; + int num_true_seqs; + int stacks_map[MAX_NUM_STACKS]; + int after_junk_num_freestacks; + int junk_move_to_stacks[MAX_NUM_STACKS]; + + int state_stacks_num; + tests_define_accessors(); + + state_stacks_num = instance->stacks_num; + + for(stack=0;stack 0) + { + card = fcs_stack_card(state,stack,cards_num-1); + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + + num_true_seqs = 1; + + + for(h=cards_num-2;h>=-1;h--) + { + for(ds=0;ds 0) + { + for(dc=dest_cards_num-1;dc>=0;dc--) + { + dest_card = fcs_stack_card(state, ds, dc); + if ((fcs_card_suit(dest_card) == suit) && + (fcs_card_card_num(dest_card) == (card_num+1)) + ) + { + /* This is a suitable parent - let's check if there's a sequence above it. */ + + /* + * above_c - the height of the card that is to be checked. + * above_card - the card at height above_c+1 + * up_above_card - the card at height above_c + * + * */ + int above_c; + fcs_card_t above_card, up_above_card; + + num_separate_false_seqs = 0; + above_card = fcs_stack_card(state, ds, dest_cards_num-1); + above_num_true_seqs[num_separate_false_seqs] = 1; + for(above_c = dest_cards_num-2 ; + above_c > dc ; + above_c-- + ) + { + up_above_card = fcs_stack_card(state, ds, above_c); + if (! fcs_is_ss_false_parent(up_above_card, above_card)) + { + seq_points[num_separate_false_seqs++] = above_c+1; + above_num_true_seqs[num_separate_false_seqs] = 1; + } + above_num_true_seqs[num_separate_false_seqs] += ! (fcs_card_suit(up_above_card) == fcs_card_suit(above_card)); + above_card = up_above_card; + } + + if (dc < dest_cards_num - 1) + { + seq_points[num_separate_false_seqs++] = above_c+1; + } + + for(a=0;a 0) && (stacks_map[clear_junk_dest_stack] == 0)) + { + fcs_card_t clear_junk_dest_card; + + clear_junk_dest_card = fcs_stack_card(state, clear_junk_dest_stack, clear_junk_stack_len-1); + if (fcs_is_ss_false_parent(clear_junk_dest_card, fcs_stack_card(state, ds, seq_points[false_seq_index]))) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks) >= above_num_true_seqs[false_seq_index]) + { + stacks_map[clear_junk_dest_stack] = 1; + break; + } + } + } + } + + if (clear_junk_dest_stack == state_stacks_num) + { + clear_junk_dest_stack = -1; + } + + if (clear_junk_dest_stack == -1) + { + /* Check if there is a vacant stack */ + if (num_freestacks > 0) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks-1) >= above_num_true_seqs[false_seq_index]) + { + /* Find an empty stack and designate it as the destination for the junk */ + for( + clear_junk_dest_stack = 0; + clear_junk_dest_stack < state_stacks_num; + clear_junk_dest_stack++ + ) + { + if ((fcs_stack_len(state, clear_junk_dest_stack) == 0) && (stacks_map[clear_junk_dest_stack] == 0)) + { + stacks_map[clear_junk_dest_stack] = 1; + break; + } + } + } + after_junk_num_freestacks--; + } + } + + if ((clear_junk_dest_stack == -1)) + { + break; + } + junk_move_to_stacks[false_seq_index] = clear_junk_dest_stack; + } + + if (false_seq_index == num_separate_false_seqs) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks) >= num_true_seqs) + { + /* + * We can do it - so let's move everything. + * Notice that we only put the child in a different stack + * then the parent and let it move to the parent in the + * next iteration of the program + * */ + + sfs_check_state_begin(); + + my_copy_stack(ds); + my_copy_stack(stack); + + + /* Move the junk cards to their place */ + + for(false_seq_index=0; + false_seq_indexstacks_num; + + for(stack=0;stack 0) + { + for( sc = cards_num-1 ; sc >= 0 ; sc-- ) + { + int above_c; + fcs_card_t above_card, up_above_card; + int end_of_src_seq; + + card = fcs_stack_card(state, stack, sc); + suit = fcs_card_suit(card); + card_num = fcs_card_card_num(card); + + num_true_seqs = 1; + + for (end_of_src_seq = sc+1; end_of_src_seq < cards_num ; end_of_src_seq++) + { + above_card = fcs_stack_card(state, stack, end_of_src_seq); + if (!fcs_is_ss_false_parent(card, above_card)) + { + break; + } + if (fcs_card_suit(above_card) != fcs_card_suit(card)) + { + num_true_seqs++; + } + card = above_card; + } + + if (end_of_src_seq == cards_num) + { + continue; + } + + /* Split the cards above it into false sequences */ + + num_separate_false_seqs = 0; + above_card = fcs_stack_card(state, stack, cards_num-1); + above_num_true_seqs[num_separate_false_seqs] = 1; + for(above_c = cards_num-2 ; + above_c > end_of_src_seq-1 ; + above_c-- + ) + { + up_above_card = fcs_stack_card(state, stack, above_c); + if (! fcs_is_ss_false_parent(up_above_card, above_card)) + { + seq_points[num_separate_false_seqs++] = above_c+1; + above_num_true_seqs[num_separate_false_seqs] = 1; + } + above_num_true_seqs[num_separate_false_seqs] += ! (fcs_card_suit(up_above_card) == fcs_card_suit(above_card)); + above_card = up_above_card; + } + + if (end_of_src_seq-1 < cards_num-1) + { + seq_points[num_separate_false_seqs++] = above_c+1; + } + + for(ds=0;ds 0) + { + dest_card = fcs_stack_card(state, ds, dest_cards_num-1); + if ((fcs_card_suit(dest_card) == suit) && + (fcs_card_card_num(dest_card) == (card_num+1)) + ) + { + /* This is a suitable parent - let's check if we + * have enough empty stacks to make the move feasible */ + + for(a=0;a 0) && (stacks_map[clear_junk_dest_stack] == 0)) + { + fcs_card_t clear_junk_dest_card; + + clear_junk_dest_card = fcs_stack_card(state, clear_junk_dest_stack, clear_junk_stack_len-1); + if (fcs_is_ss_false_parent(clear_junk_dest_card, fcs_stack_card(state, stack, seq_points[false_seq_index]))) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks) >= above_num_true_seqs[false_seq_index]) + { + stacks_map[clear_junk_dest_stack] = 1; + break; + } + } + } + } + + if (clear_junk_dest_stack == state_stacks_num) + { + clear_junk_dest_stack = -1; + } + + if (clear_junk_dest_stack == -1) + { + /* Check if there is a vacant stack */ + if (num_freestacks > 0) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks-1) >= above_num_true_seqs[false_seq_index]) + { + /* Find an empty stack and designate it as the destination for the junk */ + for( + clear_junk_dest_stack = 0; + clear_junk_dest_stack < state_stacks_num; + clear_junk_dest_stack++ + ) + { + if ((fcs_stack_len(state, clear_junk_dest_stack) == 0) && (stacks_map[clear_junk_dest_stack] == 0)) + { + stacks_map[clear_junk_dest_stack] = 1; + break; + } + } + } + after_junk_num_freestacks--; + } + } + + if ((clear_junk_dest_stack == -1)) + { + break; + } + junk_move_to_stacks[false_seq_index] = clear_junk_dest_stack; + } + + if (false_seq_index == num_separate_false_seqs) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks) > num_true_seqs) + { + sfs_check_state_begin(); + + my_copy_stack(stack); + my_copy_stack(ds); + + + + /* Let's boogie - we can move everything */ + + /* Move the junk cards to their place */ + + for(false_seq_index=0; + false_seq_indexstacks_num; + + for(stack=0;stack 0) + { + card = fcs_stack_card(state,stack,cards_num-1); + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + num_src_junk_true_seqs = 1; + + + for(h=cards_num-2;h>=-1;h--) + { + if (h == -1) + { + break; + } + card = fcs_stack_card(state, stack, h); + if (fcs_card_card_num(card) != card_num+1) + { + break; + } + if (fcs_card_suit(card) != suit) + { + num_src_junk_true_seqs++; + } + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + } + + if (h != -1) + { + end_of_junk = h; + num_true_seqs = 1; + + for(;h>=-1;h--) + { + if (h == -1) + { + break; + } + card = fcs_stack_card(state,stack,h); + if (fcs_card_card_num(card) != card_num+1) + { + break; + } + if (fcs_card_suit(card) != suit) + { + num_true_seqs++; + } + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + } + + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + + for(ds=0;ds 1) + { + /* Start at the card below the top one, so we will + * make sure there's at least some junk above it + * */ + for(dc=dest_cards_num-2;dc>=0;dc--) + { + dest_card = fcs_stack_card(state, ds, dc); + if ((fcs_card_suit(dest_card) == suit) && + (fcs_card_card_num(dest_card) == (card_num+1)) + ) + { + /* This is a suitable parent - let's check if there's a sequence above it. */ + int above_c; + fcs_card_t above_card, up_above_card; + + num_separate_false_seqs = 0; + above_card = fcs_stack_card(state, ds, dest_cards_num-1); + above_num_true_seqs[num_separate_false_seqs] = 1; + for(above_c = dest_cards_num-2 ; + above_c > dc ; + above_c-- + ) + { + up_above_card = fcs_stack_card(state, ds, above_c); + if (! fcs_is_ss_false_parent(up_above_card, above_card)) + { + seq_points[num_separate_false_seqs++] = above_c+1; + above_num_true_seqs[num_separate_false_seqs] = 1; + } + above_num_true_seqs[num_separate_false_seqs] += ! (fcs_card_suit(up_above_card) == fcs_card_suit(above_card)); + above_card = up_above_card; + } + + if (dc < dest_cards_num - 1) + { + seq_points[num_separate_false_seqs++] = above_c+1; + } + + for(a=0;a 0) && (stacks_map[clear_junk_dest_stack] == 0)) + { + fcs_card_t clear_junk_dest_card; + + clear_junk_dest_card = fcs_stack_card(state, clear_junk_dest_stack, clear_junk_stack_len-1); + if (fcs_is_ss_false_parent(clear_junk_dest_card, the_card)) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks) >= the_num_true_seqs) + { + stacks_map[clear_junk_dest_stack] = 1; + break; + } + } + } + } + + if (clear_junk_dest_stack == state_stacks_num) + { + clear_junk_dest_stack = -1; + } + + if (clear_junk_dest_stack == -1) + { + /* Check if there is a vacant stack */ + if (num_freestacks > 0) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks-1) >= the_num_true_seqs) + { + /* Find an empty stack and designate it as the destination for the junk */ + for( + clear_junk_dest_stack = 0; + clear_junk_dest_stack < state_stacks_num; + clear_junk_dest_stack++ + ) + { + if ((fcs_stack_len(state, clear_junk_dest_stack) == 0) && (stacks_map[clear_junk_dest_stack] == 0)) + { + stacks_map[clear_junk_dest_stack] = 1; + break; + } + } + } + after_junk_num_freestacks--; + } + } + + if ((clear_junk_dest_stack == -1)) + { + break; + } + junk_move_to_stacks[false_seq_index] = clear_junk_dest_stack; + } + + if (false_seq_index == num_separate_false_seqs+1) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks) >= num_true_seqs) + { + /* We can do it - so let's move everything */ + + sfs_check_state_begin(); + + my_copy_stack(stack); + my_copy_stack(ds); + + + /* Move the junk cards to their place */ + + for(false_seq_index=0; + false_seq_indexstacks_num; + + for(stack=0;stack 0) + { + card = fcs_stack_card(state,stack,cards_num-1); + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + num_true_seqs = 1; + + for(h=cards_num-2;h>=-1;h--) + { + if (h == -1) + { + break; + } + card = fcs_stack_card(state,stack,h); + if (fcs_card_card_num(card) != card_num+1) + { + break; + } + if (fcs_card_suit(card) != suit) + { + num_true_seqs++; + } + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + } + if (h == -1) + { + for(ds=0;ds 0) + { + for(dc=dest_cards_num-1;dc>=0;dc--) + { + dest_card = fcs_stack_card(state, ds, dc); + if ( + (fcs_card_card_num(dest_card) == (card_num+1)) + ) + { + /* This is a suitable parent - let's check if there's a sequence above it. */ + int above_c; + fcs_card_t above_card, up_above_card; + + num_separate_false_seqs = 0; + above_card = fcs_stack_card(state, ds, dest_cards_num-1); + above_num_true_seqs[num_separate_false_seqs] = 1; + for(above_c = dest_cards_num-2 ; + above_c > dc ; + above_c-- + ) + { + up_above_card = fcs_stack_card(state, ds, above_c); + if (! fcs_is_ss_false_parent(up_above_card, above_card)) + { + seq_points[num_separate_false_seqs++] = above_c+1; + above_num_true_seqs[num_separate_false_seqs] = 1; + } + above_num_true_seqs[num_separate_false_seqs] += ! (fcs_card_suit(up_above_card) == fcs_card_suit(above_card)); + above_card = up_above_card; + } + + if (dc < dest_cards_num - 1) + { + seq_points[num_separate_false_seqs++] = above_c+1; + } + + for(a=0;a 0) && (stacks_map[clear_junk_dest_stack] == 0)) + { + fcs_card_t clear_junk_dest_card; + + clear_junk_dest_card = fcs_stack_card(state, clear_junk_dest_stack, clear_junk_stack_len-1); + if (fcs_is_ss_false_parent(clear_junk_dest_card, the_card)) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks) >= the_num_true_seqs) + { + stacks_map[clear_junk_dest_stack] = 1; + break; + } + } + } + } + + if (clear_junk_dest_stack == state_stacks_num) + { + clear_junk_dest_stack = -1; + } + + if ((clear_junk_dest_stack == -1)) + { + break; + } + junk_move_to_stacks[false_seq_index] = clear_junk_dest_stack; + } + + if (false_seq_index == num_separate_false_seqs) + { + /* This is a suitable parent - let's check if we + * have enough empty stacks to make the move feasible */ + if (calc_max_sequence_move(0, num_freestacks) >= num_true_seqs) + { + /* We can do it - so let's move */ + + sfs_check_state_begin(); + + my_copy_stack(stack); + my_copy_stack(ds); + + + /* Move the junk cards to their place */ + + for(false_seq_index=0; + false_seq_indexstacks_num; + + for(stack=0 ; stack < state_stacks_num ; stack++) + { + cards_num = fcs_stack_len(state, stack); + if (cards_num > 2) + { + /* Search for a parent card */ + for(pc=0; pc < cards_num-1 ; pc++) + { + parent_card = fcs_stack_card(state, stack, pc); + if ( + fcs_is_ss_true_parent( + parent_card, + fcs_stack_card(state, stack, pc+1) + ) + ) + { + continue; + } + + + for(cc = pc + 2 ; cc < cards_num ; cc++) + { + child_card = fcs_stack_card(state, stack, cc); + if (fcs_is_ss_true_parent( + parent_card, + child_card + ) + ) + { + /* We have a matching parent and child cards */ +#if 0 + printf("Stack %i, Parent %i, Child %i\n", stack, pc, cc); + fflush(stdout); +#endif + + /* + * Now let's try to find stacks to place the cards above + * the child card. + * */ + + int above_num_true_seqs[MAX_NUM_CARDS_IN_A_STACK]; + int seq_points[MAX_NUM_CARDS_IN_A_STACK]; + int stacks_map[MAX_NUM_STACKS]; + int junk_move_to_stacks[MAX_NUM_STACKS]; + int num_separate_false_seqs; + + fcs_card_t above_card, up_above_card; + int above_c; + + int end_of_child_seq; + int child_num_true_seqs; + + end_of_child_seq = cc; + child_num_true_seqs = 1; + while ((end_of_child_seq+1 < cards_num) && + fcs_is_ss_false_parent( + fcs_stack_card(state, stack, end_of_child_seq), + fcs_stack_card(state, stack, end_of_child_seq+1) + ) + ) + { + child_num_true_seqs += (!fcs_is_ss_true_parent( + fcs_stack_card(state, stack, end_of_child_seq), + fcs_stack_card(state, stack, end_of_child_seq+1) + )); + end_of_child_seq++; + } + + num_separate_false_seqs = 0; + above_card = fcs_stack_card(state, stack, cards_num-1); + above_num_true_seqs[num_separate_false_seqs] = 1; + for(above_c = cards_num-2; + above_c > end_of_child_seq ; + above_c-- + ) + { + up_above_card = fcs_stack_card(state, stack, above_c); + if (! fcs_is_ss_false_parent(up_above_card, above_card)) + { + seq_points[num_separate_false_seqs++] = above_c+1; + above_num_true_seqs[num_separate_false_seqs] = 1; + } + above_num_true_seqs[num_separate_false_seqs] += ! (fcs_card_suit(up_above_card) == fcs_card_suit(above_card)); + above_card = up_above_card; + } + + if (end_of_child_seq < cards_num - 1) + { + seq_points[num_separate_false_seqs++] = above_c+1; + } + + /* Add the child to the seq_points */ + child_seq_index = num_separate_false_seqs; + above_num_true_seqs[num_separate_false_seqs] = child_num_true_seqs; + seq_points[num_separate_false_seqs++] = cc; + + /* Add the cards between the parent and the child to the seq_points */ + + above_card = fcs_stack_card(state, stack, cc-1); + above_num_true_seqs[num_separate_false_seqs] = 1; + for(above_c = cc-2; + above_c > pc ; + above_c-- + ) + { + up_above_card = fcs_stack_card(state, stack, above_c); + if (! fcs_is_ss_false_parent(up_above_card, above_card)) + { + seq_points[num_separate_false_seqs++] = above_c+1; + above_num_true_seqs[num_separate_false_seqs] = 1; + } + above_num_true_seqs[num_separate_false_seqs] += ! (fcs_card_suit(up_above_card) == fcs_card_suit(above_card)); + above_card = up_above_card; + } + + if (pc < cc - 1) + { + seq_points[num_separate_false_seqs++] = above_c+1; + } + + + + for(a = 0 ; a < state_stacks_num ; a++) + { + stacks_map[a] = 0; + } + stacks_map[stack] = 1; + + after_junk_num_freestacks = num_freestacks; + + for(false_seq_index=0;false_seq_index 0) && (stacks_map[clear_junk_dest_stack] == 0)) + { + fcs_card_t clear_junk_dest_card; + + clear_junk_dest_card = fcs_stack_card(state, clear_junk_dest_stack, clear_junk_stack_len-1); + if (fcs_is_ss_false_parent(clear_junk_dest_card, fcs_stack_card(state, stack, seq_points[false_seq_index]))) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks) >= above_num_true_seqs[false_seq_index]) + { + stacks_map[clear_junk_dest_stack] = 1; + break; + } + } + } + } + + if (clear_junk_dest_stack == state_stacks_num) + { + clear_junk_dest_stack = -1; + } + + if (clear_junk_dest_stack == -1) + { + /* Check if there is a vacant stack */ + if (num_freestacks > 0) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks-1) >= above_num_true_seqs[false_seq_index]) + { + /* Find an empty stack and designate it as the destination for the junk */ + for( + clear_junk_dest_stack = 0; + clear_junk_dest_stack < state_stacks_num; + clear_junk_dest_stack++ + ) + { + if ((fcs_stack_len(state, clear_junk_dest_stack) == 0) && (stacks_map[clear_junk_dest_stack] == 0)) + { + stacks_map[clear_junk_dest_stack] = 1; + break; + } + } + } + after_junk_num_freestacks--; + } + } + + if ((clear_junk_dest_stack == -1)) + { + break; + } + junk_move_to_stacks[false_seq_index] = clear_junk_dest_stack; + } + + if (false_seq_index == num_separate_false_seqs) + { + /* Let's check if we can move the child after we are done moving all the junk cards */ + if (calc_max_sequence_move(0, after_junk_num_freestacks) >= child_num_true_seqs) + { + /* We can do it - so let's move everything */ + + sfs_check_state_begin(); + + /* Move the junk cards to their place */ + + my_copy_stack(stack); + + for(false_seq_index=0; + false_seq_index +#include +#include + +#include "fcs_config.h" +#include "state.h" +#include "card.h" +#include "fcs_enums.h" +#include "app_str.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + + +#ifndef min +#define min(a,b) ((a)<(b)?(a):(b)) +#endif + + +#ifdef DEBUG_STATES + +fcs_card_t freecell_solver_empty_card = {0,0}; + +#elif defined(COMPACT_STATES) || defined (INDIRECT_STACK_STATES) + +fcs_card_t freecell_solver_empty_card = (fcs_card_t)0; + +#endif + +static int fcs_card_compare(const void * card1, const void * card2) +{ + const fcs_card_t * c1 = (const fcs_card_t *)card1; + const fcs_card_t * c2 = (const fcs_card_t *)card2; + + if (fcs_card_card_num(*c1) > fcs_card_card_num(*c2)) + { + return 1; + } + else if (fcs_card_card_num(*c1) < fcs_card_card_num(*c2)) + { + return -1; + } + else + { + if (fcs_card_suit(*c1) > fcs_card_suit(*c2)) + { + return 1; + } + else if (fcs_card_suit(*c1) < fcs_card_suit(*c2)) + { + return -1; + } + else + { + return 0; + } + } +} + +#ifdef DEBUG_STATES +static int fcs_stack_compare(const void * s1, const void * s2) +{ + fcs_card_t card1 = ((const fc_stack_t *)s1)->cards[0]; + fcs_card_t card2 = ((const fc_stack_t *)s2)->cards[0]; + + return fcs_card_compare(&card1, &card2); +} +#elif defined(COMPACT_STATES) +static int fcs_stack_compare(const void * s1, const void * s2) +{ + fcs_card_t card1 = ((fcs_card_t*)s1)[1]; + fcs_card_t card2 = ((fcs_card_t*)s2)[1]; + + return fcs_card_compare(&card1, &card2); +} +#elif defined(INDIRECT_STACK_STATES) + + +#if MAX_NUM_DECKS == 1 +static int fcs_stack_compare_for_stack_sort(const void * s1, const void * s2) +{ + fcs_card_t card1 = ((fcs_card_t*)s1)[1]; + fcs_card_t card2 = ((fcs_card_t*)s2)[1]; + + return fcs_card_compare(&card1, &card2); +} +#endif + +int freecell_solver_stack_compare_for_comparison(const void * v_s1, const void * v_s2) +{ + const fcs_card_t * s1 = (const fcs_card_t *)v_s1; + const fcs_card_t * s2 = (const fcs_card_t *)v_s2; + + int min_len; + int a, ret; + + min_len = min(s1[0], s2[0]); + + for(a=0;a s2[0]) + { + return 1; + } + else + { + return 0; + } +} + +#endif + +#ifdef FCS_WITH_TALONS +static int fcs_talon_compare_with_context(const void * p1, const void * p2, fcs_compare_context_t context) +{ + fcs_card_t * t1 = (fcs_card_t *)p1; + fcs_card_t * t2 = (fcs_card_t *)p2; + + if (t1[0] < t2[0]) + { + return -1; + } + else if (t1[0] > t2[0]) + { + return 1; + } + else + { + return memcmp(t1,t2,t1[0]+1); + } +} +#endif + +#ifdef DEBUG_STATES +void freecell_solver_canonize_state(fcs_state_with_locations_t * state, int freecells_num, int stacks_num) +{ + int b,c; + + fc_stack_t temp_stack; + fcs_card_t temp_freecell; + int temp_loc; + + /* Insertion-sort the stacks */ + for(b=1;b0) && + (fcs_stack_compare( + &(state->s.stacks[c]), + &(state->s.stacks[c-1]) + ) < 0) + ) + { + temp_stack = state->s.stacks[c]; + state->s.stacks[c] = state->s.stacks[c-1]; + state->s.stacks[c-1] = temp_stack; + + temp_loc = state->stack_locs[c]; + state->stack_locs[c] = state->stack_locs[c-1]; + state->stack_locs[c-1] = temp_loc; + + c--; + } + } + + /* Insertion sort the freecells */ + + for(b=1;b0) && + (fcs_card_compare( + &(state->s.freecells[c]), + &(state->s.freecells[c-1]) + ) < 0) + ) + { + temp_freecell = state->s.freecells[c]; + state->s.freecells[c] = state->s.freecells[c-1]; + state->s.freecells[c-1] = temp_freecell; + + temp_loc = state->fc_locs[c]; + state->fc_locs[c] = state->fc_locs[c-1]; + state->fc_locs[c-1] = temp_loc; + + c--; + } + } +} + +#elif defined(COMPACT_STATES) + +void freecell_solver_canonize_state( + fcs_state_with_locations_t * state, + int freecells_num, + int stacks_num) +{ + int b,c; + + char temp_stack[(MAX_NUM_CARDS_IN_A_STACK+1)]; + fcs_card_t temp_freecell; + char temp_loc; + + /* Insertion-sort the stacks */ + + for(b=1;b0) && + (fcs_stack_compare( + state->s.data+c*(MAX_NUM_CARDS_IN_A_STACK+1), + state->s.data+(c-1)*(MAX_NUM_CARDS_IN_A_STACK+1) + ) < 0) + ) + { + memcpy(temp_stack, state->s.data+c*(MAX_NUM_CARDS_IN_A_STACK+1), (MAX_NUM_CARDS_IN_A_STACK+1)); + memcpy(state->s.data+c*(MAX_NUM_CARDS_IN_A_STACK+1), state->s.data+(c-1)*(MAX_NUM_CARDS_IN_A_STACK+1), (MAX_NUM_CARDS_IN_A_STACK+1)); + memcpy(state->s.data+(c-1)*(MAX_NUM_CARDS_IN_A_STACK+1), temp_stack, (MAX_NUM_CARDS_IN_A_STACK+1)); + + temp_loc = state->stack_locs[c]; + state->stack_locs[c] = state->stack_locs[c-1]; + state->stack_locs[c-1] = temp_loc; + + c--; + } + } + + /* Insertion-sort the freecells */ + + for(b=1;b0) && + (fcs_card_compare( + state->s.data+FCS_FREECELLS_OFFSET+c, + state->s.data+FCS_FREECELLS_OFFSET+c-1 + ) < 0) + ) + { + temp_freecell = (state->s.data[FCS_FREECELLS_OFFSET+c]); + state->s.data[FCS_FREECELLS_OFFSET+c] = state->s.data[FCS_FREECELLS_OFFSET+c-1]; + state->s.data[FCS_FREECELLS_OFFSET+c-1] = temp_freecell; + + temp_loc = state->fc_locs[c]; + state->fc_locs[c] = state->fc_locs[c-1]; + state->fc_locs[c-1] = temp_loc; + + c--; + } + } +} +#elif defined(INDIRECT_STACK_STATES) +void freecell_solver_canonize_state( + fcs_state_with_locations_t * state, + int freecells_num, + int stacks_num) +{ + int b,c; + fcs_card_t * temp_stack; + fcs_card_t temp_freecell; + char temp_loc; + + /* Insertion-sort the stacks */ + for(b=1;b0) && + ( +#if MAX_NUM_DECKS > 1 + freecell_solver_stack_compare_for_comparison +#else + fcs_stack_compare_for_stack_sort +#endif + ( + (state->s.stacks[c]), + (state->s.stacks[c-1]) + ) + < 0 + ) + ) + { + temp_stack = state->s.stacks[c]; + state->s.stacks[c] = state->s.stacks[c-1]; + state->s.stacks[c-1] = temp_stack; + + temp_loc = state->stack_locs[c]; + state->stack_locs[c] = state->stack_locs[c-1]; + state->stack_locs[c-1] = temp_loc; + + c--; + } + } + + /* Insertion sort the freecells */ + + for(b=1;b0) && + (fcs_card_compare( + &(state->s.freecells[c]), + &(state->s.freecells[c-1]) + ) < 0) + ) + { + temp_freecell = state->s.freecells[c]; + state->s.freecells[c] = state->s.freecells[c-1]; + state->s.freecells[c-1] = temp_freecell; + + temp_loc = state->fc_locs[c]; + state->fc_locs[c] = state->fc_locs[c-1]; + state->fc_locs[c-1] = temp_loc; + + c--; + } + } +} + +#endif + +static void fcs_state_init( + fcs_state_with_locations_t * state, + int stacks_num +#ifdef INDIRECT_STACK_STATES + ,fcs_card_t * indirect_stacks_buffer +#endif + ) +{ + int a; + memset((void*)&(state->s), 0, sizeof(fcs_state_t)); + for(a=0;astack_locs[a] = a; + } +#ifdef INDIRECT_STACK_STATES + for(a=0;as.stacks[a] = &indirect_stacks_buffer[a << 7]; + memset(state->s.stacks[a], '\0', MAX_NUM_DECKS*52+1); + } + for(;as.stacks[a] = NULL; + } +#endif + for(a=0;afc_locs[a] = a; + } +} + + +#if (FCS_STATE_STORAGE != FCS_STATE_STORAGE_INDIRECT) +int freecell_solver_state_compare(const void * s1, const void * s2) +{ + return memcmp(s1,s2,sizeof(fcs_state_t)); +} + +int freecell_solver_state_compare_equal(const void * s1, const void * s2) +{ + return (!memcmp(s1,s2,sizeof(fcs_state_t))); +} + + +int freecell_solver_state_compare_with_context( + const void * s1, + const void * s2, + fcs_compare_context_t context + ) +{ + (void)context; + return memcmp(s1,s2,sizeof(fcs_state_t)); +} +#else +int freecell_solver_state_compare_indirect(const void * s1, const void * s2) +{ + return memcmp(*(fcs_state_with_locations_t * *)s1, *(fcs_state_with_locations_t * *)s2, sizeof(fcs_state_t)); +} + +int freecell_solver_state_compare_indirect_with_context(const void * s1, const void * s2, void * context) +{ + return memcmp(*(fcs_state_with_locations_t * *)s1, *(fcs_state_with_locations_t * *)s2, sizeof(fcs_state_t)); +} +#endif + +static const char * const freecells_prefixes[] = { "FC:", "Freecells:", "Freecell:", ""}; +static const char * const foundations_prefixes[] = { "Decks:", "Deck:", "Founds:", "Foundations:", "Foundation:", "Found:", ""}; +static const char * const talon_prefixes[] = { "Talon:", "Queue:" , ""}; +static const char * const num_redeals_prefixes[] = { "Num-Redeals:", "Readels-Num:", "Readeals-Number:", ""}; + +#ifdef WIN32 +#define strncasecmp(a,b,c) (strnicmp((a),(b),(c))) +#endif + +int freecell_solver_initial_user_state_to_c( + const char * string, + fcs_state_with_locations_t * out_state, + int freecells_num, + int stacks_num, + int decks_num +#ifdef FCS_WITH_TALONS + ,int talon_type +#endif +#ifdef INDIRECT_STACK_STATES + , fcs_card_t * indirect_stacks_buffer +#endif + ) +{ + fcs_state_with_locations_t ret_with_locations; + + int s,c; + const char * str; + fcs_card_t card; + int first_line; + + int prefix_found; + const char * const * prefixes; + int i; + int decks_index[4]; + + fcs_state_init( + &ret_with_locations, + stacks_num +#ifdef INDIRECT_STACK_STATES + , indirect_stacks_buffer +#endif + ); + str = string; + + first_line = 1; + +#define ret (ret_with_locations.s) +/* Handle the end of string - shouldn't happen */ +#define handle_eos() \ + { \ + if ((*str) == '\0') \ + { \ + return FCS_USER_STATE_TO_C__PREMATURE_END_OF_INPUT; \ + } \ + } + +#ifdef FCS_WITH_TALONS + if (talon_type == FCS_TALON_KLONDIKE) + { + fcs_klondike_talon_num_redeals_left(ret) = -1; + } +#endif + + for(s=0;s= decks_num) + { + decks_index[d] = 0; + } + } + s--; + continue; + } + +#ifdef FCS_WITH_TALONS + prefixes = talon_prefixes; + prefix_found = 0; + for(i=0;prefixes[i][0] != '\0'; i++) + { + if (!strncasecmp(str, prefixes[i], strlen(prefixes[i]))) + { + prefix_found = 1; + str += strlen(prefixes[i]); + break; + } + } + + if (prefix_found) + { + /* Input the Talon */ + int talon_size; + + talon_size = MAX_NUM_DECKS*52+16; + ret.talon = malloc(sizeof(fcs_card_t)*talon_size); + fcs_talon_pos(ret) = 0; + + for(c=0 ; c < talon_size ; c++) + { + /* Move to the next card */ + if (c!=0) + { + while( + ((*str) != ' ') && + ((*str) != '\t') && + ((*str) != '\n') && + ((*str) != '\r') + ) + { + handle_eos(); + str++; + } + if ((*str == '\n') || (*str == '\r')) + { + break; + } + } + + while ((*str == ' ') || (*str == '\t')) + { + str++; + } + + if ((*str == '\n') || (*str == '\r')) + { + break; + } + + card = fcs_card_user2perl(str); + + fcs_put_card_in_talon(ret, c+(talon_type==FCS_TALON_KLONDIKE), card); + } + fcs_talon_len(ret) = c; + + if (talon_type == FCS_TALON_KLONDIKE) + { + int talon_len; + + talon_len = fcs_talon_len(ret); + fcs_klondike_talon_len(ret) = talon_len; + fcs_klondike_talon_stack_pos(ret) = -1; + fcs_klondike_talon_queue_pos(ret) = 0; + } + + s--; + continue; + } + + prefixes = num_redeals_prefixes; + prefix_found = 0; + for(i=0;prefixes[i][0] != '\0'; i++) + { + if (!strncasecmp(str, prefixes[i], strlen(prefixes[i]))) + { + prefix_found = 1; + str += strlen(prefixes[i]); + break; + } + } + + if (prefix_found) + { + while ((*str < '0') && (*str > '9') && (*str != '\n')) + { + handle_eos(); + str++; + } + if (*str != '\n') + { + int num_redeals; + + num_redeals = atoi(str); + if (talon_type == FCS_TALON_KLONDIKE) + { + fcs_klondike_talon_num_redeals_left(ret) = + (num_redeals < 0) ? + (-1) : + ((num_redeals > 127) ? 127 : num_redeals) + ; + } + } + s--; + continue; + } +#endif + + for(c=0 ; c < MAX_NUM_CARDS_IN_A_STACK ; c++) + { + /* Move to the next card */ + if (c!=0) + { + while( + ((*str) != ' ') && + ((*str) != '\t') && + ((*str) != '\n') && + ((*str) != '\r') + ) + { + handle_eos(); + str++; + } + if ((*str == '\n') || (*str == '\r')) + { + break; + } + } + + while ((*str == ' ') || (*str == '\t')) + { + str++; + } + if ((*str == '\n') || (*str == '\r')) + { + break; + } + card = fcs_card_user2perl(str); + + fcs_push_card_into_stack(ret, s, card); + } + } + + *out_state = ret_with_locations; + return FCS_USER_STATE_TO_C__SUCCESS; +} + +#undef ret +#undef handle_eos + +int freecell_solver_check_state_validity( + fcs_state_with_locations_t * state_with_locations, + int freecells_num, + int stacks_num, + int decks_num, +#ifdef FCS_WITH_TALONS + int talon_type, +#endif + fcs_card_t * misplaced_card) +{ + int cards[4][14]; + int c, s, d, f; + + fcs_state_t * state; + + state = (&(state_with_locations->s)); + + /* Initialize all cards to 0 */ + for(d=0;d<4;d++) + { + for(c=1;c<=13;c++) + { + cards[d][c] = 0; + } + } + + /* Mark the cards in the decks */ + for(d=0;ds)); + + if (canonized_order_output) + { + for(a=0;astack_locs[a])] = a; + } + for(a=0;afc_locs[a])] = a; + } + } + + for(a=0;a max_num_cards) + { + max_num_cards = fcs_stack_len(*state, stack_locs[s]); + } + } + + for(card_num=0;card_num= fcs_stack_len(*state, stack_locs[s])) + { + freecell_solver_append_string_sprintf( + app_str, + " " + ); + } + else + { + freecell_solver_append_string_sprintf( + app_str, + "%3s ", + fcs_card_perl2user( + fcs_stack_card( + *state, + stack_locs[s], + card_num), + stack_card_, + display_10_as_t + ) + ); + } + } + freecell_solver_append_string_sprintf(app_str, "%s", "\n"); + } + } + else + { + freecell_solver_append_string_sprintf(app_str, "%s", "Foundations: "); + for(a=0;a(MAX_NUM_DECKS*52) +#define MAX_NUM_CARDS_IN_A_STACK (MAX_NUM_DECKS*52) +#else +#define MAX_NUM_CARDS_IN_A_STACK (MAX_NUM_INITIAL_CARDS_IN_A_STACK+12) +#endif + +#define MAX_NUM_SCANS_BUCKETS 1 +#define MAX_NUM_SCANS (MAX_NUM_SCANS_BUCKETS * (sizeof(int)*8)) + +/********** + * TODO: Change 5 to the log2 of sizeof(int)*8 + * + ************/ + +#define is_scan_visited(ptr_state, scan_id) (ptr_state->scan_visited[(scan_id)>>5] & (1 << ((scan_id)&((1<<(5))-1)))) +#define set_scan_visited(ptr_state, scan_id) { ptr_state->scan_visited[(scan_id)>>5] |= (1 << ((scan_id)&((1<<(5))-1))); } + + +#ifdef DEBUG_STATES + +struct fcs_struct_card_t +{ + short card_num; + char suit; + char flags; +}; + +typedef struct fcs_struct_card_t fcs_card_t; + +struct fcs_struct_stack_t +{ + unsigned int num_cards; + fcs_card_t cards[MAX_NUM_CARDS_IN_A_STACK]; +}; + +typedef struct fcs_struct_stack_t fc_stack_t; + +struct fcs_struct_state_t +{ + fc_stack_t stacks[MAX_NUM_STACKS]; + fcs_card_t freecells[MAX_NUM_FREECELLS]; + int foundations[MAX_NUM_DECKS*4]; +#ifdef FCS_WITH_TALONS + fcs_card_t * talon; + char talon_params[4]; +#endif +}; + +typedef struct fcs_struct_state_t fcs_state_t; + +#if 0 +struct fcs_struct_state_with_locations_t +{ + fcs_state_t s; + int stack_locs[MAX_NUM_STACKS]; + int fc_locs[MAX_NUM_FREECELLS]; + struct fcs_struct_state_with_locations_t * parent; + fcs_move_stack_t * moves_to_parent; + int depth; + int visited; + int visited_iter; + int num_active_children; + int scan_visited[MAX_NUM_SCANS_BUCKETS]; +}; + +typedef struct fcs_struct_state_with_locations_t fcs_state_with_locations_t; +#endif +typedef int fcs_locs_t; + +#define fcs_stack_len(state, s) \ + ( (state).stacks[(s)].num_cards ) + +#define fcs_stack_card(state, s, c) \ + ( (state).stacks[(s)].cards[(c)] ) + +#define fcs_stack_card_suit(state, s, c) \ + ( fcs_card_suit(fcs_stack_card((state),(s),(c))) ) + +#define fcs_stack_card_num(state, s, c) \ + ( fcs_card_card_num(fcs_stack_card((state),(s),(c))) ) + +#define fcs_card_card_num(card) \ + ( (card).card_num ) + +#define fcs_card_suit(card) \ + ((int)( (card).suit )) + +#define fcs_card_get_flipped(card) \ + ( (card).flags ) + +#define fcs_freecell_card(state, f) \ + ( (state).freecells[(f)] ) + +#define fcs_freecell_card_num(state, f) \ + ( fcs_card_card_num(fcs_freecell_card((state),(f))) ) + +#define fcs_freecell_card_suit(state, f) \ + ( fcs_card_suit(fcs_freecell_card((state),(f))) ) + +#define fcs_foundation_value(state, found) \ + ( (state).foundations[(found)] ) + +#define fcs_increment_foundation(state, found) \ + ( (state).foundations[(found)]++ ) + +#define fcs_set_foundation(state, found, value) \ + ( (state).foundations[(found)] = (value) ) + +#define fcs_pop_stack_card(state, s, into) \ + { \ + into = (state).stacks[(s)].cards[(state).stacks[(s)].num_cards-1]; \ + (state).stacks[(s)].cards[(state).stacks[(s)].num_cards-1] = fcs_empty_card; \ + (state).stacks[(s)].num_cards--; \ + } + +#define fcs_push_stack_card_into_stack(state, ds, ss, sc) \ + { \ + (state).stacks[(ds)].cards[(state).stacks[(ds)].num_cards] = (state).stacks[(ss)].cards[(sc)]; \ + (state).stacks[(ds)].num_cards++; \ + } + +#define fcs_push_card_into_stack(state, ds, from) \ + { \ + (state).stacks[(ds)].cards[(state).stacks[(ds)].num_cards] = (from); \ + (state).stacks[(ds)].num_cards++; \ + } + +#define fcs_duplicate_state(dest, src) \ + (dest) = (src) + +#define fcs_put_card_in_freecell(state, f, card) \ + (state).freecells[(f)] = (card) + +#define fcs_empty_freecell(state, f) \ + (state).freecells[(f)] = fcs_empty_card + +#define fcs_card_set_suit(card, d) \ + (card).suit = (d) + +#define fcs_card_set_num(card, num) \ + (card).card_num = (num) + +#define fcs_card_set_flipped(card, flipped) \ + (card).flags = (flipped) + +#define fcs_flip_stack_card(state, s, c) \ + fcs_card_set_flipped(fcs_stack_card((state),(s),(c)), 0) + +#ifdef FCS_WITH_TALONS +#define fcs_talon_len(state) \ + ((state).talon_params[0]) + +#define fcs_talon_pos(state) \ + ((state).talon_params[1]) + +#define fcs_get_talon_card(state, pos) \ + ((state).talon[pos]) + +#define fcs_put_card_in_talon(state, pos, card) \ + ((state).talon[pos] = (card)) +#endif + +#define fcs_copy_stack(state, idx, buffer) {} + +#elif defined(COMPACT_STATES) /* #ifdef DEBUG_STATES */ + + + + + + + +typedef char fcs_card_t; +/* + * Card: + * Bits 0-3 - Card Number + * Bits 4-5 - Deck + * + */ + +struct fcs_struct_state_t +{ + char data[MAX_NUM_STACKS*(MAX_NUM_CARDS_IN_A_STACK+1)+MAX_NUM_FREECELLS+4*MAX_NUM_DECKS]; +#ifdef FCS_WITH_TALON + fcs_card_t * talon; + char talon_params[4]; +#endif +}; +/* + * Stack: 0 - Number of cards + * 1-19 - Cards + * Stacks: stack_num*20 where stack_num >= 0 and + * stack_num <= (MAX_NUM_STACKS-1) + * Bytes: (MAX_NUM_STACKS*20) to + * (MAX_NUM_STACKS*20+MAX_NUM_FREECELLS-1) + * are Freecells. + * Bytes: (MAX_NUM_STACKS*20+MAX_NUM_FREECELLS) to + * MAX_NUM_STACKS*20+MAX_NUM_FREECELLS+3 + * are Foundations. + * */ + +/* ===== Depracated Information ===== + * Stack: 0 - Number of cards 1-19 - Cards + * Stacks: stack_num*20 where stack_num >= 0 and stack_num <= 7 + * Bytes 160-163 - Freecells + * Bytes 164-167 - Decks + */ + +typedef struct fcs_struct_state_t fcs_state_t; + +#if 0 +struct fcs_struct_state_with_locations_t +{ + fcs_state_t s; + char stack_locs[MAX_NUM_STACKS]; + char fc_locs[MAX_NUM_FREECELLS]; + struct fcs_struct_state_with_locations_t * parent; + fcs_move_stack_t * moves_to_parent; + int depth; + int visited; + int visited_iter; + int num_active_children; + int scan_visited[MAX_NUM_SCANS_BUCKETS]; +}; + +typedef struct fcs_struct_state_with_locations_t fcs_state_with_locations_t; +#endif +typedef char fcs_locs_t; + +#define fcs_card_card_num(card) \ + ( (card) & 0x0F ) + +#define fcs_card_suit(card) \ + ( ((card) >> 4) & 0x03 ) + +#define fcs_stack_len(state, s) \ + ( (size_t)(state).data[s*(MAX_NUM_CARDS_IN_A_STACK+1)] ) + +#define fcs_stack_card(state, s, c) \ + ( (state).data[(s)*(MAX_NUM_CARDS_IN_A_STACK+1)+(c)+1] ) + +#define fcs_stack_card_num(state, s, c) \ + ( fcs_card_card_num(fcs_stack_card((state),(s),(c))) ) + +#define fcs_stack_card_suit(state, s, c) \ + ( fcs_card_suit(fcs_stack_card((state),(s),(c))) ) + +#define FCS_FREECELLS_OFFSET ((MAX_NUM_STACKS)*(MAX_NUM_CARDS_IN_A_STACK+1)) + +#define fcs_freecell_card(state, f) \ + ( (state).data[FCS_FREECELLS_OFFSET+(f)] ) + +#define fcs_freecell_card_num(state, f) \ + ( fcs_card_card_num(fcs_freecell_card((state),(f))) ) + +#define fcs_freecell_card_suit(state, f) \ + ( fcs_card_suit(fcs_freecell_card((state),(f))) ) + +#define FCS_FOUNDATIONS_OFFSET (((MAX_NUM_STACKS)*(MAX_NUM_CARDS_IN_A_STACK+1))+(MAX_NUM_FREECELLS)) + +#define fcs_foundation_value(state, d) \ + ( (state).data[FCS_FOUNDATIONS_OFFSET+(d)]) + +#define fcs_increment_foundation(state, d) \ + ( (state).data[FCS_FOUNDATIONS_OFFSET+(d)]++ ) + +#define fcs_set_foundation(state, d, value) \ + ( (state).data[FCS_FOUNDATIONS_OFFSET+(d)] = (value) ) + +#define fcs_pop_stack_card(state, s, into) \ + { \ + into = fcs_stack_card((state), (s), (fcs_stack_len((state), (s))-1)); \ + (state).data[((s)*(MAX_NUM_CARDS_IN_A_STACK+1))+1+(fcs_stack_len((state), (s))-1)] = fcs_empty_card; \ + (state).data[(s)*(MAX_NUM_CARDS_IN_A_STACK+1)]--; \ + } + +#define fcs_push_card_into_stack(state, ds, from) \ + { \ + (state).data[(ds)*(MAX_NUM_CARDS_IN_A_STACK+1)+1+fcs_stack_len((state), (ds))] = (from); \ + (state).data[(ds)*(MAX_NUM_CARDS_IN_A_STACK+1)]++; \ + } + +#define fcs_push_stack_card_into_stack(state, ds, ss, sc) \ + fcs_push_card_into_stack((state), (ds), fcs_stack_card((state), (ss), (sc))) + +#define fcs_duplicate_state(dest, src) \ + (dest) = (src) + +#define fcs_put_card_in_freecell(state, f, card) \ + (state).data[FCS_FREECELLS_OFFSET+(f)] = (card); + +#define fcs_empty_freecell(state, f) \ + fcs_put_card_in_freecell((state), (f), fcs_empty_card) + +#define fcs_card_set_num(card, num) \ + (card) = (((card)&0xF0)|(num)); + +#define fcs_card_set_suit(card, suit) \ + (card) = (((card)&0x4F)|((suit)<<4)); + +#define fcs_card_set_flipped(card, flipped) \ + (card) = (((card)&((fcs_card_t)0x3F))|((fcs_card_t)((flipped)<<6))) + +#define fcs_card_get_flipped(card) \ + ( (card) >> 6 ) + + +#ifdef FCS_WITH_TALONS +#define fcs_talon_len(state) \ + ((state).talon_params[0]) + +#define fcs_talon_pos(state) \ + ((state).talon_params[1]) + +#define fcs_put_card_in_talon(state, pos, card) \ + ((state).talon[pos] = (card)) + +#define fcs_get_talon_card(state, pos) \ + ((state).talon[pos]) +#endif + +#define fcs_flip_stack_card(state, s, c) \ + (fcs_card_set_flipped(fcs_stack_card((state),(s),(c)), ((fcs_card_t)0) )) + +#define fcs_copy_stack(state, idx, buffer) {} + +#elif defined(INDIRECT_STACK_STATES) /* #ifdef DEBUG_STATES + #elif defined(COMPACT_STATES) + */ + +typedef char fcs_card_t; + +struct fcs_struct_state_t +{ + fcs_card_t * stacks[MAX_NUM_STACKS]; + fcs_card_t freecells[MAX_NUM_FREECELLS]; + char foundations[MAX_NUM_DECKS*4]; +#ifdef FCS_WITH_TALONS + fcs_card_t * talon; + char talon_params[4]; +#endif +}; + +typedef struct fcs_struct_state_t fcs_state_t; + +#define fcs_card_card_num(card) \ + ( (card) & 0x0F ) + +#define fcs_card_suit(card) \ + ( ((card) >> 4) & 0x03 ) + +#define fcs_card_get_flipped(card) \ + ( (card) >> 6 ) + +#define fcs_standalone_stack_len(stack) \ + ( (size_t)(stack[0]) ) + +#define fcs_stack_len(state, s) \ + ( (unsigned int)(state).stacks[(s)][0] ) + +#define fcs_stack_card(state, s, c) \ + ( (state).stacks[(s)][c+1] ) + +#define fcs_stack_card_num(state, s, c) \ + ( fcs_card_card_num(fcs_stack_card((state),(s),(c))) ) + +#define fcs_stack_card_suit(state, s, c) \ + ( fcs_card_suit(fcs_stack_card((state),(s),(c))) ) + +#define fcs_freecell_card(state, f) \ + ( (state).freecells[(f)] ) + +#define fcs_freecell_card_num(state, f) \ + ( fcs_card_card_num(fcs_freecell_card((state),(f))) ) + +#define fcs_freecell_card_suit(state, f) \ + ( fcs_card_suit(fcs_freecell_card((state),(f))) ) + +#define fcs_foundation_value(state, d) \ + ( (state).foundations[(d)] ) + +#define fcs_increment_foundation(state, d) \ + ( (state).foundations[(d)]++ ) + +#define fcs_set_foundation(state, d, value) \ + ( (state).foundations[(d)] = (value) ) + +#define fcs_pop_stack_card(state, s, into) \ + { \ + into = fcs_stack_card((state), (s), (fcs_stack_len((state), (s))-1)); \ + (state).stacks[s][fcs_stack_len((state), (s))] = fcs_empty_card; \ + (state).stacks[s][0]--; \ + } + + +#define fcs_push_card_into_stack(state, ds, from) \ + { \ + (state).stacks[(ds)][fcs_stack_len((state), (ds))+1] = (from); \ + (state).stacks[(ds)][0]++; \ + } + +#define fcs_push_stack_card_into_stack(state, ds, ss, sc) \ + fcs_push_card_into_stack((state), (ds), fcs_stack_card((state), (ss), (sc))) + +#define fcs_put_card_in_freecell(state, f, card) \ + (state).freecells[(f)] = (card) + +#define fcs_empty_freecell(state, f) \ + fcs_put_card_in_freecell((state), (f), fcs_empty_card) + +#define fcs_card_set_num(card, num) \ + (card) = (((card)&0xF0)|(num)) + +#define fcs_card_set_suit(card, suit) \ + (card) = (((card)&0x4F)|((suit)<<4)) + +#define fcs_card_set_flipped(card, flipped) \ + (card) = (fcs_card_t)(((card)&0x3F)|((fcs_card_t)(flipped<<6))) + +#ifdef FCS_WITH_TALONS +#define fcs_talon_len(state) \ + ((state).talon_params[0]) + +#define fcs_talon_pos(state) \ + ((state).talon_params[1]) + +#define fcs_put_card_in_talon(state, pos, card) \ + ((state).talon[pos] = (card)) + +#define fcs_get_talon_card(state, pos) \ + ((state).talon[pos]) +#endif + +#define fcs_flip_stack_card(state, s, c) \ + (fcs_card_set_flipped(fcs_stack_card(state,s,c), ((fcs_card_t)0) )) + + +#define fcs_duplicate_state(dest,src) \ + { \ + (dest) = (src); \ + (dest).stacks_copy_on_write_flags = 0; \ + } + +#define fcs_copy_stack(state, idx, buffer) \ + { \ + if (! ((state).stacks_copy_on_write_flags & (1 << idx))) \ + { \ + size_t stack_len; \ + (state).stacks_copy_on_write_flags |= (1 << idx); \ + stack_len = fcs_stack_len((state).s,idx); \ + memcpy(&buffer[idx << 7], (state).s.stacks[idx], stack_len+1); \ + (state).s.stacks[idx] = &buffer[idx << 7]; \ + } \ + } + + +typedef char fcs_locs_t; + +#endif /* #ifdef DEBUG_STATES - + #elif defined COMPACT_STATES - + #elif defined INDIRECT_STACK_STATES + */ + +struct fcs_struct_state_with_locations_t +{ + fcs_state_t s; + fcs_locs_t stack_locs[MAX_NUM_STACKS]; + fcs_locs_t fc_locs[MAX_NUM_FREECELLS]; + struct fcs_struct_state_with_locations_t * parent; + fcs_move_stack_t * moves_to_parent; + int depth; + /* + * This field contains global, scan-independant flags, which are used + * from the FCS_VISITED_T enum below. + * + * FCS_VISITED_VISITED - deprecated + * + * FCS_VISITED_IN_SOLUTION_PATH - indicates that the state is in the + * solution path found by the scan. (used by the optimization scan) + * + * FCS_VISITED_IN_OPTIMIZED_PATH - indicates that the state is in the + * optimized solution path which is computed by the optimization scan. + * + * FCS_VISITED_DEAD_END - indicates that the state does not lead to + * anywhere useful, and scans should not examine it in the first place. + * */ + int visited; + /* + * The iteration in which this state was marked as visited + * */ + int visited_iter; + /* + * This is the number of direct children of this state which were not + * yet declared as dead ends. Once this counter reaches zero, this + * state too is declared as a dead end. + * */ + int num_active_children; + /* + * This is a vector of flags - one for each scan. Each indicates whether + * its scan has already visited this state + * */ + int scan_visited[MAX_NUM_SCANS_BUCKETS]; +#ifdef INDIRECT_STACK_STATES + /* + * A vector of flags that indicates which stacks were already copied. + * */ + int stacks_copy_on_write_flags; +#endif +}; + +typedef struct fcs_struct_state_with_locations_t fcs_state_with_locations_t; + + +extern fcs_card_t freecell_solver_empty_card; +#define fcs_empty_card freecell_solver_empty_card + + +#ifdef FCS_WITH_TALONS +#define fcs_klondike_talon_len(state) \ + ((state).talon[0]) + +#define fcs_klondike_talon_stack_pos(state) \ + ((state).talon_params[0]) + +#define fcs_klondike_talon_queue_pos(state) \ + ((state).talon_params[1]) + +#define fcs_klondike_talon_num_redeals_left(state) \ + ((state).talon_params[2]) + +#define fcs_klondike_talon_get_top_card(state) \ + ((state).talon[(int)fcs_klondike_talon_stack_pos(state)]) + +#define fcs_klondike_talon_queue_to_stack(state) \ + ( ((state).talon[(int)((++fcs_klondike_talon_stack_pos(state))+1)]) = \ + ((state).talon[(int)((fcs_klondike_talon_queue_pos(state)++)+1)]) ) + +#define fcs_klondike_talon_redeal_bare(state) \ + { \ + fcs_klondike_talon_stack_pos(state) = -1; \ + fcs_klondike_talon_queue_pos(state) = 0; \ + } + +#define fcs_klondike_talon_decrement_stack(state) \ + ((state).talon[(int)((fcs_klondike_talon_stack_pos(state)--)+1)] = fcs_empty_card) +#endif + + +extern void freecell_solver_canonize_state( + fcs_state_with_locations_t * state, + int freecells_num, + int stacks_num + ); + +#define fcs_canonize_state(state,freecells_num,stacks_num) freecell_solver_canonize_state((state),(freecells_num),(stacks_num)) + +#if (FCS_STATE_STORAGE != FCS_STATE_STORAGE_INDIRECT) + +#if (FCS_STATE_STORAGE != FCS_STATE_STORAGE_LIBREDBLACK_TREE) +typedef void * fcs_compare_context_t; +#else +typedef const void * fcs_compare_context_t; +#endif + +extern int freecell_solver_state_compare(const void * s1, const void * s2); +extern int freecell_solver_state_compare_equal(const void * s1, const void * s2); +extern int freecell_solver_state_compare_with_context(const void * s1, const void * s2, fcs_compare_context_t context); +#else +extern int freecell_solver_state_compare_indirect(const void * s1, const void * s2); +extern int freecell_solver_state_compare_indirect_with_context(const void * s1, const void * s2, void * context); +#endif + +#ifdef FCS_WITH_TALONS +extern int fcs_talon_compare_with_context(const void * s1, const void * s2, fcs_compare_context_t context); +#endif + +enum FCS_USER_STATE_TO_C_RETURN_CODES +{ + FCS_USER_STATE_TO_C__SUCCESS = 0, + FCS_USER_STATE_TO_C__PREMATURE_END_OF_INPUT +}; + +int freecell_solver_initial_user_state_to_c( + const char * string, + fcs_state_with_locations_t * out_state, + int freecells_num, + int stacks_num, + int decks_num +#ifdef FCS_WITH_TALONS + ,int talon_type +#endif +#ifdef INDIRECT_STACK_STATES + , fcs_card_t * indirect_stacks_buffer +#endif + ); + + +extern char * freecell_solver_state_as_string( + fcs_state_with_locations_t * state, + int freecells_num, + int stacks_num, + int decks_num, + int parseable_output, + int canonized_order_output, + int display_10_as_t + ); + +enum FCS_STATE_VALIDITY_CODES +{ + FCS_STATE_VALIDITY__OK = 0, + FCS_STATE_VALIDITY__EMPTY_SLOT = 3, + FCS_STATE_VALIDITY__EXTRA_CARD = 2, + FCS_STATE_VALIDITY__MISSING_CARD = 1, + FCS_STATE_VALIDITY__PREMATURE_END_OF_INPUT = 4 +}; + +extern int freecell_solver_check_state_validity( + fcs_state_with_locations_t * state, + int freecells_num, + int stacks_num, + int decks_num, +#ifdef FCS_WITH_TALONS + int talon_type, +#endif + fcs_card_t * misplaced_card + ); + +#ifdef __cplusplus +} +#endif + +enum FCS_VISITED_T +{ + FCS_VISITED_VISITED = 0x1, + FCS_VISITED_IN_SOLUTION_PATH = 0x2, + FCS_VISITED_IN_OPTIMIZED_PATH = 0x4, + FCS_VISITED_DEAD_END = 0x8, + FCS_VISITED_ALL_TESTS_DONE = 0x10 +}; + + +#endif /* FC_SOLVE__STATE_H */ diff --git a/kpat/freecell-solver/test_arr.h b/kpat/freecell-solver/test_arr.h new file mode 100644 index 00000000..cfc5cd12 --- /dev/null +++ b/kpat/freecell-solver/test_arr.h @@ -0,0 +1,136 @@ +/* + * test_arr.h - header file for some routines and macros involving tests and + * the like for Freecell Solver. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2002 + * + * This file is in the public domain (it's uncopyrighted). + * */ + +#ifndef FC_SOLVE__TEST_ARR_H +#define FC_SOLVE__TEST_ARR_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*freecell_solver_solve_for_state_test_t)( + freecell_solver_soft_thread_t *, + fcs_state_with_locations_t *, + int, + int, + fcs_derived_states_list_t *, + int + ); + +extern freecell_solver_solve_for_state_test_t freecell_solver_sfs_tests[FCS_TESTS_NUM]; + +/* + * This macro determines if child can be placed above parent. + * + * The variable sequences_are_built_by has to be initialized to + * the sequences_are_built_by member of the instance. + * + * */ +#define fcs_is_parent_card(child, parent) \ + ((fcs_card_card_num(child)+1 == fcs_card_card_num(parent)) && \ + ((sequences_are_built_by == FCS_SEQ_BUILT_BY_RANK) ? \ + 1 : \ + ((sequences_are_built_by == FCS_SEQ_BUILT_BY_SUIT) ? \ + (fcs_card_suit(child) == fcs_card_suit(parent)) : \ + ((fcs_card_suit(child) & 0x1) != (fcs_card_suit(parent)&0x1)) \ + )) \ + ) + +/* + * This macro traces the path of the state up to the original state, + * and thus calculates its real depth. + * + * It then assigns the newly updated depth throughout the path. + * + * */ +#define calculate_real_depth(ptr_state_orig) \ +{ \ + if (calc_real_depth) \ + { \ + int this_real_depth = 0; \ + fcs_state_with_locations_t * ptr_state = (ptr_state_orig); \ + /* Count the number of states until the original state. */ \ + while(ptr_state != NULL) \ + { \ + ptr_state = ptr_state->parent; \ + this_real_depth++; \ + } \ + this_real_depth--; \ + ptr_state = (ptr_state_orig); \ + /* Assign the new depth throughout the path*/ \ + while (ptr_state->depth != this_real_depth) \ + { \ + ptr_state->depth = this_real_depth; \ + this_real_depth--; \ + ptr_state = ptr_state->parent; \ + } \ + } \ +} \ + +/* + * This macro marks a state as a dead end, and afterwards propogates + * this information to its parent and ancestor states. + * */ +#define mark_as_dead_end(ptr_state_input) \ +{ \ + if (scans_synergy) \ + { \ + fcs_state_with_locations_t * ptr_state = (ptr_state_input); \ + /* Mark as a dead end */ \ + ptr_state->visited |= FCS_VISITED_DEAD_END; \ + ptr_state = ptr_state->parent; \ + if (ptr_state != NULL) \ + { \ + /* Decrease the refcount of the state */ \ + ptr_state->num_active_children--; \ + while((ptr_state->num_active_children == 0) && (ptr_state->visited & FCS_VISITED_ALL_TESTS_DONE)) \ + { \ + /* Mark as dead end */ \ + ptr_state->visited |= FCS_VISITED_DEAD_END; \ + /* Go to its parent state */ \ + ptr_state = ptr_state->parent; \ + if (ptr_state == NULL) \ + { \ + break; \ + } \ + /* Decrease the refcount */ \ + ptr_state->num_active_children--; \ + } \ + } \ + } \ +} + +/* + * This macro checks if we need to terminate from running this soft + * thread and return to the soft thread manager with an + * FCS_STATE_SUSPEND_PROCESS + * */ +#define check_if_limits_exceeded() \ + ( \ + ((instance->max_num_times >= 0) && \ + (instance->num_times >= instance->max_num_times)) \ + || \ + ((hard_thread->ht_max_num_times >= 0) && \ + (hard_thread->num_times >= hard_thread->ht_max_num_times)) \ + || \ + ((hard_thread->max_num_times >= 0) && \ + (hard_thread->num_times >= hard_thread->max_num_times)) \ + || \ + ((instance->max_num_states_in_collection >= 0) && \ + (instance->num_states_in_collection >= \ + instance->max_num_states_in_collection) \ + ) \ + ) + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/kpat/freecell-solver/tests.h b/kpat/freecell-solver/tests.h new file mode 100644 index 00000000..ce0b35b5 --- /dev/null +++ b/kpat/freecell-solver/tests.h @@ -0,0 +1,307 @@ +/* + * fcs.h - header file of the test functions for Freecell Solver. + * + * The test functions code is found in freecell.c + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#ifndef FC_SOLVE__TESTS_H +#define FC_SOLVE__TESTS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include "fcs_isa.h" +#include "fcs.h" + +#include "test_arr.h" + + +/* + * The number of cards that can be moved is + * (freecells_number + 1) * 2 ^ (free_stacks_number) + * + * See the Freecell FAQ and the source code of PySol + * + * */ +#define calc_max_sequence_move(fc_num, fs_num) \ + ((instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD) ? \ + ( \ + (instance->unlimited_sequence_move) ? \ + INT_MAX : \ + (((fc_num)+1)<<(fs_num)) \ + ) : \ + ((fc_num)+1) \ + ) + +#include "caas.h" + +/* + * These are some macros to make it easier for the programmer. + * */ +#define state_with_locations (*ptr_state_with_locations) +#define state (ptr_state_with_locations->s) +#define new_state_with_locations (*ptr_new_state_with_locations) +#define new_state (ptr_new_state_with_locations->s) + +#define sfs_check_state_begin() \ + fcs_state_ia_alloc_into_var(ptr_new_state_with_locations, hard_thread); \ + fcs_duplicate_state(new_state_with_locations, state_with_locations); \ + /* Some A* and BFS parameters that need to be initialized in \ + * the derived state. \ + * */ \ + ptr_new_state_with_locations->parent = ptr_state_with_locations; \ + ptr_new_state_with_locations->moves_to_parent = moves; \ + /* Make sure depth is consistent with the game graph. \ + * I.e: the depth of every newly discovered state is derived from \ + * the state from which it was discovered. */ \ + ptr_new_state_with_locations->depth = ptr_state_with_locations->depth + 1; \ + /* Mark this state as a state that was not yet visited */ \ + ptr_new_state_with_locations->visited = 0; \ + /* It's a newly created state which does not have children yet. */ \ + ptr_new_state_with_locations->num_active_children = 0; \ + memset(ptr_new_state_with_locations->scan_visited, '\0', \ + sizeof(ptr_new_state_with_locations->scan_visited) \ + ); \ + fcs_move_stack_reset(moves); \ + + + + +#define sfs_check_state_end() \ +/* The last move in a move stack should be FCS_MOVE_TYPE_CANONIZE \ + * because it indicates that the order of the stacks and freecells \ + * need to be recalculated \ + * */ \ +fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE); \ +fcs_move_stack_push(moves, temp_move); \ + \ +{ \ + fcs_state_with_locations_t * existing_state; \ + check = freecell_solver_check_and_add_state( \ + soft_thread, \ + ptr_new_state_with_locations, \ + &existing_state \ + ); \ + if ((check == FCS_STATE_BEGIN_SUSPEND_PROCESS) || \ + (check == FCS_STATE_SUSPEND_PROCESS)) \ + { \ + /* This state is not going to be used, so \ + * let's clean it. */ \ + fcs_state_ia_release(hard_thread); \ + return check; \ + } \ + else if (check == FCS_STATE_ALREADY_EXISTS) \ + { \ + fcs_state_ia_release(hard_thread); \ + calculate_real_depth(existing_state); \ + /* Re-parent the existing state to this one. \ + * \ + * What it means is that if the depth of the state if it \ + * can be reached from this one is lower than what it \ + * already have, then re-assign its parent to this state. \ + * */ \ + if (reparent && \ + (existing_state->depth > ptr_state_with_locations->depth+1)) \ + { \ + /* Make a copy of "moves" because "moves" will be destroyed */\ + existing_state->moves_to_parent = \ + freecell_solver_move_stack_compact_allocate( \ + hard_thread, moves \ + ); \ + if (!(existing_state->visited & FCS_VISITED_DEAD_END)) \ + { \ + if ((--existing_state->parent->num_active_children) == 0) \ + { \ + mark_as_dead_end( \ + existing_state->parent \ + ); \ + } \ + ptr_state_with_locations->num_active_children++; \ + } \ + existing_state->parent = ptr_state_with_locations; \ + existing_state->depth = ptr_state_with_locations->depth + 1; \ + } \ + fcs_derived_states_list_add_state( \ + derived_states_list, \ + existing_state \ + ); \ + } \ + else \ + { \ + fcs_derived_states_list_add_state( \ + derived_states_list, \ + ptr_new_state_with_locations \ + ); \ + } \ +} + + +/* + This macro checks if the top card in the stack is a flipped card + , and if so flips it so its face is up. + */ +#define fcs_flip_top_card(stack) \ +{ \ + int cards_num; \ + cards_num = fcs_stack_len(new_state,stack); \ + \ + if (cards_num > 0) \ + { \ + if (fcs_card_get_flipped( \ + fcs_stack_card( \ + new_state, \ + stack, \ + cards_num-1) \ + ) == 1 \ + ) \ + { \ + fcs_flip_stack_card(new_state,stack,cards_num-1); \ + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_FLIP_CARD); \ + fcs_move_set_src_stack(temp_move, stack); \ + \ + fcs_move_stack_push(moves, temp_move); \ + } \ + } \ +} + + +/* + * dest is the destination stack + * source is the source stack + * start is the start height + * end is the end height + * a is the iterator + * */ +#define fcs_move_sequence(dest, source, start, end, a) \ +{ \ + for ( a = (start) ; a <= (end) ; a++) \ + { \ + fcs_push_stack_card_into_stack(new_state, dest, source, a); \ + } \ + \ + for ( a = (start) ; a <= (end) ; a++) \ + { \ + fcs_pop_stack_card(new_state, source, temp_card); \ + } \ + \ + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_STACK_TO_STACK); \ + fcs_move_set_src_stack(temp_move, source); \ + fcs_move_set_dest_stack(temp_move, dest); \ + fcs_move_set_num_cards_in_seq(temp_move, (end)-(start)+1); \ + \ + fcs_move_stack_push(moves, temp_move); \ +} + +/* + * This test declares a few access variables that are used in all + * the tests. + * */ +#define tests_declare_accessors() \ + freecell_solver_hard_thread_t * hard_thread; \ + freecell_solver_instance_t * instance; \ + fcs_state_with_locations_t * ptr_new_state_with_locations; \ + fcs_move_stack_t * moves; \ + char * indirect_stacks_buffer; \ + int calc_real_depth; \ + int scans_synergy + +/* + * This macro defines these accessors to have some value. + * */ +#define tests_define_accessors() \ + hard_thread = soft_thread->hard_thread; \ + instance = hard_thread->instance; \ + moves = hard_thread->reusable_move_stack; \ + indirect_stacks_buffer = hard_thread->indirect_stacks_buffer; \ + calc_real_depth = instance->calc_real_depth; \ + scans_synergy = instance->scans_synergy; + + + +extern int freecell_solver_sfs_simple_simon_move_sequence_to_founds( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ); +extern int freecell_solver_sfs_simple_simon_move_sequence_to_true_parent( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ); + +extern int freecell_solver_sfs_simple_simon_move_whole_stack_sequence_to_false_parent( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ); + +extern int freecell_solver_sfs_simple_simon_move_sequence_to_true_parent_with_some_cards_above( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ); + +extern int freecell_solver_sfs_simple_simon_move_sequence_with_some_cards_above_to_true_parent( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ); + +extern int freecell_solver_sfs_simple_simon_move_sequence_with_junk_seq_above_to_true_parent_with_some_cards_above( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ); + +extern int freecell_solver_sfs_simple_simon_move_whole_stack_sequence_to_false_parent_with_some_cards_above( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ); + +extern int freecell_solver_sfs_simple_simon_move_sequence_to_parent_on_the_same_stack( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ); + +#ifdef __cplusplus +} +#endif + +#define my_copy_stack(idx) fcs_copy_stack(new_state_with_locations, idx, indirect_stacks_buffer); + +#endif /* FC_SOLVE__TESTS_H */ diff --git a/kpat/freecell.cpp b/kpat/freecell.cpp new file mode 100644 index 00000000..7a72c1fc --- /dev/null +++ b/kpat/freecell.cpp @@ -0,0 +1,854 @@ +/*--------------------------------------------------------------------------- + + freecell.cpp implements a patience card game + + Copyright (C) 1997 Rodolfo Borges + (C) 2000 Stephan Kulow + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +---------------------------------------------------------------------------*/ + +#include "freecell.h" +#include +#include "deck.h" +#include +#include +#include +#include +#include +#include "cardmaps.h" + +#include "freecell-solver/fcs_user.h" +#include "freecell-solver/fcs_cl.h" + +const int CHUNKSIZE = 100; + +void FreecellPile::moveCards(CardList &c, Pile *to) +{ + if (c.count() == 1) { + Pile::moveCards(c, to); + return; + } + FreecellBase *b = dynamic_cast(dealer()); + if (b) { + b->moveCards(c, this, to); + } +} + +//-------------------------------------------------------------------------// + +FreecellBase::FreecellBase( int decks, int stores, int freecells, int fill, bool unlimit, + KMainWindow* parent, const char* name) + : Dealer(parent,name), +solver_instance(0), es_filling(fill), solver_ret(FCS_STATE_NOT_BEGAN_YET), +unlimited_move(unlimit) +{ + deck = Deck::new_deck(this, decks); + deck->hide(); + + kdDebug(11111) << "cards " << deck->cards().count() << endl; + Pile *t; + for (int i = 0; i < stores; i++) { + FreecellPile *p = new FreecellPile(1 + i, this); + store.append(p); + p->setAddFlags(Pile::addSpread | Pile::several); + p->setRemoveFlags(Pile::several); + p->setCheckIndex(0); + } + + for (int i = 0; i < freecells; i++) + { + t = new Pile (1 + stores +i, this); + freecell.append(t); + t->setType(Pile::FreeCell); + } + + for (int i = 0; i < decks * 4; i++) + { + t = new Pile(1 + stores + freecells +i, this); + target.append(t); + t->setType(Pile::KlondikeTarget); + // COOLO: I'm still not too sure about that t->setRemoveFlags(Pile::Default); + } + + setActions(Dealer::Demo | Dealer::Hint); +} + +FreecellBase::~FreecellBase() +{ + if (solver_instance) + { + freecell_solver_user_free(solver_instance); + solver_instance = NULL; + } +} +//-------------------------------------------------------------------------// + +void FreecellBase::restart() +{ + freeSolution(); + deck->collectAndShuffle(); + deal(); +} + +QString suitToString(Card::Suit s) { + switch (s) { + case Card::Clubs: + return "C"; + case Card::Hearts: + return "H"; + case Card::Diamonds: + return "D"; + case Card::Spades: + return "S"; + } + return QString::null; +} + +QString rankToString(Card::Rank r) +{ + switch (r) { + case Card::King: + return "K"; + case Card::Ace: + return "A"; + case Card::Jack: + return "J"; + case Card::Queen: + return "Q"; + default: + return QString::number(r); + } +} + +int getDeck(Card::Suit suit) +{ + switch (suit) { + case Card::Hearts: + return 0; + case Card::Spades: + return 1; + case Card::Diamonds: + return 2; + case Card::Clubs: + return 3; + } + return 0; +} + +static const char * freecell_solver_cmd_line_args[280] = +{ +"--method", "soft-dfs", "-to", "0123456789", "-step", +"500", "--st-name", "1", "-nst", "--method", +"soft-dfs", "-to", "0123467", "-step", "500", +"--st-name", "2", "-nst", "--method", "random-dfs", +"-seed", "2", "-to", "0[01][23456789]", "-step", +"500", "--st-name", "3", "-nst", "--method", +"random-dfs", "-seed", "1", "-to", "0[0123456789]", +"-step", "500", "--st-name", "4", "-nst", "--method", +"random-dfs", "-seed", "3", "-to", "0[01][23467]", +"-step", "500", "--st-name", "5", "-nst", "--method", +"random-dfs", "-seed", "4", "-to", "0[0123467]", +"-step", "500", "--st-name", "9", "-nst", "--method", +"random-dfs", "-to", "[01][23456789]", "-seed", "8", +"-step", "500", "--st-name", "10", "-nst", +"--method", "random-dfs", "-to", "[01][23456789]", +"-seed", "268", "-step", "500", "--st-name", "12", +"-nst", "--method", "a-star", "-asw", +"0.2,0.3,0.5,0,0", "-step", "500", "--st-name", "16", +"-nst", "--method", "a-star", "-to", "0123467", +"-asw", "0.5,0,0.3,0,0", "-step", "500", "--st-name", +"18", "-nst", "--method", "soft-dfs", "-to", +"0126394875", "-step", "500", "--st-name", "19", +"--prelude", +"350@2,350@5,350@9,350@12,350@2,350@10,350@3,350@9,350@5,350@18,350@2,350@5,350@4,350@10,350@4,350@12,1050@9,700@18,350@10,350@5,350@2,350@10,1050@16,350@2,700@4,350@10,1050@2,1400@3,350@18,1750@5,350@16,350@18,700@4,1050@12,2450@5,1400@18,1050@2,1400@10,6300@1,4900@12,8050@18", +"-ni", "--method", "soft-dfs", "-to", "01ABCDE", +"-step", "500", "--st-name", "0", "-nst", "--method", +"random-dfs", "-to", "[01][ABCDE]", "-seed", "1", +"-step", "500", "--st-name", "1", "-nst", "--method", +"random-dfs", "-to", "[01][ABCDE]", "-seed", "2", +"-step", "500", "--st-name", "2", "-nst", "--method", +"random-dfs", "-to", "[01][ABCDE]", "-seed", "3", +"-step", "500", "--st-name", "3", "-nst", "--method", +"random-dfs", "-to", "01[ABCDE]", "-seed", "268", +"-step", "500", "--st-name", "4", "-nst", "--method", +"a-star", "-to", "01ABCDE", "-step", "500", +"--st-name", "5", "-nst", "--method", "a-star", +"-to", "01ABCDE", "-asw", "0.2,0.3,0.5,0,0", "-step", +"500", "--st-name", "6", "-nst", "--method", +"a-star", "-to", "01ABCDE", "-asw", "0.5,0,0.5,0,0", +"-step", "500", "--st-name", "7", "-nst", "--method", +"random-dfs", "-to", "[01][ABD][CE]", "-seed", "1900", +"-step", "500", "--st-name", "8", "-nst", "--method", +"random-dfs", "-to", "[01][ABCDE]", "-seed", "192", +"-step", "500", "--st-name", "9", "-nst", "--method", +"random-dfs", "-to", "[01ABCDE]", "-seed", "1977", +"-step", "500", "--st-name", "10", "-nst", +"--method", "random-dfs", "-to", "[01ABCDE]", "-seed", +"24", "-step", "500", "--st-name", "11", "-nst", +"--method", "soft-dfs", "-to", "01ABDCE", "-step", +"500", "--st-name", "12", "-nst", "--method", +"soft-dfs", "-to", "ABC01DE", "-step", "500", +"--st-name", "13", "-nst", "--method", "soft-dfs", +"-to", "01EABCD", "-step", "500", "--st-name", "14", +"-nst", "--method", "soft-dfs", "-to", "01BDAEC", +"-step", "500", "--st-name", "15", "--prelude", +"1000@0,1000@3,1000@0,1000@9,1000@4,1000@9,1000@3,1000@4,2000@2,1000@0,2000@1,1000@14,2000@11,1000@14,1000@3,1000@11,1000@2,1000@0,2000@4,2000@10,1000@0,1000@2,2000@10,1000@0,2000@11,2000@1,1000@10,1000@2,1000@10,2000@0,1000@9,1000@1,1000@2,1000@14,3000@8,1000@2,1000@14,1000@1,1000@10,3000@6,2000@4,1000@2,2000@0,1000@2,1000@11,2000@6,1000@0,5000@1,1000@0,2000@1,1000@2,3000@3,1000@10,1000@14,2000@6,1000@0,1000@2,2000@11,6000@8,8000@9,3000@1,2000@10,2000@14,3000@15,4000@0,1000@8,1000@10,1000@14,7000@0,14000@2,6000@3,7000@4,1000@8,4000@9,2000@15,2000@6,4000@3,2000@4,3000@15,2000@0,6000@1,2000@4,4000@6,4000@9,4000@14,7000@8,3000@0,3000@1,5000@2,3000@3,4000@9,8000@10,9000@3,5000@8,7000@11,11000@12,12000@0,8000@3,11000@9,9000@15,7000@2,12000@8,16000@5,8000@13,18000@0,9000@15,12000@10,16000@0,14000@3,16000@9,26000@4,23000@3,42000@6,22000@8,27000@10,38000@7,41000@0,42000@3,84000@13,61000@15,159000@5,90000@9" +}; + +void FreecellBase::findSolution() +{ + kdDebug(11111) << "findSolution\n"; + + QString output = solverFormat(); + kdDebug(11111) << output << endl; + + int ret; + + /* If solver_instance was not initialized yet - initialize it */ + if (! solver_instance) + { + solver_instance = freecell_solver_user_alloc(); + + char * error_string; + int error_arg; + char * known_parameters[1] = {NULL}; + + + ret = freecell_solver_user_cmd_line_parse_args( + solver_instance, + sizeof(freecell_solver_cmd_line_args)/sizeof(freecell_solver_cmd_line_args[0]), + freecell_solver_cmd_line_args, + 0, + known_parameters, + NULL, + NULL, + &error_string, + &error_arg + ); + + + assert(!ret); + } + /* + * I'm using the more standard interface instead of the depracated + * user_set_game one. I'd like that each function will have its + * own dedicated purpose. + * + * Shlomi Fish + * */ +#if 0 + ret = freecell_solver_user_set_game(solver_instance, + freecell.count(), + store.count(), + deck->decksNum(), + FCS_SEQ_BUILT_BY_ALTERNATE_COLOR, + unlimited_move, + es_filling); + assert(!ret); +#else + freecell_solver_user_set_num_freecells(solver_instance,freecell.count()); + freecell_solver_user_set_num_stacks(solver_instance,store.count()); + freecell_solver_user_set_num_decks(solver_instance,deck->decksNum()); + freecell_solver_user_set_sequences_are_built_by_type(solver_instance, FCS_SEQ_BUILT_BY_ALTERNATE_COLOR); + freecell_solver_user_set_sequence_move(solver_instance, unlimited_move); + freecell_solver_user_set_empty_stacks_filled_by(solver_instance, es_filling); + +#endif + + freecell_solver_user_limit_iterations(solver_instance, CHUNKSIZE); + + solver_ret = freecell_solver_user_solve_board(solver_instance, + output.latin1()); + resumeSolution(); +} + +void FreecellBase::resumeSolution() +{ + if (!solver_instance) + return; + + emit gameInfo(i18n("%1 tries - depth %2") + .arg(freecell_solver_user_get_num_times(solver_instance)) + .arg(freecell_solver_user_get_current_depth(solver_instance))); + + if (solver_ret == FCS_STATE_WAS_SOLVED) + { + emit gameInfo(i18n("solved after %1 tries"). + arg(freecell_solver_user_get_num_times( + solver_instance))); + kdDebug(11111) << "solved\n"; + Dealer::demo(); + return; + } + if (solver_ret == FCS_STATE_IS_NOT_SOLVEABLE) { + int moves = freecell_solver_user_get_num_times(solver_instance); + freeSolution(); + emit gameInfo(i18n("unsolved after %1 moves") + .arg(moves)); + stopDemo(); + return; + } + + unsigned int max_iters = freecell_solver_user_get_limit_iterations( + solver_instance) + CHUNKSIZE; + freecell_solver_user_limit_iterations(solver_instance, + max_iters); + + if (max_iters > 120000) { + solver_ret = FCS_STATE_IS_NOT_SOLVEABLE; + resumeSolution(); + return; + } + + solver_ret = freecell_solver_user_resume_solution(solver_instance); + QTimer::singleShot(0, this, SLOT(resumeSolution())); + +} +MoveHint *FreecellBase::translateMove(void *m) { + fcs_move_t move = *(static_cast(m)); + uint cards = fcs_move_get_num_cards_in_seq(move); + Pile *from = 0; + Pile *to = 0; + + switch(fcs_move_get_type(move)) + { + case FCS_MOVE_TYPE_STACK_TO_STACK: + from = store[fcs_move_get_src_stack(move)]; + to = store[fcs_move_get_dest_stack(move)]; + break; + + case FCS_MOVE_TYPE_FREECELL_TO_STACK: + from = freecell[fcs_move_get_src_freecell(move)]; + to = store[fcs_move_get_dest_stack(move)]; + cards = 1; + break; + + case FCS_MOVE_TYPE_FREECELL_TO_FREECELL: + from = freecell[fcs_move_get_src_freecell(move)]; + to = freecell[fcs_move_get_dest_freecell(move)]; + cards = 1; + break; + + case FCS_MOVE_TYPE_STACK_TO_FREECELL: + from = store[fcs_move_get_src_stack(move)]; + to = freecell[fcs_move_get_dest_freecell(move)]; + cards = 1; + break; + + case FCS_MOVE_TYPE_STACK_TO_FOUNDATION: + from = store[fcs_move_get_src_stack(move)]; + cards = 1; + to = 0; + break; + + case FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION: + from = freecell[fcs_move_get_src_freecell(move)]; + cards = 1; + to = 0; + } + assert(from); + assert(cards <= from->cards().count()); + assert(to || cards == 1); + Card *c = from->cards()[from->cards().count() - cards]; + + if (!to) + to = findTarget(c); + assert(to); + return new MoveHint(c, to); +} + +QString FreecellBase::solverFormat() const +{ + QString output; + QString tmp; + for (uint i = 0; i < target.count(); i++) { + if (target[i]->isEmpty()) + continue; + tmp += suitToString(target[i]->top()->suit()) + "-" + rankToString(target[i]->top()->rank()) + " "; + } + if (!tmp.isEmpty()) + output += QString::fromLatin1("Foundations: %1\n").arg(tmp); + + tmp.truncate(0); + for (uint i = 0; i < freecell.count(); i++) { + if (freecell[i]->isEmpty()) + tmp += "- "; + else + tmp += rankToString(freecell[i]->top()->rank()) + suitToString(freecell[i]->top()->suit()) + " "; + } + if (!tmp.isEmpty()) + output += QString::fromLatin1("Freecells: %1\n").arg(tmp); + + for (uint i = 0; i < store.count(); i++) + { + CardList cards = store[i]->cards(); + for (CardList::ConstIterator it = cards.begin(); it != cards.end(); ++it) + output += rankToString((*it)->rank()) + suitToString((*it)->suit()) + " "; + output += "\n"; + } + return output; +} + +// Idea stolen from klondike.cpp +// +// This function returns true when it is certain that the card t is no longer +// needed on any of the play piles. +// +// To determine wether a card is no longer needed on any of the play piles we +// obviously must know what a card can be used for there. According to the +// rules a card can be used to store another card with 1 less unit of value +// and opposite color. This is the only thing that a card can be used for +// there. Therefore the cards with lowest value (1) are useless there (base +// case). The other cards each have 2 cards that can be stored on them, let us +// call those 2 cards *depending cards*. +// +// The object of the game is to put all cards on the target piles. Therefore +// cards that are no longer needed on any of the play piles should be put on +// the target piles if possible. Cards on the target piles can not be moved +// and they can not store any of its depending cards. Let us call this that +// the cards on the target piles are *out of play*. +// +// The simple and obvious rule is: +// A card is no longer needed when both of its depending cards are out of +// play. +// +// More complex: +// Assume card t is red. Now, if the lowest unplayed black card is +// t.value()-2, then t may be needed to hold that black t.value()-1 card. +// If the lowest unplayed black card is t.value()-1, it will be playable +// to the target, unless it is needed for a red card of value t.value()-2. +// +// So, t is not needed if the lowest unplayed red card is t.value()-2 and the +// lowest unplayed black card is t.value()-1, OR if the lowest unplayed black +// card is t.value(). So, no recursion needed - we did it ahead of time. + +bool FreecellBase::noLongerNeeded(const Card & t) +{ + + if (t.rank() <= Card::Two) return true; // Base case. + + bool cardIsRed = t.isRed(); + + uint numSame = 0, numDiff = 0; + Card::Rank lowSame = Card::King, lowDiff = Card::King; + for (PileList::Iterator it = target.begin(); it != target.end(); ++it) + { + if ((*it)->isEmpty()) + continue; + if ((*it)->top()->isRed() == cardIsRed) { + numSame++; + if ((*it)->top()->rank() < lowSame) + lowSame = static_cast((*it)->top()->rank()+1); + } else { + numDiff++; + if ((*it)->top()->rank() < lowDiff) + lowDiff = static_cast((*it)->top()->rank()+1); + } + } + if (numSame < target.count()/2) lowSame = Card::Ace; + if (numDiff < target.count()/2) lowDiff = Card::Ace; + + return (lowDiff >= t.rank() || + (lowDiff >= t.rank()-1 && lowSame >= t.rank()-2)); +} + +// This is the getHints() from dealer.cpp with one line changed +// to use noLongerNeeded() to decide if the card should be +// dropped or not. +// +// I would recommend adding a virtual bool noLongerNeeded(const Card &t) +// to the base class (Dealer) that just returns true, and then calling +// it like is done here. That would preserve current functionality +// but eliminate this code duplication +void FreecellBase::getHints() +{ + for (PileList::Iterator it = piles.begin(); it != piles.end(); ++it) + { + if (!takeTargetForHints() && (*it)->target()) + continue; + + Pile *store = *it; + if (store->isEmpty()) + continue; +// kdDebug(11111) << "trying " << store->top()->name() << endl; + + CardList cards = store->cards(); + while (cards.count() && !cards.first()->realFace()) cards.remove(cards.begin()); + + CardList::Iterator iti = cards.begin(); + while (iti != cards.end()) + { + if (store->legalRemove(*iti)) { +// kdDebug(11111) << "could remove " << (*iti)->name() << endl; + for (PileList::Iterator pit = piles.begin(); pit != piles.end(); ++pit) + { + Pile *dest = *pit; + if (dest == store) + continue; + if (store->indexOf(*iti) == 0 && dest->isEmpty() && !dest->target()) + continue; + if (!dest->legalAdd(cards)) + continue; + + bool old_prefer = checkPrefering( dest->checkIndex(), dest, cards ); + if (!takeTargetForHints() && dest->target()) + newHint(new MoveHint(*iti, dest, noLongerNeeded(*(*iti)))); + else { + store->hideCards(cards); + // if it could be here as well, then it's no use + if ((store->isEmpty() && !dest->isEmpty()) || !store->legalAdd(cards)) + newHint(new MoveHint(*iti, dest)); + else { + if (old_prefer && !checkPrefering( store->checkIndex(), + store, cards )) + { // if checkPrefers says so, we add it nonetheless + newHint(new MoveHint(*iti, dest)); + } + } + store->unhideCards(cards); + } + } + } + cards.remove(iti); + iti = cards.begin(); + } + } +} + +void FreecellBase::demo() +{ + if (solver_instance && solver_ret == FCS_STATE_WAS_SOLVED) { + Dealer::demo(); + return; + } + towait = (Card*)-1; + unmarkAll(); + kdDebug(11111) << "demo " << (solver_ret != FCS_STATE_IS_NOT_SOLVEABLE) << endl; + if (solver_ret != FCS_STATE_IS_NOT_SOLVEABLE) + findSolution(); +} + +MoveHint *FreecellBase::chooseHint() +{ + if (solver_instance && freecell_solver_user_get_moves_left(solver_instance)) { + + emit gameInfo(i18n("%1 moves before finish").arg(freecell_solver_user_get_moves_left(solver_instance))); + + fcs_move_t move; + if (!freecell_solver_user_get_next_move(solver_instance, &move)) { + MoveHint *mh = translateMove(&move); + oldmoves.append(mh); + return mh; + } else + return 0; + } else + return Dealer::chooseHint(); +} + +void FreecellBase::countFreeCells(int &free_cells, int &free_stores) const +{ + free_cells = 0; + free_stores = 0; + + for (uint i = 0; i < freecell.count(); i++) + if (freecell[i]->isEmpty()) free_cells++; + if (es_filling == FCS_ES_FILLED_BY_ANY_CARD) + for (uint i = 0; i < store.count(); i++) + if (store[i]->isEmpty()) free_stores++; +} + +void FreecellBase::freeSolution() +{ + for (HintList::Iterator it = oldmoves.begin(); it != oldmoves.end(); ++it) + delete *it; + oldmoves.clear(); + + if (!solver_instance) + return; + freecell_solver_user_recycle(solver_instance); + solver_ret = FCS_STATE_NOT_BEGAN_YET; +} + +void FreecellBase::stopDemo() +{ + Dealer::stopDemo(); + freeSolution(); +} + +void FreecellBase::moveCards(CardList &c, FreecellPile *from, Pile *to) +{ + if (!demoActive() && solver_instance) { + freeSolution(); + } + + assert(c.count() > 1); + if (unlimited_move) { + from->Pile::moveCards(c, to); + return; + } + setWaiting(true); + + from->moveCardsBack(c); + waitfor = c.first(); + connect(waitfor, SIGNAL(stoped(Card*)), SLOT(waitForMoving(Card*))); + + PileList fcs; + + for (uint i = 0; i < freecell.count(); i++) + if (freecell[i]->isEmpty()) fcs.append(freecell[i]); + + PileList fss; + + if (es_filling == FCS_ES_FILLED_BY_ANY_CARD) + for (uint i = 0; i < store.count(); i++) + if (store[i]->isEmpty() && to != store[i]) fss.append(store[i]); + + if (fcs.count() == 0) { + assert(fss.count()); + fcs.append(fss.last()); + fss.remove(fss.fromLast()); + } + while (moves.count()) { delete moves.first(); moves.remove(moves.begin()); } + + movePileToPile(c, to, fss, fcs, 0, c.count(), 0); + + if (!waitfor->animated()) + QTimer::singleShot(0, this, SLOT(startMoving())); +} + +struct MoveAway { + Pile *firstfree; + int start; + int count; +}; + +void FreecellBase::movePileToPile(CardList &c, Pile *to, PileList fss, PileList &fcs, uint start, uint count, int debug_level) +{ + kdDebug(11111) << debug_level << " movePileToPile" << c.count() << " " << start << " " << count << endl; + uint moveaway = 0; + if (count > fcs.count() + 1) { + moveaway = (fcs.count() + 1); + while (moveaway * 2 < count) + moveaway <<= 1; + } + kdDebug(11111) << debug_level << " moveaway " << moveaway << endl; + + QValueList moves_away; + + if (count - moveaway < (fcs.count() + 1) && (count <= 2 * (fcs.count() + 1))) { + moveaway = count - (fcs.count() + 1); + } + while (count > fcs.count() + 1) { + assert(fss.count()); + MoveAway ma; + ma.firstfree = fss[0]; + ma.start = start; + ma.count = moveaway; + moves_away.append(ma); + fss.remove(fss.begin()); + movePileToPile(c, ma.firstfree, fss, fcs, start, moveaway, debug_level + 1); + start += moveaway; + count -= moveaway; + moveaway >>= 1; + if ((count > (fcs.count() + 1)) && (count <= 2 * (fcs.count() + 1))) + moveaway = count - (fcs.count() + 1); + } + uint moving = QMIN(count, QMIN(c.count() - start, fcs.count() + 1)); + assert(moving); + + for (uint i = 0; i < moving - 1; i++) { + moves.append(new MoveHint(c[c.count() - i - 1 - start], fcs[i])); + } + moves.append(new MoveHint(c[c.count() - start - 1 - (moving - 1)], to)); + + for (int i = moving - 2; i >= 0; --i) + moves.append(new MoveHint(c[c.count() - i - 1 - start], to)); + + while (moves_away.count()) + { + MoveAway ma = moves_away.last(); + moves_away.remove(moves_away.fromLast()); + movePileToPile(c, to, fss, fcs, ma.start, ma.count, debug_level + 1); + fss.append(ma.firstfree); + } +} + +void FreecellBase::startMoving() +{ + kdDebug(11111) << "startMoving\n"; + if (moves.isEmpty()) { + if (demoActive() && towait) { + waitForDemo(towait); + } + setWaiting(false); + takeState(); + return; + } + + MoveHint *mh = moves.first(); + moves.remove(moves.begin()); + CardList empty; + empty.append(mh->card()); + assert(mh->card() == mh->card()->source()->top()); + assert(mh->pile()->legalAdd(empty)); + mh->pile()->add(mh->card()); + mh->pile()->moveCardsBack(empty, true); + waitfor = mh->card(); + kdDebug(11111) << "wait for moving end " << mh->card()->name() << endl; + connect(mh->card(), SIGNAL(stoped(Card*)), SLOT(waitForMoving(Card*))); + delete mh; +} + +void FreecellBase::newDemoMove(Card *m) +{ + Dealer::newDemoMove(m); + if (m != m->source()->top()) + m->disconnect(); +} + +void FreecellBase::waitForMoving(Card *c) +{ + if (waitfor != c) + return; + c->disconnect(); + startMoving(); +} + +bool FreecellBase::cardDblClicked(Card *c) +{ + // target move + if (Dealer::cardDblClicked(c)) + return true; + + if (c->animated()) + return false; + + if (c == c->source()->top() && c->realFace()) + for (uint i = 0; i < freecell.count(); i++) + if (freecell[i]->isEmpty()) { + CardList empty; + empty.append(c); + c->source()->moveCards(empty, freecell[i]); + canvas()->update(); + return true; + } + return false; +} + +bool FreecellBase::CanPutStore(const Pile *c1, const CardList &c2) const +{ + int fcs, fss; + countFreeCells(fcs, fss); + + if (c1->isEmpty()) // destination is empty + fss--; + + if (!unlimited_move && int(c2.count()) > ((fcs)+1)<isEmpty()) + return true; + + Card *c = c2.first(); // we assume there are only valid sequences + + // ok if in sequence, alternate colors + return ((c1->top()->rank() == (c->rank()+1)) + && (c1->top()->isRed() != c->isRed())); +} + +bool FreecellBase::checkAdd(int, const Pile *c1, const CardList &c2) const +{ + return CanPutStore(c1, c2); +} + +//-------------------------------------------------------------------------// + +bool FreecellBase::checkRemove(int checkIndex, const Pile *p, const Card *c) const +{ + if (checkIndex != 0) + return false; + + // ok if just one card + if (c == p->top()) + return true; + + // Now we're trying to move two or more cards. + + // First, let's check if the column is in valid + // (that is, in sequence, alternated colors). + int index = p->indexOf(c) + 1; + const Card *before = c; + while (true) + { + c = p->at(index++); + + if (!((c->rank() == (before->rank()-1)) + && (c->isRed() != before->isRed()))) + { + return false; + } + if (c == p->top()) + return true; + before = c; + } + + return true; +} + +//-------------------------------------------------------------------------// + +class Freecell : public FreecellBase +{ +public: + Freecell( KMainWindow* parent=0, const char* name=0); + virtual void deal(); +}; + +Freecell::Freecell( KMainWindow* parent, const char* name) + : FreecellBase(1, 8, 4, FCS_ES_FILLED_BY_ANY_CARD, false, parent, name) +{ + for (int i = 0; i < 8; i++) + store[i]->move(8 + ( cardMap::CARDX() * 11 / 10 + 1 ) * i, 8 + cardMap::CARDY() * 11 / 10); + + const int right = 8 + ( cardMap::CARDX() * 11 / 10 + 1 ) * 7 + cardMap::CARDX(); + + for (int i = 0; i < 4; i++) + freecell[i]->move(8 + ( cardMap::CARDX() * 13 / 12 ) * i, 8); + + for (int i = 0; i < 4; i++) + target[i]->move(right - (3-i) * ( cardMap::CARDX() * 13 / 12 ) -cardMap::CARDX() , 8); +} + +void Freecell::deal() +{ + int column = 0; + while (!deck->isEmpty()) + { + store[column]->add (deck->nextCard(), false, true); + column = (column + 1) % 8; + } +} + +static class LocalDealerInfo3 : public DealerInfo +{ +public: + LocalDealerInfo3() : DealerInfo(I18N_NOOP("&Freecell"), 3) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Freecell(parent); } +} ldi8; + +//-------------------------------------------------------------------------// + +#include"freecell.moc" diff --git a/kpat/freecell.h b/kpat/freecell.h new file mode 100644 index 00000000..aba311b6 --- /dev/null +++ b/kpat/freecell.h @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------- + + freecell.cpp implements a patience card game + + Copyright (C) 1997 Rodolfo Borges + (C) 2000 Stephan Kulow + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +---------------------------------------------------------------------------*/ + +#ifndef _FREECELL_H_ +#define _FREECELL_H_ + +#include "dealer.h" + +class FreecellPile : public Pile +{ +public: + FreecellPile(int _index, Dealer* parent = 0) : Pile(_index, parent) {} + virtual void moveCards(CardList &c, Pile *to); +}; + +class FreecellBase : public Dealer +{ + Q_OBJECT + +public: + FreecellBase( int decks, int stores, int freecells, int es_filling, bool unlimited_move, + KMainWindow* parent=0, const char* name=0); + void moveCards(CardList &c, FreecellPile *from, Pile *to); + QString solverFormat() const; + virtual ~FreecellBase(); + +public slots: + virtual void deal() = 0; + virtual void restart(); + void waitForMoving(Card *c); + void startMoving(); + void resumeSolution(); + virtual void demo(); + +protected: + virtual bool checkRemove( int checkIndex, const Pile *c1, const Card *c) const; + virtual bool checkAdd ( int checkIndex, const Pile *c1, const CardList& c2) const; + + bool CanPutStore(const Pile *c1, const CardList& c2) const; + bool CanRemove(const Pile *c1, const Card *c) const; + + void countFreeCells(int &free_cells, int &free_stores) const; + + virtual void getHints(); + void movePileToPile(CardList &c, Pile *to, PileList fss, PileList &fcs, + uint start, uint count, int debug_level); + + Pile *pileForName(QString line) const; + void findSolution(); + + virtual MoveHint *chooseHint(); + MoveHint *translateMove(void *m); + void freeSolution(); + + virtual void stopDemo(); + virtual void newDemoMove(Card *m); + virtual bool cardDblClicked(Card *c); + +protected: + QValueList store; + PileList freecell; + PileList target; + Deck *deck; +private: + HintList moves; + HintList oldmoves; + Card *waitfor; + void *solver_instance; + int es_filling; + int solver_ret; + bool unlimited_move; + bool noLongerNeeded(const Card &); +}; + +#endif + +//-------------------------------------------------------------------------// diff --git a/kpat/gamestats.ui b/kpat/gamestats.ui new file mode 100644 index 00000000..2d1e5443 --- /dev/null +++ b/kpat/gamestats.ui @@ -0,0 +1,272 @@ + +GameStats + + + GameStats + + + + 0 + 0 + 310 + 211 + + + + Statistics + + + true + + + + unnamed + + + + layout3 + + + + unnamed + + + + textLabel1 + + + + 1 + 5 + 0 + 0 + + + + Game: + + + + + GameType + + + + 7 + 0 + 0 + 0 + + + + + + + + layout5 + + + + unnamed + + + + layout2 + + + + unnamed + + + + Won + + + %1 + + + AlignVCenter|AlignRight + + + + + WonPerc + + + (%1%) + + + + + textLabel7 + + + Longest winning streak: + + + + + textLabel2 + + + Games played: + + + + + LooseStreak + + + %1 + + + AlignVCenter|AlignRight + + + + + textLabel8 + + + Longest losing streak: + + + + + WinStreak + + + %1 + + + AlignVCenter|AlignRight + + + + + textLabel3 + + + Games won: + + + + + Played + + + %1 + + + AlignVCenter|AlignRight + + + + + + + spacer2 + + + Horizontal + + + Expanding + + + + 250 + 20 + + + + + + + + spacer3 + + + Vertical + + + Expanding + + + + 20 + 71 + + + + + + Layout1 + + + + unnamed + + + 0 + + + 6 + + + + Horizontal Spacing2 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + buttonOk + + + &OK + + + + + + true + + + true + + + + + + + + + buttonOk + clicked() + GameStats + accept() + + + GameType + activated(int) + GameStats + setGameType(int) + + + + setGameType(int) + + + diff --git a/kpat/gamestatsimpl.cpp b/kpat/gamestatsimpl.cpp new file mode 100644 index 00000000..a3a67a43 --- /dev/null +++ b/kpat/gamestatsimpl.cpp @@ -0,0 +1,57 @@ +#include "gamestatsimpl.h" +#include "dealer.h" +#include "version.h" + +#include +#include + +#include +#include +#include + +GameStatsImpl::GameStatsImpl(QWidget* aParent, const char* aname) + : GameStats(aParent, aname) +{ + QStringList list; + QValueList::ConstIterator it; + for (it = DealerInfoList::self()->games().begin(); + it != DealerInfoList::self()->games().end(); ++it) + { + // while we develop, it may happen that some lower + // indices do not exist + uint index = (*it)->gameindex; + for (uint i = 0; i <= index; i++) + if (list.count() <= i) + list.append("unknown"); + list[index] = i18n((*it)->name); + list[index].replace('&',""); + } + GameType->insertStringList(list); + showGameType(0); +} + +void GameStatsImpl::showGameType(int id) +{ + GameType->setCurrentItem(id); + setGameType(id); +} + +void GameStatsImpl::setGameType(int id) +{ + // Trick to reset string to original value + languageChange(); + KConfig *config = kapp->config(); + KConfigGroupSaver kcs(config, scores_group); + unsigned int t = config->readUnsignedNumEntry(QString("total%1").arg(id),0); + Played->setText(Played->text().arg(t)); + unsigned int w = config->readUnsignedNumEntry(QString("won%1").arg(id),0); + Won->setText(Won->text().arg(w)); + if (t) + WonPerc->setText(WonPerc->text().arg(w*100/t)); + else + WonPerc->setText(WonPerc->text().arg(0)); + WinStreak->setText( + WinStreak->text().arg(config->readUnsignedNumEntry(QString("maxwinstreak%1").arg(id),0))); + LooseStreak->setText( + LooseStreak->text().arg(config->readUnsignedNumEntry(QString("maxloosestreak%1").arg(id),0))); +} diff --git a/kpat/gamestatsimpl.h b/kpat/gamestatsimpl.h new file mode 100644 index 00000000..192b7793 --- /dev/null +++ b/kpat/gamestatsimpl.h @@ -0,0 +1,16 @@ +#ifndef GAMESTATS_IMPL_H_ +#define GAMESTATS_IMPL_H_ + +#include "gamestats.h" + +class GameStatsImpl : public GameStats +{ + public: + GameStatsImpl(QWidget* aParent, const char* aname); + + virtual void setGameType(int i); + virtual void showGameType(int i); +}; + +#endif + diff --git a/kpat/golf.cpp b/kpat/golf.cpp new file mode 100644 index 00000000..71bdfbb5 --- /dev/null +++ b/kpat/golf.cpp @@ -0,0 +1,169 @@ +#include "golf.h" +#include +#include "deck.h" +#include +#include "cardmaps.h" + +HorRightPile::HorRightPile( int _index, Dealer* parent) + : Pile(_index, parent) +{ +} + +QSize HorRightPile::cardOffset( bool _spread, bool, const Card *) const +{ + if (_spread) + return QSize(+hspread(), 0); + + return QSize(0, 0); +} + +//-------------------------------------------------------------------------// + +Golf::Golf( KMainWindow* parent, const char* _name) + : Dealer( parent, _name ) +{ + const int dist_x = cardMap::CARDX() * 11 / 10 + 1; + const int pile_dist = 10 + 3 * cardMap::CARDY(); + + deck = Deck::new_deck( this); + deck->move(10, pile_dist); + connect(deck, SIGNAL(clicked(Card*)), SLOT(deckClicked(Card*))); + + for( int r = 0; r < 7; r++ ) { + stack[r]=new Pile(1+r, this); + stack[r]->move(10+r*dist_x,10); + stack[r]->setAddFlags( Pile::addSpread | Pile::disallow); + stack[r]->setCheckIndex( 1 ); + } + + waste=new HorRightPile(8,this); + waste->move(10 + cardMap::CARDX() * 5 / 4, pile_dist); + waste->setTarget(true); + waste->setCheckIndex( 0 ); + waste->setAddFlags( Pile::addSpread); + + setActions(Dealer::Hint | Dealer::Demo); +} + +//-------------------------------------------------------------------------// + +bool Golf::checkAdd( int checkIndex, const Pile *c1, const CardList& cl) const +{ + if (checkIndex == 1) + return false; + + Card *c2 = cl.first(); + + kdDebug(11111)<<"check add "<< c1->name()<<" " << c2->name() <<" "<rank() != (c1->top()->rank()+1)) && (c2->rank() != (c1->top()->rank()-1))) + return false; + + return true; +} + +bool Golf::checkRemove( int checkIndex, const Pile *, const Card *c2) const +{ + if (checkIndex == 0) + return false; + return (c2 == c2->source()->top()); +} + +//-------------------------------------------------------------------------// + +void Golf::restart() +{ + deck->collectAndShuffle(); + deal(); +} + +void Golf::deckClicked(Card *) +{ + if (deck->isEmpty()) { + return; + + } + Card *c = deck->nextCard(); + waste->add(c, true, true); + int x = int(c->x()); + int y = int(c->y()); + c->move(deck->x(), deck->y()); + c->flipTo(x, y, 8); +} + +//-------------------------------------------------------------------------// + +void Golf::deal() +{ + for(int r=0;r<7;r++) + { + for(int i=0;i<5;i++) + { + stack[r]->add(deck->nextCard(),false,true); + } + } + waste->add(deck->nextCard(),false,false); + +} + +Card *Golf::demoNewCards() +{ + deckClicked(0); + return waste->top(); +} + +bool Golf::cardClicked(Card *c) +{ + if (c->source()->checkIndex() !=1) { + return Dealer::cardClicked(c); + } + + if (c != c->source()->top()) + return false; + + Pile*p=findTarget(c); + if (p) + { + CardList empty; + empty.append(c); + c->source()->moveCards(empty, p); + canvas()->update(); + return true; + } + return false; +} + +bool Golf::isGameLost() const +{ + if( !deck->isEmpty()) + return false; + + bool onecard = false; + + for( int r = 0; r < 7; r++ ) { + if( !stack[r]->isEmpty()){ + onecard = true; + CardList stackTops; + stackTops.append(stack[r]->top()); + if(this->checkAdd(0,waste,stackTops)) + return false; + } + } + + return onecard; +} + + +static class LocalDealerInfo13 : public DealerInfo +{ +public: + LocalDealerInfo13() : DealerInfo(I18N_NOOP("Go&lf"), 12) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Golf(parent); } +} ldi13; + +//-------------------------------------------------------------------------// + +#include"golf.moc" + +//-------------------------------------------------------------------------// + diff --git a/kpat/golf.h b/kpat/golf.h new file mode 100644 index 00000000..9c3b6421 --- /dev/null +++ b/kpat/golf.h @@ -0,0 +1,45 @@ +#ifndef _GOLF_H_ +#define _GOLF_H_ + +#include "dealer.h" + +class HorRightPile : public Pile +{ + Q_OBJECT + +public: + HorRightPile( int _index, Dealer* parent = 0); + virtual QSize cardOffset( bool _spread, bool _facedown, const Card *before) const; +}; + +class Golf : public Dealer +{ + Q_OBJECT + +public: + Golf( KMainWindow* parent=0, const char* name=0); + void deal(); + virtual void restart(); + virtual bool isGameLost() const; + +protected slots: + void deckClicked(Card *); + +protected: + virtual bool startAutoDrop() { return false; } + virtual Card *demoNewCards(); + virtual bool cardClicked(Card *c); + +private: // functions + virtual bool checkAdd( int checkIndex, const Pile *c1, const CardList& c2) const; + virtual bool checkRemove( int checkIndex, const Pile *c1, const Card *c2) const; + +private: + Pile* stack[7]; + HorRightPile* waste; + Deck* deck; +}; + +#endif + +//-------------------------------------------------------------------------// diff --git a/kpat/grandf.cpp b/kpat/grandf.cpp new file mode 100644 index 00000000..9756b840 --- /dev/null +++ b/kpat/grandf.cpp @@ -0,0 +1,227 @@ +/***********************-*-C++-*-******** + + grandf.cpp implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + (C) 2000 Stephan Kulow + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +****************************************/ + +#include "grandf.h" +#include +#include "deck.h" +#include +#include +#include "cardmaps.h" + +Grandf::Grandf( KMainWindow* parent, const char *name ) + : Dealer( parent, name ) +{ + deck = Deck::new_deck(this); + deck->hide(); + + const int distx = cardMap::CARDX() * 14 / 10; + + for (int i=0; i<4; i++) { + target[i] = new Pile(i+1, this); + target[i]->move(10+(i+1)*distx, 10); + target[i]->setType(Pile::KlondikeTarget); + } + + for (int i=0; i<7; i++) { + store[i] = new Pile(5+i, this); + store[i]->move(10+distx*i, 10 + cardMap::CARDY() * 15 / 10); + store[i]->setAddFlags(Pile::addSpread | Pile::several); + store[i]->setRemoveFlags(Pile::several | Pile::autoTurnTop); + store[i]->setCheckIndex(1); + } + + setActions(Dealer::Hint | Dealer::Demo | Dealer::Redeal); +} + +void Grandf::restart() { + deck->collectAndShuffle(); + deal(); + numberOfDeals = 1; +} + +void Grandf::redeal() { + unmarkAll(); + + if (numberOfDeals < 3) { + collect(); + deal(); + numberOfDeals++; + } + if (numberOfDeals == 3) { + aredeal->setEnabled(false); + } + takeState(); +} + +Card *Grandf::demoNewCards() +{ + if (numberOfDeals < 3) { + redeal(); + return store[3]->top(); + } else + return 0; +} + +void Grandf::deal() { + int start = 0; + int stop = 7-1; + int dir = 1; + + for (int round=0; round < 7; round++) + { + int i = start; + do + { + Card *next = deck->nextCard(); + if (next) + store[i]->add(next, i != start, true); + i += dir; + } while ( i != stop + dir); + int t = start; + start = stop; + stop = t+dir; + dir = -dir; + } + + int i = 0; + Card *next = deck->nextCard(); + while (next) + { + store[i+1]->add(next, false , true); + next = deck->nextCard(); + i = (i+1)%6; + } + + for (int round=0; round < 7; round++) + { + Card *c = store[round]->top(); + if (c) + c->turn(true); + } + aredeal->setEnabled(true); + canvas()->update(); +} + +/***************************** + + Does the collecting step of the game + + NOTE: this is not quite correct -- the piles should be turned + facedown (ie partially reversed) during collection. + +******************************/ +void Grandf::collect() { + unmarkAll(); + + for (int pos = 6; pos >= 0; pos--) { + CardList p = store[pos]->cards(); + for (CardList::ConstIterator it = p.begin(); it != p.end(); ++it) + deck->add(*it, true, false); + } +} + +bool Grandf::checkAdd( int checkIndex, const Pile *c1, const CardList& c2) const { + assert (checkIndex == 1); + if (c1->isEmpty()) + return c2.first()->rank() == Card::King; + else + return (c2.first()->rank() == c1->top()->rank() - 1) + && c2.first()->suit() == c1->top()->suit(); +} + +QString Grandf::getGameState() const +{ + return QString::number(numberOfDeals); +} + +void Grandf::setGameState( const QString &s) +{ + numberOfDeals = s.toInt(); + aredeal->setEnabled(numberOfDeals < 3); +} + +bool Grandf::isGameLost() const +{ + // If we can redeal, then nothing's lost yet. + if (numberOfDeals <3) + return false; + + // Work through the stores, look for killer criteria. + for(int i=0; i < 7; i++) { + + /* If this store is empty, then iterate through the other stores and + * check if there is a (visible) King card. If so, then we could move + * that to the free store (which means a turn is possible, so the + * game is not lost yet). + */ + if(store[i]->isEmpty()){ + for(int i2=1; i2 < 7; i2++) { + int j=(i+i2) % 7; + CardList p = store[j]->cards(); + for (CardList::ConstIterator it = p.begin(); it != p.end(); ++it){ + Card *c= *it; + if( it != p.begin() && c->realFace() && c->rank() == Card::King) + return false; + } + } + } + else{ + /* If this store has an Ace as it's top card, then we can start a + * new target pile! + */ + if(store[i]->top()->rank() == Card::Ace) + return false; + + /* Check whether the top card of this store could be added to + * any of the target piles. + */ + for(int j=0; j <4; j++) + if( !target[j]->isEmpty()) + if(store[i]->top()->suit() == target[j]->top()->suit()) + if( store[i]->top()->rank() == target[j]->top()->rank() +1) + return false; + + /* Check whether any (group of) cards from another store could + * be put onto this store's top card. + */ + for(int i2=1; i2 < 7; i2++) { + int j=(i+i2) % 7; + CardList p = store[j]->cards(); + for (CardList::ConstIterator it = p.begin(); it != p.end(); ++it){ + Card *c= *it; + if( c->realFace() && + c->rank() == (store[i]->top()->rank()-1) && + c->suit() == store[i]->top()->suit() ) + return false; + } + } + } + } + return true; // can't move. +} + +static class LocalDealerInfo1 : public DealerInfo +{ +public: + LocalDealerInfo1() : DealerInfo(I18N_NOOP("&Grandfather"), 1) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Grandf(parent); } +} gfdi; + +#include "grandf.moc" diff --git a/kpat/grandf.h b/kpat/grandf.h new file mode 100644 index 00000000..db24483f --- /dev/null +++ b/kpat/grandf.h @@ -0,0 +1,65 @@ +/***********************-*-C++-*-******** + + grandf.cpp implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + + +// +// 7 positions, all cards on table, follow suit +// ( I don't know a name for this one, but I learned it from my grandfather.) + +****************************************/ + + +#ifndef P_GRANDF_7 +#define P_GRANDF_7 + +#include "dealer.h" + +class KAction; +class Pile; +class Deck; +class KMainWindow; + +class Grandf : public Dealer { + Q_OBJECT + +public: + Grandf( KMainWindow* parent=0, const char* name=0); + +public slots: + void redeal(); + void deal(); + virtual void restart(); + virtual bool isGameLost() const; + + +protected: + void collect(); + virtual bool checkAdd ( int checkIndex, const Pile *c1, const CardList& c2) const; + virtual QString getGameState() const; + virtual void setGameState( const QString & stream ); + virtual Card *demoNewCards(); + +private: + Pile* store[7]; + Pile* target[4]; + Deck *deck; + int numberOfDeals; + +}; + +#endif diff --git a/kpat/green.png b/kpat/green.png new file mode 100644 index 00000000..26793078 Binary files /dev/null and b/kpat/green.png differ diff --git a/kpat/gypsy.cpp b/kpat/gypsy.cpp new file mode 100644 index 00000000..db58f7cd --- /dev/null +++ b/kpat/gypsy.cpp @@ -0,0 +1,117 @@ +#include "gypsy.h" +#include +#include "deck.h" +#include "cardmaps.h" + +Gypsy::Gypsy( KMainWindow* parent, const char *name ) + : Dealer( parent, name ) +{ + const int dist_x = cardMap::CARDX() * 11 / 10 + 1; + const int dist_y = cardMap::CARDY() * 11 / 10 + 1; + + deck = Deck::new_deck(this, 2); + deck->move(10 + dist_x / 2 + 8*dist_x, 10 + 45 * cardMap::CARDY() / 10); + + connect(deck, SIGNAL(clicked(Card*)), SLOT(slotClicked(Card *))); + + for (int i=0; i<8; i++) { + target[i] = new Pile(i+1, this); + target[i]->move(10+dist_x*(8+(i/4)), 10 + (i%4)*dist_y); + target[i]->setAddType(Pile::KlondikeTarget); + } + + for (int i=0; i<8; i++) { + store[i] = new Pile(9+i, this); + store[i]->move(10+dist_x*i, 10); + store[i]->setAddType(Pile::GypsyStore); + store[i]->setRemoveType(Pile::FreecellStore); + } + + setActions(Dealer::Hint | Dealer::Demo); +} + +void Gypsy::restart() { + deck->collectAndShuffle(); + deal(); +} + +void Gypsy::dealRow(bool faceup) { + for (int round=0; round < 8; round++) + store[round]->add(deck->nextCard(), !faceup, true); +} + +void Gypsy::deal() { + dealRow(false); + dealRow(false); + dealRow(true); + takeState(); +} + +Card *Gypsy::demoNewCards() +{ + if (deck->isEmpty()) + return 0; + dealRow(true); + return store[0]->top(); +} + +bool Gypsy::isGameLost() const { + if(!deck->isEmpty()) + return false; + + for(int i=0; i < 8; i++){ + if(store[i]->isEmpty()) + return false; + + if(store[i]->top()->rank() == Card::Ace) + return false; + + for(int j=0; j <8; j++){ + if(!target[j]->isEmpty() && + (store[i]->top()->suit()==target[j]->top()->suit()) && + (store[i]->top()->rank()==(target[j]->top()->rank()+1))) + return false; + } + } + + for(int i=0; i < 8; i++) { + Card *cnext=store[i]->top(); + int indexi=store[i]->indexOf(cnext); + + Card *cardi= 0; + do{ + cardi=cnext; + if (indexi>0) + cnext=store[i]->at( --indexi ); + + for(int k=0; k <8; k++) { + if (i == k) + continue; + + if((cardi->rank()+1 == store[k]->top()->rank()) && + cardi->isRed() != store[k]->top()->isRed()){ + + // this test doesn't apply if indexi==0, but fails gracefully. + if(cnext->rank() == store[k]->top()->rank() && + cnext->suit() == store[k]->top()->suit()) + break; //nothing gained; keep looking. + + return false;// TODO: look deeper, move may not be helpful. + } + } + + } while((indexi>=0) && (cardi->rank()+1 == cnext->rank()) && + (cardi->isRed() != cnext->isRed())); + } + + return true; +} + +static class LocalDealerInfo7 : public DealerInfo +{ +public: + LocalDealerInfo7() : DealerInfo(I18N_NOOP("Gy&psy"), 7) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Gypsy(parent); } +} gyfdi; + +#include "gypsy.moc" diff --git a/kpat/gypsy.h b/kpat/gypsy.h new file mode 100644 index 00000000..ea28b04e --- /dev/null +++ b/kpat/gypsy.h @@ -0,0 +1,34 @@ + +#ifndef GYPSY_H +#define GYPSY_H + +#include "dealer.h" + +class KAction; +class Pile; +class Deck; +class KMainWindow; + +class Gypsy : public Dealer { + Q_OBJECT + +public: + Gypsy( KMainWindow* parent=0, const char* name=0); + virtual bool isGameLost() const; + +public slots: + void slotClicked(Card *) { dealRow(true); } + void deal(); + virtual void restart(); + +private: // functions + void dealRow(bool faceup); + virtual Card *demoNewCards(); + +private: + Pile* store[8]; + Pile* target[8]; + Deck *deck; +}; + +#endif diff --git a/kpat/hint.h b/kpat/hint.h new file mode 100644 index 00000000..1d1c2594 --- /dev/null +++ b/kpat/hint.h @@ -0,0 +1,28 @@ +#ifndef HINT_H +#define HINT_H + + +class Card; +class Pile; + + +class MoveHint +{ +public: + MoveHint(Card *card, Pile *to, bool d=true); + + bool dropIfTarget() const { return m_dropiftarget; } + Card *card() const { return m_card; } + Pile *pile() const { return m_to; } + +private: + Card *m_card; + Pile *m_to; + bool m_dropiftarget; +}; + + +typedef QValueList HintList; + + +#endif diff --git a/kpat/icons/Makefile.am b/kpat/icons/Makefile.am new file mode 100644 index 00000000..24597080 --- /dev/null +++ b/kpat/icons/Makefile.am @@ -0,0 +1,6 @@ + +EXTRA_DIST = kpat-lq.png + +KDE_ICON = kpat + + diff --git a/kpat/icons/hi128-app-kpat.png b/kpat/icons/hi128-app-kpat.png new file mode 100644 index 00000000..5bb5cdc7 Binary files /dev/null and b/kpat/icons/hi128-app-kpat.png differ diff --git a/kpat/icons/hi16-app-kpat.png b/kpat/icons/hi16-app-kpat.png new file mode 100644 index 00000000..a33e861c Binary files /dev/null and b/kpat/icons/hi16-app-kpat.png differ diff --git a/kpat/icons/hi22-app-kpat.png b/kpat/icons/hi22-app-kpat.png new file mode 100644 index 00000000..529d5de5 Binary files /dev/null and b/kpat/icons/hi22-app-kpat.png differ diff --git a/kpat/icons/hi32-app-kpat.png b/kpat/icons/hi32-app-kpat.png new file mode 100644 index 00000000..b5424928 Binary files /dev/null and b/kpat/icons/hi32-app-kpat.png differ diff --git a/kpat/icons/hi48-app-kpat.png b/kpat/icons/hi48-app-kpat.png new file mode 100644 index 00000000..41ea79b4 Binary files /dev/null and b/kpat/icons/hi48-app-kpat.png differ diff --git a/kpat/icons/hi64-app-kpat.png b/kpat/icons/hi64-app-kpat.png new file mode 100644 index 00000000..0ec12569 Binary files /dev/null and b/kpat/icons/hi64-app-kpat.png differ diff --git a/kpat/icons/kpat-lq.png b/kpat/icons/kpat-lq.png new file mode 100644 index 00000000..8ea647d2 Binary files /dev/null and b/kpat/icons/kpat-lq.png differ diff --git a/kpat/idiot.cpp b/kpat/idiot.cpp new file mode 100644 index 00000000..3ac49ef5 --- /dev/null +++ b/kpat/idiot.cpp @@ -0,0 +1,234 @@ +/* + idiot.cpp implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, + provided that the above copyright notice appear in all copies and that + both that copyright notice and this permission notice appear in + supporting documentation. + + This file is provided AS IS with no warranties of any kind. The author + shall have no liability with respect to the infringement of copyrights, + trade secrets or any patents by this file or any part thereof. In no + event will the author be liable for any lost revenue or profits or + other special, indirect and consequential damages. + + 4 positions, remove lowest card(s) of suit +*/ + + +#include "idiot.h" +#include +#include "deck.h" +#include "cardmaps.h" + + +Idiot::Idiot( KMainWindow* parent, const char* _name) + : Dealer( parent, _name ) +{ + // Create the deck to the left. + m_deck = Deck::new_deck( this ); + m_deck->move(10, 10); + + const int distx = cardMap::CARDX() + cardMap::CARDX() / 10 + 1; + + // Create 4 piles where the cards will be placed during the game. + for( int i = 0; i < 4; i++ ) { + m_play[i] = new Pile( i + 1, this); + + m_play[i]->setAddFlags( Pile::addSpread ); + m_play[i]->setRemoveFlags( Pile::disallow ); + m_play[i]->move(10 + cardMap::CARDX() * 18 / 10 + distx * i, 10); + } + + // Create the discard pile to the right + m_away = new Pile( 5, this ); + m_away->setTarget(true); + m_away->setRemoveFlags(Pile::disallow); + m_away->move(10 + cardMap::CARDX() * 5 / 2 + distx * 4, 10); + + setActions(Dealer::Hint | Dealer::Demo); +} + + +void Idiot::restart() +{ + m_deck->collectAndShuffle(); + deal(); +} + + +inline bool higher( const Card* c1, const Card* c2) +{ + // Sanity check. + if (!c1 || !c2 || c1 == c2) + return false; + + // Must be same suit. + if (c1->suit() != c2->suit()) + return false; + + // Aces form a special case. + if (c2->rank() == Card::Ace) + return true; + if (c1->rank() == Card::Ace) + return false; + + return (c1->rank() < c2->rank()); +} + + +bool Idiot::canMoveAway(Card *c) +{ + return ( higher( c, m_play[ 0 ]->top() ) || + higher( c, m_play[ 1 ]->top() ) || + higher( c, m_play[ 2 ]->top() ) || + higher( c, m_play[ 3 ]->top() ) ); +} + + +bool Idiot::cardClicked(Card *c) +{ + // If the deck is clicked, deal 4 more cards. + if (c->source() == m_deck) { + deal(); + return true; + } + + // Only the top card of a pile can be clicked. + if (c != c->source()->top()) + return false; + + bool didMove = true; + if ( canMoveAway(c) ) + // Add to 'm_away', face up, no spread + m_away->add(c, false, false); + else if ( m_play[ 0 ]->isEmpty() ) + // Add to pile 1, face up, spread. + m_play[0]->add(c, false, true); + else if ( m_play[ 1 ]->isEmpty() ) + // Add to pile 2, face up, spread. + m_play[1]->add(c, false, true); + else if ( m_play[ 2 ]->isEmpty() ) + // Add to pile 3, face up, spread. + m_play[2]->add( c, false, true); + else if ( m_play[ 3 ]->isEmpty() ) + // Add to pile 4, face up, spread. + m_play[3]->add(c, false, true); + else + didMove = false; + + return true; // may be a lie, but noone cares +} + + +// The game is won when: +// 1. all cards are dealt. +// 2. all piles contain exactly one ace. +// 3. the rest of the cards are thrown away (follows automatically from 1, 2. +// +bool Idiot::isGameWon() const +{ + // Criterium 1. + if (!m_deck->isEmpty()) + return false; + + // Criterium 2. + for (int i = 0; i < 4; i++) { + if (m_play[i]->cardsLeft() != 1 || m_play[i]->top()->rank() != Card::Ace) + return false; + } + + return true; +} + + +// This patience doesn't support double click. +// + +bool Idiot::cardDblClicked(Card *) +{ + return false; // nothing - nada +} + + +// Deal 4 cards face up - one on each pile. +// + +void Idiot::deal() +{ + if ( m_deck->isEmpty() ) + return; + + // Move the four top cards of the deck to the piles, faceup, spread out. + for ( int i = 0; i < 4; i++ ) + m_play[ i ]->add( m_deck->nextCard(), false, true ); +} + + +void Idiot::getHints() +{ + bool cardMoved = false; + for ( int i = 0; i < 4; i++ ) + if ( canMoveAway( m_play[i]->top() ) ) { + cardMoved = true; + newHint(new MoveHint(m_play[i]->top(), m_away)); + } + + if (cardMoved) + return; + + // now let's try to be a bit clever with the empty piles + for( int i = 0; i < 4; i++ ) { + if (m_play[i]->isEmpty()) { + // Find a card to move there + int biggestPile = -1; + int sizeBiggestPile = -1; + for( int j = 0; j < 4; j++ ) { + if ( i != j && m_play[j]->cardsLeft()>1 ) { + + // Ace on top of the pile? -> move it + if ( m_play[j]->top()->rank() == Card::Ace ) { + biggestPile = j; + break; + } + + // Otherwise choose the biggest pile + if ( m_play[j]->cardsLeft() > sizeBiggestPile ) { + sizeBiggestPile = m_play[j]->cardsLeft(); + biggestPile = j; + } + } + } + + if ( biggestPile != -1 ) { + newHint(new MoveHint(m_play[biggestPile]->top(), m_play[i])); + return; + } + } + } +} + + +Card *Idiot::demoNewCards() +{ + if ( m_deck->isEmpty() ) + return 0; + + deal(); + + return m_play[0]->top(); +} + + +static class LocalDealerInfo2 : public DealerInfo +{ +public: + LocalDealerInfo2() : DealerInfo(I18N_NOOP("&Aces Up"), 2) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Idiot(parent); } +} ldi4; + + +#include "idiot.moc" diff --git a/kpat/idiot.h b/kpat/idiot.h new file mode 100644 index 00000000..101957c0 --- /dev/null +++ b/kpat/idiot.h @@ -0,0 +1,59 @@ +/***********************-*-C++-*-******** + + idiot.h implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +****************************************/ + + +#ifndef P_IDIOT +#define P_IDIOT + + +#include "dealer.h" + + +class Idiot: public Dealer +{ + Q_OBJECT + +public: + + Idiot( KMainWindow* parent = 0, const char* name = 0 ); + + virtual bool isGameWon() const; + +protected: + virtual bool cardClicked(Card *); + virtual bool cardDblClicked(Card *); + virtual void getHints(); + virtual Card *demoNewCards(); + virtual bool startAutoDrop() { return false; } + +public slots: + + virtual void restart(); + void deal(); + +private: + bool canMoveAway(Card *c); + + Pile *m_play[ 4 ]; + Pile *m_away; + Deck *m_deck; +}; + +#endif diff --git a/kpat/kings.cpp b/kpat/kings.cpp new file mode 100644 index 00000000..7654a88f --- /dev/null +++ b/kpat/kings.cpp @@ -0,0 +1,132 @@ +#include "kings.h" +#include +#include +#include "deck.h" +#include +#include "freecell-solver/fcs_enums.h" +#include "cardmaps.h" + +Kings::Kings( KMainWindow* parent, const char *name ) + : FreecellBase( 2, 8, 8, FCS_ES_FILLED_BY_KINGS_ONLY, true, parent, name ) +{ + const int dist_x = cardMap::CARDX() * 11 / 10 + 1; + + for (int i=0; i<8; i++) { + target[i]->move((8 + i/4) * dist_x + 10 + cardMap::CARDX() * 4 / 10, 10 + (i % 4) * cardMap::CARDY() * 14 / 10 ); + store[i]->move(10+dist_x*i, 10 + cardMap::CARDY() * 5 / 4); + store[i]->setSpread(13); + freecell[i]->move(10 + dist_x * i, 10); + } +} + +void Kings::demo() +{ + Dealer::demo(); +} + +void Kings::deal() { + CardList cards = deck->cards(); + CardList::Iterator it = cards.begin(); + int cn = 0; + for (int stack = -1; stack < 8; ) + { + while (it != cards.end() && (*it)->rank() != Card::King) { + if (stack >= 0) { + store[stack]->add(*it, false, true); + cn++; + } + ++it; + } + if (it == cards.end()) + break; + cn++; + store[++stack]->add(*it, false, true); + if (stack == 0) { + cards = deck->cards(); // reset to start + it = cards.begin(); + } else + ++it; + } + assert(cn == 104); +} + +bool Kings::isGameLost() const { + int i,indexi; + Card *c,*cnext,*ctarget; + CardList targets,ctops; + + for(i=0; i < 8; i++){ + if(freecell[i]->isEmpty()) + return false; + if(store[i]->isEmpty()) + return false; + if(store[i]->top()->rank() == Card::Ace) + return false; + } + + for(i=0; i < 8; i++){ + if(!target[i]->isEmpty()) + targets.append(target[i]->top()); + + if(!store[i]->isEmpty()) + ctops.append(store[i]->top()); + } + + for(i=0; i < 8; i++){ + if(store[i]->isEmpty()) + continue; + + c=store[i]->top(); + for (CardList::Iterator it = targets.begin(); it != targets.end(); ++it) { + ctarget=*it; + if(c->rank()-1 == ctarget->rank() && + c->suit() == ctarget->suit()){ + kdDebug(11111)<< "test 1" << endl; + return false; + } + } + + for(indexi=store[i]->indexOf(store[i]->top()); indexi>=0;indexi--){ + c=store[i]->at(indexi); + if(indexi > 0) + cnext=store[i]->at(indexi-1); + + for (CardList::Iterator it = ctops.begin(); it != ctops.end(); ++it) { + ctarget=*it; + if(c->rank()+1 == ctarget->rank() && + c->isRed() != ctarget->isRed()){ + + if(indexi == 0) + return false; + + if(cnext->rank() != ctarget->rank() + || cnext->suit() != ctarget->suit()) + return false; + } + } + if(cnext->rank() != c->rank()+1 && + cnext->isRed() != c->isRed()) + break; + } + } + + return true; +} + +#if 0 +NOTE: When this is reenabled, renumber the following patiences back again: +Golf +Klondike, draw 3 +Spider Easy +Spider Medium +Spider Hard + +static class LocalDealerInfo12 : public DealerInfo +{ +public: + LocalDealerInfo12() : DealerInfo(I18N_NOOP("&The Kings"), 12) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Kings(parent); } +} gfdi12; +#endif + +#include "kings.moc" diff --git a/kpat/kings.h b/kpat/kings.h new file mode 100644 index 00000000..e3a60475 --- /dev/null +++ b/kpat/kings.h @@ -0,0 +1,22 @@ +#ifndef _KINGS_H_ +#define _KINGS_H_ + +#include "freecell.h" + +class Pile; +class Deck; +class KMainWindow; + +class Kings : public FreecellBase { + Q_OBJECT + +public: + Kings( KMainWindow* parent=0, const char* name=0); + virtual bool isGameLost() const; + +public slots: + virtual void deal(); + virtual void demo(); +}; + +#endif diff --git a/kpat/klondike.cpp b/kpat/klondike.cpp new file mode 100644 index 00000000..ff561fa3 --- /dev/null +++ b/kpat/klondike.cpp @@ -0,0 +1,495 @@ +/***********************-*-C++-*-******** + + klondike.cpp implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +// +// 7 positions, alternating red and black +// + +****************************************/ + +#include "klondike.h" +#include +#include "deck.h" +#include +#include +#include "cardmaps.h" + +class KlondikePile : public Pile +{ +public: + KlondikePile( int _index, Dealer* parent) + : Pile(_index, parent) {} + + void clearSpread() { cardlist.clear(); } + + void addSpread(Card *c) { + cardlist.append(c); + } + virtual QSize cardOffset( bool _spread, bool, const Card *c) const { + kdDebug(11111) << "cardOffset " << _spread << " " << (c? c->name() : "(null)") << endl; + if (cardlist.contains(const_cast(c))) + return QSize(+dspread(), 0); + return QSize(0, 0); + } +private: + CardList cardlist; +}; + +Klondike::Klondike( bool easy, KMainWindow* parent, const char* _name ) + : Dealer( parent, _name ) +{ + // The units of the follwoing constants are pixels + const int margin = 10; // between card piles and board edge + const int hspacing = cardMap::CARDX() / 6 + 1; // horizontal spacing between card piles + const int vspacing = cardMap::CARDY() / 4; // vertical spacing between card piles + + deck = Deck::new_deck(this); + deck->move(margin, margin); + + EasyRules = easy; + + pile = new KlondikePile( 13, this); + + pile->move(margin + cardMap::CARDX() + cardMap::CARDX() / 4, margin); + // Move the visual representation of the pile to the intended position + // on the game board. + + pile->setAddFlags(Pile::disallow); + pile->setRemoveFlags(Pile::Default); + + for( int i = 0; i < 7; i++ ) { + play[ i ] = new Pile( i + 5, this); + play[i]->move(margin + (cardMap::CARDX() + hspacing) * i, margin + cardMap::CARDY() + vspacing); + play[i]->setAddType(Pile::KlondikeStore); + play[i]->setRemoveFlags(Pile::several | Pile::autoTurnTop | Pile::wholeColumn); + } + + for( int i = 0; i < 4; i++ ) { + target[ i ] = new Pile( i + 1, this ); + target[i]->move(margin + (3 + i) * (cardMap::CARDX()+ hspacing), margin); + target[i]->setAddType(Pile::KlondikeTarget); + if (EasyRules) // change default + target[i]->setRemoveFlags(Pile::Default); + else + target[i]->setRemoveType(Pile::KlondikeTarget); + } + + setActions(Dealer::Hint | Dealer::Demo); + + redealt = false; +} + +// This function returns true when it is certain that the card t is no longer +// needed on any of the play piles. This function is recursive but the +// recursion will not get deep. +// +// To determine wether a card is no longer needed on any of the play piles we +// obviously must know what a card can be used for there. According to the +// rules a card can be used to store another card with 1 less unit of value +// and opposite color. This is the only thing that a card can be used for +// there. Therefore the cards with lowest value (1) are useless there (base +// case). The other cards each have 2 cards that can be stored on them, let us +// call those 2 cards *depending cards*. +// +// The object of the game is to put all cards on the target piles. Therefore +// cards that are no longer needed on any of the play piles should be put on +// the target piles if possible. Cards on the target piles can not be moved +// and they can not store any of its depending cards. Let us call this that +// the cards on the target piles are *out of play*. +// +// The simple and obvious rule is: +// A card is no longer needed when both of its depending cards are out of +// play. +// +// But using only the simplest rule to determine if a card is no longer +// needed on any of the play piles is not ambitios enough. Therefore, if a +// depending card is not out of play, we test if it could become out of play. +// The requirement for getting a card out of play is that it can be placed on +// a target pile and that it is no longer needed on any of the play piles +// (this is why this function is recursive). This more ambitious rule lets +// us extend the base case with the second lowest value (2). +bool Klondike::noLongerNeeded(Card::Rank r, Card::Suit s) { + + if (r <= Card::Two) return true; // Base case. + + // Find the 2 suits of opposite color. "- 1" is used here because the + // siuts are ranged 1 .. 4 but target_tops is indexed 0 .. 3. (Of course + // the subtraction of 1 does not affect performance because it is a + // constant expression that is calculated at compile time). + unsigned char a = Card::Clubs - 1, b = Card::Spades - 1; + if (s == Card::Clubs || s == Card::Spades) + a = Card::Diamonds - 1, b = Card::Hearts - 1; + + const Card::Rank depending_rank = static_cast(r - 1); + return + (((target_tops[a] >= depending_rank) + || + ((target_tops[a] >= depending_rank - 1) + && + (noLongerNeeded + (depending_rank, static_cast(a + 1))))) + && + ((target_tops[b] >= depending_rank) + || + ((target_tops[b] >= depending_rank - 1) + && + (noLongerNeeded + (depending_rank, static_cast(b + 1)))))); +} + +bool Klondike::tryToDrop(Card *t) +{ + if (!t || !t->realFace() || t->takenDown()) + return false; + +// kdDebug(11111) << "tryToDrop " << t->name() << endl; + + Pile *tgt = findTarget(t); + if (tgt) { + newHint + (new MoveHint(t, tgt, noLongerNeeded(t->rank(), t->suit()))); + return true; + } + return false; +} + +void Klondike::getHints() { + + target_tops[0] = target_tops[1] = target_tops[2] = target_tops[3] + = Card::None; + + for( int i = 0; i < 4; i++ ) + { + Card *c = target[i]->top(); + if (!c) continue; + target_tops[c->suit() - 1] = c->rank(); + } + + + Card* t[7]; + for(int i=0; i<7;i++) + t[i] = play[i]->top(); + + for(int i=0; i<7; i++) + { + CardList list = play[i]->cards(); + + for (CardList::ConstIterator it = list.begin(); it != list.end(); ++it) + { + if (!(*it)->isFaceUp()) + continue; + + CardList empty; + empty.append(*it); + + for (int j = 0; j < 7; j++) + { + if (i == j) + continue; + + if (play[j]->legalAdd(empty)) { + if (((*it)->rank() != Card::King) || it != list.begin()) { + newHint(new MoveHint(*it, play[j])); + break; + } + } + } + break; // the first face up + } + + tryToDrop(play[i]->top()); + } + if (!pile->isEmpty()) + { + Card *t = pile->top(); + if (!tryToDrop(t)) + { + for (int j = 0; j < 7; j++) + { + CardList empty; + empty.append(t); + if (play[j]->legalAdd(empty)) { + newHint(new MoveHint(t, play[j])); + break; + } + } + } + } +} + +Card *Klondike::demoNewCards() { + deal3(); + if (!deck->isEmpty() && pile->isEmpty()) + deal3(); // again + return pile->top(); +} + +void Klondike::restart() { + kdDebug(11111) << "restart\n"; + deck->collectAndShuffle(); + redealt = false; + deal(); +} + +void Klondike::deal3() +{ + int draw; + + if ( EasyRules ) { + draw = 1; + } else { + draw = 3; + } + + pile->clearSpread(); + + if (deck->isEmpty()) + { + redeal(); + return; + } + + // move the cards back on the deck, so we can have three new + for (int i = 0; i < pile->cardsLeft(); ++i) { + pile->at(i)->move(pile->x(), pile->y()); + } + + for (int flipped = 0; flipped < draw ; ++flipped) { + + Card *item = deck->nextCard(); + if (!item) { + kdDebug(11111) << "deck empty!!!\n"; + return; + } + pile->add(item, true, true); // facedown, nospread + if (flipped < draw - 1) + pile->addSpread(item); + // move back to flip + item->move(deck->x(), deck->y()); + + item->flipTo( int(pile->x()) + pile->dspread() * (flipped), int(pile->y()), 8 * (flipped + 1) ); + } + +} + +// Add cards from pile to deck, in reverse direction +void Klondike::redeal() { + + CardList pilecards = pile->cards(); + if (EasyRules) + // the remaining cards in deck should be on top + // of the new deck + pilecards += deck->cards(); + + for (int count = pilecards.count() - 1; count >= 0; --count) + { + Card *card = pilecards[count]; + card->setAnimated(false); + deck->add(card, true, false); // facedown, nospread + } + + redealt = true; +} + +void Klondike::deal() { + for(int round=0; round < 7; round++) + for (int i = round; i < 7; i++ ) + play[i]->add(deck->nextCard(), i != round, true); +} + +bool Klondike::cardClicked(Card *c) { + kdDebug(11111) << "card clicked " << c->name() << endl; + + if (Dealer::cardClicked(c)) + return true; + + if (c->source() == deck) { + pileClicked(deck); + return true; + } + + return false; +} + +void Klondike::pileClicked(Pile *c) { + kdDebug(11111) << "pile clicked " << endl; + Dealer::pileClicked(c); + + if (c == deck) { + deal3(); + } +} + +bool Klondike::startAutoDrop() +{ + bool pileempty = pile->isEmpty(); + if (!Dealer::startAutoDrop()) + return false; + if (pile->isEmpty() && !pileempty) + deal3(); + return true; +} + + +bool Klondike::isGameLost() const +{ + kdDebug( 11111 ) << "Is the game lost?" << endl; + + if (!deck->isEmpty()) { + kdDebug( 11111 ) << "We should only check this when the deck is exhausted." << endl; + return false; + } + + // Check whether top of the pile can be added to any of the target piles. + if ( !pile->isEmpty() ) { + for ( int i = 0; i < 4; ++i ) { + if ( target[ i ]->isEmpty() ) { + continue; + } + if ( pile->top()->suit() == target[ i ]->top()->suit() && + pile->top()->rank() - 1 == target[ i ]->top()->rank() ) { + kdDebug( 11111 ) << "No, the source pile's top card could be added to target pile " << i << endl; + return false; + } + } + } + + // Create a card list - srcPileCards - that contains all accessible + // cards in the pile and the deck. + CardList srcPileCards; + if ( EasyRules ) { + srcPileCards = pile->cards(); + } else { + /* In the draw3 mode, not every card in the source pile is + * accessible, but only every third one. + */ + for ( unsigned int i = 2; i < pile->cards().count(); i += 3 ) { + kdDebug( 11111 ) << "Found card "<< pile->cards()[i]->name()<< endl; + srcPileCards += pile->cards()[ i ]; + } + if ( !pile->cards().isEmpty() && pile->cards().count() % 3 != 0 ) { + kdDebug( 11111 ) << "Found last card "<< pile->cards()[pile->cards().count() - 1]->name()<< endl; + srcPileCards += pile->cards()[ pile->cards().count() - 1 ]; + } + } + + // Check all seven stores + for ( int i = 0; i < 7; ++i ) { + + // If this store is empty... + if ( play[ i ]->isEmpty() ) { + // ...check whether the pile contains a king we could move here. + CardList::ConstIterator it = srcPileCards.begin(); + CardList::ConstIterator end = srcPileCards.end(); + for ( ; it != end; ++it ) { + if ( ( *it )->rank() == Card::King ) { + kdDebug( 11111 ) << "No, the pile contains a king which we could move onto store " << i << endl; + return false; + } + } + + // ...check whether any of the other stores contains a (visible) + // king we could move here. + for ( int j = 0; j < 7; ++j ) { + if ( j == i || play[ j ]->isEmpty() ) { + continue; + } + const CardList cards = play[ j ]->cards(); + CardList::ConstIterator it = ++cards.begin(); + CardList::ConstIterator end = cards.end(); + for ( ; it != end; ++it ) { + if ( ( *it )->realFace() && ( *it )->rank() == Card::King ) { + kdDebug( 11111 ) << "No, store " << j << " contains a visible king which we could move onto store " << i << endl; + return false; + } + } + } + } else { // This store is not empty... + Card *topCard = play[ i ]->top(); + + // ...check whether the top card is an Ace (we can start a target) + if ( topCard->rank() == Card::Ace ) { + kdDebug( 11111 ) << "No, store " << i << " has an Ace, we could start a target pile." << endl; + return false; + } + + // ...check whether the top card can be added to any target pile + for ( int targetIdx = 0; targetIdx < 4; ++targetIdx ) { + if ( target[ targetIdx ]->isEmpty() ) { + continue; + } + if ( target[ targetIdx ]->top()->suit() == topCard->suit() && + target[ targetIdx ]->top()->rank() == topCard->rank() - 1 ) { + kdDebug( 11111 ) << "No, store " << i << "'s top card could be added to target pile " << targetIdx << endl; + return false; + } + } + + // ...check whether the source pile contains a card which can be + // put onto this store. + CardList::ConstIterator it = srcPileCards.begin(); + CardList::ConstIterator end = srcPileCards.end(); + for ( ; it != end; ++it ) { + if ( ( *it )->isRed() != topCard->isRed() && + ( *it )->rank() == topCard->rank() - 1 ) { + kdDebug( 11111 ) << "No, the pile contains a card which we could add to store " << i << endl; + return false; + } + } + + // ...check whether any of the other stores contains a visible card + // which can be put onto this store, and which is on top of an + // uncovered card. + for ( int j = 0; j < 7; ++j ) { + if ( j == i ) { + continue; + } + const CardList cards = play[ j ]->cards(); + CardList::ConstIterator it = cards.begin(); + CardList::ConstIterator end = cards.end(); + for ( ; it != end; ++it ) { + if ( ( *it )->realFace() && + ( *it )->isRed() != topCard->isRed() && + ( *it )->rank() == topCard->rank() - 1 ) { + kdDebug( 11111 ) << "No, store " << j << " contains a card which we could add to store " << i << endl; + return false; + } + } + } + } + } + kdDebug( 11111 ) << "Yep, all hope is lost." << endl; + return true; +} + +static class LocalDealerInfo0 : public DealerInfo +{ +public: + LocalDealerInfo0() : DealerInfo(I18N_NOOP("&Klondike"), 0) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Klondike(true, parent); } +} ldi0; + +static class LocalDealerInfo14 : public DealerInfo +{ +public: + LocalDealerInfo14() : DealerInfo(I18N_NOOP("Klondike (&draw 3)"), 13) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Klondike(false, parent); } +} ldi14; + + +#include "klondike.moc" diff --git a/kpat/klondike.h b/kpat/klondike.h new file mode 100644 index 00000000..b1b7e673 --- /dev/null +++ b/kpat/klondike.h @@ -0,0 +1,71 @@ +/***********************-*-C++-*-******** + + klondike.cpp implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + + +// +// 7 positions, alternating red and black +// + + +****************************************/ + +#ifndef P_KLONDIKE +#define P_KLONDIKE + +#include "dealer.h" + +class KlondikePile; + +class Klondike : public Dealer { + Q_OBJECT + +public: + Klondike( bool easy, KMainWindow* parent=0, const char* name=0); + +public: + virtual void restart(); + virtual bool startAutoDrop(); + + void redeal(); // put pile back into deck + void deal(); + void deal3(); // move up to 3 cards from deck to pile + + virtual bool cardClicked(Card *); + virtual void pileClicked(Pile *c); + virtual void getHints(); + + virtual Card *demoNewCards(); + + bool tryToDrop(Card *t); + + virtual bool isGameLost() const; + +private: + bool EasyRules; + bool redealt; + + Pile* play[7]; + Pile* target[4]; + + KlondikePile *pile; + Deck* deck; + Card::Rank target_tops[4]; + bool noLongerNeeded(Card::Rank r, Card::Suit s ); +}; + +#endif diff --git a/kpat/kpat.desktop b/kpat/kpat.desktop new file mode 100644 index 00000000..f1df018b --- /dev/null +++ b/kpat/kpat.desktop @@ -0,0 +1,116 @@ +[Desktop Entry] +Name=Patience +Name[ar]=لعبة الصبر (Patience) +Name[az]=SÉ™bir +Name[be]=ПаÑÑŒÑÐ½Ñ +Name[bn]=পেশেনà§à¦¸ +Name[br]=Habaskter +Name[bs]=Pasijans +Name[ca]=Paciència +Name[cs]=Passiáns +Name[da]=Kabale +Name[de]=Patiencen +Name[el]=Πασιέντζα +Name[eo]=Solitero +Name[es]=Solitario +Name[et]=Kaardimängud +Name[eu]=Pazientzia +Name[fi]=Pasianssi +Name[fr]=Réussite +Name[gl]=Solitario +Name[hi]=पेशेनà¥à¤¸ +Name[hr]=Pasijans +Name[hu]=Pasziánsz +Name[is]=Kaplar +Name[it]=Solitario +Name[ja]=ソリティア +Name[lt]=Atkaklumas +Name[lv]=PacietÄ«ba +Name[mk]=ПаÑÐ¸Ñ˜Ð°Ð½Ñ +Name[mt]=PaÄ‹enzja +Name[nb]=Kabal +Name[ne]=धैरà¥à¤¯ +Name[nn]=Kabal +Name[pa]=ਪੇਟੀਨਸ਼ +Name[pl]=Pasjans +Name[pt]=Paciência +Name[pt_BR]=Paciência +Name[ro]=PasenÅ£e +Name[se]=Kabála +Name[sl]=Pasjansa +Name[sv]=Patiens +Name[ta]=பொறà¯à®®à¯ˆ +Name[tg]=ПаÑÑÐ½Ñ +Name[tr]=Sabır +Name[uk]=Ð¢ÐµÑ€Ð¿Ñ–Ð½Ð½Ñ +Name[ven]=Mukhondeleli +Name[wa]=Pacyince +Name[xh]= Nyamezela +Name[zh_CN]=è€å¿ƒ +Name[zh_TW]=Patience è€å¿ƒ +Name[zu]=Isineke +Exec=kpat %i %m -caption "%c" +Type=Application +Icon=kpat +DocPath=kpat/index.html +GenericName=Patience Card Game +GenericName[af]=Patience Kaart Speletjie +GenericName[be]=Картачны паÑÑŒÑÐ½Ñ +GenericName[bg]=ПаÑÐ¸Ð°Ð½Ñ +GenericName[bn]=পেশেনà§à¦¸ নামের তাস খেলা +GenericName[br]=C'hoari a habaskter +GenericName[bs]=Pasijans igra s kartama +GenericName[ca]=Partida de cartes paciència +GenericName[cs]=Karetní hra passiáns +GenericName[cy]=Gêm Cerdiau Patience +GenericName[da]=Kabale-kortspil +GenericName[de]=Patiencen legen +GenericName[el]=Παιχνίδι καÏτών patience +GenericName[eo]=Pacienca Kartludo +GenericName[es]=Juego de cartas de solitario +GenericName[et]=Kaardimäng +GenericName[eu]=Pazientziako karta-jokoa +GenericName[fa]=بازی Patience Card +GenericName[fi]=Pasianssikorttipeli +GenericName[fr]=Jeux de réussite +GenericName[gl]=Solitario, xogo de cartas +GenericName[he]=משחק ×§×œ×¤×™× +GenericName[hi]=पेशेंस ताश का खेल +GenericName[hr]=KartaÅ¡ka igra pasijansa +GenericName[hu]=Pasziánsz +GenericName[is]=Kaplar +GenericName[it]=Solitario +GenericName[ja]=Patience カードゲーム +GenericName[km]=ល្បែង​បៀ Patience +GenericName[ko]=ì¹´ë“œ ë†€ì´ +GenericName[lt]=KantrybÄ—s kortų žaidimas +GenericName[lv]=PacietÄ«bas kÄrÅ¡u spÄ“le +GenericName[mk]=ПаÑÐ¸Ñ˜Ð°Ð½Ñ - игра Ñо карти +GenericName[nb]=Kabalspill +GenericName[nds]=Patiencen leggen +GenericName[ne]=धैरà¥à¤¯ कारà¥à¤¡ खेल +GenericName[nl]=Patience-kaartspel +GenericName[nn]=Kabalspel +GenericName[pl]=Gra karciana Patience +GenericName[pt]=Jogo de Paciência +GenericName[pt_BR]=Jogo de Cartas Paciência +GenericName[ro]=Joc de pasenÅ£e +GenericName[ru]=ПаÑÑŒÑÐ½Ñ +GenericName[se]=Kabálaspeallu +GenericName[sk]=Kartová hra Patience +GenericName[sl]=Igra s kartami Patience +GenericName[sr]=Игра Ñа картама Patience +GenericName[sr@Latn]=Igra sa kartama Patience +GenericName[sv]=Patienskortspel +GenericName[ta]=பொறà¯à®®à¯ˆà®¯à®¾à®© சீடà¯à®Ÿà¯ விளையாடà¯à®Ÿà¯ +GenericName[tg]=Бозии ПаÑÑнÑи Кортӣ +GenericName[tr]=Sabır Kart Oyunu +GenericName[uk]=Гра в карти Ð¢ÐµÑ€Ð¿Ñ–Ð½Ð½Ñ +GenericName[ven]=Mutambo wa Magarata wa Mukondeleli +GenericName[wa]=CwÃ¥rdjeu d' pacyince +GenericName[xh]=Ikhadi lomdlalo wokunyamezela +GenericName[zh_CN]=考验è€å¿ƒçš„ç‰Œç±»æ¸¸æˆ +GenericName[zh_TW]=è€å¿ƒçš„紙牌éŠæˆ² +GenericName[zu]=Umdlalo wesineke wamakhadi +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;CardGame; diff --git a/kpat/kpatui.rc b/kpat/kpatui.rc new file mode 100644 index 00000000..218d9c39 --- /dev/null +++ b/kpat/kpatui.rc @@ -0,0 +1,40 @@ + + + + + &Game + + + + + + + + + + + + + + + &Edit + + + &Settings + + + + + + + +Main Toolbar + + + + + + + + + diff --git a/kpat/main.cpp b/kpat/main.cpp new file mode 100644 index 00000000..58ad8652 --- /dev/null +++ b/kpat/main.cpp @@ -0,0 +1,73 @@ +/* + patience -- main program + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + + */ + +#include +#include +#include + +#include "version.h" +#include "pwidget.h" + +static const char description[] = I18N_NOOP("KDE Patience Game"); + +static KCmdLineOptions options[] = +{ + { "+file", I18N_NOOP("File to load"), 0 }, + KCmdLineLastOption +}; + +int main( int argc, char **argv ) +{ + KAboutData aboutData( "kpat", I18N_NOOP("KPatience"), + KPAT_VERSION, description, KAboutData::License_GPL, + "(c) 1995, Paul Olav Tvete\n" + "(c) 2000 Stephan Kulow"); + aboutData.addAuthor("Paul Olav Tvete"); + aboutData.addAuthor("Mario Weilguni",0,"mweilguni@kde.org"); + aboutData.addAuthor("Matthias Ettrich",0,"ettrich@kde.org"); + aboutData.addAuthor("Rodolfo Borges",I18N_NOOP("Some Game Types"),"barrett@9hells.org"); + aboutData.addAuthor("Peter H. Ruegg",0,"kpat@incense.org"); + aboutData.addAuthor("Michael Koch", I18N_NOOP("Bug fixes"), "koch@kde.org"); + aboutData.addAuthor("Marcus Meissner", I18N_NOOP("Shuffle algorithm for game numbers"), + "mm@caldera.de"); + aboutData.addAuthor("Shlomi Fish", I18N_NOOP("Freecell Solver"), "shlomif@vipe.technion.ac.il"); + aboutData.addAuthor("Stephan Kulow", I18N_NOOP("Rewrite and current maintainer"), + "coolo@kde.org"); + aboutData.addAuthor("Erik Sigra", I18N_NOOP("Improved Klondike"), "sigra@home.se"); + aboutData.addAuthor("Josh Metzler", I18N_NOOP("Spider Implementation"), "joshdeb@metzlers.org"); + aboutData.addAuthor("Maren Pakura", I18N_NOOP("Documentation"), "maren@kde.org"); + aboutData.addAuthor("Inge Wallin", I18N_NOOP("Bug fixes"), "inge@lysator.liu.se"); + + KCmdLineArgs::init( argc, argv, &aboutData ); + KCmdLineArgs::addCmdLineOptions (options); + KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); + + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + + if (a.isRestored()) + RESTORE(pWidget) + else { + pWidget *w = new pWidget; + if (args->count()) + w->openGame(args->url(0)); + a.setMainWidget(w); + w->show(); + } + return a.exec(); +} diff --git a/kpat/mod3.cpp b/kpat/mod3.cpp new file mode 100644 index 00000000..c69aa8e4 --- /dev/null +++ b/kpat/mod3.cpp @@ -0,0 +1,312 @@ +/*--------------------------------------------------------------------------- + + mod3.cpp implements a patience card game + + Copyright (C) 1997 Rodolfo Borges + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +---------------------------------------------------------------------------*/ + +#include "mod3.h" +#include "cardmaps.h" +#include +#include "deck.h" +#include + +//-------------------------------------------------------------------------// + +Mod3::Mod3( KMainWindow* parent, const char* _name) + : Dealer( parent, _name ) +{ + const int dist_x = cardMap::CARDX() * 11 / 10 + 1; + const int dist_y = cardMap::CARDY() * 11 / 10 + 1; + const int margin = cardMap::CARDY() / 3; + + // This patience uses 2 deck of cards. + deck = Deck::new_deck( this, 2); + deck->move(8 + dist_x * 8 + 20, 8 + dist_y * 3 + margin); + + connect(deck, SIGNAL(clicked(Card*)), SLOT(deckClicked(Card*))); + + aces = new Pile(50, this); + aces->move(16 + dist_x * 8, 8 + dist_y / 2); + aces->setTarget(true); + aces->setCheckIndex(2); + aces->setAddFlags(Pile::addSpread | Pile::several); + + for ( int r = 0; r < 4; r++ ) { + for ( int c = 0; c < 8; c++ ) { + stack[r][c] = new Pile ( r * 10 + c + 1, this ); + stack[r][c]->move( 8 + dist_x * c, + 8 + dist_y * r + margin * ( r == 3 )); + + // The first 3 rows are the playing field, the fourth is the store. + if ( r < 3 ) { + stack[r][c]->setCheckIndex( 0 ); + stack[r][c]->setTarget(true); + } else { + stack[r][c]->setAddFlags( Pile::addSpread ); + stack[r][c]->setCheckIndex( 1 ); + } + } + } + + setTakeTargetForHints(true); + setActions(Dealer::Hint | Dealer::Demo ); +} + + +//-------------------------------------------------------------------------// + + +bool Mod3::checkAdd( int checkIndex, const Pile *c1, const CardList& cl) const +{ + // kdDebug(11111) << "checkAdd " << checkIndex << " " << c1->top()->name() << " " << c1->index() << " " << c1->index() / 10 << endl; + if (checkIndex == 0) { + Card *c2 = cl.first(); + + if (c1->isEmpty()) + return (c2->rank() == ( ( c1->index() / 10 ) + 2 ) ); + + kdDebug(11111) << "not empty\n"; + + if (c1->top()->suit() != c2->suit()) + return false; + + kdDebug(11111) << "same suit\n"; + if (c2->rank() != (c1->top()->rank()+3)) + return false; + + kdDebug(11111) << "+3 " << c1->cardsLeft() << " " << c1->top()->rank() << " " << c1->index()+1 << endl; + if (c1->cardsLeft() == 1) + return (c1->top()->rank() == ((c1->index() / 10) + 2)); + + kdDebug(11111) << "+1\n"; + + return true; + } else if (checkIndex == 1) { + return c1->isEmpty(); + } else if (checkIndex == 2) { + return cl.first()->rank() == Card::Ace; + } else return false; +} + + +bool Mod3::checkPrefering( int checkIndex, const Pile *c1, const CardList& c2) const +{ + return (checkIndex == 0 && c1->isEmpty() + && c2.first()->rank() == (c1->index()+1)); +} + + +//-------------------------------------------------------------------------// + + +void Mod3::restart() +{ + deck->collectAndShuffle(); + deal(); +} + + +//-------------------------------------------------------------------------// + + +void Mod3::dealRow(int row) +{ + if (deck->isEmpty()) + return; + + for (int c = 0; c < 8; c++) { + Card *card; + + card = deck->nextCard(); + stack[row][c]->add (card, false, true); + } +} + + +void Mod3::deckClicked(Card*) +{ + kdDebug(11111) << "deck clicked " << deck->cardsLeft() << endl; + if (deck->isEmpty()) + return; + + unmarkAll(); + dealRow(3); + takeState(); +} + + +//-------------------------------------------------------------------------// + + +void Mod3::deal() +{ + unmarkAll(); + CardList list = deck->cards(); +/* for (CardList::Iterator it = list.begin(); it != list.end(); ++it) + if ((*it)->rank() == Card::Ace) { + aces->add(*it); + (*it)->hide(); + } +*/ + kdDebug(11111) << "init " << aces->cardsLeft() << " " << deck->cardsLeft() << endl; + + for (int r = 0; r < 4; r++) + dealRow(r); +} + +Card *Mod3::demoNewCards() +{ + if (deck->isEmpty()) + return 0; + deckClicked(0); + return stack[3][0]->top(); +} + +bool Mod3::startAutoDrop() { + return false; +} + +bool Mod3::isGameLost() const +{ + int n,row,col; + kdDebug(11111) << "isGameLost ?"<< endl; + + bool nextTest=false; + + // If there is an empty stack or an ace below, the game is not lost. + for (col=0; col < 8; col++){ + if (stack[3][col]->isEmpty() + || stack[3][col]->at(0)->rank() == Card::Ace) + return false; + } + + // Ok, so no empty stack below. + // If there is neither an empty stack on the board (an ace counts + // as this) nor a card placed in the correct row, all is lost. + // Otherwise we have to do more tests. + for (n = 0; n < 24; n++) { + row = n / 8; + col = n % 8; + + // If there is a stack on the board that is either empty or + // contains an ace, the game is not finished. + if (stack[row][col]->isEmpty() + || stack[row][col]->at(0)->rank() == Card::Ace) { + nextTest = true; + break; + } + + // If there is a card that is correctly placed, the game is + // not lost. + if (stack[row][col]->at(0)->rank() == Card::Two + row) { + nextTest = true; + break; + } + } + if (!nextTest) + return true; + + // If there are more cards in the deck, the game is not lost. + if (!deck->isEmpty()) + return false; + + int n2, row2, col2, col3; + Card *ctop; + Card *card; + + // For all stacks on the board, check if: + // + for (n = 0; n < 24; n++){ + row = n / 8; + col = n % 8; + + // Empty stack: Can we move a card there? + if (stack[row][col]->isEmpty()) { + // Can we move a card from below? + for (col3=0; col3 < 8; col3++) { + if (stack[3][col3]->top()->rank() == (Card::Two+row)) + return false; + } + + // Can we move a card from another row? + for (n2 = 0; n2 < 16; n2++) { + row2 = (row + 1 + (n2 / 8)) % 3; + col2 = n2 % 8; + + if (stack[row2][col2]->isEmpty()) + continue; + if (stack[row2][col2]->top()->rank() == (Card::Two + row)) + return false; + } + } + else { + // Non-empty stack. + ctop = stack[row][col]->top(); + kdDebug(11111) << "considering ["<name() << flush; + + // Card not in its final position? Then we can't build on it. + if (stack[row][col]->at(0)->rank() != Card::Two + row) + continue; + + // Can we move a card from below here? + for (col3 = 0; col3 < 8; col3++) { + card = stack[3][col3]->top(); + if (card->suit() == ctop->suit() + && card->rank() == ctop->rank() + 3) + return false; + } + kdDebug(11111) <<" Can't stack from bottom row" << flush; + + // Can we move a card from another stack here? + for (int n_2 = 1; n_2 < 24; n_2++) { + n2 = (n + n_2) % 24; + row2 = n2 / 8; + col2 = n2 % 8; + + if (stack[row2][col2]->isEmpty()) + continue; + + card = stack[row2][col2]->top(); + + // Only consider cards that are not on top of other cards. + if (stack[row2][col2]->indexOf(card) != 0) + continue; + + if (card->suit() == ctop->suit() + && card->rank() == ctop->rank() + 3) + return false; + } + } + } + + return true; +} + + +static class LocalDealerInfo5 : public DealerInfo +{ +public: + LocalDealerInfo5() : DealerInfo(I18N_NOOP("M&od3"), 5) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Mod3(parent); } +} ldi5; + +//-------------------------------------------------------------------------// + +#include"mod3.moc" + +//-------------------------------------------------------------------------// + diff --git a/kpat/mod3.h b/kpat/mod3.h new file mode 100644 index 00000000..17ff6aa2 --- /dev/null +++ b/kpat/mod3.h @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------- + + mod3.cpp implements a patience card game + + Copyright (C) 1997 Rodolfo Borges + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + + (I don't know a name for this one, if you do, please tell me.) + +---------------------------------------------------------------------------*/ + +#ifndef _MOD3_H_ +#define _MOD3_H_ + +#include "dealer.h" + +class Mod3 : public Dealer +{ + Q_OBJECT + +public: + Mod3( KMainWindow* parent=0, const char* name=0); + + void deal(); + + virtual void restart(); + virtual bool isGameLost() const; + virtual bool startAutoDrop(); + +public slots: + void deckClicked(Card *c); + +protected: + virtual Card *demoNewCards(); + +private: // functions + virtual bool checkAdd( int checkIndex, const Pile *c1, const CardList& c2) const; + virtual bool checkPrefering( int checkIndex, const Pile *c1, const CardList& c2) const; + + void dealRow(int row); + +private: + Deck *deck; + + Pile *stack[4][8]; + Pile *aces; +}; + +#endif + +//-------------------------------------------------------------------------// diff --git a/kpat/napoleon.cpp b/kpat/napoleon.cpp new file mode 100644 index 00000000..ffdf245c --- /dev/null +++ b/kpat/napoleon.cpp @@ -0,0 +1,204 @@ +/* + napoleon.cpp implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + + */ + +#include "napoleon.h" +#include +#include "deck.h" +#include "cardmaps.h" + +Napoleon::Napoleon( KMainWindow* parent, const char* _name ) + : Dealer( parent, _name ) +{ + deck = Deck::new_deck( this ); + connect(deck, SIGNAL(clicked(Card *)), SLOT(deal1(Card*))); + + pile = new Pile( 1, this ); + pile->setAddFlags( Pile::disallow ); + + for (int i = 0; i < 4; i++) + { + store[i] = new Pile( 2 + i, this ); + store[i]->setCheckIndex( 0 ); + target[i] = new Pile( 6 + i, this); + target[i]->setRemoveFlags( Pile::disallow ); + target[i]->setCheckIndex(2); + target[i]->setTarget(true); + } + + const int dist_store = cardMap::CARDX() * 55 / 100; + const int dist_target = dist_store / 2; + const int centre_x = 10 + cardMap::CARDX() + dist_store; + const int centre_y = 10 + cardMap::CARDY() + dist_store; + + deck->move( centre_x + cardMap::CARDX() * 47 / 10, centre_y + cardMap::CARDY() + dist_store); + pile->move( centre_x + cardMap::CARDX() * 33 / 10, centre_y + cardMap::CARDY() + dist_store); + + centre = new Pile( 10, this ); + centre->setRemoveFlags( Pile::disallow ); + centre->setCheckIndex(1); + centre->setTarget(true); + + store[0]->move( centre_x, centre_y - cardMap::CARDY() - dist_store ); + store[1]->move( centre_x + cardMap::CARDX() + dist_store, centre_y); + store[2]->move( centre_x, centre_y + cardMap::CARDY() + dist_store ); + store[3]->move( centre_x - cardMap::CARDX() - dist_store, centre_y); + target[0]->move( centre_x - cardMap::CARDX() - dist_target, centre_y - cardMap::CARDY() - dist_target ); + target[1]->move( centre_x + cardMap::CARDX() + dist_target, centre_y - cardMap::CARDY() - dist_target); + target[2]->move( centre_x + cardMap::CARDX() + dist_target, centre_y + cardMap::CARDY() + dist_target); + target[3]->move( centre_x - cardMap::CARDX() - dist_target, centre_y + cardMap::CARDY() + dist_target); + centre->move(centre_x, centre_y); + + setActions(Dealer::Hint | Dealer::Demo); +} + +void Napoleon::restart() { + deck->collectAndShuffle(); + deal(); +} + +bool Napoleon::CanPutTarget( const Pile* c1, const CardList& cl) const { + Card *c2 = cl.first(); + + if (c1->isEmpty()) + return c2->rank() == Card::Seven; + else + return (c2->rank() == c1->top()->rank() + 1); +} + +bool Napoleon::CanPutCentre( const Pile* c1, const CardList& cl) const { + Card *c2 = cl.first(); + + if (c1->isEmpty()) + return c2->rank() == Card::Six; + + if (c1->top()->rank() == Card::Ace) + return (c2->rank() == Card::Six); + else + return (c2->rank() == c1->top()->rank() - 1); +} + +bool Napoleon::checkAdd( int checkIndex, const Pile *c1, const CardList& c2) const +{ + switch (checkIndex) { + case 0: + return c1->isEmpty(); + case 1: + return CanPutCentre(c1, c2); + case 2: + return CanPutTarget(c1, c2); + default: + return false; + } +} + +void Napoleon::deal() { + if (deck->isEmpty()) + return; + + for (int i=0; i<4; i++) + store[i]->add(deck->nextCard(), false, false); +} + +void Napoleon::deal1(Card *) { + Card *c = deck->nextCard(); + if (!c) + return; + pile->add(c, true, false); + c->move(deck->x(), deck->y()); + c->flipTo(int(pile->x()), int(pile->y()), 8); +} + +Card *Napoleon::demoNewCards() +{ + if (deck->isEmpty()) + return 0; + deal1(0); + return pile->top(); +} + +void Napoleon::getHints() { + CardList cards; + for (int i = 0; i < 4; i++) + { + if (!store[i]->isEmpty()) + cards.append(store[i]->top()); + } + if (pile->top()) + cards.append(pile->top()); + + for (CardList::Iterator it = cards.begin(); it != cards.end(); ++it) { + CardList empty; + empty.append(*it); + if (CanPutCentre(centre, empty)) { + newHint(new MoveHint(*it, centre)); + continue; + } + for (int i = 0; i < 4; i++) { + if (CanPutTarget(target[i], empty)) { + newHint(new MoveHint(*it, target[i])); + break; + } + } + } + if (pile->isEmpty()) + return; + + for (int i = 0; i < 4; i++) { + if (store[i]->isEmpty()) { + newHint(new MoveHint(pile->top(), store[i])); + return; + } + } +} + +bool Napoleon::isGameLost() const +{ + CardList cards; + for (int i = 0; i < 4; i++) + { + if (store[i]->isEmpty()) + return false; + else + cards.append(store[i]->top()); + } + + if (pile->top()) + cards.append(pile->top()); + + for (CardList::Iterator it = cards.begin(); it != cards.end(); ++it) { + CardList empty; + empty.append(*it); + if(CanPutCentre(centre,empty)) return false; + for(int i=0; i<4; i++) + if(CanPutTarget(target[i],empty)) return false; + } + + return (deck->isEmpty()); +} + + + +static class LocalDealerInfo4 : public DealerInfo +{ +public: + LocalDealerInfo4() : DealerInfo(I18N_NOOP("&Napoleon's Tomb"), 4) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Napoleon(parent); } +} ldi3; + +#include "napoleon.moc" diff --git a/kpat/napoleon.h b/kpat/napoleon.h new file mode 100644 index 00000000..a89f68b9 --- /dev/null +++ b/kpat/napoleon.h @@ -0,0 +1,56 @@ +/***********************-*-C++-*-******** + + napoleon.cpp implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +****************************************/ + + +#ifndef P_NAPOLEON +#define P_NAPOLEON + +#include "dealer.h" + +class Napoleon : public Dealer { + Q_OBJECT +public: + Napoleon (KMainWindow* parent=0, const char* name=0); + + virtual void restart(); + virtual void getHints(); + virtual Card *demoNewCards(); + virtual bool startAutoDrop() { return false; } + virtual bool isGameLost() const; + +public slots: + void deal1(Card *c); + +private: + void deal(); + + bool CanPutTarget( const Pile *c1, const CardList& c2) const; + bool CanPutCentre( const Pile* c1, const CardList& c2) const; + + virtual bool checkAdd ( int checkIndex, const Pile *c1, const CardList& c2) const; + + Pile* pile; + Pile* target[4]; + Pile* centre; + Pile* store[4]; + Deck* deck; +}; + +#endif diff --git a/kpat/pile.cpp b/kpat/pile.cpp new file mode 100644 index 00000000..dd419580 --- /dev/null +++ b/kpat/pile.cpp @@ -0,0 +1,463 @@ +#include "pile.h" +#include "dealer.h" +#include +#include +#include "cardmaps.h" +#include +#include "speeds.h" + +const int Pile::RTTI = 1002; + +const int Pile::Default = 0x0000; +const int Pile::disallow = 0x0001; +const int Pile::several = 0x0002; // default: move one card + +// Add-flags +const int Pile::addSpread = 0x0100; + +// Remove-flags +const int Pile::autoTurnTop = 0x0200; +const int Pile::wholeColumn = 0x0400; + + + +Pile::Pile( int _index, Dealer* _dealer) + : QCanvasRectangle( _dealer->canvas() ), + m_dealer(_dealer), + m_atype(Custom), + m_rtype(Custom), + myIndex(_index), + _target(false) +{ + // Make the patience aware of this pile. + dealer()->addPile(this); + + QCanvasRectangle::setVisible(true); // default + _checkIndex = -1; + m_addFlags = 0; + m_removeFlags = 0; + + setBrush(Qt::black); + setPen(QPen(Qt::black)); + + setZ(0); + initSizes(); +} + + +void Pile::initSizes() +{ + setSpread( cardMap::CARDY() / 5 + 1 ); + setHSpread( cardMap::CARDX() / 9 + 1 ); + setDSpread( cardMap::CARDY() / 8 ); + + setSize( cardMap::CARDX(), cardMap::CARDY() ); +} + +void Pile::setType(PileType type) +{ + setAddType(type); + setRemoveType(type); +} + +void Pile::setAddType(PileType _type) +{ + m_atype = _type; + switch (_type) { + case Custom: + case FreeCell: + break; + case KlondikeTarget: + setTarget(true); + break; + case KlondikeStore: + case GypsyStore: + case FreecellStore: + setAddFlags(Pile::addSpread | Pile::several); + break; + } +} + +void Pile::setRemoveType(PileType _type) +{ + m_rtype = _type; + switch (_type) { + case Custom: + break; + case KlondikeTarget: + setRemoveFlags(Pile::disallow); + break; + case KlondikeStore: + case GypsyStore: + case FreeCell: + break; + case FreecellStore: + setRemoveFlags(Pile::several | Pile::autoTurnTop); + break; + } +} + +Pile::~Pile() +{ + dealer()->removePile(this); + + for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it) + { + if ((*it)->source() != this) { + int i = -13; + if ((*it)->source()) + i = (*it)->source()->index(); + kdDebug(11111) << "pile doesn't match " << index() << " - " << i << endl; + } + (*it)->setSource(0); + } +} + +void Pile::resetCache() +{ + cache.resize(0, 0); + cache_selected.resize(0, 0); +} + +void Pile::drawShape ( QPainter & painter ) +{ + if (isSelected()) { + if (cache.isNull()) + dealer()->drawPile(cache, this, false); + painter.drawPixmap(int(x()), int(y()), cache); + } else { + if (cache_selected.isNull()) + dealer()->drawPile(cache_selected, this, true); + painter.drawPixmap(int(x()), int(y()), cache_selected); + } +} + +bool Pile::legalAdd( const CardList& _cards ) const +{ + if ( m_addFlags & disallow ) + return false; + + if ( !( m_addFlags & several ) && _cards.count() > 1 ) + return false; + + // getHint removes cards without turning, so it could be it + // checks later if cards can be added to a face down card + if (top() && !top()->realFace()) + return false; + + switch (addType()) { + case Custom: + return dealer()->checkAdd( checkIndex(), this, _cards ); + break; + case KlondikeTarget: + return add_klondikeTarget(_cards); + break; + case FreecellStore: + case KlondikeStore: + return add_klondikeStore(_cards); + break; + case GypsyStore: + return add_gypsyStore(_cards); + break; + case FreeCell: + return add_freeCell(_cards); + } + return false; +} + +bool Pile::legalRemove(const Card *c) const +{ + if ( m_removeFlags & disallow ) { + return false; + } + if ( !( m_removeFlags & several ) && top() != c) + return false; + + switch (removeType()) { + case Custom: + return dealer()->checkRemove( checkIndex(), this, c); + break; + case KlondikeTarget: + case GypsyStore: + case KlondikeStore: + break; + case FreecellStore: + return remove_freecellStore(c); + break; + case FreeCell: + return (top() == c); + break; + } + return true; +} + +void Pile::setVisible(bool vis) +{ + QCanvasRectangle::setVisible(vis); + dealer()->enlargeCanvas(this); + + for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it) + { + (*it)->setVisible(vis); + dealer()->enlargeCanvas(*it); + } +} + +void Pile::moveBy(double dx, double dy) +{ + QCanvasRectangle::moveBy(dx, dy); + dealer()->enlargeCanvas(this); + + for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it) + { + (*it)->moveBy(dx, dy); + dealer()->enlargeCanvas(*it); + } +} + +int Pile::indexOf(const Card *c) const +{ + assert(c->source() == this); + return m_cards.findIndex(const_cast(c)); // the list is of non-const cards +} + +Card *Pile::at(int index) const +{ + if (index < 0 || index >= int(m_cards.count())) + return 0; + return *m_cards.at(index); +} + +// Return the top card of this pile. +// + +Card *Pile::top() const +{ + if (m_cards.isEmpty()) + return 0; + + return m_cards.last(); +} + +void Pile::clear() +{ + for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it) + { + (*it)->setSource(0); + } + m_cards.clear(); +} + +void Pile::add( Card *_card, int index) +{ + if (_card->source() == this) + return; + + Pile *source = _card->source(); + if (source) { + _card->setTakenDown(source->target() && !target()); + source->remove(_card); + } + + _card->setSource(this); + + if (index == -1) + m_cards.append(_card); + else { + while (m_cards.count() <= uint(index)) + m_cards.append(0); + assert(m_cards[index] == 0); + m_cards[index] = _card; + } +} + + +// Return the number of pixels in x and y that the card should be +// offset from the start position of the pile. +// +// Note: Default is to only have vertical spread (Y direction). + +QSize Pile::cardOffset( bool _spread, bool _facedown, const Card *before) const +{ + if (_spread) { + if (_facedown) + return QSize(0, dspread()); + else { + if (before && !before->isFaceUp()) + return QSize(0, dspread()); + else + return QSize(0, spread()); + } + } + + return QSize(0, 0); +} + +/* override cardtype (for initial deal ) */ +void Pile::add( Card* _card, bool _facedown, bool _spread ) +{ + if (!_card) + return; + + // The top card + Card *t = top(); + + // If this pile is visible, then also show the card. + if (isVisible()) + _card->show(); + else + _card->hide(); + + _card->turn( !_facedown ); + + QSize offset = cardOffset(_spread, _facedown, t); + + int x2, y2, z2; + + if (t) { + x2 = int(t->realX() + offset.width()); + y2 = int(t->realY() + offset.height()); + z2 = int(t->realZ() + 1); + } else { + x2 = int(x()); + y2 = int(y()); + z2 = int(z() + 1); + } + + add(_card); + + if (_facedown || !isVisible()) { + _card->move( x2, y2 ); + _card->setZ( z2 ); + } else { + _card->moveTo(x2, y2, z2, STEPS_INITIALDEAL); + } + + dealer()->enlargeCanvas(_card); +} + +void Pile::remove(Card *c) +{ + assert(m_cards.contains(c)); + m_cards.remove(c); +} + +void Pile::hideCards( const CardList & cards ) +{ + for (CardList::ConstIterator it = cards.begin(); it != cards.end(); ++it) + m_cards.remove(*it); +} + +void Pile::unhideCards( const CardList & cards ) +{ + for (CardList::ConstIterator it = cards.begin(); it != cards.end(); ++it) + m_cards.append(*it); +} + +CardList Pile::cardPressed(Card *c) +{ + CardList result; + + if (!legalRemove(c)) + return result; + + int below = -1; + + if (!c->isFaceUp()) + return result; + + for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it) + { + if (c == *it) { + below = 0; + } + if (below >= 0) { + (*it)->setAnimated(false); + (*it)->setZ(128 + below); + below++; + result.append(*it); + } + } + return result; +} + +void Pile::moveCards(CardList &cl, Pile *to) +{ + if (!cl.count()) + return; + + for (CardList::Iterator it = cl.begin(); it != cl.end(); ++it) + to->add(*it); + + if (m_removeFlags & autoTurnTop && top()) { + Card *t = top(); + if (!t->isFaceUp()) { + t->flipTo(int(t->x()), int(t->y()), 8); + canvas()->update(); + } + } + + to->moveCardsBack(cl, false); +} + +void Pile::moveCardsBack(CardList &cl, bool anim) +{ + if (!cl.count()) + return; + + Card *c = cl.first(); + + Card *before = 0; + QSize off; + + int steps = STEPS_MOVEBACK; + if (!anim) + steps = 0; + + for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it) + { + if (c == *it) { + if (before) { + off = cardOffset(m_addFlags & Pile::addSpread, false, before); + c->moveTo( before->realX() + off.width(), + before->realY() + off.height(), + before->realZ() + 1, steps); + dealer()->enlargeCanvas(c); + } + else { + c->moveTo( int(x()), int(y()), int(z()) + 1, steps); + } + break; + } else + before = *it; + } + + before = c; + CardList::Iterator it = cl.begin(); // == c + ++it; + + off = cardOffset(m_addFlags & Pile::addSpread, false, 0); + + for (; it != cl.end(); ++it) + { + (*it)->moveTo( before->realX() + off.width(), + before->realY() + off.height(), + before->realZ() + 1, steps); + dealer()->enlargeCanvas(*it); + before = *it; + } +} + +bool Pile::cardClicked(Card *c) +{ + emit clicked(c); + return false; +} + +bool Pile::cardDblClicked(Card *c) +{ + emit dblClicked(c); + return false; +} + +#include "pile.moc" diff --git a/kpat/pile.h b/kpat/pile.h new file mode 100644 index 00000000..b2b553b0 --- /dev/null +++ b/kpat/pile.h @@ -0,0 +1,154 @@ +#ifndef _PILE_H +#define _PILE_H + + +#include "card.h" +#include + + +class Dealer; + + +/** + * + * Pile -- A pile on the board that can hold cards. + * + */ + +class Pile : public QObject, public QCanvasRectangle +{ + Q_OBJECT + +public: + + enum PileType { Custom, + KlondikeTarget, + KlondikeStore, + GypsyStore, + FreeCell, + FreecellStore}; + + // Add- and remove-flags + static const int Default; + static const int disallow; + static const int several; // default: move one card + + // Add-flags + static const int addSpread; + + // Remove-flags + static const int autoTurnTop; + static const int wholeColumn; + + Pile( int _index, Dealer* parent = 0); + virtual ~Pile(); + + Dealer *dealer() const { return m_dealer; } + CardList cards() const { return m_cards; } + + bool legalAdd(const CardList &c ) const; + bool legalRemove(const Card *c) const; + + virtual void moveCards(CardList &c, Pile *to = 0); + void moveCardsBack(CardList &c, bool anim = true); + + void setAddFlags( int flag ) { m_addFlags = flag; } + void setRemoveFlags( int flag ) { m_removeFlags = flag; } + + void setCheckIndex( int index ) { _checkIndex = index; } + virtual int checkIndex() const { return _checkIndex; } + + void setTarget(bool t) { _target = t; } + bool target() const { return _target; } + + CardList cardPressed(Card *c); + + Card *top() const; + + void add( Card *c, bool facedown, bool spread); // for initial deal + void add( Card *c, int index = -1); + void remove(Card *c); + void clear(); + + int index() const { return myIndex; } + bool isEmpty() const { return m_cards.count() == 0; } + + virtual void drawShape ( QPainter & p ); + static const int RTTI; + + virtual int rtti() const { return RTTI; } + + virtual void setVisible(bool vis); + virtual void moveBy(double dx, double dy); + + int cardsLeft() const { return m_cards.count(); } + + int indexOf(const Card *c) const; + Card *at(int index) const; + + void hideCards( const CardList & cards ); + void unhideCards( const CardList & cards ); + + virtual QSize cardOffset( bool _spread, bool _facedown, const Card *before) const; + + void resetCache(); + virtual void initSizes(); + + void setType( PileType t); + void setAddType( PileType t); + void setRemoveType( PileType t); + PileType addType() const { return m_atype; } + PileType removeType() const { return m_rtype; } + + // pile_algorithms + bool add_klondikeTarget( const CardList& c2 ) const; + bool add_klondikeStore( const CardList& c2 ) const; + bool add_gypsyStore( const CardList& c2 ) const; + bool add_freeCell( const CardList& c2) const; + + bool remove_freecellStore( const Card *c) const; + + // The spread properties. + int spread() const { return _spread; } + void setSpread(int s) { _spread = s; } + int dspread() const { return _dspread; } + void setDSpread(int s) { _dspread = s; } + int hspread() const { return _hspread; } + void setHSpread(int s) { _hspread = s; } + +public slots: + virtual bool cardClicked(Card *c); + virtual bool cardDblClicked(Card *c); + +signals: + void clicked(Card *c); + void dblClicked(Card *c); + +protected: + int m_removeFlags; + int m_addFlags; + CardList m_cards; + +private: + // Reference to the patience this pile is a part of. + Dealer *m_dealer; + + // Properties of the pile. + PileType m_atype; // Addtype + PileType m_rtype; // Removetype + int _spread; + int _hspread; + int _dspread; + + int _checkIndex; + int myIndex; + bool _target; + + // Graphics + KPixmap cache; + KPixmap cache_selected; +}; + +typedef QValueList PileList; + +#endif diff --git a/kpat/pile_algorithms.cpp b/kpat/pile_algorithms.cpp new file mode 100644 index 00000000..7ca3469c --- /dev/null +++ b/kpat/pile_algorithms.cpp @@ -0,0 +1,69 @@ +#include "pile.h" +#include + +bool Pile::add_klondikeTarget( const CardList& c2 ) const +{ + Card *newone = c2.first(); + if (isEmpty()) + return (newone->rank() == Card::Ace); + + return (newone->rank() == top()->rank() + 1) + && (top()->suit() == newone->suit()); +} + +bool Pile::add_klondikeStore( const CardList& c2 ) const +{ + Card *newone = c2.first(); + if (isEmpty()) { + return (newone->rank() == Card::King); + } + + return (newone->rank() == top()->rank() - 1) + && (top()->isRed() != newone->isRed()); +} + +bool Pile::add_gypsyStore( const CardList& c2) const +{ + Card *newone = c2.first(); + if (isEmpty()) + return true; + + return (newone->rank() == top()->rank() - 1) + && (top()->isRed() != newone->isRed()); +} + +bool Pile::add_freeCell( const CardList & cards) const +{ + return (cards.count() == 1 && isEmpty()); +} + +bool Pile::remove_freecellStore( const Card *c) const +{ + // ok if just one card + if (c == top()) + return true; + + // Now we're trying to move two or more cards. + + // First, let's check if the column is in valid + // (that is, in sequence, alternated colors). + int index = indexOf(c) + 1; + const Card *before = c; + while (true) + { + c = at(index++); + + if (!((c->rank() == (before->rank()-1)) + && (c->isRed() != before->isRed()))) + { + kdDebug(11111) << c->name() << " - " << before->name() << endl; + return false; + } + if (c == top()) + return true; + before = c; + } + + return true; +} + diff --git a/kpat/pwidget.cpp b/kpat/pwidget.cpp new file mode 100644 index 00000000..dae69f60 --- /dev/null +++ b/kpat/pwidget.cpp @@ -0,0 +1,560 @@ +/* + patience -- main program + Copyright (C) 1995 Paul Olav Tvete + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, + provided that the above copyright notice appear in all copies and that + both that copyright notice and this permission notice appear in + supporting documentation. + + This file is provided AS IS with no warranties of any kind. The author + shall have no liability with respect to the infringement of copyrights, + trade secrets or any patents by this file or any part thereof. In no + event will the author be liable for any lost revenue or profits or + other special, indirect and consequential damages. + + + Heavily modified by Mario Weilguni +*/ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pwidget.h" +#include "version.h" +#include "dealer.h" +#include "cardmaps.h" +#include "speeds.h" +#include "gamestatsimpl.h" + + +static pWidget *current_pwidget = 0; + + +void saveGame(int) { + current_pwidget->saveGame(); +} + +pWidget::pWidget() + : KMainWindow(0, "pwidget"), dill(0) +{ + current_pwidget = this; + // KCrash::setEmergencySaveFunction(::saveGame); + KStdAction::quit(kapp, SLOT(quit()), actionCollection(), "game_exit"); + + undo = KStdAction::undo(this, SLOT(undoMove()), + actionCollection(), "undo_move"); + undo->setEnabled(false); + (void)KStdAction::openNew(this, SLOT(newGame()), + actionCollection(), "new_game"); + (void)KStdAction::open(this, SLOT(openGame()), + actionCollection(), "open"); + recent = KStdAction::openRecent(this, SLOT(openGame(const KURL&)), + actionCollection(), "open_recent"); + recent->loadEntries(KGlobal::config()); + (void)KStdAction::saveAs(this, SLOT(saveGame()), + actionCollection(), "save"); + (void)new KAction(i18n("&Choose Game..."), 0, this, SLOT(chooseGame()), + actionCollection(), "choose_game"); + (void)new KAction(i18n("Restart &Game"), QString::fromLatin1("reload"), 0, + this, SLOT(restart()), + actionCollection(), "restart_game"); + (void)KStdAction::help(this, SLOT(helpGame()), actionCollection(), "help_game"); + + games = new KSelectAction(i18n("&Game Type"), 0, this, + SLOT(newGameType()), + actionCollection(), "game_type"); + QStringList list; + QValueList::ConstIterator it; + uint max_type = 0; + + for (it = DealerInfoList::self()->games().begin(); + it != DealerInfoList::self()->games().end(); ++it) + { + // while we develop, it may happen that some lower + // indices do not exist + uint index = (*it)->gameindex; + for (uint i = 0; i <= index; i++) + if (list.count() <= i) + list.append("unknown"); + list[index] = i18n((*it)->name); + if (max_type < index) + max_type = index; + } + games->setItems(list); + + KGlobal::dirs()->addResourceType("wallpaper", KStandardDirs::kde_default("data") + "kpat/backgrounds/"); + KGlobal::dirs()->addResourceType("wallpaper", KStandardDirs::kde_default("data") + "ksnake/backgrounds/"); + wallpapers = new KSelectAction(i18n("&Change Background"), 0, this, + SLOT(changeWallpaper()), + actionCollection(), "wallpaper"); + list.clear(); + wallpaperlist.clear(); + QStringList wallpaperlist2 = KGlobal::dirs()->findAllResources("wallpaper", QString::null, + false, true, list); + QStringList list2; + for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) { + QString file = *it; + int rindex = file.findRev('.'); + if (rindex != -1) { + QString ext = file.mid(rindex + 1).lower(); + if (ext == "jpeg" || ext == "png" || ext == "jpg") { + list2.append(file.left(rindex)); + wallpaperlist.append( file ); + } + } + } + + wallpapers->setItems(list2); + wallpapers->setCurrentItem(list2.findIndex("No-Ones-Laughing-3")); + + changeWallpaper(); + + (void)new cardMap(midcolor); + + backs = new KAction(i18n("&Switch Cards..."), 0, this, + SLOT(changeBackside()), + actionCollection(), "backside"); + stats = new KAction(i18n("&Statistics"), 0, this, SLOT(showStats()), + actionCollection(),"game_stats"); + + animation = new KToggleAction(i18n( "&Animation on Startup" ), + 0, this, SLOT(animationChanged()), + actionCollection(), "animation"); + dropaction = new KToggleAction(i18n("&Enable Autodrop"), + 0, this, SLOT(enableAutoDrop()), + actionCollection(), "enable_autodrop"); + dropaction->setCheckedState(i18n("Disable Autodrop")); + + KConfig *config = kapp->config(); + KConfigGroupSaver cs(config, settings_group ); + + QString bgpath = config->readPathEntry("Background"); + kdDebug(11111) << "bgpath '" << bgpath << "'" << endl; + if (bgpath.isEmpty()) + bgpath = locate("wallpaper", "No-Ones-Laughing-3.jpg"); + background = QPixmap(bgpath); + + bool animate = config->readBoolEntry( "Animation", true); + animation->setChecked( animate ); + + bool autodrop = config->readBoolEntry("Autodrop", true); + dropaction->setChecked(autodrop); + + uint game = config->readNumEntry("DefaultGame", 0); + if (game > max_type) + game = max_type; + games->setCurrentItem(game); + + statusBar()->insertItem( "", 1, 0, true ); + + createGUI(QString::null, false); + KAcceleratorManager::manage(menuBar()); + + newGameType(); + adjustSize(); + setAutoSaveSettings(); +} + +pWidget::~pWidget() +{ + delete dill; +} + +void pWidget::undoMove() { + if( dill ) + dill->undo(); +} + +void pWidget::helpGame() +{ + if (!dill) + return; + kapp->invokeHelp(dill->anchorName()); +} + +void pWidget::undoPossible(bool poss) +{ + undo->setEnabled(poss); +} + +void pWidget::changeBackside() { + KConfig *config = kapp->config(); + KConfigGroupSaver kcs(config, settings_group); + + QString deck = config->readEntry("Back", KCardDialog::getDefaultDeck()); + QString cards = config->readEntry("Cards", KCardDialog::getDefaultCardDir()); + if (KCardDialog::getCardDeck(deck, cards, this, KCardDialog::Both) == QDialog::Accepted) + { + QString imgname = KCardDialog::getCardPath(cards, 11); + + QImage image; + image.load(imgname); + if( image.isNull()) { + kdDebug(11111) << "cannot load card pixmap \"" << imgname << "\" in " << cards << "\n"; + return; + } + + bool change = false; + if (image.width() != cardMap::CARDX() || image.height() != cardMap::CARDY()) + { + change = true; + if (KMessageBox::warningContinueCancel(this, i18n("The cards you have chosen have a different " + "size than the ones you are currently using. " + "This requires the current game to be restarted.")) == KMessageBox::Cancel) + return; + } + setBackSide(deck, cards); + if (change) { + + newGameType(); + } + } + +} + +void pWidget::changeWallpaper() +{ + QString bgpath=locate("wallpaper", wallpaperlist[wallpapers->currentItem()]); + if (bgpath.isEmpty()) + return; + background = QPixmap(bgpath); + if (background.isNull()) { + KMessageBox::sorry(this, i18n("Couldn't load wallpaper
%1
").arg(bgpath)); + return; + } + + QImage bg = background.convertToImage().convertDepth(8, 0); + if (bg.isNull() || !bg.numColors()) + return; + long r = 0; + long g = 0; + long b = 0; + for (int i = 0; i < bg.numColors(); ++i) + { + QRgb rgb = bg.color(i); + r += qRed(rgb); + g += qGreen(rgb); + b += qBlue(rgb); + } + r /= bg.numColors(); + b /= bg.numColors(); + g /= bg.numColors(); + midcolor = QColor(r, b, g); + + if (dill) { + KConfig *config = kapp->config(); + KConfigGroupSaver kcs(config, settings_group); + + QString deck = config->readEntry("Back", KCardDialog::getDefaultDeck()); + QString dummy = config->readEntry("Cards", KCardDialog::getDefaultCardDir()); + setBackSide(deck, dummy); + + config->writePathEntry("Background", bgpath); + dill->setBackgroundPixmap(background, midcolor); + dill->canvas()->setAllChanged(); + dill->canvas()->update(); + } +} + +void pWidget::animationChanged() { + bool anim = animation->isChecked(); + KConfig *config = kapp->config(); + KConfigGroupSaver cs(config, settings_group ); + config->writeEntry( "Animation", anim); +} + +void pWidget::enableAutoDrop() +{ + bool drop = dropaction->isChecked(); + KConfig *config = kapp->config(); + KConfigGroupSaver cs(config, settings_group ); + config->writeEntry( "Autodrop", drop); + dill->setAutoDropEnabled(drop); +} + +void pWidget::newGame() +{ + // Check if the user is already running a game, and if she is, + // then ask if she wants to abort it. + if (!dill->isGameWon() && !dill->isGameLost() + && KMessageBox::warningContinueCancel(0, + i18n("You are already running an unfinished game. " + "If you abort the old game to start a new one, " + "the old game will be registered as a loss in " + "the statistics file.\n" + "What do you want to do?"), + i18n("Abort Current Game?"), + i18n("Abort Old Game"), + "careaboutstats" ) == KMessageBox::Cancel) + return; + + dill->setGameNumber(kapp->random()); + setGameCaption(); + restart(); +} + + +void pWidget::restart() +{ + statusBar()->clear(); + dill->startNew(); +} + +void pWidget::setGameCaption() +{ + QString name = games->currentText(); + QString newname; + QString gamenum; + gamenum.setNum( dill->gameNumber() ); + for (uint i = 0; i < name.length(); i++) + if (name.at(i) != QChar('&')) + newname += name.at(i); + + setCaption( newname + " - " + gamenum ); +} + +void pWidget::newGameType() +{ + delete dill; + dill = 0; + slotUpdateMoves(); + + uint id = games->currentItem(); + for (QValueList::ConstIterator it = DealerInfoList::self()->games().begin(); it != DealerInfoList::self()->games().end(); ++it) { + if ((*it)->gameindex == id) { + dill = (*it)->createGame(this); + QString name = (*it)->name; + name = name.replace(QRegExp("[&']"), ""); + name = name.replace(QRegExp("[ ]"), "_").lower(); + dill->setAnchorName("game_" + name); + connect(dill, SIGNAL(saveGame()), SLOT(saveGame())); + connect(dill, SIGNAL(gameInfo(const QString&)), + SLOT(slotGameInfo(const QString &))); + connect(dill, SIGNAL(updateMoves()), + SLOT(slotUpdateMoves())); + dill->setGameId(id); + dill->setupActions(); + dill->setBackgroundPixmap(background, midcolor); + dill->startNew(); + break; + } + } + + if (!dill) { + kdError() << "unimplemented game type " << id << endl; + dill = DealerInfoList::self()->games().first()->createGame(this); + } + + connect(dill, SIGNAL(undoPossible(bool)), SLOT(undoPossible(bool))); + connect(dill, SIGNAL(gameWon(bool)), SLOT(gameWon(bool))); + connect(dill, SIGNAL(gameLost()), SLOT(gameLost())); + + dill->setAutoDropEnabled(dropaction->isChecked()); + + // it's a bit tricky - we have to do this here as the + // base class constructor runs before the derived class's + dill->takeState(); + + setGameCaption(); + + KConfig *config = kapp->config(); + KConfigGroupSaver kcs(config, settings_group); + config->writeEntry("DefaultGame", id); + + QSize min(700,400); + min = min.expandedTo(dill->minimumCardSize()); + dill->setMinimumSize(min); + dill->resize(min); + updateGeometry(); + setCentralWidget(dill); + dill->show(); +} + +void pWidget::showEvent(QShowEvent *e) +{ + if (dill) + dill->setMinimumSize(QSize(0,0)); + KMainWindow::showEvent(e); +} + +void pWidget::slotGameInfo(const QString &text) +{ + statusBar()->message(text, 3000); +} + +void pWidget::slotUpdateMoves() +{ + int moves = 0; + if ( dill ) moves = dill->getMoves(); + statusBar()->changeItem( i18n("1 move", "%n moves", moves), 1 ); +} + +void pWidget::setBackSide(const QString &deck, const QString &cards) +{ + KConfig *config = kapp->config(); + KConfigGroupSaver kcs(config, settings_group); + QPixmap pm(deck); + if(!pm.isNull()) { + cardMap::self()->setBackSide(pm, false); + config->writeEntry("Back", deck); + bool ret = cardMap::self()->setCardDir(cards); + if (!ret) { + config->writeEntry("Back", ""); + + } + config->writeEntry("Cards", cards); + cardMap::self()->setBackSide(pm, true); + } else + KMessageBox::sorry(this, + i18n("Could not load background image!")); + + if (dill) { + dill->canvas()->setAllChanged(); + dill->canvas()->update(); + } +} + +void pWidget::chooseGame() +{ + bool ok; + long number = KInputDialog::getText(i18n("Game Number"), i18n("Enter a game number (FreeCell deals are the same as in the FreeCell FAQ):"), QString::number(dill->gameNumber()), 0, this).toLong(&ok); + if (ok) { + dill->setGameNumber(number); + setGameCaption(); + restart(); + } +} + +void pWidget::gameWon(bool withhelp) +{ + QString congrats; + if (withhelp) + congrats = i18n("Congratulations! We have won!"); + else + congrats = i18n("Congratulations! You have won!"); +#if TEST_SOLVER == 0 + KMessageBox::information(this, congrats, i18n("Congratulations!")); +#endif + QTimer::singleShot(0, this, SLOT(newGame())); +#if TEST_SOLVER == 1 + dill->demo(); +#endif +} + +void pWidget::gameLost() +{ + QString dontAskAgainName = "gameLostDontAskAgain"; + + // The following code is taken out of kmessagebox.cpp in kdeui. + // Is there a better way? + KConfig *config = 0; + QString grpNotifMsgs = QString::fromLatin1("Notification Messages"); + + config = KGlobal::config(); + KConfigGroupSaver saver(config, + QString::fromLatin1("Notification Messages")); + QString dontAsk = config->readEntry(dontAskAgainName).lower(); + + // If we are ordered never to ask again and to continue the game, + // then do so. + if (dontAsk == "no") + return; + // If it says yes, we ask anyway. Just starting a new game would + // be incredibly annoying. + if (dontAsk == "yes") + dontAskAgainName = QString::null; + + if (KMessageBox::questionYesNo(this, i18n("You could not win this game, " + "but there is always a second try.\nStart a new game?"), + i18n("Could Not Win!"), + i18n("New Game"), + KStdGuiItem::cont(), + dontAskAgainName) == KMessageBox::Yes) { + + QTimer::singleShot(0, this, SLOT(newGame())); + } +} + +void pWidget::openGame(const KURL &url) +{ + QString tmpFile; + if( KIO::NetAccess::download( url, tmpFile, this ) ) + { + QFile of(tmpFile); + of.open(IO_ReadOnly); + QDomDocument doc; + QString error; + if (!doc.setContent(&of, &error)) + { + KMessageBox::sorry(this, error); + return; + } + uint id = doc.documentElement().attribute("id").toUInt(); + + if (id != (Q_UINT32)games->currentItem()) { + games->setCurrentItem(id); + newGameType(); + if (!dill) { + KMessageBox::error(this, i18n("The saved game is of unknown type!")); + games->setCurrentItem(0); + newGameType(); + } + } + dill->openGame(doc); + setGameCaption(); + KIO::NetAccess::removeTempFile( tmpFile ); + recent->addURL(url); + recent->saveEntries(KGlobal::config()); + } +} + +void pWidget::openGame() +{ + KURL url = KFileDialog::getOpenURL(); + openGame(url); +} + +void pWidget::saveGame() +{ + KURL url = KFileDialog::getSaveURL(); + KTempFile file; + QDomDocument doc("kpat"); + dill->saveGame(doc); + QTextStream *stream = file.textStream(); + *stream << doc.toString(); + file.close(); + KIO::NetAccess::upload(file.name(), url, this); + recent->addURL(url); + recent->saveEntries(KGlobal::config()); +} + +void pWidget::showStats() +{ + GameStatsImpl* dlg = new GameStatsImpl(this,"statistics dialog"); + if (dill) + dlg->showGameType(dill->gameId()); + dlg->exec(); +} + +#include "pwidget.moc" + diff --git a/kpat/pwidget.h b/kpat/pwidget.h new file mode 100644 index 00000000..8781960f --- /dev/null +++ b/kpat/pwidget.h @@ -0,0 +1,90 @@ +/* -*- C++ -*- + * + * patience -- main program + * Copyright (C) 1995 Paul Olav Tvete + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + * + * + * Heavily modified by Mario Weilguni + * + */ + +#ifndef __PWIDGET__H__ +#define __PWIDGET__H__ + +#include + +class Dealer; +class KToggleAction; +class KSelectAction; +class KRecentFilesAction; +class KAction; +class QWidgetStack; +class QLabel; + +class pWidget: public KMainWindow { + Q_OBJECT + +public: + pWidget(); + ~pWidget(); + +public slots: + void undoMove(); + void changeBackside(); + void animationChanged(); + void newGameType(); + void restart(); + + void openGame(); + void openGame(const KURL &url); + void saveGame(); + + void newGame(); + void chooseGame(); + void undoPossible(bool poss); + void gameWon(bool withhelp); + void gameLost(); + void changeWallpaper(); + void slotGameInfo(const QString &); + void slotUpdateMoves(); + void helpGame(); + void enableAutoDrop(); + void showStats(); + +private: + void setGameCaption(); + void setBackSide(const QString &deck, const QString &dir); + virtual void showEvent(QShowEvent *e); + +private: + // Members + + Dealer *dill; // The current patience + + KSelectAction *games; + KSelectAction *wallpapers; + KAction *backs; + KAction *undo; + KToggleAction *animation; + KToggleAction *dropaction; + KAction *stats; + + QPixmap background; + QColor midcolor; + QStringList wallpaperlist; + KRecentFilesAction *recent; +}; + +#endif diff --git a/kpat/simon.cpp b/kpat/simon.cpp new file mode 100644 index 00000000..287ecd00 --- /dev/null +++ b/kpat/simon.cpp @@ -0,0 +1,156 @@ +#include "simon.h" +#include +#include +#include "deck.h" +#include +#include "cardmaps.h" + +Simon::Simon( KMainWindow* parent, const char *name ) + : Dealer( parent, name ) +{ + deck = Deck::new_deck(this); + deck->move(10, 10); + deck->hide(); + + const int dist_x = cardMap::CARDX() * 11 / 10 + 1; + + for (int i=0; i<4; i++) { + target[i] = new Pile(i+1, this); + target[i]->move(10+(i+3)*dist_x, 10); + target[i]->setRemoveFlags(Pile::disallow); + target[i]->setAddFlags(Pile::several); + target[i]->setCheckIndex(0); + target[i]->setTarget(true); + } + + for (int i=0; i<10; i++) { + store[i] = new Pile(5+i, this); + store[i]->move(15+dist_x*i, 10 + cardMap::CARDY() * 73 / 50); + store[i]->setAddFlags(Pile::addSpread | Pile::several); + store[i]->setRemoveFlags(Pile::several); + store[i]->setCheckIndex(1); + } + + setActions(Dealer::Hint | Dealer::Demo); +} + +void Simon::restart() { + deck->collectAndShuffle(); + deal(); +} + +void Simon::deal() { + int piles = 3; + + for (int round = 0; round < 8; round++) + { + for (int j = 0; j < piles; j++) + { + store[j]->add(deck->nextCard(), false, true); + } + piles++; + } + assert(deck->isEmpty()); +} + +bool Simon::checkPrefering( int checkIndex, const Pile *c1, const CardList& c2) const +{ + if (checkIndex == 1) { + if (c1->isEmpty()) + return false; + + return (c1->top()->suit() == c2.first()->suit()); + } else return false; // it's just important to keep this unique +} + +bool Simon::checkAdd( int checkIndex, const Pile *c1, const CardList& c2) const +{ + if (checkIndex == 1) { + if (c1->isEmpty()) + return true; + + return (c1->top()->rank() == c2.first()->rank() + 1); + } else { + if (!c1->isEmpty()) + return false; + return (c2.first()->rank() == Card::King && c2.last()->rank() == Card::Ace); + } +} + +bool Simon::checkRemove(int checkIndex, const Pile *p, const Card *c) const +{ + if (checkIndex != 1) + return false; + + // ok if just one card + if (c == p->top()) + return true; + + // Now we're trying to move two or more cards. + + // First, let's check if the column is in valid + // (that is, in sequence, alternated colors). + int index = p->indexOf(c) + 1; + const Card *before = c; + while (true) + { + c = p->at(index++); + + if (!((c->rank() == (before->rank()-1)) + && (c->suit() == before->suit()))) + { + return false; + } + if (c == p->top()) + return true; + before = c; + } + + return true; +} + +bool Simon::isGameLost() const +{ + kdDebug(11111) <<"isGameLost" << endl; + for (int i=0; i<10; i++) { + if(store[i]->isEmpty()) + return false; + kdDebug(11111) <<"store["<top(); + int indexi=store[i]->indexOf(top); + while(--indexi >=0){ + kdDebug(11111) <name() << endl; + c=store[i]->at(indexi); + if(c->suit() == top->suit() && + (top->rank()+1) == c->rank()) + top=c; + else + break; + } + + kdDebug(11111) <<"selected: " << top->name() << endl; + for(int j=1; j <10; j++){ + int k=(i+j) % 10; + + if(store[k]->isEmpty()) + return false; + + kdDebug(11111) <<"vs "<top()->name() << endl; + if((top->rank() +1) == store[k]->top()->rank()) + return false; + } + } + + return true; +} + +static class LocalDealerInfo9 : public DealerInfo +{ +public: + LocalDealerInfo9() : DealerInfo(I18N_NOOP("&Simple Simon"), 9) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Simon(parent); } +} gfi9; + +#include "simon.moc" diff --git a/kpat/simon.h b/kpat/simon.h new file mode 100644 index 00000000..91a50a1b --- /dev/null +++ b/kpat/simon.h @@ -0,0 +1,29 @@ +#ifndef SIMON_H +#define SIMON_H + +#include "dealer.h" + +class Simon : public Dealer { + Q_OBJECT + +public: + Simon( KMainWindow* parent=0, const char* name=0); + +public slots: + void deal(); + virtual void restart(); + virtual bool isGameLost() const; + + +protected: + virtual bool checkAdd ( int checkIndex, const Pile *c1, const CardList& c2) const; + virtual bool checkPrefering( int checkIndex, const Pile *c1, const CardList& c2) const; + virtual bool checkRemove( int checkIndex, const Pile *c1, const Card *c) const; + +private: + Pile* store[10]; + Pile* target[4]; + Deck *deck; +}; + +#endif diff --git a/kpat/speeds.h b/kpat/speeds.h new file mode 100644 index 00000000..9f2c646d --- /dev/null +++ b/kpat/speeds.h @@ -0,0 +1,26 @@ +#ifndef __SPEEDS_H_ +#define __SPEEDS_H_ + +#define TEST_SOLVER 0 + +#ifdef TEST_SOLVER +#define STEPS_AUTODROP 8 +#define STEPS_WON 20 +#define STEPS_DEMO 7 +#define STEPS_MOVEBACK 7 +#define STEPS_INITIALDEAL 10 + +#define TIME_BETWEEN_MOVES 200 +#else + +#define STEPS_AUTODROP 1 +#define STEPS_WON 20 +#define STEPS_DEMO 1 +#define STEPS_MOVEBACK 1 +#define STEPS_INITIALDEAL 1 + +#define TIME_BETWEEN_MOVES 2 + +#endif + +#endif diff --git a/kpat/spider.cpp b/kpat/spider.cpp new file mode 100644 index 00000000..262c49b9 --- /dev/null +++ b/kpat/spider.cpp @@ -0,0 +1,484 @@ +/*--------------------------------------------------------------------------- + + spider.cpp implements a patience card game + + Copyright (C) 2003 Josh Metzler + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +---------------------------------------------------------------------------*/ + +#include "spider.h" +#include "cardmaps.h" +#include +#include "deck.h" +#include + +void SpiderPile::moveCards(CardList &c, Pile *to) +{ + Pile::moveCards(c, to); + + // if this is a leg pile, don't do anything special + if ( to->checkIndex() == 0 ) + return; + + // if the top card of the list I just moved is an Ace, + // the run I just moved is the same suit as the pile, + // and the destination pile now has more than 12 cards, + // then it could have a full deck that needs removed. + if (c.last()->rank() == Card::Ace && + c.first()->suit() == to->top()->suit() && + to->cardsLeft() > 12) { + Spider *b = dynamic_cast(dealer()); + if (b) { + b->checkPileDeck(to); + } + } +} + +//-------------------------------------------------------------------------// + +Spider::Spider(int suits, KMainWindow* parent, const char* _name) + : Dealer(parent, _name) +{ + const int dist_x = cardMap::CARDX() * 11 / 10 + 1; + const int dist_y = cardMap::CARDY() * 11 / 10 + 1; + + deck = Deck::new_deck(this, 2, suits); + + // I deal the cards into 'redeal' piles, so hide the deck + deck->setVisible(false); + + // Dealing the cards out into 5 piles so the user can see how many + // sets of 10 cards are left to be dealt out + for( int column = 0; column < 5; column++ ) { + redeals[column] = new Pile(column + 1, this); + redeals[column]->move(8 + dist_x / 3 * (23 + column), 8 + dist_y * 4.5); + redeals[column]->setZ(5-column); + redeals[column]->setCheckIndex(0); + redeals[column]->setAddFlags(Pile::disallow); + redeals[column]->setRemoveFlags(Pile::disallow); + connect(redeals[column], SIGNAL(clicked(Card*)), SLOT(deckClicked(Card*))); + } + + // The 10 playing piles + for( int column = 0; column < 10; column++ ) { + stack[column] = new SpiderPile(column + 6, this); + stack[column]->move(8 + dist_x * column, 8); + stack[column]->setZ(20); + stack[column]->setCheckIndex(1); + stack[column]->setAddFlags(Pile::addSpread | Pile::several); + stack[column]->setRemoveFlags(Pile::several | + Pile::autoTurnTop | Pile::wholeColumn); + } + + // The 8 'legs' so named by me because spiders have 8 legs - why + // else the name Spider? + for( int column = 0; column < 8; column++ ) { + legs[column] = new Pile(column + 16, this); + legs[column]->move(8 + dist_x / 3 * column, 8 + dist_y * 4.5); + legs[column]->setZ(column+1); + legs[column]->setCheckIndex(0); + legs[column]->setAddFlags(Pile::disallow); + legs[column]->setRemoveFlags(Pile::disallow); + legs[column]->setTarget(true); + } + + // Moving an A-K run to a leg is not really an autoDrop - the + // user should have no choice. Also, it must be moved A first, ... + // up to K so the King will be on top. + setAutoDropEnabled(false); + setActions(Dealer::Hint | Dealer::Demo ); +} + +//-------------------------------------------------------------------------// + +bool Spider::checkAdd(int /*checkIndex*/, const Pile *c1, const CardList& c2) const +{ + // assuming the cardlist is a valid unit, since I allowed + // it to be removed - can drop any card on empty pile or + // on any suit card of one higher rank + if (c1->isEmpty() || c1->top()->rank() == c2.first()->rank()+1) + return true; + + return false; +} + +bool Spider::checkRemove(int /*checkIndex*/, const Pile *p, const Card *c) const +{ + // if the pile from c up is decreasing by 1 and all the same suit, ok + // note that this is true if c is the top card + const Card *before; + int index = p->indexOf(c); + while (c != p->top()) { + before = c; + c = p->at(++index); + if (before->suit() != c->suit() || before->rank() != c->rank()+1) + return false; + } + return true; +} + +void Spider::getHints() +{ + kdDebug(11111) << "get hints" << endl; + // first, get runs from each stack + CardList cl[10]; + + Pile* empty = NULL; + for (int column = 0; column < 10; column++) { + if (stack[column]->isEmpty()) + empty = stack[column]; + else + cl[column] = getRun(stack[column]->top()); + } + + // if I can build a run from Ace->King in one suit then + // hint those moves + HintList hl; + for (int s = Card::Clubs; s <= Card::Spades; s++) { + bool bGrowing = true; + int vTopNew = 0; + int colNew = -1; + while (bGrowing && vTopNew < 13) { + bGrowing = false; + int col = colNew; + int vTop = vTopNew; + for (int column = 0; column < 10; column++) { + if (cl[column].isEmpty() || col == column) + continue; + if (cl[column].last()->suit() == s && + cl[column].last()->rank() <= vTop+1 && + cl[column].first()->rank() > vTop) + { + bGrowing = true; + if (cl[column].first()->rank() > vTopNew) { + colNew = column; + vTopNew = cl[column].first()->rank(); + } + } + } + if (bGrowing && vTop) + hl.append(new MoveHint(cl[col][vTop- + cl[colNew].last()->rank()+1], stack[colNew])); + } + if (vTopNew == 13) + hints += hl; + else + for (HintList::Iterator it = hl.begin(); it != hl.end(); ++it) + delete *it; + hl.clear(); + } + + // now check to see if a run from one column can go on the end + // of a run from another stack + for (int column = 0; column < 10; column++) { + if (cl[column].isEmpty()) + continue; + + // if there is an empty column and this stack is on + // another card, hint + if (empty && cl[column].count() < (uint)stack[column]->cardsLeft()) { + newHint(new MoveHint(cl[column].first(), empty)); + continue; + } + + // now see if I can move this stack to any other column + for (int c2 = 0; c2 < 10; c2++) { + if (c2 == column || cl[c2].isEmpty()) + continue; + + if (cl[c2].last()->rank() == cl[column].first()->rank()+1) + { + // I can hint this move - should I? + int index = stack[column]->indexOf(cl[column].first()); + + // if target pile is the same suit as this card, + // or if there are no cards under this one, + // or if it couldn't move to where it is now, + // or if the card under this one is face down, hint + if (cl[c2].last()->suit() == cl[column].first()->suit() || + index == 0 || stack[column]->at(index-1)->rank() != + cl[column].first()->rank()+1 || + !(stack[column]->at(index-1)->realFace())) + newHint(new MoveHint(cl[column].first(), stack[c2])); + } + } + } +} + +MoveHint *Spider::chooseHint() +{ + kdDebug(11111) << "choose 1 of " << hints.count() << " hints" << endl; + if (hints.isEmpty()) + return 0; + + // first, choose a card that is moving to the same suit + for (HintList::ConstIterator it = hints.begin(); it != hints.end(); ++it) + { + if (!(*it)->pile()->isEmpty() && + (*it)->pile()->top()->suit() == (*it)->card()->suit()) + return *it; + } + + // second, choose a card that is moving from the base + for (HintList::ConstIterator it = hints.begin(); it != hints.end(); ++it) + { + if ((*it)->card()->source() && + (*it)->card()->source()->at(0) == (*it)->card()) + return *it; + } + + // otherwise, go with a random hint + return hints[randseq.getLong(hints.count())]; +} + +//-------------------------------------------------------------------------// + +QString Spider::getGameState() const +{ + return QString::number(m_leg*10 + m_redeal); +} + +void Spider::setGameState(const QString &stream) +{ + int i = stream.toInt(); + + if (m_leg > i/10) { + for (m_leg--; m_leg > i/10; m_leg--) + legs[m_leg]->setVisible(false); + legs[m_leg]->setVisible(false); + } else + for (; m_leg < i/10; m_leg++) + legs[m_leg]->setVisible(true); + + if (m_redeal > i%10) { + for (m_redeal--; m_redeal > i%10; m_redeal--) + redeals[m_redeal]->setVisible(true); + redeals[m_redeal]->setVisible(true); + } else + for (; m_redeal < i%10; m_redeal++) + redeals[m_redeal]->setVisible(false); +} + +//-------------------------------------------------------------------------// + +void Spider::restart() +{ + deck->collectAndShuffle(); + deal(); +} + +//-------------------------------------------------------------------------// + +CardList Spider::getRun(Card *c) const +{ + CardList result; + + Pile *p = c->source(); + if (!p || p->isEmpty()) + return result; + + result.append(c); + + Card::Suit s = c->suit(); + int v = c->rank(); + + int index = p->indexOf(c); + c = p->at(--index); + while (index >= 0 && c->realFace() && + c->suit() == s && c->rank() == ++v) + { + result.prepend(c); + c = p->at(--index); + } + + return result; +} + +void Spider::checkPileDeck(Pile *check) +{ + kdDebug(11111) << "check for run" << endl; + if (check->isEmpty()) + return; + + if (check->top()->rank() == Card::Ace) { + // just using the CardList to see if this goes to King + CardList run = getRun(check->top()); + if (run.first()->rank() == Card::King) { + legs[m_leg]->setVisible(true); + + // remove this full deck from this pile + CardList cl; + for (int i = 0; i < 13; i++ ) { + cl.append(check->cards().last()); + check->moveCards(cl, legs[m_leg]); + cl.clear(); + } + m_leg++; + } + } +} + +void Spider::dealRow() +{ + if (m_redeal > 4) + return; + + for (int column = 0; column < 10; column++) { + stack[column]->add(redeals[m_redeal]->top(), false, true); + + // I may put an Ace on a K->2 pile so it could need cleared. + if (stack[column]->top()->rank() == Card::Ace) + checkPileDeck(stack[column]); + } + + redeals[m_redeal++]->setVisible(false); +} + +//-------------------------------------------------------------------------// + +void Spider::deal() +{ + unmarkAll(); + + m_leg = 0; + m_redeal = 0; + + int column = 0; + // deal face down cards (5 to first 4 piles, 4 to last 6) + for (int i = 0; i < 44; i++ ) { + stack[column]->add(deck->nextCard(), true, true); + column = (column + 1) % 10; + } + // deal face up cards, one to each pile + for (int i = 0; i < 10; i++ ) { + stack[column]->add(deck->nextCard(), false, true); + column = (column + 1) % 10; + } + // deal the remaining cards into 5 'redeal' piles + for (int column = 0; column < 5; column++ ) + for (int i = 0; i < 10; i++ ) + redeals[column]->add(deck->nextCard(), true, false); + + // make the leg piles invisible + for (int i = 0; i < 8; i++ ) + legs[i]->setVisible(false); + // make the redeal piles visible + for (int i = 0; i < 5; i++ ) + redeals[i]->setVisible(true); +} + +Card *Spider::demoNewCards() +{ + if (m_leg > 4) + return 0; + deckClicked(0); + return stack[0]->top(); +} + +void Spider::deckClicked(Card*) +{ + kdDebug(11111) << "deck clicked " << m_redeal << endl; + if (m_redeal > 4) + return; + + unmarkAll(); + dealRow(); + takeState(); +} + +bool Spider::isGameLost() const +{ + kdDebug(11111) << "isGameLost ?"<< endl; + + // if there are still cards to deal out, you have not lost + if (m_redeal < 5) + return false; + + // first, get runs from each stack - returning if empty + CardList cl[10]; + + for (int column = 0; column < 10; column++) { + if (stack[column]->isEmpty()) + return false; + cl[column] = getRun(stack[column]->top()); + } + + // from this point on, I know that none of the columns is empty + // now check to see if a run from one column can go on the end + // of a run from another stack + for (int column = 0; column < 10; column++) + for (int c2 = 0; c2 < 10; c2++) { + if (c2 == column) + continue; + + // if I can move this run to another pile, I'm not done + if (cl[c2].last()->rank() == cl[column].first()->rank()+1) + return false; + } + + // if you can build a run from Ace->King in one suit then + // you can clear it and keep playing + for (int s = Card::Clubs; s <= Card::Spades; s++) { + bool bGrowing = true; + int vTop = 0; + while (bGrowing && vTop < 13) { + bGrowing = false; + int column = 0; + while (column < 10 && !bGrowing) { + if (cl[column].last()->suit() == s && + cl[column].last()->rank() <= vTop+1 && + cl[column].first()->rank() > vTop) + { + bGrowing = true; + vTop = cl[column].first()->rank(); + } + column++; + } + } + // if you can build such a pile, you can continue + if (vTop == 13) + return false; + } + + return true; +} + +static class LocalDealerInfo15 : public DealerInfo +{ +public: + LocalDealerInfo15() : DealerInfo(I18N_NOOP("S&pider (Easy)"), 14) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Spider(1, parent); } +} ldi15; + +static class LocalDealerInfo16 : public DealerInfo +{ +public: + LocalDealerInfo16() : DealerInfo(I18N_NOOP("Spider (&Medium)"), 15) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Spider(2, parent); } +} ldi16; + +static class LocalDealerInfo17 : public DealerInfo +{ +public: + LocalDealerInfo17() : DealerInfo(I18N_NOOP("Spider (&Hard)"), 16) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Spider(4, parent); } +} ldi17; + +//-------------------------------------------------------------------------// + +#include "spider.moc" + +//-------------------------------------------------------------------------// + diff --git a/kpat/spider.h b/kpat/spider.h new file mode 100644 index 00000000..ea2cc165 --- /dev/null +++ b/kpat/spider.h @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------- + + spider.cpp implements a patience card game + + Copyright (C) 2003 Josh Metzler + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +---------------------------------------------------------------------------*/ + +#ifndef _SPIDER_H_ +#define _SPIDER_H_ + +#include "dealer.h" + +class SpiderPile : public Pile +{ +public: + SpiderPile(int _index, Dealer* parent = 0) : Pile(_index, parent) {} + virtual void moveCards(CardList &c, Pile *to); + CardList getRun(); +}; + +class Spider : public Dealer +{ + Q_OBJECT + +public: + Spider(int suits, KMainWindow *parent=0, const char *name=0); + void deal(); + void dealRow(); + void checkPileDeck(Pile *to); + virtual void restart(); + virtual bool isGameLost() const; + +public slots: + void deckClicked(Card *c); + +protected: + virtual bool checkRemove(int /*checkIndex*/, const Pile *p, const Card *c) const; + virtual bool checkAdd(int /*checkIndex*/, const Pile *c1, const CardList &c2) const; + virtual QString getGameState() const; + virtual void setGameState(const QString &stream); + virtual void getHints(); + virtual MoveHint *chooseHint(); + virtual Card *demoNewCards(); + +private: + CardList getRun(Card *c) const; + + SpiderPile *stack[10]; + Pile *legs[8]; + int m_leg; + Pile *redeals[5]; + int m_redeal; + Deck *deck; +}; + +#endif + +//-------------------------------------------------------------------------// diff --git a/kpat/version.h b/kpat/version.h new file mode 100644 index 00000000..b5eae065 --- /dev/null +++ b/kpat/version.h @@ -0,0 +1,6 @@ +#ifndef KPAT_VERSION +#define KPAT_VERSION "2.2.2" +#endif + +#define settings_group "General Settings" +#define scores_group "Scores" diff --git a/kpat/yukon.cpp b/kpat/yukon.cpp new file mode 100644 index 00000000..859b3746 --- /dev/null +++ b/kpat/yukon.cpp @@ -0,0 +1,130 @@ +#include "yukon.h" +#include +#include +#include "deck.h" +#include +#include "cardmaps.h" + +Yukon::Yukon( KMainWindow* parent, const char *name ) + : Dealer( parent, name ) +{ + const int dist_x = cardMap::CARDX() * 11 / 10 + 1; + const int dist_y = cardMap::CARDY() * 11 / 10 + 1; + + deck = Deck::new_deck(this); + deck->move(10, 10+dist_y*3); + deck->hide(); + + for (int i=0; i<4; i++) { + target[i] = new Pile(i+1, this); + target[i]->move(20+7*dist_x, 10+dist_y *i); + target[i]->setType(Pile::KlondikeTarget); + } + + for (int i=0; i<7; i++) { + store[i] = new Pile(5+i, this); + store[i]->move(15+dist_x*i, 10); + store[i]->setAddType(Pile::KlondikeStore); + store[i]->setRemoveFlags(Pile::several | Pile::autoTurnTop); + } + + setActions(Dealer::Hint | Dealer::Demo); +} + +void Yukon::restart() { + deck->collectAndShuffle(); + deal(); +} + +void Yukon::deal() { + for (int round = 0; round < 11; round++) + { + for (int j = 0; j < 7; j++) + { + bool doit = false; + switch (j) { + case 0: + doit = (round == 0); + break; + default: + doit = (round < j + 5); + } + if (doit) + store[j]->add(deck->nextCard(), round < j && j != 0, true); + } + } +} + +bool Yukon::isGameLost() const { + int i,j,k,l,indexi,freeStore=0; + Card *c, *cNewTop; + + kdDebug(11111) <<"isGameLost" << endl; + + for(i=0; i < 7; i++){ + if( store[i]->isEmpty() ){ + freeStore++; + continue; + } + + if(store[i]->top()->rank() == Card::Ace || + ! store[i]->top()->isFaceUp()) + return false; + + for(indexi=store[i]->indexOf(store[i]->top()); indexi >=0; indexi--){ + + c=store[i]->at(indexi); + if( !c->isFaceUp() ) + break; + + if(freeStore > 0 && indexi > 0 && c->rank() == Card::King) + return false; + + for(j=0; j < 4;j++){ + if(!target[j]->isEmpty() && + c->rank()-1 == target[j]->top()->rank() && + c->suit() == target[j]->top()->suit()) + return false; + } + + for(j=1; j < 7; j++){ + k=(i+j) % 7; + if( !store[k]->isEmpty() ) { + if(c->rank()+1 == store[k]->top()->rank() && + (c->isRed() != store[k]->top()->isRed())){ + + if(indexi == 0) + return false; + else{ + cNewTop=store[i]->at(indexi-1); + if(!cNewTop->isFaceUp()) + return false; + if(cNewTop->rank() == Card::Ace) + return false; + if(cNewTop->rank() != store[k]->top()->rank() || + cNewTop->isRed() != store[k]->top()->isRed()) + return false; + + for(l=0; l < 4;l++){ + if(!target[l]->isEmpty() && + cNewTop->rank()-1 == target[l]->top()->rank() && + cNewTop->suit() == target[l]->top()->suit()) + return false; + } + } + } + } + } + } + } + return (freeStore!=7); +} + +static class LocalDealerInfo10 : public DealerInfo +{ +public: + LocalDealerInfo10() : DealerInfo(I18N_NOOP("&Yukon"), 10) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Yukon(parent); } +} gfi10; + +#include "yukon.moc" diff --git a/kpat/yukon.h b/kpat/yukon.h new file mode 100644 index 00000000..9b407ac3 --- /dev/null +++ b/kpat/yukon.h @@ -0,0 +1,23 @@ +#ifndef YUKON_H +#define YUKON_H + +#include "dealer.h" + +class Yukon : public Dealer { + Q_OBJECT + +public: + Yukon( KMainWindow* parent=0, const char* name=0); + virtual bool isGameLost() const; + +public slots: + void deal(); + virtual void restart(); + +private: + Pile* store[7]; + Pile* target[4]; + Deck *deck; +}; + +#endif diff --git a/kpoker/AUTHORS b/kpoker/AUTHORS new file mode 100644 index 00000000..866b0428 --- /dev/null +++ b/kpoker/AUTHORS @@ -0,0 +1,11 @@ +Codeing: +Jochen Tuchbreiter +Mario Weilguni +Andreas Beckermann +Inge Wallin + +Helping persons: +Chris Holmes - idea of writing this game and parts of the visual appearence +John Fitzgibbon - provided the card images +Nico Schirwing - drew the backsides of the cards + diff --git a/kpoker/ChangeLog b/kpoker/ChangeLog new file mode 100644 index 00000000..f38991bf --- /dev/null +++ b/kpoker/ChangeLog @@ -0,0 +1,306 @@ +2005-09-15 Inge Wallin + + Bump version number for release of KDE 3.5 + * version.h (KPOKER_RELEASE_DATE): Bump version from 1.0 to 1.1 + +2005-02-18 Inge Wallin + + * version.h (KPOKER_VERSION): Bumped version to 1.0 for KDE 3.4. + +2005-01-26 Inge Wallin + + ----------------- CVS commit on stuff below -------------------- + + Code cleaning + * Make all blink parameters follow the m_ convention. + +2005-01-18 + + Continue the code cleaning. + * kpoker.h (PokerGameType): new type + * (PokerGame): All public members now private. + * Make the player array owned by kpok instead of PokerGame. + + ----------------- CVS commit on stuff below -------------------- + + Continue making more members of PokerGame private. + * kpoker.h (PokerGame::m_activePlayers): now private + + ----------------- CVS commit on stuff below -------------------- + + * Bump version to 0.8alpha in anticipation of KDE 3.4. + + Make members of PokerGame private. + * kpoker.cpp (Pokergame::newGame): new method + (PokerGame::m_minBet and m_maxBet): now private + +2004-11-20 Inge Wallin + + Fix bug 93636: When "Fold" is pressed, the current round should be + ended immediately. + * kpoker.cpp (out): Call drawClick(), i.e. go to next game state, + instead of setting up the drawButton to let the user do it. + + ----------------- CVS commit on stuff below -------------------- + + Fix bug 93635: The status bar is wrong in many places. + * top.cpp (PokerWindow): connect the signal to clear the status + bar to the proper slot. + +2004-11-03 Inge Wallin + + Rename the game states into something more logical + * kpoker.{h,cpp}: Renaming of poker game states + + Exchange the faulty term "mix" for the correct one "shuffle". + * poker.{h,cpp}: mix() -> shuffle() + * misc files: call shuffle() instead. + +2004-09-12 Inge Wallin + + Fix bug 88584: Use proper poker terminology: + * kpoker.cpp (kpok::misc): Set text on "Draw" button according to + game state + + Code cleaning: + * kpoker.{h,cpp} (kpok): remove member newGameDlg + * kpoker.{h,cpp} (PokerGameState): StateDraw -> StateStartRound + * kpoker.{h,cpp} (PokerGameState): uppercase state names. + * misc: added some comments + + ----------------- CVS commit on stuff below -------------------- + + Continue separation of class kpok into model/view + * kpoker.h (PokerGame): make m_deck, m_state, m_pot, m_isDirty + private members and create accessor methods for them. + * kpoker.cpp, kpoker.cpp: Use the accessor methods. + * kpoker.{h,cpp} (PokerGame::newRound): new method + +2004-09-11 Inge Wallin + + Introduced a class CardDeck + * poker.{h,cpp} (CardDeck): new class + * kpoker.{h,cpp} (PokerGame): new class + * kpoker.{h,cpp} (kpok::done): removed + + Started separation of kpok into a poker game class and a view: + * kpoker.{h,cpp} (PokerGame): new class + + Some cleanup + * player.{h,cpp}: Renamed class Player into PokerPlayer + * kpoker.{h,cpp}: references to Player -> PokerPlayer + + ----------------- CVS commit on stuff below -------------------- + + Remove a lot of unused methods and other cleanup + * poker.{h,cpp} (cleanFoundCards()): make protected + * player.h (Player::getCard()): don't convert to int. + * player.h (Player::cleanFoundCard()): remove + * playerbox.{h,cpp}: Prefix all members with m_. + (PlayerBox::getCashLabel{X,Y}): remove + + ----------------- CVS commit on stuff below -------------------- + + Break out all poker and card stuff into its own file + * poker.{h,cpp}: New files. + * player.{h,cpp}(card/poker stuff): moved to poker.{h,cpp} + + ----------------- CVS commit on stuff below -------------------- + + Remove all debug traces: + * player.h(Player::takeCard()) + * player.cpp(PokerHand::operator<()) + * kpoker.cpp(kpok::winner()) + + Move loading of card images to class CardImages: + * kpaint.{h,cpp}(CardImages::loadCards(),CardImages::loadDeck()): new methods. + * kpoker.{h,cpp}(loadCards,loadDeck): removed + + Remove somestuff.cpp: + * somestuff.cpp: removed + * kpok::initWindow(): moved to kpoker.cpp + * Makefile.am(kpoker_SOURCES): remove somestuff.cpp + + ----------------- CVS commit on stuff below -------------------- + + Use the constants from the card classes. + * defines.h: Removed highestCard and CARDS + * player.h(numCards): new constant + * kpaint.cpp, kpoker.cpp, player.cpp, playerbox.cpp, + somestuff.cpp: Use numCards and PokerHandSize instead + + Rename initSomeStuff() into initWindow() + * somestuff.cpp: Rename + * kpoker.cpp(kpok::kpok): Call initWindow() instead + + Make the card images private. + * kpaint.h, kpaint.cpp(CardImages): make m_deck and m_cardImages + private + * kpoker.cpp (loadCards, loadDeck): Call the loading of cards + differently + +2004-09-05 Inge Wallin + + Start structural cleanup + * kpoker.cpp (winner): Move from testers.cpp to kpoker.cpp. + * testers.cpp: deleted + +2004-09-03 Inge Wallin + + Fix bug 88548: Kpoker: When you save a game and then immediately + quit, kpoker asks if you want to save the game anyway + * kpoker::isDirty: New member + * misc: set and use isDirty + +2004-09-01 Inge Wallin + + * Removed the old code from the time before the switch of the + poker evaluation code. This was all contained in #if 0 - #endif + pairs. + +2004-08-31 Inge Wallin + + Finish the great code cleanup. + * kpoker.h, kpoker.cpp (only visually, though. Structure remains + to be cleaned further.) + + * main.cpp (main): Fixed copyright notice in About dialog. + +2004-08-30 Inge Wallin + + Continue the great code cleanup. + * optionsdlg.h, optionsdlg.cpp + * newgamedlg.h, newgamedlg.cpp + +2004-08-29 Inge Wallin + + Continue the great code cleanup. + * sound.cpp + * top.h, top.cpp + * defines.h + * kpaint.h, kpaint.cpp + * playerbox.h, playerbox.cpp + * kpoker.h (Status): Renamed into PokerGameState + * betbox.h, betbox.cpp + * somestuff.cpp + * testers.cpp + * player.h, player.cpp + +2004-08-28 Inge Wallin + + Start of the great code cleanup + * global.h + * main.cpp + +2004-08-26 Inge Wallin + + Rewrote the whole evaluation of a poker hand. + * player.h, player.cpp (PokerHand): New class + * kpaint.cpp, kpoker.cpp, kpoker.h, playerbox.cpp, testers.cpp: + use the new code. + +2004-08-22 Inge Wallin + + Substituted the cardHelp array with a function Player::rank() + * player.h (cardHelp): removed + * player.cpp (initCardHelp): removed + * player.cpp (rank): new function + * kpoker.cpp (misc): don't call initCardHelp(). + + +---------------------------------------------------------------- +FIXME: The rest of this file should be named NEWS instead, since it is + only about releases. + +Version 0.7 (Andreas Beckermann ) + +- added computer player +- fixed some bugs, added many more +- added bet/raise +- added a QGroupBox for every player +- improved status +- added possibility to start the game without installing +- added an optionsdialog +- added further options which are not in the dialog +- moved clickToHold and LHLabel to the new statusbar +- started API documenting +- much more + +------------------------------------------------------------------------------ +Version 0.6 (Mario Weilguni ) +- made it work with new KDE-2 API +- made it work with --enable-final + +------------------------------------------------------------------------------ +Version 0.5 + +- fixed a problem with the recognition of straights (the combo ace-2-3-4-5 was + not recognized in all versions <0.4.1 + +- cleaned up the code a little + +- KPoker is now considered to be stable - I fixed all reported bugs and I did + not find any new ones :) + +------------------------------------------------------------------------------ +Version 0.4.1 + +Silly me deleted some important lines that were initializing a timer while +merging my version of 0.4 with the one in the CVS. This caused 0.4 to +segfault when you press "Draw Cards" :-( + +- [Robert William] put version.h back in. + +- integrated version.h so that there is only one #define for the version & + release date (and not multiple ones like before) + +- put epilogue into the documentation + +------------------------------------------------------------------------------ +Version 0.4 + +- deleted version.h because it is of no use +- put in that nice "wave" effect +- added sm-support +- some internal stuff (as usual :) + +------------------------------------------------------------------------------ +Version 0.3.1 + +- [Robert Williams] added getHelpMenu() +- [Robert Williams] added version.h +- changed minor look&feel stuff + +------------------------------------------------------------------------------ +Version 0.3 + +- kpoker got adapted to the new fsstnd (thanks Coolo !) +- kpoker now supports KLocale (only works in the kdegames-distribution) +- added german language file (kdegames distribution only) +- kpoker should now run on DEC Alphas (thanks Uwe Thiem :) + +------------------------------------------------------------------------------ +Version 0.2 + +- reduced number of colors needed by the images for the backsides of cards +- KPoker documentation is now written in .sgml format +- lots of changes in makefiles +- fixed that "#include " bug. +- sound support (experimental - *please* report problems) + +------------------------------------------------------------------------------ +Version 0.1.2 + +- made kpoker use KTopLevelWidget + the new menubars +- fixed silly problem with cardImage +- finished the helpfile +- built in new icon +- built in new backsides of cards (1000 thanks to our "icon/graphics Man") + +------------------------------------------------------------------------------ +Version 0.1.1 + +- made kpoker more "KDE conform" (like inserted separator between + help/help and help/about) +- fixed a few silly bugs/problems +- started to do the helpfile (halfway done right now) diff --git a/kpoker/DESCRIPTION b/kpoker/DESCRIPTION new file mode 100644 index 00000000..3f679e18 --- /dev/null +++ b/kpoker/DESCRIPTION @@ -0,0 +1,2 @@ +A little clone of those highly addictive, simple pocket poker games. + diff --git a/kpoker/Makefile.am b/kpoker/Makefile.am new file mode 100644 index 00000000..8c72a34f --- /dev/null +++ b/kpoker/Makefile.am @@ -0,0 +1,20 @@ +SUBDIRS = sounds + +INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes) +METASOURCES = AUTO +KDE_ICON = kpoker + +bin_PROGRAMS = kpoker +kpoker_SOURCES = kpoker.cpp kpaint.cpp top.cpp main.cpp betbox.cpp \ + optionsdlg.cpp player.cpp playerbox.cpp newgamedlg.cpp poker.cpp +kpoker_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kpoker_LDADD = $(LIB_KDEGAMES) +kpoker_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +xdg_apps_DATA = kpoker.desktop + +rcdir = $(kde_datadir)/kpoker +rc_DATA = kpokerui.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kpoker.pot diff --git a/kpoker/README b/kpoker/README new file mode 100644 index 00000000..923ece84 --- /dev/null +++ b/kpoker/README @@ -0,0 +1,20 @@ +This is version 0.8 (nearly) of KPoker a not that simple poker clone +for the K Desktop Environment . + +This version is considered to be pretty stable. Please take a look at +the file ChangeLog to see what was changed recently. + +Have fun with the game and report any problems / bugs suggestions. + + + Andreas Beckerman and Inge Wallin + + +Copyright (c) 1997-2000, by Jochen Tuchbreiter +This Program is distributed under the GPL. See the file COPYING for details + +This game is maintained by +Andreas Beckermann +Inge Wallin + + diff --git a/kpoker/TODO b/kpoker/TODO new file mode 100644 index 00000000..fb954169 --- /dev/null +++ b/kpoker/TODO @@ -0,0 +1,38 @@ + * Now + + + * Soon + + + * Further code cleanups: + + - Separate the kpok class into a game object that holds all the + players, what state the game is in, the pot, etc, and a view of + the same. + + This should result in a new class PokerGame and another one + PokerGameView. The class kpok (why the name abbreviation?) must + die. + + - Separate card blinking into the card widget. + + - Separate the waving motion into the winner box widget. + + - CardImages shouldn't inherit QWidget. + + - Make CardWidget::heldLabel private. + + * Betting figures are funny. The program indicates that we have bet + even before the "Draw" or "Fold" buttons are pressed. Then the bet + amount decreases again. The correct behaviour is to not add the + amount until the user OKs it, i.e. presses "See". + + Also, the "Adjust Bet" button doesn't look like it does anything as + it is now, although it actually does. + + * Rewrite the README file. It is totally out of date. + + * The documentation is totally out of sync with the program. + + * Hiscore table? + diff --git a/kpoker/betbox.cpp b/kpoker/betbox.cpp new file mode 100644 index 00000000..e152573f --- /dev/null +++ b/kpoker/betbox.cpp @@ -0,0 +1,136 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include + +#include +#include + +#include "betbox.h" + + +BetBox::BetBox(QWidget* parent, const char* name) + : QGroupBox(parent, name) +{ + QVBoxLayout* topLayout = new QVBoxLayout(this, 1, 1); + QGridLayout* g = new QGridLayout(topLayout, 2, 2, 1); + QHBoxLayout* l = new QHBoxLayout(topLayout, 1); + + bet5Up = new QPushButton(this); + g->addWidget(bet5Up, 0, 0); + bet10Up = new QPushButton(this); + g->addWidget(bet10Up, 0, 1); + bet5Down = new QPushButton(this); + g->addWidget(bet5Down, 1, 0); + bet10Down = new QPushButton(this); + g->addWidget(bet10Down, 1, 1); + + adjustBet = new QPushButton(this); + l->addWidget(adjustBet, 0); + l->addStretch(1); + foldButton = new QPushButton(this); + l->addWidget(foldButton, 0); + + bet5Up->setText(QString("+%1").arg(KGlobal::locale()->formatMoney(5))); + bet10Up->setText(QString("+%1").arg(KGlobal::locale()->formatMoney(10))); + bet5Down->setText(QString("-%1").arg(KGlobal::locale()->formatMoney(5))); + bet10Down->setText(QString("-%1").arg(KGlobal::locale()->formatMoney(10))); + adjustBet->setText(i18n("Adjust Bet")); + foldButton->setText(i18n("Fold")); + + //connects + connect(bet5Up, SIGNAL(clicked()), SLOT(bet5UpClicked())); + connect(bet10Up, SIGNAL(clicked()), SLOT(bet10UpClicked())); + connect(bet5Down, SIGNAL(clicked()), SLOT(bet5DownClicked())); + connect(bet10Down, SIGNAL(clicked()), SLOT(bet10DownClicked())); + connect(foldButton, SIGNAL(clicked()), SLOT(foldClicked())); + connect(adjustBet, SIGNAL(clicked()), SLOT(adjustBetClicked())); + + stopRaise(); +} + + +BetBox::~BetBox() +{ + delete bet5Up; + delete bet10Up; + delete bet5Down; + delete bet10Down; + delete adjustBet; + delete foldButton; +} + + +void BetBox::bet5UpClicked() +{ + emit betChanged(5); +} + +void BetBox::bet10UpClicked() +{ + emit betChanged(10); +} + +void BetBox::bet5DownClicked() +{ + emit betChanged(-5); +} + +void BetBox::bet10DownClicked() +{ + emit betChanged(-10); +} + + +void BetBox::adjustBetClicked() +{ + emit betAdjusted(); +} + + +void BetBox::foldClicked() +{ + emit fold(); +} + + +void BetBox::beginRaise() +{ + adjustBet->setEnabled(true); + foldButton->setEnabled(true); + + bet5Up->setEnabled(false); + bet10Up->setEnabled(false); + bet5Down->setEnabled(false); + bet10Down->setEnabled(false); +} + + +void BetBox::stopRaise() +{ + adjustBet->setEnabled(false); + foldButton->setEnabled(false); + + bet5Up->setEnabled(true); + bet10Up->setEnabled(true); + bet5Down->setEnabled(true); + bet10Down->setEnabled(true); +} + + +#include "betbox.moc" diff --git a/kpoker/betbox.h b/kpoker/betbox.h new file mode 100644 index 00000000..2aa54894 --- /dev/null +++ b/kpoker/betbox.h @@ -0,0 +1,114 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef BETBOX_H +#define BETBOX_H + +#include + +class QPushButton; + + +/** + * This class provides a QGroupBox with several button + * + * The bet up / down buttons are used to change the player bet directly, + * the adjustBet and out buttons depend on the computers bet + * @short This is a box with several buttons used to bet / raise + **/ +class BetBox : public QGroupBox +{ + Q_OBJECT + + public: + BetBox(QWidget* parent = 0, const char* name = 0); + ~BetBox(); + + + /** + * Disables the usual bet up/down buttons and shows the adjust bet and fold buttons + * + * Used when the compputer player raised the players bet / raise + **/ + void beginRaise(); + + /** + * Hides the Adjust bet / Fold buttons and enables the usual bet up / down buttons + **/ + void stopRaise(); + + signals: + /** + * This signal is emitted when the user clicks on a bet up / down button + * + * The Value of the Button is sent as a parameter (change) + **/ + void betChanged(int change); + + /** + * This signal is emitted when the user clicks on the adjust bet button + **/ + void betAdjusted(); + + /** + * This signal is emitted when the user clicks on the fold button + **/ + void fold(); + + protected slots: + /** + * Emits the signal @ref betChanged(5) + **/ + void bet5UpClicked(); + + /** + * Emits the signal @ref betChanged(10) + **/ + void bet10UpClicked(); + + /** + * Emits the signal @ref betChanged(-5) + **/ + void bet5DownClicked(); + + /** + * Emits the signal @ref betChanged(-10) + **/ + void bet10DownClicked(); + + /** + * Emits the signal @ref betAdjusted() + **/ + void adjustBetClicked(); + + /** + * Emits the signal @ref fold() + **/ + void foldClicked(); + + + private: + QPushButton *bet5Up; + QPushButton *bet10Up; + QPushButton *bet5Down; + QPushButton *bet10Down; + QPushButton *adjustBet; + QPushButton *foldButton; +}; + + +#endif diff --git a/kpoker/defines.h b/kpoker/defines.h new file mode 100644 index 00000000..3da071f6 --- /dev/null +++ b/kpoker/defines.h @@ -0,0 +1,75 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +/* Some defines for the outlook etc. */ +#ifndef DEFINES_H +#define DEFINES_H + + +// ---------------------------------------------------------------- +// Graphical layout + + +#define cardHeight 96 +#define cardWidth 72 + +#define cardDistFromTop 20 +#define cardDistFromBottom 10 +#define cardHDist 10 + +#define CLHBorderDistance 5 +#define CLHDistFromTop 180 +#define CLHWidth 140 + +// Some derived constants +#define PLAYERBOX_WIDTH ((cardWidth + cardHDist) * PokerHandSize + cardHDist + 135) +#define PLAYERBOX_HEIGHT (cardHeight + cardDistFromTop + cardDistFromBottom) +#define DISTANCE_FROM_2ND_BOX 100 + +#define wonLabelVDist (cardHeight + cardDistFromTop + cardDistFromBottom + 10) +#define clickToHoldVDist 130 + +#define BORDER 10 +#define PLAYERBOX_BORDERS 15 +#define PLAYERBOX_HDISTANCEOFWIDGETS 10 + + +#define MAX_PLAYERS 2 // TODO: maximal 4-> see lskat for computer players + + +// ---------------------------------------------------------------- +// Default values + + +#define START_MONEY 100 +#define DEFAULT_PLAYERS 1 // change to 2 later + +// Will be overridden by config file: +#define SOUND_DEFAULT true +#define BLINKING_DEFAULT true +#define SHOWNEWGAME_DEFAULT false +#define ADJUST_DEFAULT true +#define LOADGAME_DEFAULT true + +// changeable in OptionsDlg (and will be overridden by config file): +#define DRAWDELAY 300 +#define MIN_BET 5 +#define MAX_BET 20 +#define CASH_PER_ROUND 5 + + +#endif diff --git a/kpoker/global.h b/kpoker/global.h new file mode 100644 index 00000000..d4755abd --- /dev/null +++ b/kpoker/global.h @@ -0,0 +1,27 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __GLOBAL__H__ +#define __GLOBAL__H__ + +#include +#include + +extern KLocale *locale; +extern KConfig *conf; + +#endif + diff --git a/kpoker/hi128-app-kpoker.png b/kpoker/hi128-app-kpoker.png new file mode 100644 index 00000000..e2a5570a Binary files /dev/null and b/kpoker/hi128-app-kpoker.png differ diff --git a/kpoker/hi16-app-kpoker.png b/kpoker/hi16-app-kpoker.png new file mode 100644 index 00000000..38b3a8c0 Binary files /dev/null and b/kpoker/hi16-app-kpoker.png differ diff --git a/kpoker/hi22-app-kpoker.png b/kpoker/hi22-app-kpoker.png new file mode 100644 index 00000000..9e939a49 Binary files /dev/null and b/kpoker/hi22-app-kpoker.png differ diff --git a/kpoker/hi32-app-kpoker.png b/kpoker/hi32-app-kpoker.png new file mode 100644 index 00000000..409f4764 Binary files /dev/null and b/kpoker/hi32-app-kpoker.png differ diff --git a/kpoker/hi48-app-kpoker.png b/kpoker/hi48-app-kpoker.png new file mode 100644 index 00000000..097fc5fd Binary files /dev/null and b/kpoker/hi48-app-kpoker.png differ diff --git a/kpoker/hi64-app-kpoker.png b/kpoker/hi64-app-kpoker.png new file mode 100644 index 00000000..9ad09b3b Binary files /dev/null and b/kpoker/hi64-app-kpoker.png differ diff --git a/kpoker/kpaint.cpp b/kpoker/kpaint.cpp new file mode 100644 index 00000000..c6322aee --- /dev/null +++ b/kpoker/kpaint.cpp @@ -0,0 +1,209 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +// QT includes +#include +#include + +// KDE includes +//#include +#include +#include + +// own includes +#include "defines.h" +#include "poker.h" +#include "kpaint.h" + + +// ================================================================ +// class CardImages + + +QPixmap *CardImages::m_cardPixmaps; +QPixmap *CardImages::m_deck; + + +CardImages::CardImages(QWidget* parent, const char* name) + : QWidget(parent, name) +{ + m_cardPixmaps = new QPixmap[numCards]; + m_deck = new QPixmap; + + // Hide the window. + // FIXME: Why is this a QWidget? + hide(); + // loadCards(); +} + + +CardImages::~CardImages() +{ + delete[] m_cardPixmaps; + delete m_deck; +} + + +QPixmap * +CardImages::getCardImage(int card) const +{ + if (card == 0) + return m_deck; + else + return &m_cardPixmaps[card-1]; +} + + +// Load all the card images from the directory 'cardDir'. + +void +CardImages::loadCards(QString cardDir) +{ + for (int i = 0; i < numCards; i++) { + QString card = KCardDialog::getCardPath(cardDir, i + 1); + + if (card.isEmpty() || !m_cardPixmaps[i].load(card)) { + if (!card.isEmpty()) + kdWarning() << "Could not load " << card << " trying default" << endl; + card = KCardDialog::getCardPath(KCardDialog::getDefaultCardDir(), i+1); + if (!m_cardPixmaps[i].load(card)) { + kdError() << "Could not load " << card << endl; + } + } + } +} + + +// Load the backside of the card deck from the file name in 'path'. + +void +CardImages::loadDeck(QString path) +{ + if (!m_deck->load(path)) { + kdWarning() << "Could not load deck - loading default deck" << endl; + path = KCardDialog::getDefaultDeck(); + if (!m_deck->load(path)) + kdError() << "Could not load deck" << endl; + } +} + + +// ================================================================ +// class CardWidget + + +extern CardImages *cardImages; + + +CardWidget::CardWidget( QWidget *parent, const char *name ) + : QPushButton( parent, name ) +{ + m_held = false; + + setBackgroundMode( NoBackground ); // disables flickering + connect(this, SIGNAL(clicked()), this, SLOT(ownClick())); + + setFixedSize(cardWidth, cardHeight); +} + + +void CardWidget::paintCard(int cardType) +{ + // Remap the card from the natural poker card values to the names + // used by the card decks in KDE. + // + // FIXME: This is an ugly hack. The correct way would be to add a + // method paintCard(CardValue card), but that will have to + // wait until we break out the card stuff to its own file so + // that there is something to include. + int card; + int rank; + int suit; + + if (cardType == 0) + card = 0; + else { + suit = (cardType - 1) % 4; + rank = (cardType - 1) / 4; + + rank = 12 - rank; // ace-two --> two-ace + switch (suit) { + case 0: break; // Clubs + case 1: suit = 3; break; // Diamonds + case 2: suit = 1; break; // Spades + case 3: suit = 2; break; // Hearts + } + card = rank * 4 + suit + 1; + } + + // Select the pixmap to use. +#if 0 + if (card == 0) { + m_pm = &cardImage->m_deck; + } else { + m_pm = &cardImage->m_cardPixmaps[card-1]; + } +#else + m_pm = cardImages->getCardImage(card); +#endif + + // Set the pixmap in the QPushButton that we inherit from. + if ( m_pm->size() != QSize( 0, 0 ) ) { // is an image loaded? + setPixmap(*m_pm); + } +} + + +void CardWidget::repaintDeck() +{ + setPixmap(*m_pm); + setFixedSize(cardImages->getWidth(), cardImages->getHeight()); + + ((QWidget*) parent())->layout()->invalidate(); + ((QWidget*) parent())->setFixedSize( ((QWidget*) parent())->sizeHint()); +} + + +/* Emit the pClicked signal. + */ + +void CardWidget::ownClick() +{ + emit pClicked(this); +} + + +bool CardWidget::getHeld() +{ + return m_held; +} + + +void CardWidget::setHeld(bool newheld) +{ + m_held = newheld; +} + + +bool CardWidget::toggleHeld() +{ + m_held = !m_held; + return m_held; +} + + +#include "kpaint.moc" diff --git a/kpoker/kpaint.h b/kpoker/kpaint.h new file mode 100644 index 00000000..ae4c1d29 --- /dev/null +++ b/kpoker/kpaint.h @@ -0,0 +1,121 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef __CardWidget__ +#define __CardWidget__ + + +#include + +#include +#include + + +class QLabel; + + +/** + * This class loads all pictures, first. So they don't have to be loaded after the start again + * @short A help class for loading the pictures + **/ + +class CardImages : public QWidget +{ + Q_OBJECT + + public: + CardImages( QWidget *parent = 0, const char *name = 0 ); + ~CardImages(); + + // FIXME: Use CardValue instead of int when the cards are in their + // own file. + QPixmap *getCardImage(int card) const; + QPixmap *getDeck() const { return m_deck; } + + int getWidth() const { return m_cardPixmaps[0].width(); } + int getHeight() const { return m_cardPixmaps[0].height(); } + + void loadDeck(QString path); + void loadCards(QString cardDir); + + private: + static QPixmap *m_cardPixmaps; + static QPixmap *m_deck; +}; + + +/** + * This class extends the QPushButton by some methods / variables to provide a card with held labels and so on + * + * @short The cards + **/ +class CardWidget : public QPushButton +{ + Q_OBJECT + + public: + CardWidget( QWidget *parent=0, const char *name=0 ); + + /** + * Paints the deck if cardType = 0 or the card specified in cardType + * @param cardType the card to be painted. 0 is the deck + **/ + void paintCard(int cardType); + + /** + * @return The held status of this card + **/ + bool getHeld(); + + /** + * Sets the new held + * @param newHeld specifies the new held + **/ + void setHeld(bool newheld); + + /** + * Toggle the boolean member m_held. + **/ + bool toggleHeld(); /* returns the new value of held*/ + + + void repaintDeck(); + + signals: + /** + * This signal is emitted by @ref ownClick() + * @param CardWidget is a this pointer + **/ + void pClicked(CardWidget *); + + protected slots: + /** + * Emits the signal @ref pClicked() when the player clicks on the card + **/ + void ownClick(); + + + private: + QPixmap *m_pm; // the loaded pixmap + bool m_held; + + public: + QLabel *heldLabel; +}; + + +#endif diff --git a/kpoker/kpoker.cpp b/kpoker/kpoker.cpp new file mode 100644 index 00000000..2956b42c --- /dev/null +++ b/kpoker/kpoker.cpp @@ -0,0 +1,1484 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include + +// QT includes +#include +#include +#include +#include +#include + +// KDE includes +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// own includes +#include "betbox.h" +#include "kpaint.h" +#include "optionsdlg.h" +#include "newgamedlg.h" +#include "player.h" +#include "playerbox.h" +#include "version.h" +#include "kpoker.h" +#include "defines.h" + + +// ================================================================ +// Class PokerGame + + +PokerGame::PokerGame(KRandomSequence *random) + : m_deck(random) +{ + // We don't need to save if we just started. + m_isDirty = false; + + // Some defaults. + m_type = SinglePlayer; +} + + +PokerGame::~PokerGame() +{ + m_activePlayers.clear(); + m_removedPlayers.clear(); +} + + +void +PokerGame::dealCards(PokerPlayer *player, bool skip[]) +{ + CardValue card; + + for (int i = 0; i < PokerHandSize; i++) { + if (skip[i]) + continue; + + card = m_deck.getTopCard(); + + player->takeCard(i, card); + } +} + + +void +PokerGame::activatePlayer(PokerPlayer *player) +{ + if (!m_activePlayers.contains(player)) { + m_activePlayers.append(player); + m_removedPlayers.remove(player); + } +} + + +void +PokerGame::inactivatePlayer(PokerPlayer *player) +{ + if (m_activePlayers.contains(player)) { + m_activePlayers.remove(player); + m_removedPlayers.append(player); + } +} + + +void +PokerGame::newGame(PokerGameType type, + int numPlayers, PokerPlayer *players, + int minBet, int maxBet) +{ + m_type = type; + + // Store the players. These are never changed in the current implementation. + m_numPlayers = numPlayers; + m_players = players; + + setBettingLimits(minBet, maxBet); + + setState(StateStartRound); + + // Initiate player arrays. Make all players active from the start. + m_activePlayers.clear(); + m_removedPlayers.clear(); + for (unsigned int i = 0; i < m_numPlayers; i++) + m_activePlayers.append(&m_players[i]); + + // Inform players how much they may bet. + for (unsigned int i = 0; i < m_activePlayers.count(); i++) + m_activePlayers.at(i)->setBetDefaults(m_minBet, m_maxBet); +} + + +void +PokerGame::newRound() +{ + // Reset the pot to zero. + m_pot = 0; + + // Collect the cards and shuffle them. + m_deck.reset(); + m_deck.shuffle(); + + // Clear the list of active and removed players. + m_activePlayers.clear(); + m_removedPlayers.clear(); +} + + +// ================================================================ +// Global variables + + +CardImages *cardImages; + + +// ================================================================ +// Class kpok + + +kpok::kpok(QWidget *parent, const char *name) + : QWidget(parent, name), + m_game(&m_random) +{ + QString version; + + m_random.setSeed(0); + + // This class owns the players. Create them here. + // Currently we always allocate two - one human and one computer. + m_numPlayers = 2; // FIXME: Hard coded!! + m_players = new PokerPlayer[m_numPlayers]; + + // Initialize the first one to human... + m_players[0].setHuman(); + m_players[0].setName(i18n("You")); + + // ...and the rest to computer players. + for (int unsigned i = 1; i < m_numPlayers; i++) + m_players[i].setName(QString("Computer %1").arg(i-1)); + + lastHandText = ""; + + version = kapp->caption() + " " + KPOKER_VERSION; + setCaption( version ); + + mOptions = 0; + + playerBox = 0; + adjust = false; + + initWindow(); // FIXME: Change this name!!! + initSound(); + + if (!readEntriesAndInitPoker()) + exit(0); +} + + +kpok::~kpok() +{ + if (mOptions != 0) { + if (m_numPlayers > 1) + m_game.setBettingLimits(mOptions->getMinBet(), mOptions->getMaxBet()); + + drawDelay = mOptions->getDrawDelay(); + // cashPerRound = mOptions->getCashPerRound(); // NOT(!) configurable + } + + KConfig* conf = kapp->config(); + conf->setGroup("General"); + conf->writeEntry("MinBet", m_game.getMinBet()); + conf->writeEntry("MaxBet", m_game.getMaxBet()); + // conf->writeEntry("CashPerRound", cashPerRound); + conf->writeEntry("DrawDelay", drawDelay); + + + delete[] m_players; + + for (unsigned int i = 0; i < m_numPlayers; i++) + delete playerBox[i]; + delete[] playerBox; + + delete mOptions; +} + + +// Init the main window and load the configuration and card images. +// +// Only called by kpok::kpok() once. + +void kpok::initWindow() +{ + m_blinking = true; + m_blinkStat = 0; + m_blinkingBox = 0; + + // General font stuff. Define myFixedFont and wonFont. + QFont myFixedFont; + myFixedFont.setPointSize(12); + QFont wonFont; + wonFont.setPointSize(14); + wonFont.setBold(true); + + topLayout = new QVBoxLayout(this, BORDER); + QVBoxLayout* topInputLayout = new QVBoxLayout; + topLayout->addLayout(topInputLayout); + + QHBoxLayout* betLayout = new QHBoxLayout; + inputLayout = new QHBoxLayout; + inputLayout->addLayout(betLayout); + topInputLayout->addLayout(inputLayout); + + // The draw button + drawButton = new QPushButton(this); + drawButton->setText(i18n("&Deal")); + connect(drawButton, SIGNAL(clicked()), this, SLOT(drawClick())); + inputLayout->addWidget(drawButton); + inputLayout->addStretch(1); + + // The waving text + QFont waveFont; + waveFont.setPointSize(16); + waveFont.setBold(true); + QFontMetrics tmp(waveFont); + + // The widget where the winner is announced. + mWonWidget = new QWidget(this); + inputLayout->addWidget(mWonWidget, 2); + mWonWidget->setMinimumHeight(50); //FIXME hardcoded value for the wave + mWonWidget->setMinimumWidth(tmp.width(i18n("You won %1").arg(KGlobal::locale()->formatMoney(100))) + 20); // workaround for width problem in wave + QHBoxLayout* wonLayout = new QHBoxLayout(mWonWidget); + wonLayout->setAutoAdd(true); + + wonLabel = new QLabel(mWonWidget); + wonLabel->setFont(wonFont); + wonLabel->setAlignment(AlignCenter); + wonLabel->hide(); + inputLayout->addStretch(1); + + // The pot view + potLabel = new QLabel(this); + potLabel->setFont(myFixedFont); + potLabel->setFrameStyle(QFrame::WinPanel | QFrame::Sunken); + inputLayout->addWidget(potLabel, 0, AlignCenter); + + // Label widget in the lower left. + clickToHold = new QLabel(this); + clickToHold->hide(); + + // Timers + blinkTimer = new QTimer(this); + connect( blinkTimer, SIGNAL(timeout()), SLOT(bTimerEvent()) ); + + waveTimer = new QTimer(this); + connect( waveTimer, SIGNAL(timeout()), SLOT(waveTimerEvent()) ); + + drawTimer = new QTimer(this); + connect (drawTimer, SIGNAL(timeout()), SLOT(drawCardsEvent()) ); + + // and now the betUp/Down Buttons + betBox = new BetBox(this, 0); + betLayout->addWidget(betBox); + connect(betBox, SIGNAL(betChanged(int)), this, SLOT(betChange(int))); + connect(betBox, SIGNAL(betAdjusted()), this, SLOT(adjustBet())); + connect(betBox, SIGNAL(fold()), this, SLOT(out())); + + // some tips + QToolTip::add(drawButton, i18n("Continue the round")); + QToolTip::add(potLabel, i18n("The current pot")); + + // Load all cards into pixmaps first -> in the constructor. + cardImages = new CardImages(this, 0); + + // The configuration + KConfig* conf = kapp->config(); + conf->setGroup("General"); + + // Load the card deck. + if (conf->readBoolEntry("RandomDeck", true)) { + cardImages->loadDeck(KCardDialog::getRandomDeck()); + } else { + cardImages->loadDeck(conf->readPathEntry("DeckPath", KCardDialog::getDefaultDeck())); + } + if (conf->readBoolEntry("RandomCardDir", true)) { + cardImages->loadCards(KCardDialog::getRandomCardDir()); + } else { + cardImages->loadCards(conf->readPathEntry("CardPath", + KCardDialog::getDefaultCardDir())); + } +} + + +// Start a new complete game (not a round). + +void kpok::newGame() +{ + NewGameDlg* newGameDlg; + + // Get a "New Game" dialog. + lastHandText = ""; + newGameDlg = new NewGameDlg(this); + newGameDlg->hideReadingFromConfig(); + + if (newGameDlg->exec()) { + stopBlinking(); + stopDrawing(); + stopWave(); + + // Delete the old values. + for (unsigned int i = 0; i < m_numPlayers; i++) + delete playerBox[i]; + delete[] playerBox;// necessary? + + int numPlayers = newGameDlg->getPlayers(); + + // Most things will be done in initPoker. + initPoker(numPlayers); + + // Set/show the name and money of all players. + for (unsigned int i = 0; i < m_numPlayers; i++) { + m_players[i].setName(newGameDlg->name(i)); + playerBox[i]->showName(); + m_players[i].setCash((newGameDlg->money() >= m_game.getMinBet()) + ? newGameDlg->money() : START_MONEY); + m_game.setDirty(); + } + + // Show the money for all players and the pot. + paintCash(); + + // Activate the Draw button. + drawButton->setText(i18n("&Deal")); + drawButton->setEnabled(true); + } + + delete newGameDlg; +} + + +void kpok::newRound() +{ + bool onePlayerGame = false; + + m_game.newRound(); + + playerBox[0]->setHeldEnabled(false); + + if (m_numPlayers == 1) + onePlayerGame = true; + + readOptions(); // maybe some options have changed so check em + + if (m_players[0].getCash() < m_game.getMinBet()) + noMoney(); + else { + for (unsigned int i = 0; i < m_numPlayers; i++) { + if (m_players[i].getCash() >= m_game.getMinBet()) + m_game.activatePlayer(&m_players[i]); + else + removePlayerFromRound(&m_players[i]); + } + } + + if (m_game.getNumActivePlayers() == 1 && m_game.getType() != SinglePlayer) + switchToOnePlayerRules(); + + m_blinkingBox = 0; + wonLabel->hide(); + stopBlinking(); + stopWave(); + + for (int i = 0; i < m_game.getNumActivePlayers(); i++) + m_game.getActivePlayer(i)->newRound(); + + // We are beginning a new round so every card is available. + drawAllDecks(); + playerBox[0]->showHelds(false); + + // Deal first cards of the round + bool skip[PokerHandSize]; + for (int i = 0; i < PokerHandSize; i++) + skip[i] = false; + + for (int i = 0; i < m_game.getNumActivePlayers(); i++) + drawCards(m_game.getActivePlayer(i), skip); + + if (m_game.getNumActivePlayers() > 1) { + findHumanPlayer()->changeBet(m_game.getMinBet()); + m_game.bet(m_game.getMinBet()); + betBox->show(); + } + else { + m_game.getActivePlayer(0)->changeBet(cashPerRound); + betBox->hide(); + } + + paintCash(); + + drawTimer->start(drawDelay, TRUE); +} + + +void kpok::bet() +{ + // The players will bet, now. Player 1 (the human one ;-)) has + // already bet using the betBox ALL players (except player 1 who has + // already had) will get a chance to raise the value if nobody + // raises further that player will not get another chance, else he + // will. + + bool raised = false; + int oldMustBet = 0; + if (m_game.getState() == StateBet2) + oldMustBet = currentMustBet; + + if (m_game.getState() == StateBet1 || m_game.getState() == StateBet2) { + // The betting has just begun. This can be either the first or + // the second betting phase (state == StateBet1 or StateBet2). + // FIXME: These state names must be changed! + + // Find out how much the other players must bet. + currentMustBet = findHumanPlayer()->getCurrentBet(); + + // First bet as usual. + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + PokerPlayer *player = m_game.getActivePlayer(i); + int playerBet = 0; + + if (player->getHuman()) + continue; + + if (m_game.getState() == StateBet1) // first bet phase + playerBet = player->bet(currentMustBet); + else if (m_game.getState() == StateBet2) // 2nd bet phase + playerBet = player->raise(currentMustBet); + + if (playerBet < currentMustBet) { + removePlayerFromRound(player); + // FIXME: This should be bet from the beginning! + if (m_game.getState() == StateBet1) + m_game.bet(m_game.getMinBet()); + i--; + } + else { + // The player is seeing or has raised. + + if (playerBet > currentMustBet) + raised = true; + + currentMustBet = playerBet; + m_game.bet(currentMustBet - oldMustBet); + } + } + } + else if (m_game.getState() == StateRaise1 + || m_game.getState() == StateRaise2) { + + // The bet has been raised. + paintCash(); + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + PokerPlayer *player = m_game.getActivePlayer(i); + + // human player + if (player->getCurrentBet() < currentMustBet && player->getHuman()) { + removePlayerFromRound(player); + i--; + } + } + } + + paintCash(); + + + oldBet_raise = findHumanPlayer()->getCurrentBet(); // used by out() only + if (m_game.getState() == StateBet1 || m_game.getState() == StateBet2) { + if (raised) { + if (m_game.getState() == StateBet1) + m_game.setState(StateRaise1); + else + m_game.setState(StateRaise2); + + if (adjust) + adjustBet(); + else + out(); // not necessary + betBox->setEnabled(true); + betBox->beginRaise(); + if (adjust) + emit statusBarMessage(i18n("Clicking on draw means you adjust your bet")); + else + emit statusBarMessage(i18n("Clicking on draw means you are out")); + } + else { + if (m_game.getState() == StateBet1) + m_game.setState(StateExchangeCards); + else + m_game.setState(StateSee); + } + } + else if (m_game.getState() == StateRaise1 && !raised) { + emit clearStatusBar(); + // weWillAdjustLabel->hide(); + betBox->stopRaise(); + m_game.setState(StateExchangeCards); + } + else if (m_game.getState() == StateRaise2 && !raised) { + emit clearStatusBar(); + // weWillAdjustLabel->hide(); + betBox->stopRaise(); + m_game.setState(StateSee); + } + + + // Check if player 1 is out -> for players > 2 + // TODO: maybe if (!m_activePlayers.contains(humanPlayer)) + // {exchangeCards(); bet(); displayerWinner_computer();return;} + + + // don't continue game if player 1 is alone + // TODO: port to players > 2 + // this is ONLY working for players <= 2 + if (m_game.getNumInactivePlayers() >= 1 + && m_game.getNumActivePlayers() == 1) + displayWinner_Computer(m_game.getActivePlayer(0), true); +} + + +void kpok::out() +{ + // weWillAdjustLabel->hide(); + emit clearStatusBar(); + + m_game.bet(oldBet_raise - findHumanPlayer()->getCurrentBet()); + findHumanPlayer()->changeBet(oldBet_raise + - findHumanPlayer()->getCurrentBet()); + paintCash(); + + // Go to next state immediately. (Previously we told the user to + // click the action button.) + drawClick(); + //drawButton->setText(i18n("&End Round")); +} + + +void kpok::adjustBet() +{ + emit clearStatusBar(); + + betChange(currentMustBet - findHumanPlayer()->getCurrentBet()); + paintCash(); +} + + +// Initiate for a completely new game (not a round). +// +// This is called from newGame(), readEntriesAndInitPoker(), loadGame(). +// + +void kpok::initPoker(unsigned int numPlayers) +{ + m_numPlayers = numPlayers; + + // Tell the game about the players also. + PokerGameType gametype = (numPlayers == 1) ? SinglePlayer : MultiPlayer; + + // Read some defaults. + kapp->config()->setGroup("General"); + int minBet = kapp->config()->readNumEntry("MinBet", MIN_BET); + int maxBet = kapp->config()->readNumEntry("MaxBet", MAX_BET); + + // Start a new poker game using the data found out above. + m_game.newGame(gametype, m_numPlayers, m_players, minBet, maxBet); + + // Not (yet) configurable + cashPerRound = CASH_PER_ROUND; + drawDelay = kapp->config()->readNumEntry("DrawDelay", DRAWDELAY); + + m_blinkingBox = 0; + currentMustBet = m_game.getMinBet(); + + // --- Graphics --- + + // Make all labels / boxes / cardwidgets for every player. + playerBox = new PlayerBox *[numPlayers]; + + for (int unsigned i = 0; i < numPlayers; i++) { + playerBox[i] = new PlayerBox(i == 0, this); + playerBox[i]->setPlayer(&m_players[i]); + if (i == 0) + topLayout->insertWidget(0, playerBox[i]); + else + topLayout->addWidget(playerBox[i]); + playerBox[i]->showName(); + + // If it has been deleted and created again it hasn't be shown + // correctly - hiding and re-showing solves the problem. + playerBox[i]->hide(); + playerBox[i]->show(); + } + + // Connects for player 1 + // + // FIXME: Make CardWidget::toggleHeld() work. + playerBox[0]->activateToggleHeld(); + connect(playerBox[0], SIGNAL(toggleHeld()), this, SLOT(toggleHeld())); + + // hide some things + playerBox[0]->showHelds(false); + wonLabel->hide(); + emit showClickToHold(false); + + // Different meaning of the status for single and multi player games. + if (m_game.getNumActivePlayers() > 1) { + setHand(i18n("Nobody"), false); + betBox->show(); + betBox->setEnabled(false); + potLabel->show(); + } + else { + setHand(i18n("Nothing")); + betBox->hide(); + potLabel->hide(); + playerBox[0]->singlePlayerGame(cashPerRound); + } + + // Some final inits. + drawStat = 0; + waveActive = 0; + fCount = 0; + + // Finally clear the pot and show the decks/cash - in one word: begin :-) + m_game.clearPot(); + drawAllDecks(); + for (unsigned int i = 0; i < m_numPlayers; i++) { + playerBox[i]->repaintCard(); + } + paintCash(); + playerBox[0]->setHeldEnabled(false); +} + + +void kpok::paintCash() +{ + for (unsigned int i = 0; i < m_numPlayers; i++) { + playerBox[i]->showCash(); + } + potLabel->setText(i18n("Pot: %1").arg(KGlobal::locale()->formatMoney(m_game.getPot()))); +} + + +void kpok::updateLHLabel() +{ + if (!lastHandText.isEmpty()) + setHand(lastHandText); + else if (m_game.getNumActivePlayers() > 1) + setHand(i18n("Nobody"), false); + else + setHand(i18n("Nothing")); +} + + +void kpok::setHand(const QString& newHand, bool lastHand) +{ + emit changeLastHand(newHand, lastHand); + + lastHandText = newHand; +} + + +void kpok::toggleHeld() +{ + if (m_game.getState() == StateBet1 || m_game.getState() == StateRaise1) + playSound("hold.wav"); +} + + +void kpok::drawClick() +{ + if (!drawButton->isEnabled()) + return; + + // If this is the start of a new round, then deal new cards. + if (m_game.getState() == StateStartRound) { + drawButton->setEnabled(false); + betBox->setEnabled(false); + newRound(); + } + else if (m_game.getState() == StateBet1) { // bet + emit showClickToHold(false); + + bet(); + if (m_game.getState() == StateExchangeCards) {// should be set in bet() + drawClick(); + } + } + else if (m_game.getState() == StateRaise1) { // continue bet + bet(); + if (m_game.getState() == StateExchangeCards) {// should be set in bet() + drawClick(); + } + } + else if (m_game.getState() == StateExchangeCards) { // exchange cards + drawButton->setEnabled(false); + playerBox[0]->setHeldEnabled(false); + betBox->setEnabled(false); + bool skip[PokerHandSize]; + for (int i = 0; i < PokerHandSize; i++) + skip[i] = false; + + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + if (!m_game.getActivePlayer(i)->getHuman()) + m_game.getActivePlayer(i)->exchangeCards(skip); + else { + for (int i = 0; i < PokerHandSize; i++) { + skip[i] = playerBox[0]->getHeld(i); + if (!skip[i]) + playerBox[0]->paintDeck(i); + } + } + drawCards(m_game.getActivePlayer(i), skip); + } + + if (playerBox[0]->getHeld(0)) + drawTimer->start(0, TRUE); + else + drawTimer->start(drawDelay, TRUE); + } + else if (m_game.getState() == StateBet2) { // raise + setBetButtonEnabled(false); + bet(); + + if (m_game.getState() == StateSee)//should be set in bet()->if no one has raised + drawClick(); + } + else if (m_game.getState() == StateRaise2) { + bet(); + if (m_game.getState() == StateSee) + drawClick(); + } + else if (m_game.getState() == StateSee) + winner(); +} + + +void kpok::drawCards(PokerPlayer* p, bool skip[]) +{ + m_game.dealCards(p, skip); +} + + +void kpok::displayWinner_Computer(PokerPlayer* winner, bool othersPassed) +{ + // Determine the box that contains the winner. + for (unsigned int i = 0; i < m_numPlayers; i++) { + if (&m_players[i] == winner) + m_blinkingBox = i; + } + + // Give the pot to the winner. + winner->setCash(winner->getCash() + m_game.getPot()); + m_game.setDirty(); + + // Generate a string with winner info and show it. + QString label; + if (winner->getHuman()) + label = i18n("You won %1").arg(KGlobal::locale()->formatMoney(m_game.getPot())); + else + label = i18n("%1 won %2").arg(winner->getName()).arg(KGlobal::locale()->formatMoney(m_game.getPot())); + wonLabel->setText(label); + + // Start the waving motion of the text. + QFont waveFont; + waveFont.setBold(true); + waveFont.setPointSize(16); + QFontMetrics tmp(waveFont); + mWonWidget->setMinimumWidth(tmp.width(label) + 20); + + // Play a suitable sound. + if (winner->getHuman()) { + playSound("win.wav"); + wonLabel->hide(); + startWave(); + } + else { + playSound("lose.wav"); + wonLabel->show(); + } + + m_game.clearPot(); + m_game.setState(StateStartRound); + drawButton->setEnabled(true); + setHand(winner->getName(), false); + paintCash(); + + // Only start blinking if player 1 is still in. + if (m_game.isActivePlayer(&m_players[0]) && !othersPassed) + startBlinking(); + + drawButton->setText(i18n("&Deal New Round")); +} + + +void kpok::showComputerCards() +{ + // Don't show cards of 'out' players. + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + if (!m_game.getActivePlayer(i)->getHuman()){ + playerBox[i]->paintCard(drawStat); //TODO change: + + if (i == 1) //TODO : CHANGE! + playSound("cardflip.wav");//perhaps in playerbox or even in cardwidget + } + } + + if (drawStat == 4) { // just did last card + drawButton->setEnabled(true); + drawStat = 0; + } else { // only inc drawStat if not done with displaying + drawStat++; + showComputerCards(); + } +} + + +void kpok::setBetButtonEnabled(bool enabled) +{ + betBox->setEnabled(enabled); +} + + +void kpok::drawCardsEvent() +{ + if (!playerBox[0]->getHeld(drawStat)) { + playerBox[0]->paintCard(drawStat); + + playSound("cardflip.wav");//maybe in playerbox or even in cardwidget + } + + if (drawStat == 4) { // just did last card + drawButton->setEnabled(true); + betBox->setEnabled(true); + drawStat = 0; + + if (m_game.getState() == StateExchangeCards) { + if (m_game.getType() == SinglePlayer) + result(); + else { + // Now give players the chance to raise. + drawButton->setText(i18n("&See!")); + m_game.setState(StateBet2); + } + } else if (m_game.getState() == StateStartRound) { + playerBox[0]->setHeldEnabled(true); + + emit showClickToHold(true); + //clickToHold->show(); + //TODO: + m_game.setState(StateBet1); + drawButton->setText(i18n("&Draw New Cards")); + } + + } else { // only inc drawStat if not done with displaying + drawStat++; + // look at next card and if it is held instantly call drawCardEvent again + if (playerBox[0]->getHeld(drawStat)) + drawTimer->start(0,TRUE); + else + drawTimer->start(drawDelay,TRUE); + } +} + + +// Called to display the result in a single player game. + +void kpok::result() +{ + int testResult = m_game.getActivePlayer(0)->testHand(); + switch (testResult) { + case Pair: + if (m_game.getActivePlayer(0)->getHand().get_firstRank() < JACK) { + displayWin(i18n("Nothing"), 0); + break; + } + + displayWin(i18n("One Pair"), 5); + break; + + case TwoPairs: + displayWin(i18n("Two Pairs"), 10); + break; + + case ThreeOfAKind: + displayWin(i18n("3 of a Kind"), 15); + break; + + case Straight: + displayWin(i18n("Straight"), 20); + break; + + case Flush: + displayWin(i18n("Flush"), 25); + break; + + case FullHouse: + displayWin(i18n("Full House"), 40); + break; + + case FourOfAKind: + displayWin(i18n("4 of a Kind"), 125); + break; + + case StraightFlush: + displayWin(i18n("Straight Flush"), 250); + break; + + case RoyalFlush: + displayWin(i18n("Royal Flush"), 2000); + break; + + default: + displayWin(i18n("Nothing"), 0); + break; + } + + startBlinking(); + m_game.setState(StateStartRound); + + if (m_game.getActivePlayer(0)->getCash() < cashPerRound) + noMoney(); +} + + +// Display winner and give money and so on. + +void kpok::winner() +{ + PokerPlayer *winner; + + showComputerCards(); + + winner = m_game.getActivePlayer(0); + for (int i = 1; i < m_game.getNumActivePlayers(); i++) { + if (winner->getHand() < m_game.getActivePlayer(i)->getHand()) { + //kdDebug() << "Hand 2 is better." << endl; + winner = m_game.getActivePlayer(i); + } + else { + //kdDebug() << "Hand 1 is better." << endl; + } + } + + displayWinner_Computer(winner, false); +} + + +void kpok::noMoney() +{ + KMessageBox::sorry(0, i18n("You Lost"), i18n("Oops, you went bankrupt.\n" + "Starting a new game.\n")); + newGame(); +} + + +void kpok::startBlinking() +{ + blinkTimer->start(650); +} + + +void kpok::stopBlinking() +{ + blinkTimer->stop(); + m_blinkStat = 1; + m_blinkingBox = 0; +} + + +void kpok::startWave() +{ + waveTimer->start(40); + waveActive = true; +} + + +void kpok::stopWave() +{ + waveTimer->stop(); + fCount = -1; /* clear image */ + repaint ( FALSE ); + waveActive = false; +} + + +void kpok::stopDrawing() +{ + drawTimer->stop(); +} + + +void kpok::waveTimerEvent() +{ + fCount = (fCount + 1) & 15; + repaint( FALSE ); +} + + +void kpok::bTimerEvent() +{ + if (m_blinking) { + if (m_blinkStat != 0) { + playerBox[m_blinkingBox]->blinkOn(); + m_blinkStat = 0; + } else { + playerBox[m_blinkingBox]->blinkOff(); + m_blinkStat = 1; + } + } +} + + +void kpok::displayWin(const QString& hand, int cashWon) +{ + QString buf; + + setHand(hand); + m_game.getActivePlayer(0)->setCash(m_game.getActivePlayer(0)->getCash() + + cashWon); + m_game.setDirty(); + paintCash(); + + if (cashWon) { + playSound("win.wav"); + buf = i18n("You won %1!").arg(KGlobal::locale()->formatMoney(cashWon)); + } else { + playSound("lose.wav"); + buf = i18n("Game Over"); // locale + } + wonLabel->setText(buf); + + if (!cashWon) + wonLabel->show(); + else { + wonLabel->hide(); + startWave(); + } + + drawButton->setText(i18n("&Deal New Round")); +} + + +void kpok::paintEvent( QPaintEvent *) +{ + /* NOTE: This was shamelessy stolen from the "hello world" example + * coming with Qt Thanks to the Qt-Guys for doing such a cool + * example 8-) + */ + + if (!waveActive) { + return; + } + + QString txt = wonLabel->text(); + wonLabel->hide(); + + static int sin_tbl[16] = { + 0, 38, 71, 92, 100, 92, 71, 38, 0, -38, -71, -92, -100, -92, -71, -38}; + + if ( txt.isEmpty() ) { + return; + } + + QFont wonFont; + wonFont.setPointSize(18); + wonFont.setBold(true); + + QFontMetrics fm = QFontMetrics(wonFont); + + int w = fm.width(txt) + 20; + int h = fm.height() * 2; + while (w > mWonWidget->width() && wonFont.pointSize() > 6) {// > 6 for emergency abort... + wonFont.setPointSize(wonFont.pointSize() - 1); + fm = QFontMetrics(wonFont); + w = fm.width(txt) + 20; + h = fm.height() * 2; + } + + int pmx = mWonWidget->width() / 2 - w / 2; + int pmy = 0; + // int pmy = (playerBox[0]->x() + playerBox[0]->height() + 10) - h / 4; + + QPixmap pm( w, h ); + pm.fill( mWonWidget, pmx, pmy ); + + if (fCount == -1) { /* clear area */ + bitBlt( mWonWidget, pmx, pmy, &pm ); + return; + } + + QPainter p; + int x = 10; + int y = h/2 + fm.descent(); + unsigned int i = 0; + p.begin( &pm ); + p.setFont( wonFont ); + p.setPen( QColor(0,0,0) ); + + while ( i < txt.length() ) { + int i16 = (fCount+i) & 15; + + p.drawText( x, y-sin_tbl[i16]*h/800, QString(txt[i]), 1 ); + x += fm.width( txt[i] ); + i++; + } + p.end(); + + // 4: Copy the pixmap to the Hello widget + bitBlt( mWonWidget, pmx, pmy, &pm ); +} + + +void kpok::drawAllDecks() +{ + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + for (int i2 = 0; i2 < PokerHandSize; i2++) { + m_game.getActivePlayer(i)->takeCard(i2, 0); + playerBox[i]->paintCard(i2); + } + } +} + + +void kpok::removePlayerFromRound(PokerPlayer* removePlayer) +{ + removePlayer->setOut(true); + m_game.inactivatePlayer(removePlayer); + + for (int i = 0; i < m_game.getNumPlayers(); i++) + playerBox[i]->showCash(); +} + + +void kpok::switchToOnePlayerRules() +{ + KMessageBox::information(0, i18n("You are the only player with money!\n" + "Switching to one player rules..."), + i18n("You Won")); + + // Hide all computer players. + for (unsigned int i = 1; i < m_numPlayers; i++) { + playerBox[i]->hide(); + } + + m_game.setType(SinglePlayer); + + betBox->hide(); + potLabel->hide(); + playerBox[0]->singlePlayerGame(cashPerRound); + setHand(i18n("Nothing")); +} + + +PokerPlayer* kpok::findHumanPlayer() +{ + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + if (m_game.getActivePlayer(i)->getHuman()) + return m_game.getActivePlayer(i); + } + + return m_game.getActivePlayer(0);//error +} + + +bool kpok::readEntriesAndInitPoker() +{ + NewGameDlg *newGameDlg = NULL; + KConfig *conf = kapp->config(); + int numPlayers = DEFAULT_PLAYERS; + + conf->setGroup("NewGameDlg"); + bool showNewGameDlg = conf->readBoolEntry("showNewGameDlgOnStartup", false); + bool aborted = false; + bool oldGame = false; + + if (showNewGameDlg) { + newGameDlg = new NewGameDlg(this); + if (!newGameDlg->exec()) + return false; // exit game + } + + if (!aborted && (!showNewGameDlg || showNewGameDlg && + newGameDlg->readFromConfigFile())) { + // Try starting an old game. + //kdDebug() << "Trying to load old game" << endl; + oldGame = loadGame(); + if (oldGame) + return true; + } + + if (!aborted && showNewGameDlg && !oldGame) { + // Don't use config file - just take the values from the dialog. + // (This is also config - the dialog defaults are from config.) + numPlayers = newGameDlg->getPlayers(); + if (numPlayers <= 0) + aborted = true; + + for (int i = 0; i < numPlayers; i++) { + m_players[i].setName(newGameDlg->name(i)); + m_players[i].setCash(newGameDlg->money()); + m_game.setDirty(); + } + } + initPoker(numPlayers); + + if (newGameDlg != 0) { + delete newGameDlg; + } + + return true; +} + + +void kpok::betChange(int betChange) +{ + PokerPlayer* p = findHumanPlayer(); + + switch(m_game.getState()){ + case StateBet1: + if (p->getCurrentBet() + betChange >= m_game.getMinBet() && + p->getCurrentBet() + betChange <= m_game.getMaxBet()) { + // Bet at least getMinBet() but not more than getMaxBet(). + if (p->changeBet(betChange)) + m_game.bet(betChange); + } + break; + case StateBet2: + case StateRaise2: + case StateRaise1: + if (p->getCurrentBet() + betChange >= currentMustBet + && p->getCurrentBet() + betChange <= currentMustBet + m_game.getMaxBet()) { + if (p->changeBet(betChange)) + m_game.bet(betChange); + } + break; + default: + break; + } + + paintCash(); +} + + +void kpok::saveGame() +{ + kapp->config()->setGroup("Save"); + saveGame(kapp->config()); +} + + +void kpok::saveGame(KConfig* conf) +{ + // kdWarning() << "save game" << endl; + int players = m_game.getNumPlayers(); + + conf->writeEntry("players", players); + conf->writeEntry("lastHandText", lastHandText); + + for (int i = 0; i < players; i++) { + conf->writeEntry(QString("Name_%1").arg(i), m_players[i].getName()); + conf->writeEntry(QString("Human_%1").arg(i), m_players[i].getHuman()); + conf->writeEntry(QString("Cash_%1").arg(i), m_players[i].getCash()); + } + + m_game.clearDirty(); +} + + +void kpok::toggleSound() { setSound(!sound); } +void kpok::toggleBlinking() { setBlinking(!m_blinking); } +void kpok::toggleAdjust() { setAdjust(!adjust); } + + +void kpok::slotPreferences() +{ + if ( mOptions==0 ) + mOptions = new OptionsDlg(this, 0, m_numPlayers); + + if (m_numPlayers > 1) + mOptions->init(drawDelay, m_game.getMaxBet(), m_game.getMinBet()); + else + mOptions->init(drawDelay, cashPerRound); + + if (!mOptions->exec()) { + delete mOptions; + mOptions = 0; + } +} + + +void kpok::setAdjust(bool ad) +{ + adjust = ad; + + //update guessed money statusbar if we currently are in need of adjust + if (m_game.getState() == StateRaise1 + || m_game.getState() == StateRaise2) { + if (adjust) + adjustBet(); + else + out(); + } +} + + +bool kpok::initSound() +{ + sound = true; + return true; +} + + +void kpok::playSound(const QString &soundname) +{ + if (sound) + KAudioPlayer::play(locate("data", QString("kpoker/sounds/")+soundname)); +} + + +void kpok::setSound(bool s) +{ + sound = s; +} + + +void kpok::readOptions() +{ + if (mOptions != 0) { + if (m_numPlayers > 1) { + m_game.setBettingLimits(mOptions->getMinBet(), mOptions->getMaxBet()); + for (int i = 0; i < m_game.getNumActivePlayers(); i++) + m_game.getActivePlayer(i)->setBetDefaults(m_game.getMinBet(), + m_game.getMaxBet()); // inform players how much they may bet + } + drawDelay = mOptions->getDrawDelay(); + // kdDebug() << cashPerRound << endl; + // cashPerRound = mOptions->getCashPerRound(); // NOT(!) configurable + delete mOptions; + mOptions = 0; + } +} + + +bool kpok::loadGame() +{ + kapp->config()->setGroup("Save"); + return loadGame(kapp->config()); +} + + +bool kpok::loadGame(KConfig* conf) +{ + int numPlayers = DEFAULT_PLAYERS; + + // conf->setGroup("Save"); + numPlayers = conf->readNumEntry("players", -1); + + if (numPlayers > 0) { + for (int i = 0; i < numPlayers; i++) { + QString buf = conf->readEntry(QString("Name_%1").arg(i), + "Player"); + m_players[i].setName(buf); + bool human = conf->readBoolEntry(QString("Human_%1").arg(i), + false); + if (human) + m_players[i].setHuman(); // i == 0 + int cash = conf->readNumEntry(QString("Cash_%1").arg(i), + START_MONEY); + m_players[i].setCash(cash); + m_game.setDirty(); + } + initPoker(numPlayers); + + // after initPoker because initPoker does a default initialization + // of lastHandText + conf->setGroup("Save"); + lastHandText = conf->readEntry("lastHandText", ""); + + return true; + } + + return false; +} + + +// These slots are called from keyboard shortcuts ('1'..'5') + +void kpok::exchangeCard1() { playerBox[0]->cardClicked(1); } +void kpok::exchangeCard2() { playerBox[0]->cardClicked(2); } +void kpok::exchangeCard3() { playerBox[0]->cardClicked(3); } +void kpok::exchangeCard4() { playerBox[0]->cardClicked(4); } +void kpok::exchangeCard5() { playerBox[0]->cardClicked(5); } + + +// This slot is called when the user wants to change some aspect of +// the card deck (front or back). +// +// FIXME: Maybe the slot should be moved to top.cpp instead and simply +// call a setDeck() or setBack() method in kpok. + +void kpok::slotCardDeck() +{ + kapp->config()->setGroup("General"); + QString deckPath = kapp->config()->readPathEntry("DeckPath", 0); + QString cardPath = kapp->config()->readPathEntry("CardPath", 0); + bool randomDeck, randomCardDir; + + // Show the "Select Card Deck" dialog and load the images for the + // selected deck, if any. + if (KCardDialog::getCardDeck(deckPath, cardPath, this, KCardDialog::Both, + &randomDeck, &randomCardDir) + == QDialog::Accepted) { + + // Load backside and front images. + if (playerBox && m_blinking && (m_blinkStat == 0)) + bTimerEvent(); + + cardImages->loadDeck(deckPath); + cardImages->loadCards(cardPath); + + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + playerBox[i]->repaintCard(); + } + + // Save selected stuff in the configuration. + kapp->config()->writePathEntry("DeckPath", deckPath); + kapp->config()->writeEntry("RandomDeck", randomDeck); + kapp->config()->writePathEntry("CardPath", cardPath); + kapp->config()->writeEntry("RandomCardDir", randomCardDir); + } +} + + +#include "kpoker.moc" diff --git a/kpoker/kpoker.desktop b/kpoker/kpoker.desktop new file mode 100644 index 00000000..000f2aca --- /dev/null +++ b/kpoker/kpoker.desktop @@ -0,0 +1,75 @@ +[Desktop Entry] +Name=KPoker +Name[af]=Kpoker +Name[be]=Покер +Name[bn]=কে-পোকার +Name[ca]=Pòquer +Name[eo]=Pokero +Name[hi]=के-पोकर +Name[is]=Póker +Name[ne]=केडीई पोकर +Name[pa]=ਕੇ-ਪੋਕਰ +Name[pl]=Poker +Name[sv]=Kpoker +Name[ta]=Kபோகà¯à®•à®°à¯ +Name[tg]=KПокер +Name[th]=โป๊à¸à¹€à¸à¸­à¸£à¹Œ - K +Name[tr]=Poker +Name[uk]=Покер +Name[zh_TW]=KPoker 紙牌éŠæˆ² +Exec=kpoker %i %m -caption "%c" +Icon=kpoker +DocPath=kpoker/index.html +GenericName=Poker Card Game +GenericName[be]=ÐšÐ°Ñ€Ñ‚Ð°Ñ‡Ð½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ Ñž покер +GenericName[bg]=Покер +GenericName[bn]=পোকার নামের তাস খেলা +GenericName[bs]=Igra pokera +GenericName[ca]=Joc de Pòquer +GenericName[cs]=Karetní hra poker +GenericName[cy]=Gêm Cerdiau Poker +GenericName[da]=Poker kortspil +GenericName[de]=Poker Kartenspiel +GenericName[el]=Παιχνίδι καÏτών Poker +GenericName[eo]=Pokera Kartludo +GenericName[es]=Juego de cartas de Poker +GenericName[et]=Pokker +GenericName[eu]=Poker jokoa +GenericName[fa]=بازی Poker Card +GenericName[fi]=Pokeri-korttipeli +GenericName[fr]=Jeu de Poker +GenericName[he]=משחק פוקר +GenericName[hr]=KartaÅ¡ka igra Pokera +GenericName[hu]=Póker +GenericName[is]=Pókerspil +GenericName[it]=Gioco del Poker +GenericName[ja]=ãƒãƒ¼ã‚«ãƒ¼ã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​បៀផែ +GenericName[ko]=ì¹´ë“œ ë†€ì´ +GenericName[lt]=Pokeris (kortų žaidimas) +GenericName[lv]=Pokera spÄ“le +GenericName[mk]=Играта покер +GenericName[nb]=Kortspillet poker +GenericName[nds]=Pokern +GenericName[ne]=पोकर कारà¥à¤¡ खेल +GenericName[nl]=Poker-kaartspel +GenericName[nn]=Kortspelet poker +GenericName[pa]=ਪੋਕਰ ਤਾਸ਼ ਖੇਡ +GenericName[pl]=Poker +GenericName[pt]=Jogo de Cartas de Póquer +GenericName[pt_BR]=Jogo de Poker +GenericName[ru]=Покер +GenericName[se]=Goartaspeallu poker +GenericName[sk]=Kartová hra Poker +GenericName[sl]=Igra s kartami pokra +GenericName[sr]=Покер, игра Ñа картама +GenericName[sr@Latn]=Poker, igra sa kartama +GenericName[sv]=Pokerkortspel +GenericName[ta]=போகà¯à®•à®°à¯ அடà¯à®Ÿà¯ˆ விளையாடà¯à®Ÿà¯ +GenericName[uk]=Карти - гра в покер +GenericName[zh_TW]=紙牌éŠæˆ² +Terminal=false +Type=Application +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;CardGame; diff --git a/kpoker/kpoker.h b/kpoker/kpoker.h new file mode 100644 index 00000000..21a41ebc --- /dev/null +++ b/kpoker/kpoker.h @@ -0,0 +1,364 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef KPOKER_H +#define KPOKER_H + + +// QT includes +#include +#include + +// KDE includes +#include + +// own includes +#include "poker.h" + + +// QT classes +class QPushButton; +class QLineEdit; +class QLabel; +class QFrame; +class QLineEdit; +class QFrame; +class QHBoxLayout; +class QVBoxLayout; + +// KDE classes +class KConfig; + + +// own classes +class BetBox; +class CardWidget; +class OptionsDlg; +class NewGameDlg; +class PokerPlayer; +class PlayerBox; + + +// ================================================================ +// Poker Game + + +enum PokerGameType { + SinglePlayer, + MultiPlayer +}; + + +enum PokerGameState { + StateStartRound=0, // Before dealing. Deal cards to switch state. + + StateBet1, + StateRaise1, + + StateExchangeCards, + + StateBet2, + StateRaise2, + + StateSee +}; + + +// A PokerGame would be the main class in any poker game. It controls +// the game flow and has control over all the players. +// +// FIXME: This is very much in flux right now. It is in the process +// of being separated from class kpok. A lot of things need to +// be done. Among them are: +// - Finish separation from kpok. + +class PokerGame { +public: + PokerGame(KRandomSequence *random); + ~PokerGame(); + + PokerGameType getType() const { return m_type; } + void setType(PokerGameType type) { m_type = type; } + + // The state of the current round. + PokerGameState getState() const { return m_state; } + void setState(PokerGameState state) { m_state = state; } + + // Money in the game. + void clearPot() { m_pot = 0; } + int getPot() const { return m_pot; } + void bet(int amount) { m_pot += amount; } + void bet(PokerPlayer * player, int amount); + + int getMinBet() const { return m_minBet; } + int getMaxBet() const { return m_maxBet; } + void setBettingLimits(int minBet, int maxBet) + { m_minBet = minBet; m_maxBet = maxBet; } + + // Players + int getNumPlayers() const { return m_numPlayers; } + int getNumActivePlayers() const { return m_activePlayers.count(); } + int getNumInactivePlayers() const { return m_removedPlayers.count(); } + PokerPlayer * getActivePlayer(unsigned int nr) { return m_activePlayers.at(nr); } + void activatePlayer(PokerPlayer *player); + void inactivatePlayer(PokerPlayer *player); + bool isActivePlayer(PokerPlayer *player) const { return m_activePlayers.contains(player); } + + // Misc + // FIXME: clearDirty should only be called by a save method + // The isDirty flag should only be set internally. + void setDirty() { m_isDirty = true; } + void clearDirty() { m_isDirty = false; } + bool isDirty() const { return m_isDirty; } + + // Some more complex methods. FIXME: These must be expanded! + void newGame(PokerGameType type, + int numPlayers, PokerPlayer *players, + int minBet, int MaxBet); + void newRound(); + + void dealCards(PokerPlayer *player, bool skip[]); + + + private: + + // ---------------------------------------------------------------- + // Properties of the entire game, not just one round: + + PokerGameType m_type; // The current type of game + unsigned int m_numPlayers; // Used for constructing and deleting only + PokerPlayer *m_players; // The players (owned by kpok) + + int m_minBet; // the money the player will bet if he wants or not + int m_maxBet; // max total bet including minBet. + + // True if we need to save before exiting. + // This is the case if the cash has changed for any of the players. + bool m_isDirty; + + // ---------------------------------------------------------------- + // Properties of the current round: + + PokerGameState m_state; // The current phase of the game round + CardDeck m_deck; // The card deck we are using + int m_pot; // The amount of money people have bet. + + // The players in the game. + QPtrList m_activePlayers; // players still in the round + QPtrList m_removedPlayers; // players out of this round +}; + + +// ================================================================ +// Poker Game View + + +class kpok : public QWidget +{ + Q_OBJECT + + public: + kpok(QWidget * parent = 0, const char *name = 0); + virtual ~kpok(); + + QString getName (int playerNr); + void paintCash(); + + bool isDirty() const { return m_game.isDirty(); } + + void setBlinking(bool bl) { m_blinking = bl; } + void setAdjust(bool ad); + void setSound(bool s); + + void updateLHLabel();//temporary function, only called once + + bool getSound() const { return sound; } + bool getBlinking() const { return m_blinking; } + bool getAdjust() const { return adjust; } + + signals: + void changeLastHand(const QString &newHand, bool lastHand = true); + void showClickToHold(bool show); + void statusBarMessage(QString); + void clearStatusBar(); + + protected: + void initWindow(); // called only once + void readOptions(); + void drawCards(PokerPlayer* p, bool skip[]); + void newRound(); + void noMoney(); + void paintEvent( QPaintEvent * ); + void playSound(const QString &filename); + void setBetButtonEnabled(bool enabled); + void setHand(const QString& newHand, bool lastHand = true); + void setLastWinner(const QString& lastWinner); + void startBlinking(); + void stopBlinking(); + void stopDrawing(); + void result(); + void winner(); + + void bet(); + + void displayWin(const QString& hand, int cashWon); + + /** + * Displays the winner, adds the pot to his money + * + * othersPassed = true means all the other players didn't bet + **/ + void displayWinner_Computer(PokerPlayer* winner, bool othersPassed); + + void removePlayerFromRound(PokerPlayer* removePlayer); + void switchToOnePlayerRules(); + + /** + * @return The human player if he is in the game + **/ + PokerPlayer* findHumanPlayer(); + + /** + * This method first reads the config file then starts a + * newGame Dialog if it wasn't forbidden in config + * + * After all options and defaults were set the method starts + * @ref initPoker() with only the number of the players as an + * argument or with a complete player class, depending on the + * options the player chose @return True if successful false + * if player clicked 'cancel' in the new game dialog + **/ + bool readEntriesAndInitPoker(); + + /** + * This should only be done in the see phase and shows the + * cards of the computer players + **/ + void showComputerCards(); + + /** + * The main method for starting the game + * + * It constructs the players if only the number of players + * are given or uses an existing array Then all player boxes + * are being constructed as well as some smaller things like + * the pot + * @param players the number of the players + * @param ownAllPlayers This is used if there is already an array of players existing e.g. from @ref readEntriesAndInitPoker() + **/ + void initPoker(unsigned int numPlayers); + + /** + * Gives all players the deck as a card + **/ + void drawAllDecks(); + + + public slots: + void slotCardDeck(); + void toggleSound(); + void toggleAdjust(); + void toggleBlinking(); + void slotPreferences(); + + bool initSound(); + + /** + * Just as the name says: This method/slot saves the current + * game (to the config file) + * + * The game can be loaded on startup by activating the button + * 'read from config' + **/ + void saveGame(KConfig* conf); + + bool loadGame(KConfig* conf); + bool loadGame(); + // void commandCallback(int id); + void newGame(); + void saveGame(); + + void exchangeCard1(); + void exchangeCard2(); + void exchangeCard3(); + void exchangeCard4(); + void exchangeCard5(); + void drawClick(); + + protected slots: + void bTimerEvent(); + void drawCardsEvent(); + void waveTimerEvent(); + + void betChange(int); + void adjustBet(); + void out(); + + void startWave(); + void stopWave(); + void toggleHeld(); // play a sound + + private: + // The "document" - the game itself + PokerGame m_game; // The game that this widget is showing. + unsigned int m_numPlayers; + PokerPlayer *m_players; // The players + + int cashPerRound; // single player game: the ante + int currentMustBet; // the minimum bet amount + int oldBet_raise; // used for raising only + + int drawDelay; + + // Graphical layout. + QVBoxLayout *topLayout; + QHBoxLayout *inputLayout; + QLabel *potLabel; + BetBox *betBox; + QPushButton *drawButton; // the main Button + QLabel *wonLabel; // the winner + QLabel *clickToHold; + QWidget *mWonWidget; + + PlayerBox **playerBox; //one box per player + + // Dialogs + OptionsDlg* mOptions; + + // Other stuff + KRandomSequence m_random; + + QTimer *blinkTimer; // the winning cards will blink + QTimer *drawTimer; // delay between drawing of the cards + QTimer *waveTimer; // for displaying of the win (if winner == human) + + bool adjust; // allow user to adjust the bet. + int drawStat; // status of drawing (which card already was drawn etc. + + bool sound; + + bool m_blinking; // True if card should blink when winning. + int m_blinkStat; // status of blinking + int m_blinkingBox; // box of winning player + + bool waveActive; + int fCount; + + QString lastHandText; +}; + +#endif diff --git a/kpoker/kpokerui.rc b/kpoker/kpokerui.rc new file mode 100644 index 00000000..3ad1ddcb --- /dev/null +++ b/kpoker/kpokerui.rc @@ -0,0 +1,30 @@ + + + + + &Settings + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kpoker/main.cpp b/kpoker/main.cpp new file mode 100644 index 00000000..b4a27509 --- /dev/null +++ b/kpoker/main.cpp @@ -0,0 +1,54 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include "version.h" +#include "top.h" + +static const char description[] = I18N_NOOP("KDE Poker Game"); + +int main( int argc, char *argv[] ) +{ + KAboutData aboutData("kpoker", I18N_NOOP("KPoker"), + KPOKER_VERSION, description, KAboutData::License_GPL, + "(c) 1997-2003 Jochen Tuchbreiter, Andreas Beckermann\n" + "(c) 2004 Jochen Tuchbreiter, Andreas Beckermann, Inge Wallin", + I18N_NOOP("For a full list of credits see helpfile\nAny suggestions, bug reports etc. are welcome")); + + aboutData.addAuthor("Inge Wallin", + I18N_NOOP("Code for poker rules"), "inge@lysator.liu.se"); + aboutData.addAuthor("Andreas Beckermann", + I18N_NOOP("Current maintainer"), "b_mann@gmx.de"); + aboutData.addAuthor("Jochen Tuchbreiter", + I18N_NOOP("Original author"), "whynot@mabi.de"); + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication myApp; + KGlobal::locale()->insertCatalogue("libkdegames"); + + if (myApp.isRestored()) + RESTORE(PokerWindow) + else { + PokerWindow *MainScreen = new PokerWindow; + myApp.setMainWidget( MainScreen ); + MainScreen->show(); + } + return myApp.exec(); +} diff --git a/kpoker/newgamedlg.cpp b/kpoker/newgamedlg.cpp new file mode 100644 index 00000000..002833b6 --- /dev/null +++ b/kpoker/newgamedlg.cpp @@ -0,0 +1,204 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +// QT includes +#include +#include +#include +#include +#include + +// KDE includes +#include +#include +#include +#include +#include + +// own includes +#include "defines.h" +#include "newgamedlg.h" + + +NewGameDlg::NewGameDlg(QWidget* parent) + : KDialogBase(Plain, i18n("New Game"), + Ok|Cancel, Ok, parent, 0, true, true) +{ + QVBoxLayout *topLayout = new QVBoxLayout(plainPage(), spacingHint()); + QHBoxLayout *l = new QHBoxLayout(topLayout); + + KConfig* conf = kapp->config(); + conf->setGroup("NewGameDlg"); + bool showNewGameDlg = conf->readBoolEntry("showNewGameDlgOnStartup", + SHOWNEWGAME_DEFAULT); + bool readConfig = conf->readBoolEntry("readFromConfig", + LOADGAME_DEFAULT); + int playerNr = conf->readNumEntry("players", DEFAULT_PLAYERS); + int money = conf->readNumEntry("startMoney", START_MONEY); + + readFromConfig = new QCheckBox(i18n("Try loading a game"), plainPage()); + readFromConfig->adjustSize(); + readFromConfig->setChecked(readConfig); + l->addWidget(readFromConfig); + + readFromConfigLabel = new QLabel(i18n("The following values are used if loading from config fails"), plainPage()); + if (!readFromConfig->isChecked()) + readFromConfigLabel->hide(); + readFromConfigLabel->adjustSize(); + l->addWidget(readFromConfigLabel); + connect(readFromConfig, SIGNAL(toggled(bool)), + this, SLOT(changeReadFromConfig(bool))); + + players = new KIntNumInput(playerNr, plainPage()); + players->setRange(1, MAX_PLAYERS); + players->setLabel(i18n("How many players do you want?")); + topLayout->addWidget(players); + + l = new QHBoxLayout(topLayout); + l->addWidget(new QLabel(i18n("Your name:"), plainPage())); + player1Name = new QLineEdit(plainPage()); + l->addWidget(player1Name); + + l = new QHBoxLayout(topLayout); + l->addWidget(new QLabel(i18n("Players' starting money:"), plainPage())); + moneyOfPlayers = new QLineEdit(QString("%1").arg(money), plainPage()); + moneyOfPlayers->setValidator( new KIntValidator( 0,999999,moneyOfPlayers ) ); + + l->addWidget(moneyOfPlayers); + + l = new QHBoxLayout(topLayout); + l->addWidget(new QLabel(i18n("The names of your opponents:"), plainPage())); + computerNames = new QComboBox(true, plainPage()); + computerNames->setInsertionPolicy(QComboBox::AtCurrent); + l->addWidget(computerNames); + + l = new QHBoxLayout(topLayout); + l->addWidget(new QLabel(i18n("Show this dialog every time on startup"), + plainPage())); + showDialogOnStartup = new QCheckBox(plainPage()); + showDialogOnStartup->setChecked(showNewGameDlg); + l->addWidget(showDialogOnStartup); + + setPlayerNames(); +} + + +NewGameDlg::~NewGameDlg() +{ + if (result() == Accepted) { + KConfig* conf = kapp->config(); + conf->setGroup("NewGameDlg"); // defaults for the newGameDlg only + conf->writeEntry("showNewGameDlgOnStartup", showOnStartup()); + conf->writeEntry("readFromConfig", readFromConfigFile()); // just a default! + conf->writeEntry("players", getPlayers()); + conf->writeEntry("startMoney", money()); + } + + //delete the visible elements: + delete readFromConfigLabel; + delete readFromConfig; + delete players; + delete moneyOfPlayers; + delete showDialogOnStartup; + delete player1Name; + delete computerNames; + +} + + +void NewGameDlg::setPlayerNames(int no, QString playerName) +{ + if (no < 0) { + kapp->config()->setGroup("Save"); + player1Name->setText(kapp->config()->readEntry("Name_0", i18n("You"))); + computerNames->clear(); + for (int i = 1; i < MAX_PLAYERS; i++) { + computerNames->insertItem(kapp->config()->readEntry(QString("Name_%1").arg(i), i18n("Computer %1").arg(i))); + } + } else if (no == 0) { + player1Name->setText(playerName); + } else { + if (computerNames->count() > no) + computerNames->insertItem(playerName, no-1); + else + computerNames->changeItem(playerName, no-1); + } +} + + +void NewGameDlg::changeReadFromConfig(bool show) +{ + if (show) + readFromConfigLabel->show(); + else + readFromConfigLabel->hide(); +} + + +bool NewGameDlg::showOnStartup() +{ + return showDialogOnStartup->isChecked(); +} + + +int NewGameDlg::getPlayers() +{ + return players->value(); +} + + +bool NewGameDlg::readFromConfigFile() +{ + return readFromConfig->isChecked(); +} + + +int NewGameDlg::money() +{ + bool ok = true; + int money = moneyOfPlayers->text().toInt(&ok); + if (ok) + return money; + else + return START_MONEY; +} + + +QString NewGameDlg::name(int nr) +{ + if (computerNames->currentText() != computerNames->text(computerNames->currentItem())) + computerNames->changeItem(computerNames->currentText(), computerNames->currentItem()); + + if (nr == 0) + return player1Name->text(); + + if (nr <= computerNames->count()) + return computerNames->text(nr-1); + + return i18n("Player"); +} + + +void NewGameDlg::hideReadingFromConfig() +{ + readFromConfig->hide(); + readFromConfigLabel->hide(); + readFromConfig->setChecked(false); +} + + +#include "newgamedlg.moc" diff --git a/kpoker/newgamedlg.h b/kpoker/newgamedlg.h new file mode 100644 index 00000000..5588f0aa --- /dev/null +++ b/kpoker/newgamedlg.h @@ -0,0 +1,104 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef NEWGAMEDLG_H +#define NEWGAMEDLG_H + + +#include + + +class KIntNumInput; + +class QLineEdit; +class QCheckBox; +class QLabel; +class QComboBox; + + +/** + * This dialog is shown on startup or when a new game is started + * + * Here you can set some options like the default money, players, the names and so on + * @short The dialog shown on startup or when a new game is started + **/ +class NewGameDlg : public KDialogBase +{ + Q_OBJECT + + public: + NewGameDlg(QWidget* parent = 0); + ~NewGameDlg(); + + /** + * @return The player number the user has set + **/ + int getPlayers(); + + /** + * @return True if the user wants to read options from the config file false if not + **/ + bool readFromConfigFile(); + + /** + * You can write the return into a config file which choses to start this dialog on startup + * @return True if the user wants this dialog to be shown on startup + **/ + bool showOnStartup(); + + /** + * @return The start money of all players + **/ + int money(); + + /** + * @param nr The number of the player + * @return The name of the player specified in nr + **/ + QString name(int nr); + + /** + * This method hides the button where the user can choose to read values from config file + * + * It is used when a new game is started - you can load with config file on startup only + **/ + void hideReadingFromConfig(); + + void setPlayerNames(int no = -1, QString playerName = 0); + + + protected slots: + /** + * This slot hides the readFromConfigLabel when the user does not want to read from config or shows it if the user does + * + * The label warns that the other values are used as default values if reading from config fails + **/ + void changeReadFromConfig( bool ); + + + private: + QLabel *readFromConfigLabel; + QCheckBox *readFromConfig; + KIntNumInput *players; + QLineEdit *moneyOfPlayers; + QCheckBox *showDialogOnStartup; + QLineEdit *player1Name; + QComboBox *computerNames; +}; + + +#endif diff --git a/kpoker/optionsdlg.cpp b/kpoker/optionsdlg.cpp new file mode 100644 index 00000000..e009c3d2 --- /dev/null +++ b/kpoker/optionsdlg.cpp @@ -0,0 +1,119 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +// QT includes +#include +#include + +// KDE includes +#include +#include + +// own includes +#include "optionsdlg.h" +#include "defines.h" + + +OptionsDlg::OptionsDlg(QWidget* parent, const char* name, int _players) + : KDialogBase(Plain, i18n("Options")/*?*/, Ok|Cancel, Ok, + parent, name, true, true) +{ + QVBoxLayout* topLayout = new QVBoxLayout(plainPage(), spacingHint()); + maxBet = 0; + minBet = 0; + + if (_players <= 0) + players = DEFAULT_PLAYERS; + else + players = _players; + + topLayout->addWidget(new QLabel(i18n("All changes will be activated in the next round."), plainPage())); + + drawDelay = new KIntNumInput(0, plainPage()); + drawDelay->setLabel(i18n("Draw delay:")); + topLayout->addWidget(drawDelay); + + if (players > 1) { + maxBet = new KIntNumInput(0, plainPage()); + maxBet->setLabel(i18n("Maximal bet:")); + topLayout->addWidget(maxBet); + + minBet = new KIntNumInput(0, plainPage()); + minBet->setLabel(i18n("Minimal bet:")); + topLayout->addWidget(minBet); + } +} + + +OptionsDlg::~OptionsDlg() +{ +} + + +void OptionsDlg::init(int _drawDelay, int _maxBetOrCashPerRound, int _minBet) +{ + if (_minBet < 0) + defaultMinBet = MIN_BET; + if (_maxBetOrCashPerRound < 0 && players > 1) + defaultMaxBet = MAX_BET; + else if (_maxBetOrCashPerRound < 0) + defaultCashPerRound = CASH_PER_ROUND; + if (_drawDelay < 0) + _drawDelay = DRAWDELAY; + + drawDelay->setValue(_drawDelay); + if (maxBet) + maxBet->setValue(_maxBetOrCashPerRound); + if (minBet && players > 1) + minBet->setValue(_minBet); +} + + +int OptionsDlg::getMaxBet() +{ + if (!maxBet || players <= 1) + return defaultMaxBet; + + return maxBet->value(); +} + + +int OptionsDlg::getMinBet() +{ + if (!minBet || players <= 1) + return defaultMinBet; + + return minBet->value(); +} + + +int OptionsDlg::getCashPerRound() +{ + if (!maxBet || players > 1) + return defaultCashPerRound; + + return maxBet->value(); +} + + +int OptionsDlg::getDrawDelay() +{ + return drawDelay->value(); +} + + +#include "optionsdlg.moc" diff --git a/kpoker/optionsdlg.h b/kpoker/optionsdlg.h new file mode 100644 index 00000000..c1573133 --- /dev/null +++ b/kpoker/optionsdlg.h @@ -0,0 +1,68 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef OPTIONSDLG_H +#define OPTIONSDLG_H + +#include + + +class QLineEdit; +class QCheckBox; +class QLabel; +class QComboBox; + +class KIntNumInput; + + +/** + * This dialog is will set some options which mostly become active when a new + * round is started + * + * Here you can set some options like the maxBet, maybe names and so on + * @short The options dialog + **/ +class OptionsDlg : public KDialogBase +{ + Q_OBJECT + + public: + OptionsDlg(QWidget* parent = 0, const char* name = 0, int _players = 1); + ~OptionsDlg(); + + void init(int _drawDelay, int _maxBetOrCashPerRound, int minBet = -1); + int getMaxBet(); + int getMinBet(); + int getCashPerRound(); + int getDrawDelay(); + + private: + int players; + int defaultMaxBet; + int defaultMinBet; + int defaultCashPerRound; + int defaultDrawDelay; + // QLineEdit* maxBet; + // QLineEdit* minBet; + // QLineEdit* drawDelay; + KIntNumInput* maxBet; + KIntNumInput* minBet; + KIntNumInput* drawDelay; +}; + + +#endif diff --git a/kpoker/player.cpp b/kpoker/player.cpp new file mode 100644 index 00000000..9fb4e5d8 --- /dev/null +++ b/kpoker/player.cpp @@ -0,0 +1,292 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include + +#include "player.h" +#include "defines.h" + + +// ================================================================ +// class Player + + +PokerPlayer::PokerPlayer() + : m_hand() +{ + m_name = "Player"; + m_isHuman = false; + + m_money = START_MONEY; + currentBet = 0; + + isOut = false; + // random.setSeed(0); +} + + +PokerPlayer::~PokerPlayer() +{ +} + + +// ---------------------------------------------------------------- + + +void PokerPlayer::giveCardsBack() +{ + m_hand.clear(); +} + + +void PokerPlayer::giveCardBack(int cardNr) +{ + m_hand.clear(cardNr); +} + + +/* Set skip[i] to true if the corresponding card should *not* be + * exchanged. + */ + +void PokerPlayer::exchangeCards(bool skip[]) +{ + //TODO: improve! + // this is still a VERY simple method! + + m_hand.analyze(); + for (int i = 0; i < PokerHandSize; i++) { + skip[i] = m_hand.getFoundCard(i); + } +} + + +/* Prepare the player for a new round. + */ + +void PokerPlayer::newRound() +{ + giveCardsBack(); + + currentBet = 0; + isOut = false; +} + + +int PokerPlayer::bet(int origBet, bool mayRaise) +{ + // NOT useable for (status == continueBet) !!! + // needs a rewrite for games with > 2 players + int bet = origBet; + + // first bet minBet + currentBet = minBet; + m_money -= currentBet; + bet -= currentBet; + int newBet = bet; // bet at least the old bet + + if (bet > getCash())// we don't have the money :-( + return 0; + + // calculate the chances and bet any value + int chance = sortedResult(); + + if (chance < 350) { // 3 of a kind or better!! + newBet = maxBet - (int)random.getLong(maxBet /2);//we subtract a + // random number to hide our cards (every player would + // know we have good cards if we would bet maxBet) + } + else if (chance < 400) { // 2 pairs + newBet = bet + (int)random.getLong(maxBet /2 + origBet); + if (newBet > maxBet) + newBet = maxBet; + } + else if (chance < 500) { // one pair + newBet = bet + (int)random.getLong(maxBet /4 + 1); + if (newBet > getCash() - 2 * minBet) + newBet = bet; + if (bet >= getCash() /3) + newBet = 0; + } + else if (chance < 506) { // best card is at least a ten + newBet = bet; + if (bet >= getCash() /3) + newBet = 0; + } + else { // bad cards + if (getCash() - bet >= bet) {// we would still have some money + newBet = bet; + if (bet >= getCash() /4) + newBet = 0; + } + else + newBet = 0; + } + + // and now a final re-check + if (newBet > bet) { + if (random.getLong(20) == 0) + newBet = bet; + else if (random.getLong(30) <= 1) + newBet = bet + (newBet - bet) /2; + } + + if (newBet > getCash()) + newBet = bet; // maybe raise only a little bit but by now just do not raise + + if (!mayRaise && newBet > bet) + newBet = bet; + + if (!changeBet(newBet)) + return 0; // BIG error + + return currentBet; +} + + +int PokerPlayer::raise(int origRaise) +{ + // NOT useable for (status == continueRaise) !!! + // needs a rewrite for games with > 2 players + int raise = origRaise - getCurrentBet(); + int newRaise = raise; + + if (newRaise > getCash())// we don't have the money :-( + return 0; + + // Calculate the chances and bet any value. + int chance = sortedResult(); + + if (chance < 350) { // 3 of a kind or better!! + newRaise = maxBet - (int)random.getLong(maxBet - maxBet /2); + // we subtract a random number to hide our cards + // (every player would know we have good cards if + // we would bet maxBet) + } + else if (chance < 400) { // 2 pairs + newRaise = raise + (int)random.getLong(maxBet /2 + origRaise + 10); + if (newRaise > maxBet) + newRaise = maxBet; + } + else if (chance < 500) { // one pair + newRaise = raise + (int)random.getLong(maxBet /4 + 1); + if (newRaise > getCash() - 2 * minBet) + newRaise = raise; + if (raise >= getCash() /2) + newRaise = 0; + } + else if (chance < 506) { // best card is at least a ten + newRaise = raise; + if (raise >= getCash() /2) + newRaise = 0; + } + else { // bad cards + if (getCash() - raise >= raise && raise <= minBet * 2) { // we would still have some money + if (raise > getCash() /2) + newRaise = 0; + else + newRaise = raise; + } + else + newRaise = 0; + } + + // And now a final re-check. + if (newRaise > raise) { + if (random.getLong(20) == 0) + newRaise = raise; + else if (random.getLong(30) <= 1) + newRaise = raise + (newRaise - raise) /2; + } + + if (newRaise > getCash()) + newRaise = raise; // maybe raise only a little bit but by now just do not raise + + if (!changeBet(newRaise)) + return 0; // BIG error + + return currentBet; +} + + +bool PokerPlayer::changeBet(int betChange) +{ + if (currentBet + betChange >= 0 && getCash() - betChange >= 0) { + setCash(getCash() - betChange); + currentBet += betChange; + return true; + } + return false; +} + + +int PokerPlayer::sortedResult() +{ + PokerHandType result = m_hand.analyze(); + + //Ok, the result produced by testHand() is a little bit... uncomfortable + //so lets sort it for use in displayWinner_Computer() + //additionally we extend the values e.g. by bestCard and so on + + int newResult = m_hand.getCardScore(); + + // FIXME: Change this so that scores are higher for better hands. + // Don't forget to change m_hand.getCardScore() as well. + switch (result) { + case RoyalFlush: + newResult += 0; // the royal flush is the best you can get + break; + case StraightFlush: + newResult += 50; // straight flush + break; + case FourOfAKind: + newResult += 100; // 4 of a kind + break; + case FullHouse: + newResult += 150; // full house + break; + case Flush: + newResult += 200; // flush + break; + case Straight: + newResult += 250; // straight + break; + case ThreeOfAKind: + newResult += 300; // 3 of a kind + break; + case TwoPairs: + newResult += 350; // two pairs + break; + case Pair: + newResult += 400; // one pair + break; + case HighCard: + { + CardValue bestCard = m_hand.findNextBest(ROOF, false); + newResult = 500 + ((int) H_ACE - (int) bestCard); + } + break; + + default: + // Shouldn't get here. + assert(0); + } + + return newResult; + // The lowest newResult is now the best. +} diff --git a/kpoker/player.h b/kpoker/player.h new file mode 100644 index 00000000..7d3988cf --- /dev/null +++ b/kpoker/player.h @@ -0,0 +1,220 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef PLAYER_H +#define PLAYER_H + +// QT includes +#include + +// KDE includes +#include +#include + +// own includes +#include "poker.h" + + +// ================================================================ +// Player classes + + +class PokerPlayer +{ + public: + PokerPlayer(); + ~PokerPlayer(); + + /** + * Calculates the money which the computer will bet + * + * use @ref changeBet() for human players + * @param bet The minimum bet that the player has to bet. probably the same as the bet the human player has bet before + * @param mayRaise Specifies if the player may raise + * @return The player's bet + **/ + int bet(int bet, bool mayRaise = true); + + /** + * Same as @ref bet() but should bet a little bit more if the cards are good enough (they were already exchanged) + **/ + int raise(int raise); + + /** + * This method changes the player's bet by betChange + * @param Specifies the change + * @return true if successful, false if not + **/ + bool changeBet(int betChange); + + /** + * Only used by computer players + * + * Calculates which cards shall be exchanged + * @param skip[] Will be set true if the card shall be exchanged (if the 1st card shall be exchanged so skip[0] will be true) + **/ + void exchangeCards(bool skip[]); + + /** + * This method will return all cards to the pile which means that all cards will be set to 0 (=deck) + **/ + void giveCardsBack(); + + /** + * Returns a card to the pile + * @param cardNr specifies the card which will be returned to the pile + **/ + void giveCardBack(int cardNr); + + /** + * Begins a new round + **/ + void newRound(); + + /** + * Sets the player's cash to newCash + * @param newCash The new cash + **/ + void setCash(int newCash) { m_money = newCash; } + + /** + * This makes the player human + **/ + void setHuman() { m_isHuman = true; } + + /** + * Sets a new name + * @param newName The new name of the player + **/ + void setName(const QString &newName) { m_name = newName; } + + /** + * Informs the player that he is out (or is not out anymore) + * @param newOut true if player is out or false if player is back to the game + **/ + void setOut(bool newOut) { isOut = newOut; } + + /** + * Takes a card + * @param nr The number of the card (0 = first card) + * @param value The card itself + **/ + void takeCard(int nr, int value) { +#if 0 + const char *suitnames[] = {"C_", "D_", "S_", "H_"}; + if (value > 0) + kdDebug() << "Got card " << suitnames[(value - 1) % 4] + << (value - 1) / 4 + 2 << endl; +#endif + m_hand.setCard(nr, (CardValue) value); + } + + /** + * Informs the player about new rules + * @param min The minimum possible bet + * @param max The maximum possible bet + **/ + void setBetDefaults(int min, int max) { minBet = min; maxBet = max; } + + /** + * @param cardNr The number of the card (0 = first card) + * @return The card + **/ + CardValue getCard(int cardNr) const { return m_hand.getCard(cardNr);} + + PokerHand &getHand() { return m_hand; } + + /** + * @return The money of the player + **/ + int getCash() const { return m_money; } + + /** + * @return How much the player has bet + **/ + int getCurrentBet() const { return currentBet; } + + /** + * Returns the found card at nr + * + * The found cards specify the best cards the player has, e.g. if the player has one pair both cards will be found here + * @param nr The number of the wanted foundCard + * @return The found card number nr + **/ + bool getFoundCard(int nr) const { return m_hand.getFoundCard(nr); } + + /** + * @return If the player is human or not + **/ + bool getHuman() const { return m_isHuman; } + + /** + * @return The name of the player + **/ + QString getName() const { return m_name; } + + // FIXME: Rename to hasFolded? + /** + * @return True if the player is out or false if not + **/ + bool out() { return isOut; } + + + /** + * This test the cards of the player; searches for the result + * + * Used by @ref sortedResult() and in one player games + * @return The result (10 = the best, 0 = nothing) + **/ + PokerHandType testHand() { return m_hand.analyze(); } + + + protected: + + /** + * This sorts the result generated by @ref testHand() a little bit to be used in games with more than one player + * @return The points of the hand (a royal flush is e.g. 0, a best card is 500 + the best card) + **/ + int sortedResult(); + + + private: + // Basic data: + QString m_name; // The name of the player. + bool m_isHuman; // True if the player is human. + + // The hand itself + PokerHand m_hand; + + // The financial situation + int m_money; + int currentBet; + + // True if we are out of the game (have folded). + bool isOut; + + // Properties of the game. + // FIXME: Move this to the game itself. + // FIXME: Add a pointer to the poker game object. + int minBet; + int maxBet; + + // Extra stuff + KRandomSequence random; +}; + +#endif diff --git a/kpoker/playerbox.cpp b/kpoker/playerbox.cpp new file mode 100644 index 00000000..cdfb216c --- /dev/null +++ b/kpoker/playerbox.cpp @@ -0,0 +1,262 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include +#include +#include + +#include +#include +#include + +#include "player.h" +#include "playerbox.h" +#include "defines.h" +#include "kpaint.h" + + +PlayerBox::PlayerBox(bool playerOne, QWidget* parent, const char* name) + : QGroupBox(parent, name) +{ + QHBoxLayout* l = new QHBoxLayout(this, PLAYERBOX_BORDERS, + PLAYERBOX_HDISTANCEOFWIDGETS); + + // The card and "held" label arrays. + m_cardWidgets = new CardWidget *[PokerHandSize]; + m_heldLabels = new QLabel *[PokerHandSize]; + + QFont myFixedFont; + myFixedFont.setPointSize(12); + + // Generate the 5 cards + for (int i = 0; i < PokerHandSize; i++) { + QVBoxLayout* vl = new QVBoxLayout(0); + l->addLayout(vl, 0); + + QHBox* cardBox = new QHBox(this); + vl->addWidget(cardBox, 0); + cardBox->setFrameStyle(Box | Sunken); + m_cardWidgets[i] = new CardWidget(cardBox); + cardBox->setFixedSize(cardBox->sizeHint()); + + // Only add the "held" labels if this is the first player (the human one). + if (playerOne) { + QHBox* b = new QHBox(this); + m_heldLabels[i] = new QLabel(b); + m_heldLabels[i]->setText(i18n("Held")); + b->setFrameStyle(Box | Sunken); + b->setFixedSize(b->sizeHint()); + m_cardWidgets[i]->heldLabel = m_heldLabels[i]; + + QHBoxLayout* heldLayout = new QHBoxLayout(0); + heldLayout->addWidget(b, 0, AlignCenter); + vl->insertLayout(0, heldLayout, 0); + vl->insertStretch(0, 1); + vl->addStretch(1); + } + } + + // Add the cash and bet labels. + { + QVBoxLayout* vl = new QVBoxLayout; + l->addLayout(vl); + vl->addStretch(); + + m_cashLabel = new QLabel(this); + m_cashLabel->setFrameStyle(QFrame::WinPanel | QFrame::Sunken); + m_cashLabel->setFont(myFixedFont); + vl->addWidget(m_cashLabel, 0, AlignHCenter); + vl->addStretch(); + + m_betLabel = new QLabel(this); + m_betLabel->setFrameStyle(QFrame::WinPanel | QFrame::Sunken); + m_betLabel->setFont(myFixedFont); + vl->addWidget(m_betLabel, 0, AlignHCenter); + vl->addStretch(); + } + + QToolTip::add(m_cashLabel, + i18n("Money of %1").arg("Player"));//change via showName() + + // Assume that we have a multiplayer game. + m_singlePlayer = false; +} + + +PlayerBox::~PlayerBox() +{ + delete[] m_cardWidgets; + delete[] m_heldLabels; +} + + +// ---------------------------------------------------------------- + + + +void PlayerBox::resizeEvent(QResizeEvent* e) +{ + QGroupBox::resizeEvent(e); + + showCash(); + showName(); +} + + +void PlayerBox::showCash() +{ + // Show the amount of cash the player has. + m_cashLabel->setText(i18n("Cash: %1") + .arg(KGlobal::locale()->formatMoney(m_player->getCash()))); + + // Show how much we have bet during this round. + if (m_player->out()) + m_betLabel->setText(i18n("Out")); + else { + if (m_singlePlayer) + m_betLabel->setText(i18n("Cash per round: %1") + .arg(KGlobal::locale()->formatMoney(m_cashPerRound))); + else + m_betLabel->setText(i18n("Bet: %1") + .arg(KGlobal::locale()-> formatMoney(m_player->getCurrentBet()))); + } +} + + +// Sshow the name of the player. Suppose that the players name has +// changed. + +void PlayerBox::showName() +{ + setTitle(m_player->getName()); + QToolTip::remove(m_cashLabel); + QToolTip::add(m_cashLabel, i18n("Money of %1").arg(m_player->getName())); +} + + +// Show or unshow all the held labels depending on the 'on' parameter. + +void PlayerBox::showHelds(bool on) +{ + for (int i = 0; i < PokerHandSize; i++) { + if (on) + m_cardWidgets[i]->heldLabel->show(); + else { + m_cardWidgets[i]->heldLabel->hide(); + m_cardWidgets[i]->setHeld(on); + } + } +} + + +void PlayerBox::paintCard(int nr) +{ + m_cardWidgets[nr]->paintCard(m_player->getCard(nr)); + m_cardWidgets[nr]->show(); +} + + +// Activate the held labels for this player (human player). + +void PlayerBox::activateToggleHeld() +{ + for (int i = 0; i < PokerHandSize; i++) { + connect(m_cardWidgets[i], SIGNAL(pClicked(CardWidget*)), + this, SLOT(cardClicked(CardWidget*))); + } +} + + +void PlayerBox::cardClicked(CardWidget* MyCW) +{ + emit toggleHeld(); + if (m_enableHeldLabels && MyCW->toggleHeld()) + MyCW->heldLabel->show(); + else + MyCW->heldLabel->hide(); +} + + +void PlayerBox::paintDeck(int nr) +{ + m_player->giveCardBack(nr); + paintCard(nr); +} + + +void PlayerBox::blinkOn() +{ + for (int i = 0; i < PokerHandSize; i++) { + if (m_player->getFoundCard(i)) + hideCard(i); + } +} + + +void PlayerBox::blinkOff() +{ + for (int i = 0; i < PokerHandSize; i++) { + if (!m_cardWidgets[i]->isVisible()) + paintCard(i); + } +} + + +void PlayerBox::setHeldEnabled(bool on) +{ + m_enableHeldLabels = on; + if (!on) { + for (int i = 0; i < PokerHandSize; i++) + m_heldLabels[i]->hide(); + } +} + + +void PlayerBox::singlePlayerGame(int newCashPerRound) +{ + m_singlePlayer = true; + m_cashPerRound = newCashPerRound; +} + + +void PlayerBox::hideCard(int nr) +{ + m_cardWidgets[nr]->hide(); +} + + +bool PlayerBox::getHeld(int nr) const +{ + return m_cardWidgets[nr]->getHeld(); +} + + +void PlayerBox::cardClicked(int no) +{ + cardClicked(m_cardWidgets[no-1]); +} + + +void PlayerBox::repaintCard() +{ + for (int i = 0; i < PokerHandSize; i++) + m_cardWidgets[i]->repaintDeck(); +} + + +#include "playerbox.moc" diff --git a/kpoker/playerbox.h b/kpoker/playerbox.h new file mode 100644 index 00000000..4d769170 --- /dev/null +++ b/kpoker/playerbox.h @@ -0,0 +1,154 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef PLAYERBOX_H +#define PLAYERBOX_H + + +#include + + +class QLabel; + + +class PokerPlayer; +class CardWidget; + + +class PlayerBox : public QGroupBox +{ + Q_OBJECT + + public: + PlayerBox(bool playerOne, QWidget* parent = 0, const char* name = 0); + ~PlayerBox(); + + void cardClicked(int no); + + /** + * Show the name of the player who owns this box. Use m_player to + * get the name. + **/ + void showName(); + + /** + * Paints the cash + **/ + void showCash(); // and some more + + /** + * Sets the player which is used e.g. by @ref showCash() + * @param p The guy who owns this box + **/ + void setPlayer(PokerPlayer* p) { m_player = p; } + + /** + * Hides the card nr (0 = first card) + * + * Used to let the cards blink. + * @param nr The number of the card which will be hidden + **/ + void hideCard(int nr); + + /** + * @param nr The number of the card (where 0 = first card) + * @return If the card nr shall be held + **/ + bool getHeld(int nr) const; + + /** + * shows all held labels or hides them all + * @param on Shows all all labels if true and hides them if false + **/ + void showHelds(bool on); + + /** + * Enables the held labels if on is true or disables them + * (e.g. after exchange phase) if false + * @param e Enables held labels if true, disables if false + **/ + void setHeldEnabled(bool on); + + // FIXME: Combine these two into paintCard(int nr, bool showFront); + /** + * Paints the card nr + * @param nr The number of the card (where 0 = first card) + **/ + void paintCard(int nr); + + /** + * Paints the deck + * @param nr The number of the card (where 0 = first card) + **/ + void paintDeck(int nr); + + /** + * Starts a one player game + * @param newCashPerRound The cash that the player has to pay every round + **/ + void singlePlayerGame(int newCashPerRound); + + /** + * Activates the held labels for this player (human player) + **/ + void activateToggleHeld(); + + /** + * Begins blinking of the winning cards + **/ + void blinkOn(); + + /** + * Stops blinking of the winning cards + **/ + void blinkOff(); + + void repaintCard(); + + + protected: + virtual void resizeEvent( QResizeEvent* e ); + + + protected slots: + void cardClicked(CardWidget* ); + + + signals: + void toggleHeld(); + + private: + // The player that acts on the hand. + PokerPlayer *m_player; // The player object. + + // Properties of the game + bool m_singlePlayer; // True if this is the only player in the game. + int m_cashPerRound; // one player game only FIXME: Rename into "ante"? + + // The card widgets and "held" widgets + CardWidget **m_cardWidgets; + QLabel **m_heldLabels; + bool m_enableHeldLabels; // True if held labels are enabled. + + // The labels at the right hand side of the box. + QLabel *m_cashLabel; + QLabel *m_betLabel; + + +}; +#endif + diff --git a/kpoker/poker.cpp b/kpoker/poker.cpp new file mode 100644 index 00000000..6143d342 --- /dev/null +++ b/kpoker/poker.cpp @@ -0,0 +1,531 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include + +#include "poker.h" + + +// ================================================================ +// Card classes + + +CardDeck::CardDeck(KRandomSequence *random) +{ + m_random = random; + + reset(); +} + + +CardDeck::~CardDeck() +{ +} + + +// ---------------------------------------------------------------- + + +void +CardDeck::reset() +{ + int i; + CardValue card; + + for (i = 0, card = lowestCard; i < numCards; i++, card = card + 1) + m_cards[i] = card; + m_topCard = 0; + +} + + +void +CardDeck::shuffle() +{ + CardValue tmp; + int card; + + if (m_topCard == numCards) + return; + + for (int i = m_topCard; i < numCards - 1; i++) { + // Choose a random card from the not-yet-shuffled ones. + card = m_random->getLong(numCards - i) + i; + + // Exchange the random card with the current card. + tmp = m_cards[i]; + m_cards[i] = m_cards[card]; + m_cards[card] = tmp; + } +} + + +CardValue +CardDeck::getTopCard() +{ + if (m_topCard == numCards) + return DECK; + + return m_cards[m_topCard++]; +} + + +// ================================================================ +// Poker types + + +QString PokerHandNames[] = { + "High Card", + "Pair", + "Two Pairs", + "Three Of A Kind", + "Straight", + "Flush", + "Full House", + "Four Of A Kind", + "Straight Flush", + "Royal Flush" +}; + + +PokerHand::PokerHand() +{ + clear(); +} + + +PokerHand::~PokerHand() +{ +} + + +// Compare two poker hands, and return true if the first one is less +// valuable than the second one. Otherwise return false. + +bool +PokerHand::operator<(PokerHand &hand2) +{ + CardValue card1; + CardValue card2; + int i; + + // Make sure all relevant fields are initialized. + if (m_changed) + analyze(); + if (hand2.m_changed) + hand2.analyze(); + +#if 0 + kdDebug() << "Hand 1: " << PokerHandNames[(int) m_type] + << " (" << ((int) m_firstRank) + 2 + << ", " << ((int) m_secondRank) + 2 + << ")" << endl; + kdDebug() << "Hand 2: " << PokerHandNames[(int) hand2.m_type] + << " (" << ((int) hand2.m_firstRank) + 2 + << ", " << ((int) hand2.m_secondRank) + 2 + << ")" << endl; +#endif + + // 1. If we have a better hand, then it is simple. + if (m_type != hand2.m_type) + return m_type < hand2.m_type; + + // 2. If the hands are equal, check the internal parts of the hand + // type (like the first and second pair of two pairs). + switch (m_type) { + case HighCard: + case Pair: + case ThreeOfAKind: + case Straight: + case FourOfAKind: + case StraightFlush: + case RoyalFlush: + if (m_firstRank != hand2.m_firstRank) + return m_firstRank < hand2.m_firstRank; + break; + + case TwoPairs: + case FullHouse: + // Compare the first rank first, and then the second. + if (m_firstRank != hand2.m_firstRank) + return m_firstRank < hand2.m_firstRank; + if (m_secondRank != hand2.m_secondRank) + return m_secondRank < hand2.m_secondRank; + break; + + case Flush: + card1 = ROOF; + card2 = ROOF; + for (i = 0; i < PokerHandSize; i++) { + card1 = findNextBest(card1, true); + card2 = hand2.findNextBest(card2, true); + + if (card1 != card2) + return card1 < card2; + } + // If we get here all the card ranks are the same in both hands. + // This means that they have to be of different suits. + break; + + default: + // Shouldn't get here. + assert(0); + } + + // 3. Ok, the hands themselves are the same. Now check if the cards + // outside the hands differ. + card1 = ROOF; + card2 = ROOF; + while (card1 != DECK) { + card1 = findNextBest(card1, false); + card2 = hand2.findNextBest(card2, false); + + if (card1 != card2) + return card1 < card2; + } + + // 4. *Every* rank is the same. Then they must differ in suit. + + card1 = rank2card(m_firstRank + 1); + card2 = rank2card(hand2.m_firstRank + 1); + + return card1 < card2; +} + + +// ---------------------------------------------------------------- +// Ordinary methods + + +// Clear the hand - set all entries to DECK. +void +PokerHand::clear() +{ + for (int i = 0; i < PokerHandSize; i++) + m_cards[i] = DECK; + + m_changed = true; +} + + +/* Throw away card no 'cardno'. + */ + +void +PokerHand::clear(int cardno) +{ + m_cards[cardno] = DECK; + + m_changed = true; +} + + +CardValue +PokerHand::getCard(int cardno) const +{ + return m_cards[cardno]; +} + + +void +PokerHand::setCard(int cardno, CardValue card) +{ + m_cards[cardno] = card; + + m_changed = true; +} + + +bool +PokerHand::findRank(CardRank the_rank) const +{ + for (int i = 0; i < PokerHandSize; i++) + if (rank(m_cards[i]) == the_rank) + return true; + + return false; +} + + +bool +PokerHand::findSuit(CardSuit the_suit) const +{ + for (int i = 0; i < PokerHandSize; i++) + if (suit(m_cards[i]) == the_suit) + return true; + + return false; +} + + +// Return the next lower card value below the card 'roof'. +// +// If 'getFound' is true, then search only those cards that are marked +// as found, i.e. are among those that determine the hand type. +// Otherwise search the cards that are *not* among these. +// + +CardValue +PokerHand::findNextBest(CardValue roof, bool getFound) const +{ + CardValue next; + + next = DECK; + for (int i = 0; i < PokerHandSize; i++) { + if (m_cards[i] > next && m_cards[i] < roof + && (getFound == m_foundCards[i])) + next = m_cards[i]; + } + + return next; +} + + +int +PokerHand::testStraight() +{ + CardRank lowest = ACE; + + /* Set ranks[i] to the value of each card and find the lowest value. */ + for (int i = 0; i < PokerHandSize; i++) { + if (rank(m_cards[i]) < lowest) + lowest = rank(m_cards[i]); + } + + // Look for special cases ace-2-3-4-5. + // (very ugly but fast to write): + if ((findRank(ACE) && findRank(TWO) + && findRank(THREE) && findRank(FOUR) + && findRank(FIVE))) { + m_firstRank = FIVE; + } + else { + + for (int i = 0; i < PokerHandSize; i++) + if (!findRank(lowest + i)) + return 0; // did not find a straight + + m_firstRank = lowest + 4; + } + + // Found a straight. Record it in foundCards[]. + for (int i = 0; i < PokerHandSize; i++) + addFoundCard(i); + + // Check for a royal flush + if (lowest == TEN) + return 2; + + // An ordinary straight: return 1. + return 1; +} + + +bool +PokerHand::testFlush() +{ + int theSuit; + CardValue highest_card; + + highest_card = m_cards[0]; + theSuit = suit(m_cards[0]); + for (int i = 1; i < PokerHandSize; i++) { + if (theSuit != suit(m_cards[i])) + return 0; + + if (m_cards[i] > highest_card) + highest_card = m_cards[i]; + } + + // Found a flush. Now record the cards. + for (int i = 0; i < PokerHandSize; i++) + addFoundCard(i); + m_firstRank = rank(highest_card); + + return true; +} + + +PokerHandType +PokerHand::analyze() +{ + if (m_changed) + m_type = do_analyze(); + + return m_type; +} + + +PokerHandType +PokerHand::do_analyze() +{ + CardValue card1; + CardValue card2; + int i; + int j; + + cleanFoundCards(); + m_changed = 0; + + int isStraight = testStraight(); + + // Detect special cases; + if (isStraight) { + if (testFlush()) { + if (isStraight == 2) + return RoyalFlush; + else + return StraightFlush; + } + else + return Straight; + } + + if (testFlush()) + return Flush; + + /* Find number of matches. */ + int matching = 0; + for (i = 0; i < PokerHandSize; i++) { + for (j = i + 1; j < PokerHandSize; j++) + if (rank(m_cards[i]) == rank(m_cards[j])) { + matching++; + addFoundCard(i); + addFoundCard(j); + } + } + + // The algorithm above gives the following results for each case below. + switch (matching) { + case 0: // High card + card1 = findNextBest(ROOF, false); + m_firstRank = rank(card1); + m_secondRank = (CardRank) -1; + + // In this case, there are no marked cards. Since we need to mark + // the best card, we have to search for it and then mark it. + for (i = 0; i < PokerHandSize; i++) { + if (m_cards[i] == card1) { + addFoundCard(i); + break; + } + } + return HighCard; + + case 1: // Pair + m_firstRank = rank(findNextBest(ROOF, true)); + m_secondRank = (CardRank) -1; + return Pair; + + case 2: // Two pairs + card1 = findNextBest(ROOF, true); + + // Must do this twice, since the first card we get is the second + // card of the first pair. + card2 = findNextBest(card1, true); + card2 = findNextBest(card2, true); + + m_firstRank = rank(card1); + m_secondRank = rank(card2); + return TwoPairs; + + case 3: // 3 of a kind + m_firstRank = rank(findNextBest(ROOF, true)); + return ThreeOfAKind; + + case 4: // Full house + // This is the only tricky case since the value is determined more + // by the 3 of a kind than by the pair. The rank of the 3 of a + // kind can be lower then the pair, though. + + // Get the best and third best cards into val1 and val2. + card1 = findNextBest(ROOF, true); + card2 = findNextBest(card1, true); + card2 = findNextBest(card2, true); + + // Now we have one of two different cases: + // 1. rank(card1) == rank(card2): the 3 of a kind is biggest. + // 2. rank(card1) > rank(card2): the pair is biggest. + if (rank(card1) == rank(card2)) { + m_firstRank = rank(card1); + m_secondRank = rank(findNextBest(card2, true)); + } + else { + m_firstRank = rank(card2); + m_secondRank = rank(card1); + } + return FullHouse; + + case 6: // 4 of a kind + m_firstRank = rank(findNextBest(ROOF, true)); + return FourOfAKind; + + default: + break; + } + + // Shouldn't get here. + assert(0); + + return (PokerHandType) -1; +} + + +// FIXME: When we fix the scores, in raise() and bet() to be higher +// with better hands, then change the "rank(C_ACE) -" stuff +// below. +// +int +PokerHand::getCardScore() const +{ + int score = 0; + + for (int i = 0; i < PokerHandSize; i++) { + if (m_foundCards[i]) + score += rank(C_ACE) - rank(m_cards[i]); + } + + return score; +} + + +bool +PokerHand::getFoundCard(int cardNum) const +{ + return m_foundCards[cardNum]; +} + + + +// ---------------------------------------------------------------- +// PokerHand protected methods + + +void +PokerHand::cleanFoundCards() +{ + for (int i = 0; i < PokerHandSize; i++) + m_foundCards[i] = false; +} + + +void +PokerHand::addFoundCard(int cardNum) +{ + m_foundCards[cardNum] = true; +} diff --git a/kpoker/poker.h b/kpoker/poker.h new file mode 100644 index 00000000..5a944d99 --- /dev/null +++ b/kpoker/poker.h @@ -0,0 +1,234 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef POKER_H +#define POKER_H + +#include +#include +#include + + +// ================================================================ +// Card classes + + +// FIXME: DECK should be renamed to NO_CARD and moved to -1. (or maybe not) +// FIXME: Add Joker as well. +// + +typedef enum { + DECK=0, + C_TWO, D_TWO, S_TWO, H_TWO, + C_THREE, D_THREE, S_THREE, H_THREE, + C_FOUR, D_FOUR, S_FOUR, H_FOUR, + C_FIVE, D_FIVE, S_FIVE, H_FIVE, + C_SIX, D_SIX, S_SIX, H_SIX, + C_SEVEN, D_SEVEN, S_SEVEN, H_SEVEN, + C_EIGHT, D_EIGHT, S_EIGHT, H_EIGHT, + C_NINE, D_NINE, S_NINE, H_NINE, + C_TEN, D_TEN, S_TEN, H_TEN, + C_JACK, D_JACK, S_JACK, H_JACK, + C_QUEEN, D_QUEEN, S_QUEEN, H_QUEEN, + C_KING, D_KING, S_KING, H_KING, + C_ACE, D_ACE, S_ACE, H_ACE, + ROOF // to get a roof on the value. +} CardValue; + +const CardValue lowestCard = C_TWO; +const CardValue highestCard = H_ACE; +const int numCards = (int) H_ACE; + + +typedef enum { + Clubs = 0, // The least valuable suit + Diamonds, + Spades, + Hearts // The most valuable suit +} CardSuit; + + +typedef enum { + TWO = 0, // The least valuable rank + THREE, + FOUR, + FIVE, + SIX, + SEVEN, + EIGHT, + NINE, + TEN, + JACK, + QUEEN, + KING, + ACE // The most valuable rank +} CardRank; + + + +inline CardValue +operator+(CardValue card, int offset) +{ + return (CardValue) ((int) card + offset); +} + + + +// Get the suit from a card. + +inline CardSuit +suit(CardValue card) +{ + // Need to subtract one since DECK == 0. + return (CardSuit) (((int) card - 1) % 4); +} + + +// Get the rank from a card + +inline CardRank +rank(CardValue card) +{ + // Need to subtract one since DECK == 0. + return (CardRank) (((int) card - 1) / 4); +} + + +// Add an offset (can be negative) to a rank. +// +// NOTE: No overflow check is done. This is mainly used for type +// conversion reasons. + +inline CardRank +operator+(CardRank rank1, int offset) +{ + return (CardRank) ((int) rank1 + offset); +} + + +// Get the first card of a certain rank. This is the same rank of clubs. + +inline CardValue +rank2card(CardRank rank) +{ + return (CardValue) (((int) rank) * 4 + 1); +} + +// ---------------------------------------------------------------- +// Class CardDeck + + +class CardDeck { + public: + CardDeck(KRandomSequence *random); + ~CardDeck(); + + void reset(); + void shuffle(); + CardValue getTopCard(); + + private: + CardValue m_cards[numCards]; + int m_topCard; + + KRandomSequence *m_random; +}; + + +// ---------------------------------------------------------------- +// Poker related + + +typedef enum { + HighCard, + Pair, + TwoPairs, + ThreeOfAKind, + Straight, + Flush, + FullHouse, + FourOfAKind, + StraightFlush, + RoyalFlush +} PokerHandType; + +// Name strings for all the hands +extern QString PokerHandNames[]; + + +// Number of cards in the hand. +const int PokerHandSize = 5; + +class PokerHand { + public: + PokerHand(); + ~PokerHand(); + + // Operators + bool operator<(PokerHand &hand2); + + // Clear the hand or just one card - set the entries in question to DECK. + void clear(); + void clear(int cardno); + + // Card stuff + CardValue getCard(int cardno) const; + void setCard(int cardno, CardValue card); + bool findRank(CardRank rank) const; + bool findSuit(CardSuit suit) const; + CardValue findNextBest(CardValue roof, bool onlyFound) const; + + // Poker stuff + int testStraight(); + bool testFlush(); + PokerHandType analyze(); + CardRank get_firstRank() const { return m_firstRank; } + CardRank get_secondRank() const { return m_secondRank; } + + bool getFoundCard(int cardNum) const; + int getCardScore() const; + + + protected: + // Clear the foundCards array. + void cleanFoundCards(); + + // Handle the "found" cards, i.e. those that comprise the scoring part. + void addFoundCard(int cardNum); + + // Analyze the poker hand. This is the most important function of all. + PokerHandType do_analyze(); + + + private: + // Primary data: The cards themselves. + CardValue m_cards[PokerHandSize]; + + // ---------------------------------------------------------------- + // Secondary data: can be derived from the Primary data. + bool m_changed; // true if something has changed since the + // secondary data was last recalculated. + + PokerHandType m_type; // Pair, Two Pairs, etc + CardRank m_firstRank; // Rank of first component + CardRank m_secondRank; // Rank of second component + + bool m_foundCards[PokerHandSize]; // True for all scoring cards +}; + + +#endif diff --git a/kpoker/sound.cpp b/kpoker/sound.cpp new file mode 100644 index 00000000..f029d03a --- /dev/null +++ b/kpoker/sound.cpp @@ -0,0 +1,47 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +#include + +// sound support +#include + +#include "global.h" +#include "kpoker.h" + + +bool kpok::initSound() +{ + sound = true; + return true; +} + +void kpok::playSound(const char *soundname) +{ + if (!sound) + return; + + KAudioPlayer::play(locate("data", QString("kpoker/sounds/")+soundname)); + +} + +void kpok::setSound(bool s) +{ + sound = s; +} + diff --git a/kpoker/sounds/Makefile.am b/kpoker/sounds/Makefile.am new file mode 100644 index 00000000..6ded1881 --- /dev/null +++ b/kpoker/sounds/Makefile.am @@ -0,0 +1,6 @@ + +wavsdir = $(kde_datadir)/kpoker/sounds +wavs_DATA = cardflip.wav hold.wav lose.wav win.wav + +EXTRA_DIST = $(wavs_DATA) + diff --git a/kpoker/sounds/cardflip.wav b/kpoker/sounds/cardflip.wav new file mode 100644 index 00000000..37e7ceb1 Binary files /dev/null and b/kpoker/sounds/cardflip.wav differ diff --git a/kpoker/sounds/hold.wav b/kpoker/sounds/hold.wav new file mode 100644 index 00000000..f6969da1 Binary files /dev/null and b/kpoker/sounds/hold.wav differ diff --git a/kpoker/sounds/lose.wav b/kpoker/sounds/lose.wav new file mode 100644 index 00000000..9f9c72c3 Binary files /dev/null and b/kpoker/sounds/lose.wav differ diff --git a/kpoker/sounds/win.wav b/kpoker/sounds/win.wav new file mode 100644 index 00000000..d7cc5b2a Binary files /dev/null and b/kpoker/sounds/win.wav differ diff --git a/kpoker/top.cpp b/kpoker/top.cpp new file mode 100644 index 00000000..b2e8137f --- /dev/null +++ b/kpoker/top.cpp @@ -0,0 +1,266 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +// QT includes +#include +#include + +// KDE includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// own includes +#include "top.h" +#include "kpoker.h" +#include "defines.h" +#include "version.h" + + +PokerWindow::PokerWindow() +{ + m_kpok = new kpok(this, 0); + setCentralWidget( m_kpok ); + m_kpok->show(); + + clickToHoldIsShown = false; + + LHLabel = new QLabel(statusBar()); + LHLabel->adjustSize(); + + connect(m_kpok, SIGNAL(changeLastHand(const QString &, bool)), + this, SLOT(setHand(const QString &, bool))); + connect(m_kpok, SIGNAL(showClickToHold(bool)), + this, SLOT(showClickToHold(bool))); + connect(m_kpok, SIGNAL(clearStatusBar()), + this, SLOT(clearStatusBar())); + connect(m_kpok, SIGNAL(statusBarMessage(QString)), + this, SLOT(statusBarMessage(QString))); + + statusBar()->addWidget(LHLabel, 0, true); + m_kpok->updateLHLabel(); + //FIXME: LHLabel is shown twize until the bar is repainted! + + initKAction(); + readOptions(); +} + + +PokerWindow::~PokerWindow() +{ +} + + +// ---------------------------------------------------------------- + + +void PokerWindow::initKAction() +{ + //Game + KStdGameAction::gameNew(m_kpok, SLOT(newGame()), actionCollection()); + KStdGameAction::save(m_kpok, SLOT(saveGame()), actionCollection()); + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + + //Settings + showMenubarAction = + KStdAction::showMenubar(this, SLOT(toggleMenubar()), actionCollection()); + + soundAction = new KToggleAction(i18n("Soun&d"), 0, m_kpok, + SLOT(toggleSound()), actionCollection(), "options_sound"); + if (m_kpok->getSound()) + m_kpok->toggleSound(); + blinkingAction = new KToggleAction(i18n("&Blinking Cards"), 0, m_kpok, + SLOT(toggleBlinking()), actionCollection(), "options_blinking"); + if (m_kpok->getBlinking()) + m_kpok->toggleBlinking(); + adjustAction = new KToggleAction(i18n("&Adjust Bet is Default"), 0, + m_kpok, SLOT(toggleAdjust()), actionCollection(), "options_adjust"); + if (m_kpok->getAdjust()) + m_kpok->toggleAdjust(); + + showStatusbarAction = + KStdAction::showStatusbar(this, SLOT(toggleStatusbar()), actionCollection()); + + KStdAction::saveOptions(this, SLOT(saveOptions()), actionCollection()); + KStdGameAction::carddecks(m_kpok, SLOT(slotCardDeck()), actionCollection()); + KStdAction::preferences(m_kpok, SLOT(slotPreferences()), actionCollection()); + + // Keyboard shortcuts. + (void)new KAction(i18n("Draw"), KShortcut(Qt::Key_Return), m_kpok, + SLOT(drawClick()), actionCollection(), "draw"); + (void)new KAction(i18n("Exchange Card 1"), KShortcut(Qt::Key_1), m_kpok, + SLOT(exchangeCard1()), actionCollection(), "exchange_card_1"); + (void)new KAction(i18n("Exchange Card 2"), KShortcut(Qt::Key_2), m_kpok, + SLOT(exchangeCard2()), actionCollection(), "exchange_card_2"); + (void)new KAction(i18n("Exchange Card 3"), KShortcut(Qt::Key_3), m_kpok, + SLOT(exchangeCard3()), actionCollection(), "exchange_card_3"); + (void)new KAction(i18n("Exchange Card 4"), KShortcut(Qt::Key_4), m_kpok, + SLOT(exchangeCard4()), actionCollection(), "exchange_card_4"); + (void)new KAction(i18n("Exchange Card 5"), KShortcut(Qt::Key_5), m_kpok, + SLOT(exchangeCard5()), actionCollection(), "exchange_card_5"); + + setupGUI( KMainWindow::Save | StatusBar | Keys | Create); +} + + +void PokerWindow::readOptions() +{ + KConfig* conf = kapp->config(); + conf->setGroup("General"); + + if (m_kpok->getSound() != conf->readBoolEntry("Sound", true)) + soundAction->activate(); + + if (m_kpok->getBlinking() != conf->readBoolEntry("Blinking", true)) + blinkingAction->activate(); + + if (m_kpok->getAdjust() != conf->readBoolEntry("Adjust", true)) + adjustAction->activate(); + + if ( showMenubarAction->isChecked() != + conf->readBoolEntry("ShowMenubar", true)) + showMenubarAction->activate(); + + if ( showStatusbarAction->isChecked() != + conf->readBoolEntry("ShowStatusbar", true)) + showStatusbarAction->activate(); +} + + +void PokerWindow::toggleMenubar() +{ + if (!menuBar()->isHidden()) + menuBar()->hide(); + else + menuBar()->show(); +} + + +void PokerWindow::toggleStatusbar() +{ + if (!statusBar()->isHidden()) + statusBar()->hide(); + else + statusBar()->show(); +} + + +/* Ask the user if he/she wants to save the game. This virtual method + * is called from the Quit KAction (I think). + */ + +bool PokerWindow::queryClose() +{ + if (!m_kpok->isDirty()) + return true; + + // Only ask if the game is changed in some way. + switch(KMessageBox::warningYesNoCancel(this, i18n("Do you want to save this game?"), QString::null, KStdGuiItem::save(), KStdGuiItem::dontSave())) { + case KMessageBox::Yes : + m_kpok->saveGame(); + return true; + case KMessageBox::No : + return true; + default : + return false; + } +} + + +/* Show the hand or winner in the status bar at the lower right. + * + * Which is shown depends on wether this is a one player game or a two + * player game. + */ + +void PokerWindow::setHand(const QString &newHand, bool lastHand) +{ + if (lastHand) + LHLabel->setText(i18n("Last hand: ") + newHand); + else + LHLabel->setText(i18n("Last winner: ") + newHand); + LHLabel->adjustSize(); +} + + +void PokerWindow::showClickToHold(bool show) +{ + if (show) { + statusBar()->clear(); + statusBar()->message(i18n("Click a card to hold it")); + clickToHoldIsShown = true; + } else if (clickToHoldIsShown) { + statusBar()->clear(); + clickToHoldIsShown = false; + } +} + + +void PokerWindow::statusBarMessage(QString s) +{ + clearStatusBar(); + statusBar()->message(s); + clickToHoldIsShown = false; +} + + +void PokerWindow::clearStatusBar() +{ + if (!clickToHoldIsShown) + statusBar()->clear(); +} + + +void PokerWindow::saveOptions() +{ + KConfig* conf = kapp->config(); + conf->setGroup("General"); + + conf->writeEntry("Sound", soundAction->isChecked()); + conf->writeEntry("Blinking", blinkingAction->isChecked()); + conf->writeEntry("Adjust", adjustAction->isChecked()); + conf->writeEntry("ShowMenubar", showMenubarAction->isChecked()); + conf->writeEntry("ShowStatusbar", showStatusbarAction->isChecked()); +} + + +bool PokerWindow::eventFilter(QObject*, QEvent* e) +{ + if (e->type() == QEvent::MouseButtonPress) { + + if (((QMouseEvent*)e)->button() == RightButton) { + QPopupMenu* popup = (QPopupMenu*) factory()->container("popup", this); + if (popup) + popup->popup(QCursor::pos()); + return true; + } else + return false; + } + + return false; +} + +#include "top.moc" + diff --git a/kpoker/top.h b/kpoker/top.h new file mode 100644 index 00000000..715aa20d --- /dev/null +++ b/kpoker/top.h @@ -0,0 +1,70 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef __PokerWindow__ +#define __PokerWindow__ + + +#include + + +class QLabel; +class KToggleAction; +class kpok; + + +class PokerWindow : public KMainWindow +{ + Q_OBJECT + + public: + PokerWindow(); + ~PokerWindow(); + + protected: + virtual bool queryClose(); + bool eventFilter(QObject*, QEvent*); + void initKAction(); + void readOptions(); + + protected slots: + // void saveProperties(KConfig*); + // void readProperties(KConfig*); + void setHand(const QString &newHand, bool lastHand = true); + void showClickToHold(bool show); + void statusBarMessage(QString); + void clearStatusBar(); + void saveOptions(); + void toggleMenubar(); + void toggleStatusbar(); + + private: + kpok *m_kpok; + + KToggleAction *soundAction; + KToggleAction *blinkingAction; + KToggleAction *adjustAction; + KToggleAction *showMenubarAction; + KToggleAction *showStatusbarAction; + + // statusbar elements: + QLabel *LHLabel; + + bool clickToHoldIsShown; +}; + +#endif diff --git a/kpoker/version.h b/kpoker/version.h new file mode 100644 index 00000000..5d3e1503 --- /dev/null +++ b/kpoker/version.h @@ -0,0 +1,3 @@ +#define KPOKER_VERSION "1.1" +#define KPOKER_RELEASE_DATE "2005-09-15" + diff --git a/kreversi/AUTHORS b/kreversi/AUTHORS new file mode 100644 index 00000000..3964c051 --- /dev/null +++ b/kreversi/AUTHORS @@ -0,0 +1,2 @@ +Mario Weilguni Initial coding +Inge Wallin Cleanups, lots of enhancements diff --git a/kreversi/ChangeLog b/kreversi/ChangeLog new file mode 100644 index 00000000..ad743814 --- /dev/null +++ b/kreversi/ChangeLog @@ -0,0 +1,553 @@ +2006-07-03 Inge Wallin + + * kreversi.cpp (slotGameOver): Set state back to Ready after the + game is finished, and before showing highscore. + +2006-07-03 Inge Wallin + + * version.h (KREVERSI_VERSION): Update version to 1.7.1 for KDE + 3.5.4. + +2006-07-03 Inge Wallin + + * Position.cpp (undoMove): Keep track of score when undoing a + move. + + * qreversigameview.h (removeMove): show game status after removing + a move. + +2006-07-02 Inge Wallin + + * qreversigameview.cpp (moveMade): Print color Red/Blue in + addition to White/Black into the game view if non-BW color is + chosen in the preferences. + + + ---------------------------------------------------------------- + New start of ChangeLogging + ---------------------------------------------------------------- + + +2005-09-15 Inge Wallin + + Bump version number for the release of KDE 3.5 + * version.h: Bump version from 1.6 to 1.7 + +2005-04-04 Inge Wallin + + Fix bug where hint and 'show legal moves' didn't work together. + * board.cpp (showHint): call drawSmallCircle if showLegalMoves is true. + (drawSmallCircle): new private method + New feature: show last move. + * board.cpp (setShowLastMove): new method + + + Refactoring: make showing of legal moves simpler + * board.cpp (showLegalMoves): Take bool for on/off instead of Movelist + (quitShowLegalMoves): removed + (m_legalMovesShowing): new bool member instead of m_legalMoves + * kreversi.cpp (misc): don't call showLegalMoves were not necessary + Fix a bug with 'show legal moves': old ones were never erased. + * board.cpp (showLegalMoves): new method broken out of updateBoard + + Some code cleaning and documentation + * DESIGN: Made documentation up-to-date + * qreversigame.{h,cpp} (updateBoard,turn): removed signals + * qreversigameview.{cpp} (slotNewGame): renamed into newGame + (updateBoard): new method + (updateMovelist): new method (empty yet) + (misc proxy methods): simplified. + + Move over more view stuff to the gameview. + * kreversi.cpp (showMove): Renamed into handleMove, most of it + moved to the view + (slotStateChange): removed slot + (turn(Color), score, stateChange): removed signals + (setState): Do the job of slotStateChange. + * qreversigameview.cpp (moveMade): do the job of showMove + + More control of the view by signals + * kreversi.cpp (showTurn): now catches sig_newGame and sig_update + from the game + (showTurn): new slot + + Let the game view be updated by signals from the game instead of + by explicit calls. + * kreversi.cpp (misc): Don't call updateboard et al. + (showColor): Removed + * qreversigame.cpp (sig_score): Removed. + * qreversigameview.{h,cpp} (slotNewGame, moveMade): new slots + +2005-04-03 Inge Wallin + + Clean up the signals from the game and change some explicit calls + to update the view into signal/slots instead. + * kreversi.{h, cpp} (showScore): removed + * qreversigame.{h,cpp} (sig_newGame, sig_update): new signals + (gameOver): signal renamed into sig_gameOver + * qreversigameview.{h,cpp} (StatusWidget::setText): new method + (createView): New private method. + (updateView): new slot + (updateStatus): new slot + (setHumanColor): new method. + + + Move the status info from the toolbar to the gameview. + * kreversi.{h,cpp} (StatusWidget): Removed class + (createStatusBar): Removed. + (m_krgame): renamed into m_game + * qreversigameview.{h,cpp} (StatusWidget): Added class + + Move the movelist to the gameview. + + Refactor: Create a new class QReversiGameView that will comprise + the entire view. + * Unfortunately the details of the change got lost in some stupid + mistake of mine. + +2005-04-02 Inge Wallin + + Fix bug 102890: The result is not put into the higscore if not all + squares are filled at the end of the game + * kreversi.cpp (KReversi): call slotNewGame + +2005-04-01 Inge Wallin + + Fix bug 102297: I am playing in KReversi as "expert" but it saves + statistics to the "beginner" records + * kreversi.h (m_lowestStrength): Should be uint instead of bool. + +2005-03-31 Inge Wallin + + Implement wish 102813: Should be able to show last move + * board.{h,cpp} (m_showLastMove, lastMoveShown): new members + (setShowLastMove, showLastMove): new methods + (updateBoard): show last move. + * kreversi.{h,cpp} (showLastMoveAction): new toggleaction + (slotShowLastMove): new slot + * kreversiui.rc (show_last_move): new action + + Some consecutive small, but important changes (latest at the top). + * Position::undoMove(): new method. + * Remove m_lastPosition from class Game. + * Rename makeMove() to doMove() and takeBackMove() to undoMove(). + + Big changes in the lower levels of the program. Mostly + simplifications. + * Move.h, Move.cpp (SimpleMove): renamed from Move + (Move): new class with undo information + * Position.h, Position.cpp: allow Move and SimpleMove in various + places. + * Game.h, Game.cpp: allow Move and SimpleMove in various places + +2005-03-30 Inge Wallin + + Continue on wish 82900 + * kreversiui.rc (viewToolBar): new toolbar for the views. + * kreversi.{h,cpp} (showLegalMovesAction): new toggleaction + (slotShowLegalMOves): new slot + (misc): check status of toggle action before showing legal moves + NOTE: This change adds a new toolbar. Before testing you must + make install. + + ---------------------------------------------------------------- + + Start on wish 82900: Show possible moves in the current position + * Move.{h,cpp} (Move): new copy constructor + * Move.h (MoveList): new type + * qreversigame.h (position): new method. + * Position.{h,cpp} (generateMoves): new method + * board.{h,cc} (showLegalMoves, quitShowLegalMoves): new methods + (setMarks): Show also legal moves. + * kreversi.cpp: call showLegalMoves() in various places. + + ---------------------------------------------------------------- + + Implement wish 82517: show moves of the game in a view + * Game.h (Game): Make members protected. + (asString): new method + * qreversigame.cpp (makeMove): emit new signal sig_move . + * kreversi.{h,cpp} (m_movesView): new member + (showMove): new slot + +2005-03-29 Inge Wallin + + Implement wish 82519: Label the board with A-H, 1-8 + * board.cpp (OFFSET): new macro + (m_marksShowing): new member + (setMarks): new method + (mousePressEvent): take into account offset. + (updateBoard): draw markings if m_marksShowing is true + (drawOnePiece): take into account offset. + (adjustSize): take into account markings + + Some cleaning + + ---------------------------------------------------------------- + + Move KReversiGame out to its own file, and remove it. + * qreversigame.{h,cpp}: new files. + (class): Inherit from Game instead of containing it. + (signal score): Rename into sig_score(). + * Makefile.am: include new files. + * board.cpp: Remove class KReversiGame + (all methods): Rename to QReversiGame + + Code cleaning + * Game.{h,cpp} (~Game): new method + +2005-03-28 Inge Wallin + + * kreversi.cpp (KReversi): Fix faulty connect(). + + +================================================================ + KDE 3.4 released +================================================================ + + +2005-02-18 Inge Wallin + + * version.h (KREVERSI_VERSION): Bumped version to 1.6 + +2004-10-31 Inge Wallin + + Better fix for bug 91055. + * kreversi.cpp (slotNewGame): Reimplement dialog using + KMessageBox::warningYesNo(). This solves the FIXME in the header. + +2004-10-15 Inge Wallin + + Fix bug 90472: KReversi: When you interrupt the computers move and + then switch sides, the program gets confused + * kreversi.cpp (slotSwitchSides): Don't allow the user to switch + sides if the computers move is interrupted. + +2004-10-11 Inge Wallin + + Code cleaning + * kreversi.{h,cpp}: Make all members follow the m_ convention. + Also added some comments. + + ----------------- CVS commit on stuff below -------------------- + + Fix bug 91055 - KReversi: If you start a new game when a game is + playing, the user is never asked for confirmation. + * kreversi.cpp (slotNewGame): Show a dialog that asks for + confirmation from the user. + +2004-10-09 Inge Wallin + + Fix bug 90203: KReversi: It should be visible when the user + interrupts the computers thinking. + * kreversi.cpp (slotInterrupt): call showTurn(). + (showTurn): Show "(interrupted)" if it is. + NOTE: This fix can't be backported easily since there is a string + freeze for BRANCH_3_3. + + ----------------- CVS commit on stuff below -------------------- + + Fix a bug that made the score unset at startup. + * kreversi.cpp (KReversi): show the score at startup. + + ----------------- CVS commit on stuff below -------------------- + + Finally make KReversi a proper Model/View program (step I.4 and + I.5 from the plan in the TODO file). + * board.{h,cpp} (KReversiGame): new class + * board.{h,cpp} (Board): new name KReversiBoardView + * Lots of minor cleanup + * DESIGN: (class diagram): new info + + ----------------- CVS commit on stuff below -------------------- + + Some minor cleanup. + +2004-10-03 Inge Wallin + + * DESIGN: New document + + ----------------- CVS commit on stuff below -------------------- + + Simplify saving of the game + * Game.{h,cpp} (move(uint)): New method. + * kreversi.cpp (saveGame): Use the new method, and don't call + loadGame to restore the Game object. + +2004-09-29 Inge Wallin + + Continue to make KReversi a proper model/view program: + Step I.1 of the plan (see TODO): Fix the class Game + * Game.h (Game): Convert to store moves instead of positions. + * Game.cpp (Game): Code cleanup and convert as above. + * Game.{h,cpp}: Follow naming conventions from the rest of the + program. + * Position.{h,cpp}: Follow naming conventions from the rest of the + program. + * Position.{h,cpp} (Position::operator=): new method. + (Position::makeMove): new method. + + Added myself in the credits in the about window. + (Will add myself to the real authors when we have KGame and + network play ready. :-) ) + +2004-09-27 Inge Wallin + + Continue to make KReversi a proper model/view program: + * Transfer ownership of Game and Engine to kreversi from Board. + board.h, board.cpp, kreversi.h, kreversi.cpp: lots of changes. + + Some other cleanup: + * SuperEngine.h (interrupt): renamed to interrupted() + +2004-09-26 Inge Wallin + + Fix bug 90195: KReversi: Changing the skill level late in a game + doesn't count as cheating: + * board.h (Board::m_lowestStrength): new member + * board.cpp (Board::newGame): set m_lowestStrength + * board.cpp (Board::setStrength): update m_lowestStrength and + update highscore type. + + Fix Bug 90190: KReversi: Switch sides and then Undo gets the + program out of sync. + * board.cpp (doUndo): If it is the computers turn to move after an + undo, call computerMakeMove(). + (doUndo): Fix repainting so that it looks nice. + +2004-09-25 Inge Wallin + + Transfer the rest of the slots for KActions to kreversi.cpp + * Board::interrupt() -> KReversi::slotInterrupt() + * Board::doContinue() -> KReversi::slotContinue() + + Rename some slots for clarity + * KReversi::switchSides() -> KReversi::slotSwitchSides() + * KReversi::showSettings() -> KReversi::slotEditSettings() + + Make a trivial function inline: + * Board::interrupt() + +2004-09-24 Inge Wallin + + Start the work to port KReversi to KGame/Kplayer: + + Transfer the slots for most KActions to kreversi.cpp + * Board::undo() -> KReversi::slotUndo() + (Board::doUndo()): Do the real work of undoing. + * Board->hint() -> KReversi::slotHint() + (Board::showHint): do the actual work of showing the hint. + + Rename some slots for clarity + * KReversi::newGame -> KReversi::slotNewGame + * KReversi::openGame -> KReversi::slotOpenGame + * KReversi::save -> KReversi::slotSave + + Make some trivial functions inline: + * Board::whoseTurn() + * Board::moveNumber() + * Board::score(Color) + * Board::interrupted() + * Board::strength() + +2004-09-23 Anne-Marie Mahfouf + + Cleaned some previously left lines of code in board.cpp + Change CustomAdditions=false back in prefs.kcfg to fix compilation + Tested Inges fix and found it works well. + +2004-09-22 Inge Wallin + + Fix bug 89829: "KReversi: When you save a game, the color for + Human and Computer is not saved" again. See the discussion on the + KDE bugzilla for details + (http://bugs.kde.org/show_bug.cgi?id=89829). + * board.cpp (Board::saveGame): Save m_humanColor as HumanColor. + * Remove saving of the side to move since this is implicit + anyway. + * (Board::loadGame): Fix loading of m_humanColor and + m_competitiveGame + * Fix emit of signal turn, and the condition to call + computerMakeMove(). + * prefs_addons.h: Removed + + +2004-09-18 Anne-Marie Mahfouf (ChangeLog entry by Inge Wallin) + + Fix bug 89829. (See above, though) + * prefs_addons.h: New file + * board.cpp (saveGame): Some changes + +2004-09-18 Inge Wallin + + Some cleaning: remove unused members, add m_ to members and some + comments. + * Engine.h (Engine::lastYield): removed + * Board.h (Board::nopaint): removed + * kreversi.{h,cpp} (KReversi::board): renamed into m_board. + +2004-09-17 Inge Wallin + + Some further cleanup: + * board.{h,cpp} (game, engine, human): renamed into m_game, + m_engine and m_humanColor. + +2004-09-16 Inge Wallin + + Support Casual and Competitive play: + * SuperEngine.cpp (SuperEngine::computeMove()): new parameter + 'competitive' + * Engine.cpp (Engine::computeMove()): new parameter 'competitive' + * board.cpp (m_competitiveGame): new member + (saveGame, loadGame): Save competitive in config file. + * kreversi.cpp (slotGameEnded): Only store result in highscore + file if the game was competitive all the time. + * kreversi.kcfg (CompetitiveGameChoice): new setting + * settings.ui: redesigned + + +2004-08-17 Inge Wallin + + Finish the big code cleanup: + * board.h, board.cpp: cleaned up + * highscores.h, highscores.cpp, + * kzoommainwindow.h, kzoommainwindow.cpp: + Converted to same coding style as rest of kreversi. + + +2004-08-16 Inge Wallin + + Continue the big code cleanup: + * Engine.h, Engine.cpp + * kreversi.h kreversi.cpp + + +2004-08-15 Inge Wallin + + Continue the big code cleanup: + * SuperEngine.h, SuperEngine.cpp + * Game.h Game.cpp + +2004-08-14 Inge Wallin + + Start of the big code cleanup: + * Move.h, Move.cpp + * Score.h, Score.cpp + * main.cpp + * Position.h, Position.cpp + + Step 2 in the plan to use KGame from libkdegames: + * Code cleaning: Add some comments, reduce complexity, improve + indentation + * Add m_to_move to class Position. + + + Step 1 in the plan to use KGame from libkdegames: + * Code cleaning: Change "enum Player" into "enum Color", since that + is really what it describes. + +2004-06-29 (1.5) Nicolas Hadacek + * use KZoomMainWindow + +2004-05-29 (1.4) Nicolas Hadacek + * fix statusbar + cleanup code + * use notify framework for sounds + +1999-06-20 Mario Weilguni + * fixed bugs with those damned KStdDirs + * removed the private wallpapers and use the KDE ones instead + * use kimgio + * fixed locating toolbar icons + * compiles now with --enable-final + +1999-06-16 Mario Weilguni + * adapted to the upcoming KDE-2 + +1999-02-01 Mario Weilguni + * fixed a warning (egcs) + +1.0 + * I finally decided that it´s stable enough. This is the final + version (if no further bugs are detected and I do not have a + good idea what to improve) + + * ChangeLog reversed + +0.6.6 + * [Robert Williams] Changed Reversi.kdelnk to kreversi.kdelnk + * [Robert Williams] Add -caption "%c" to kreversi.kdelnk + * [Robert Williams] Added kapp->getCaption() + * [Robert Williams] getHelpMenu(true, 0) -> Uses own About dialog + +0.6.5 Support for non GNUC++ compilers. + +0.6.4 fixed that get-hit-and-then-doubleclick bug + + fixed a bug that caused the computer to switch sides if no + computer move is possible instead of getting another human move + +0.6.3 sound fix: when the animation is finished, the correct piece gets + redrawn before doing a sound-sync (how could I ever believe 0.6.2 + would be the last change :-) + +0.6.2 animation fixed (hope this will be the last change) + +0.6.1 fixed that newly introduced highscore bug + computer continues now if a game was saved while thinking + +0.6: better integration into new FSSTND + fixes for new kdecore + layout management for all dialogs + mini-icon and icon + locale-strings changed - partial translation required for + other languages than english and german + removed both the kfixedtopwidget and ktablistbox + drawing a border around the reversi board + session-management - what an overkill for kreversi :-)) + you can save (and load) exactly ONE game + + +0.5: added klocale to support translation + added german translation + fixed a few bugs + tried to remove absolute widget placing/sizing + removed the date field from the HOF + grayscale support + fixed those CPU busy bug + removed the -finline-functions optimize flag (not portable) + ported to new KDE file system standards (well, mostly) + fixed segfaulting on exit + +0.4: interims release - no changelog + +0.3: Sound support (using libmediatool) + When switching sides, the Quit -> the computer made on move. FIXED + Fixed a few typos + Cursor changes when thinking + +0.2.1: times(NULL) does not work with FreeBSD (fixed) + fixed a bug (reported by Stephan Kulow) where pixmaps of pieces + are not initialized properly (could't reproduce the bug with + my system) + New "About" dialog showing all (well, most) of the contributors + All xpm's have now 8 bits per color component instead of 16. + 16 bits seems to confuse the XPM loader of Qt. + +0.2: better pieces + animations + fixed a small bug: when someone made it in the hall of fame, + he was'nt notified of this + some improvements in the Hall Of Fame + +0.1.2: background color selectable via dialog + background pixmaps implemented, selectable via menu + background pixmaps are scaled to fit size + pieces are drawn at runtime instead of pixmaps + some accelerators added + toolbar buttons for help and hint added + +0.1.1: now pixmaps are installed + a kdelnk file is installed + version numbering changed + +0.1 : First release diff --git a/kreversi/DESIGN b/kreversi/DESIGN new file mode 100644 index 00000000..d2a13202 --- /dev/null +++ b/kreversi/DESIGN @@ -0,0 +1,121 @@ +This file describes the overall design of KReversi. Some of the +classes have different names at this point, but that is indicated by a +(now: XXX) tag. These names will be changed when the KDE project has +converted from CVS to Subversion since Subversion has so much better +support for renaming files and directories. + +Almost all of the classes hold more members than are mentioned here, +but those are implementation details and would only obscure the +overall picture. This file is only to give a helicopter view of the +program, not to serve as detailed documentation. + + +Classes +======= + +Class Diagram (See details below) +------------- + +KReversi----------------------------------------------+ + | | +--------------------------------+ | + | +-----------------+ | | + | | | | + | v v v + | QReversiGameView Engine StatusBar + | | | | (shows whose turn it is) + | +- - - - - - - + | +----------+ + | | v v + | +- - - -QReversiBoardView Other widgets + | | | (movelist, score) + v v v +QReversiGame + X + v + Game + I +=========+ + I I + v v +Position Move[] + + +Legend: +XXXXXXXX> Inherits +========> Contains +--------> Ownership pointer +- - - - > Reference pointer + + +Details +------- + +ReversiPosition (now: Position) + Holds a Reversi position. This is the object that implements the + Reversi rules. + + +ReversiMove (now: Move) + A move in a Reversi game. + + +ReversiGame (now: Game) + Holds all the moves of the game being played. + Contains: ReversiPosition Holds the current position. + Move[60] + FIXME: Remove the ReversiPosition and let it be implicit? + FIXME: Implement variations (later) + + +Engine + Can generate a move, given a ReversiPosition. + + +QReversiGame + The "document" for KReversi. + Handles a game being played and sends signals to all its views + when something changes. Basically, the only difference between + this class and the more basic ReversiGame is that it sends signals + to the views. + Inherits: ReversiGame The actual game being played + + +QReversiGameView (status: PLANNED) + + Shows a view of a complete game. Currently this comprises a board + view, a listbox with a list of the moves, two status widgets + showing the current score. + + Contains: *QReversiBoardView + *QListBox + *StatusWidget (two of them) + + +QReversiBoardView + + A view for a Reversi board. The rest of the game view is + implemented in the class QReversiGameView. + + Inherits: QWidget. + Contains: *QReversiGame (not owner) + + FIXME: Enhance the view with timing information, clock, etc. + + +KReversi + The main class for the KReversi program + Contains: KActions + *QReversiGame (owner) + *KReversiGameView (owner) + *Engine (owner) + Statusbar + + FIXME: Let the class also own two players. + + +EngineView (status: PLANNED) + View of the internal thought processes of an Engine (trace, etc). + + +Some notes +---------- + +* KReversi follows the Model/View principle strictly. diff --git a/kreversi/Engine.cpp b/kreversi/Engine.cpp new file mode 100644 index 00000000..da7750ce --- /dev/null +++ b/kreversi/Engine.cpp @@ -0,0 +1,787 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class Engine produces moves from a Game object through calls to the +// function ComputeMove(). +// +// First of all: this is meant to be a simple example of a game playing +// program. Not everything is done in the most clever way, particularly not +// the way the moves are searched, but it is hopefully made in a way that makes +// it easy to understand. The function ComputeMove2() that does all the work +// is actually not much more than a hundred lines. Much could be done to +// make the search faster though, I'm perfectly aware of that. Feel free +// to experiment. +// +// The method used to generate the moves is called minimax tree search with +// alpha-beta pruning to a fixed depth. In short this means that all possible +// moves a predefined number of moves ahead are either searched or refuted +// with a method called alpha-beta pruning. A more thorough explanation of +// this method could be found at the world wide web at http: +// //yoda.cis.temple.edu:8080/UGAIWWW/lectures96/search/minimax/alpha-beta.html +// at the time this was written. Searching for "minimax" would also point +// you to information on this subject. It is probably possible to understand +// this method by reading the source code though, it is not that complicated. +// +// At every leaf node at the search tree, the resulting position is evaluated. +// Two things are considered when evaluating a position: the number of pieces +// of each color and at which squares the pieces are located. Pieces at the +// corners are valuable and give a high value, and having pieces at squares +// next to a corner is not very good and they give a lower value. In the +// beginning of a game it is more important to have pieces on "good" squares, +// but towards the end the total number of pieces of each color is given a +// higher weight. Other things, like how many legal moves that can be made in a +// position, and the number of pieces that can never be turned would probably +// make the program stronger if they were considered in evaluating a position, +// but that would make things more complicated (this was meant to be very +// simple example) and would also slow down computation (considerably?). +// +// The member m_board[10][10]) holds the current position during the +// computation. It is initiated at the start of ComputeMove() and +// every move that is made during the search is made on this board. It should +// be noted that 1 to 8 is used for the actual board, but 0 and 9 can be +// used too (they are always empty). This is practical when turning pieces +// when moves are made on the board. Every piece that is put on the board +// or turned is saved in the stack m_squarestack (see class SquareStack) so +// every move can easily be reversed after the search in a node is completed. +// +// The member m_bc_board[][] holds board control values for each square +// and is initiated by a call to the function private void SetupBcBoard() +// from Engines constructor. It is used in evaluation of positions except +// when the game tree is searched all the way to the end of the game. +// +// The two members m_coord_bit[9][9] and m_neighbor_bits[9][9] are used to +// speed up the tree search. This goes against the principle of keeping things +// simple, but to understand the program you do not need to understand them +// at all. They are there to make it possible to throw away moves where +// the piece that is played is not adjacent to a piece of opposite color +// at an early stage (because they could never be legal). It should be +// pointed out that not all moves that pass this test are legal, there will +// just be fewer moves that have to be tested in a more time consuming way. +// +// There are also two other members that should be mentioned: Score m_score +// and Score m_bc_score. They hold the number of pieces of each color and +// the sum of the board control values for each color during the search +// (this is faster than counting at every leaf node). +// + +// The classes SquareStackEntry and SquareStack implement a +// stack that is used by Engine to store pieces that are turned during +// searching (see ComputeMove()). +// +// The class MoveAndValue is used by Engine to store all possible moves +// at the first level and the values that were calculated for them. +// This makes it possible to select a random move among those with equal +// or nearly equal value after the search is completed. + + +#include + +#include "Engine.h" + + +// ================================================================ +// Class ULONG64 + + +#if !defined(__GNUC__) + + +ULONG64::ULONG64() : QBitArray(64) +{ + fill(0); +} + + +// Initialize an ULONG64 from a 32 bit value. +// + +ULONG64::ULONG64( unsigned int value ) : QBitArray(64) +{ + fill(0); + for(int i = 0; i < 32; i++) { + setBit(i, (bool)(value & 1)); + value >>= 1; + } +} + + +// Shift an ULONG64 left one bit. +// + +void ULONG64::shl() +{ + for(int i = 63; i > 0; i--) + setBit(i, testBit(i - 1)); + setBit(0, 0); +} + +#endif + + +// ================================================================ +// Classes SquareStackEntry and SquareStack + + +// A SquareStack is used to store changes to the squares on the board +// during search. + + +inline void SquareStackEntry::setXY(int x, int y) { + m_x = x; + m_y = y; +} + + +SquareStackEntry::SquareStackEntry() +{ + setXY(0,0); +} + + +// ---------------------------------------------------------------- + + +SquareStack::SquareStack() { + init(0); +} + + +SquareStack::SquareStack(int size) { + init(size); +} + + +void SquareStack::resize(int size) +{ + m_squarestack.resize(size); +} + + +// (Re)initialize the stack so that is empty, and at the same time +// resize it to 'size'. +// + +void SquareStack::init(int size) +{ + resize(size); + + m_top = 0; + for (int i = 0; i < size; i++) + m_squarestack[i].setXY(0,0); +} + + + +inline SquareStackEntry SquareStack::Pop() +{ + return m_squarestack[--m_top]; +} + + +inline void SquareStack::Push(int x, int y) +{ + m_squarestack[m_top].m_x = x; + m_squarestack[m_top++].m_y = y; +} + + +// ================================================================ +// Class MoveAndValue + + +// Class MoveAndValue aggregates a move with its value. +// + + +inline void MoveAndValue::setXYV(int x, int y, int value) +{ + m_x = x; + m_y = y; + m_value = value; +} + + +MoveAndValue::MoveAndValue() +{ + setXYV(0,0,0); +} + + +MoveAndValue::MoveAndValue(int x, int y, int value) +{ + setXYV(x, y, value); +} + + +// ================================================================ +// The Engine itself + + +// Some special values used in the search. +const int Engine::LARGEINT = 99999; +const int Engine::ILLEGAL_VALUE = 8888888; +const int Engine::BC_WEIGHT = 3; + + +Engine::Engine(int st, int sd) : SuperEngine(st, sd) +{ + SetupBcBoard(); + SetupBits(); +} + + +Engine::Engine(int st) : SuperEngine(st) +{ + SetupBcBoard(); + SetupBits(); +} + + +Engine::Engine() : SuperEngine(1) +{ + SetupBcBoard(); + SetupBits(); +} + + +// keep GUI alive +void Engine::yield() +{ + qApp->processEvents(); +} + + +// Calculate the best move from the current position, and return it. + +Move Engine::computeMove(Game *game, bool competitive) +{ + Color color; + + // A competitive game is one where we try our damnedest to make the + // best move. The opposite is a casual game where the engine might + // make "a mistake". The idea behind this is not to scare away + // newbies. The member m_competitive is used during search for this + // very move. + m_competitive = competitive; + + // Suppose that we should give a heuristic evaluation. If we are + // close to the end of the game we can make an exhaustive search, + // but that case is determined further down. + m_exhaustive = false; + + // Get the color to calculate the move for. + color = game->toMove(); + if (color == Nobody) + return Move(Nobody, -1, -1); + + // Figure out the current score + m_score.set(White, game->score(White)); + m_score.set(Black, game->score(Black)); + + // Treat the first move as a special case (we can basically just + // pick a move at random). + if (m_score.score(White) + m_score.score(Black) == 4) + return ComputeFirstMove(game); + + // Let there be room for 3000 changes during the recursive search. + // This is more than will ever be needed. + m_squarestack.init(3000); + + // Get the search depth. If we are close to the end of the game, + // the number of possible moves goes down, so we can search deeper + // without using more time. + m_depth = m_strength; + if (m_score.score(White) + m_score.score(Black) + m_depth + 3 >= 64) + m_depth = 64 - m_score.score(White) - m_score.score(Black); + else if (m_score.score(White) + m_score.score(Black) + m_depth + 4 >= 64) + m_depth += 2; + else if (m_score.score(White) + m_score.score(Black) + m_depth + 5 >= 64) + m_depth++; + + // If we are very close to the end, we can even make the search + // exhaustive. + if (m_score.score(White) + m_score.score(Black) + m_depth >= 64) + m_exhaustive = true; + + // The evaluation is a linear combination of the score (number of + // pieces) and the sum of the scores for the squares (given by + // m_bc_score). The earlier in the game, the more we use the square + // values and the later in the game the more we use the number of + // pieces. + m_coeff = 100 - (100* + (m_score.score(White) + m_score.score(Black) + + m_depth - 4)) / 60; + + // Initialize the board that we use for the search. + for (uint x = 0; x < 10; x++) + for (uint y = 0; y < 10; y++) { + if (1 <= x && x <= 8 + && 1 <= y && y <= 8) + m_board[x][y] = game->color(x, y); + else + m_board[x][y] = Nobody; + } + + // Initialize a lot of stuff that we will use in the search. + + // Initialize m_bc_score to the current bc score. This is kept + // up-to-date incrementally so that way we won't have to calculate + // it from scratch for each evaluation. + m_bc_score.set(White, CalcBcScore(White)); + m_bc_score.set(Black, CalcBcScore(Black)); + + ULONG64 colorbits = ComputeOccupiedBits(color); + ULONG64 opponentbits = ComputeOccupiedBits(opponent(color)); + + int maxval = -LARGEINT; + int max_x = 0; + int max_y = 0; + + MoveAndValue moves[60]; + int number_of_moves = 0; + int number_of_maxval = 0; + + setInterrupt(false); + + ULONG64 null_bits; + null_bits = 0; + + // The main search loop. Step through all possible moves and keep + // track of the most valuable one. This move is stored in + // (max_x, max_y) and the value is stored in maxval. + m_nodes_searched = 0; + for (int x = 1; x < 9; x++) { + for (int y = 1; y < 9; y++) { + // Don't bother with non-empty squares and squares that aren't + // neighbors to opponent pieces. + if (m_board[x][y] != Nobody + || (m_neighbor_bits[x][y] & opponentbits) == null_bits) + continue; + + + int val = ComputeMove2(x, y, color, 1, maxval, + colorbits, opponentbits); + + if (val != ILLEGAL_VALUE) { + moves[number_of_moves++].setXYV(x, y, val); + + // If the move is better than all previous moves, then record + // this fact... + if (val > maxval) { + + // ...except that we want to make the computer miss some + // good moves so that beginners can play against the program + // and not always lose. However, we only do this if the + // user wants a casual game, which is set in the settings + // dialog. + int randi = m_random.getLong(7); + if (maxval == -LARGEINT + || m_competitive + || randi < (int) m_strength) { + maxval = val; + max_x = x; + max_y = y; + + number_of_maxval = 1; + } + } + else if (val == maxval) + number_of_maxval++; + } + + // Jump out prematurely if interrupt is set. + if (interrupted()) + break; + } + } + + // long endtime = times(&tmsdummy); + + // If there are more than one best move, the pick one randomly. + if (number_of_maxval > 1) { + int r = m_random.getLong(number_of_maxval) + 1; + int i; + + for (i = 0; i < number_of_moves; i++) { + if (moves[i].m_value == maxval && --r <= 0) + break; + } + + max_x = moves[i].m_x; + max_y = moves[i].m_y; + } + + // Return a suitable move. + if (interrupted()) + return Move(Nobody, -1, -1); + else if (maxval != -LARGEINT) + return Move(color, max_x, max_y); + else + return Move(Nobody, -1, -1); +} + + +// Get the first move. We can pick any move at random. +// + +Move Engine::ComputeFirstMove(Game *game) +{ + int r; + Color color = game->toMove(); + + r = m_random.getLong(4) + 1; + + if (color == White) { + if (r == 1) return Move(color, 3, 5); + else if (r == 2) return Move(color, 4, 6); + else if (r == 3) return Move(color, 5, 3); + else return Move(color, 6, 4); + } + else { + if (r == 1) return Move(color, 3, 4); + else if (r == 2) return Move(color, 5, 6); + else if (r == 3) return Move(color, 4, 3); + else return Move(color, 6, 5); + } +} + + +// Play a move at (xplay, yplay) and generate a value for it. If we +// are at the maximum search depth, we get the value by calling +// EvaluatePosition(), otherwise we get it by performing an alphabeta +// search. +// + +int Engine::ComputeMove2(int xplay, int yplay, Color color, int level, + int cutoffval, ULONG64 colorbits, + ULONG64 opponentbits) +{ + int number_of_turned = 0; + SquareStackEntry mse; + Color opponent = ::opponent(color); + + m_nodes_searched++; + + // Put the piece on the board and incrementally update scores and bitmaps. + m_board[xplay][yplay] = color; + colorbits |= m_coord_bit[xplay][yplay]; + m_score.inc(color); + m_bc_score.add(color, m_bc_board[xplay][yplay]); + + // Loop through all 8 directions and turn the pieces that can be turned. + for (int xinc = -1; xinc <= 1; xinc++) + for (int yinc = -1; yinc <= 1; yinc++) { + if (xinc == 0 && yinc == 0) + continue; + + int x, y; + + for (x = xplay + xinc, y = yplay + yinc; m_board[x][y] == opponent; + x += xinc, y += yinc) + ; + + // If we found the end of a turnable row, then go back and turn + // all pieces on the way back. Also push the squares with + // turned pieces on the squarestack so that we can undo the move + // later. + if (m_board[x][y] == color) + for (x -= xinc, y -= yinc; x != xplay || y != yplay; + x -= xinc, y -= yinc) { + m_board[x][y] = color; + colorbits |= m_coord_bit[x][y]; + opponentbits &= ~m_coord_bit[x][y]; + + m_squarestack.Push(x, y); + + m_bc_score.add(color, m_bc_board[x][y]); + m_bc_score.sub(opponent, m_bc_board[x][y]); + number_of_turned++; + } + } + + int retval = -LARGEINT; + + // If we managed to turn at least one piece, then (xplay, yplay) was + // a legal move. Now find out the value of the move. + if (number_of_turned > 0) { + + // First adjust the number of pieces for each side. + m_score.add(color, number_of_turned); + m_score.sub(opponent, number_of_turned); + + // If we are at the bottom of the search, get the evaluation. + if (level >= m_depth) + retval = EvaluatePosition(color); // Terminal node + else { + int maxval = TryAllMoves(opponent, level, cutoffval, opponentbits, + colorbits); + + if (maxval != -LARGEINT) + retval = -maxval; + else { + + // No possible move for the opponent, it is colors turn again: + retval = TryAllMoves(color, level, -LARGEINT, colorbits, opponentbits); + + if (retval == -LARGEINT) { + + // No possible move for anybody => end of game: + int finalscore = m_score.score(color) - m_score.score(opponent); + + if (m_exhaustive) + retval = finalscore; + else { + // Take a sure win and avoid a sure loss (may not be optimal): + + if (finalscore > 0) + retval = LARGEINT - 65 + finalscore; + else if (finalscore < 0) + retval = -(LARGEINT - 65 + finalscore); + else + retval = 0; + } + } + } + } + + m_score.add(opponent, number_of_turned); + m_score.sub(color, number_of_turned); + } + + // Undo the move. Start by unturning the turned pieces. + for (int i = number_of_turned; i > 0; i--) { + mse = m_squarestack.Pop(); + m_bc_score.add(opponent, m_bc_board[mse.m_x][mse.m_y]); + m_bc_score.sub(color, m_bc_board[mse.m_x][mse.m_y]); + m_board[mse.m_x][mse.m_y] = opponent; + } + + // Now remove the new piece that we put down. + m_board[xplay][yplay] = Nobody; + m_score.sub(color, 1); + m_bc_score.sub(color, m_bc_board[xplay][yplay]); + + // Return a suitable value. + if (number_of_turned < 1 || interrupted()) + return ILLEGAL_VALUE; + else + return retval; +} + + +// Generate all legal moves from the current position, and do a search +// to see the value of them. This function returns the value of the +// most valuable move, but not the move itself. +// + +int Engine::TryAllMoves(Color opponent, int level, int cutoffval, + ULONG64 opponentbits, ULONG64 colorbits) +{ + int maxval = -LARGEINT; + + // Keep GUI alive by calling the event loop. + yield(); + + ULONG64 null_bits; + null_bits = 0; + + for (int x = 1; x < 9; x++) { + for (int y = 1; y < 9; y++) { + if (m_board[x][y] == Nobody + && (m_neighbor_bits[x][y] & colorbits) != null_bits) { + int val = ComputeMove2(x, y, opponent, level+1, maxval, opponentbits, + colorbits); + + if (val != ILLEGAL_VALUE && val > maxval) { + maxval = val; + if (maxval > -cutoffval || interrupted()) + break; + } + } + } + + if (maxval > -cutoffval || interrupted()) + break; + } + + if (interrupted()) + return -LARGEINT; + + return maxval; +} + + +// Calculate a heuristic value for the current position. If we are at +// the end of the game, do this by counting the pieces. Otherwise do +// it by combining the score using the number of pieces, and the score +// using the board control values. +// + +int Engine::EvaluatePosition(Color color) +{ + int retval; + + Color opponent = ::opponent(color); + + int score_color = m_score.score(color); + int score_opponent = m_score.score(opponent); + + if (m_exhaustive) + retval = score_color - score_opponent; + else { + retval = (100-m_coeff) * + (m_score.score(color) - m_score.score(opponent)) + + m_coeff * BC_WEIGHT * (m_bc_score.score(color) + - m_bc_score.score(opponent)); + } + + return retval; +} + + +// Calculate bitmaps for each square, and also for neighbors of each +// square. +// + +void Engine::SetupBits() +{ + //m_coord_bit = new long[9][9]; + //m_neighbor_bits = new long[9][9]; + + ULONG64 bits = 1; + + // Store a 64 bit unsigned it with the corresponding bit set for + // each square. + for (int i=1; i < 9; i++) + for (int j=1; j < 9; j++) { + m_coord_bit[i][j] = bits; +#if !defined(__GNUC__) + bits.shl(); +#else + bits *= 2; +#endif + } + + // Store a bitmap consisting of all neighbors for each square. + for (int i=1; i < 9; i++) + for (int j=1; j < 9; j++) { + m_neighbor_bits[i][j] = 0; + + for (int xinc=-1; xinc<=1; xinc++) + for (int yinc=-1; yinc<=1; yinc++) { + if (xinc != 0 || yinc != 0) + if (i + xinc > 0 && i + xinc < 9 && j + yinc > 0 && j + yinc < 9) + m_neighbor_bits[i][j] |= m_coord_bit[i + xinc][j + yinc]; + } + } +} + + +// Set up the board control values that will be used in evaluation of +// the position. +// + +void Engine::SetupBcBoard() +{ + // JAVA m_bc_board = new int[9][9]; + + for (int i=1; i < 9; i++) + for (int j=1; j < 9; j++) { + if (i == 2 || i == 7) + m_bc_board[i][j] = -1; + else + m_bc_board[i][j] = 0; + + if (j == 2 || j == 7) + m_bc_board[i][j] -= 1; + } + + m_bc_board[1][1] = 2; + m_bc_board[8][1] = 2; + m_bc_board[1][8] = 2; + m_bc_board[8][8] = 2; + + m_bc_board[1][2] = -1; + m_bc_board[2][1] = -1; + m_bc_board[1][7] = -1; + m_bc_board[7][1] = -1; + m_bc_board[8][2] = -1; + m_bc_board[2][8] = -1; + m_bc_board[8][7] = -1; + m_bc_board[7][8] = -1; +} + + +// Calculate the board control score. +// + +int Engine::CalcBcScore(Color color) +{ + int sum = 0; + + for (int i=1; i < 9; i++) + for (int j=1; j < 9; j++) + if (m_board[i][j] == color) + sum += m_bc_board[i][j]; + + return sum; +} + + +// Calculate a bitmap of the occupied squares for a certain color. +// + +ULONG64 Engine::ComputeOccupiedBits(Color color) +{ + ULONG64 retval = 0; + + for (int i=1; i < 9; i++) + for (int j=1; j < 9; j++) + if (m_board[i][j] == color) retval |= m_coord_bit[i][j]; + + return retval; +} + diff --git a/kreversi/Engine.h b/kreversi/Engine.h new file mode 100644 index 00000000..a84be895 --- /dev/null +++ b/kreversi/Engine.h @@ -0,0 +1,245 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class Engine produces moves from a Game object through calls to the +// function ComputeMove(). +// +// First of all: this is meant to be a simple example of a game playing +// program. Not everything is done in the most clever way, particularly not +// the way the moves are searched, but it is hopefully made in a way that makes +// it easy to understand. The function ComputeMove2() that does all the work +// is actually not much more than a hundred lines. Much could be done to +// make the search faster though, I'm perfectly aware of that. Feel free +// to experiment. +// +// The method used to generate the moves is called minimax tree search with +// alpha-beta pruning to a fixed depth. In short this means that all possible +// moves a predefined number of moves ahead are either searched or refuted +// with a method called alpha-beta pruning. A more thorough explanation of +// this method could be found at the world wide web at http: +// //yoda.cis.temple.edu:8080/UGAIWWW/lectures96/search/minimax/alpha-beta.html +// at the time this was written. Searching for "minimax" would also point +// you to information on this subject. It is probably possible to understand +// this method by reading the source code though, it is not that complicated. +// +// At every leaf node at the search tree, the resulting position is evaluated. +// Two things are considered when evaluating a position: the number of pieces +// of each color and at which squares the pieces are located. Pieces at the +// corners are valuable and give a high value, and having pieces at squares +// next to a corner is not very good and they give a lower value. In the +// beginning of a game it is more important to have pieces on "good" squares, +// but towards the end the total number of pieces of each color is given a +// higher weight. Other things, like how many legal moves that can be made in a +// position, and the number of pieces that can never be turned would probably +// make the program stronger if they were considered in evaluating a position, +// but that would make things more complicated (this was meant to be very +// simple example) and would also slow down computation (considerably?). +// +// The member m_board[10][10]) holds the current position during the +// computation. It is initiated at the start of ComputeMove() and +// every move that is made during the search is made on this board. It should +// be noted that 1 to 8 is used for the actual board, but 0 and 9 can be +// used too (they are always empty). This is practical when turning pieces +// when moves are made on the board. Every piece that is put on the board +// or turned is saved in the stack m_squarestack (see class SquareStack) so +// every move can easily be reversed after the search in a node is completed. +// +// The member m_bc_board[][] holds board control values for each square +// and is initiated by a call to the function private void SetupBcBoard() +// from Engines constructor. It is used in evaluation of positions except +// when the game tree is searched all the way to the end of the game. +// +// The two members m_coord_bit[9][9] and m_neighbor_bits[9][9] are used to +// speed up the tree search. This goes against the principle of keeping things +// simple, but to understand the program you do not need to understand them +// at all. They are there to make it possible to throw away moves where +// the piece that is played is not adjacent to a piece of opposite color +// at an early stage (because they could never be legal). It should be +// pointed out that not all moves that pass this test are legal, there will +// just be fewer moves that have to be tested in a more time consuming way. +// +// There are also two other members that should be mentioned: Score m_score +// and Score m_bc_score. They hold the number of pieces of each color and +// the sum of the board control values for each color during the search +// (this is faster than counting at every leaf node). +// + +// The classes SquareStackEntry and SquareStack implement a +// stack that is used by Engine to store pieces that are turned during +// searching (see ComputeMove()). +// +// The class MoveAndValue is used by Engine to store all possible moves +// at the first level and the values that were calculated for them. +// This makes it possible to select a random move among those with equal +// or nearly equal value after the search is completed. + +#ifndef __ENGINE__H__ +#define __ENGINE__H__ + +#include "SuperEngine.h" +#include "Position.h" +#include "Game.h" +#include "Move.h" +#include "Score.h" +#include +#include +#include + + +// Class ULONG64 is used as a bitmap for the squares. + +#if defined(__GNUC__) +#define ULONG64 unsigned long long int +#else +class ULONG64 : public QBitArray { +public: + ULONG64(); + ULONG64( unsigned int ); + void shl(); +}; +#endif + + +// SquareStackEntry and SquareStack are used during search to keep +// track of turned pieces. + +class SquareStackEntry +{ +public: + SquareStackEntry(); + + void setXY(int x, int y); + +public: + int m_x; + int m_y; +}; + + +class SquareStack +{ +public: + SquareStack(); + SquareStack(int size); + + void resize(int size); + void init(int size); + SquareStackEntry Pop(); + void Push(int x, int y); + +private: + QMemArray m_squarestack; + int m_top; +}; + + +// Connect a move with its value. + +class MoveAndValue +{ +public: + MoveAndValue(); + MoveAndValue(int x, int y, int value); + + void setXYV(int x, int y, int value); + +public: + int m_x; + int m_y; + int m_value; +}; + + +// The real beef of this program: the engine that finds good moves for +// the computer player. +// +class Engine : public SuperEngine { +public: + Engine(int st, int sd); + Engine(int st); + Engine(); + + Move computeMove(Game *game, bool competitive); + +private: + Move ComputeFirstMove(Game *game); + int ComputeMove2(int xplay, int yplay, Color color, int level, + int cutoffval, + ULONG64 colorbits, ULONG64 opponentbits); + + int TryAllMoves(Color opponent, int level, int cutoffval, + ULONG64 opponentbits, ULONG64 colorbits); + + int EvaluatePosition(Color color); + void SetupBcBoard(); + void SetupBits(); + int CalcBcScore(Color color); + ULONG64 ComputeOccupiedBits(Color color); + + void yield(); + +private: + static const int LARGEINT; + static const int ILLEGAL_VALUE; + static const int BC_WEIGHT; + + Color m_board[10][10]; + int m_bc_board[9][9]; + Score m_score; + Score m_bc_score; + SquareStack m_squarestack; + + int m_depth; + int m_coeff; + int m_nodes_searched; + bool m_exhaustive; + bool m_competitive; + + ULONG64 m_coord_bit[9][9]; + ULONG64 m_neighbor_bits[9][9]; +}; + +#endif diff --git a/kreversi/Game.cpp b/kreversi/Game.cpp new file mode 100644 index 00000000..e389fdd3 --- /dev/null +++ b/kreversi/Game.cpp @@ -0,0 +1,265 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class Game represents a complete or incomplete Othello game. It uses +// the classes Score and Move (and internally Position). +// You can make moves, take back one move at a time, reset to initial position +// and get certain data on the current position. + +// Public functions: + +// public Game() +// Creates a game with the initial position. + +// public void Reset() +// Resets to the initial position. + +// public boolean makeMove(Move &move) +// Makes the move m. Returns false if the move is not legal or when called +// with a move where the player is Score.NOBODY. + +// public boolean TakeBackMove() +// Takes back a move. Returns true if not at the initial position. + +// public int GetSquare(int x, int y) +// Returns the piece at (x, y). Returns Score.NOBODY if the square is not +// occupied. + +// public int GetScore(int player) +// Returns the score for player. + +// public Move GetLastMove() +// Returns the last move. Returns null if at the initial position. + +// public boolean MoveIsLegal(Move m) +// Checks if move m is legal. + +// public boolean MoveIsPossible(int player) +// Checks if there is a legal move for player. + +// public boolean MoveIsAtAllPossible() +// Checks if there are any legal moves at all. + +// public int GetMoveNumber() +// Returns move number. + +// public int GetWhoseTurn() +// Returns the player in turn to play (if there are no legal moves +// Score.NOBODY is returned). + +// public Move[] TurnedByLastMove() +// Returns a vector of the squares that were changed by the last move. +// The move that was actually played is at index 0. At the initial +// position the length of the vector returned is zero. (Could be used +// for faster updates of a graphical board). + + +#include + +#include "Game.h" + + + +Game::Game() +{ + newGame(); +} + +Game::~Game() +{ +} + + +// Start a new game and reset the position to before the first move. +// + +void Game::newGame() +{ + m_position.setupStartPosition(); + m_moveNumber = 0; +} + + +// Return the last move made in the game. +// + +Move Game::lastMove() const +{ + // If no moves where made, return a NULL move. + if (m_moveNumber == 0) + return Move(); + + return m_moves[m_moveNumber - 1]; +} + + +Move +Game::move(uint moveNo) const +{ + assert(moveNo < m_moveNumber); + + return m_moves[moveNo]; +} + + + +// Return true if the move is legal in the current position. +// + +bool Game::moveIsLegal(SimpleMove &move) const +{ + return m_position.moveIsLegal(move); +} + + +// Return true if the color can make a move in the current position. + +bool Game::moveIsPossible(Color color) const +{ + return m_position.moveIsPossible(color); +} + + +// Return true if any side can make a move in the current position. +// + +bool Game::moveIsAtAllPossible() const +{ + return m_position.moveIsAtAllPossible(); +} + + +// Make a move in the game, resulting in a new position. +// +// If everything went well, return true. Otherwise return false and +// do nothing. + +bool Game::doMove(Move &move) +{ + Position lastPos = m_position; + + // Some sanity checks. + if (move.color() == Nobody) + return false; + + if (toMove() != move.color()) + return false; + + // Make the move in the position and store it. Don't allow illegal moves. + if (!m_position.doMove(move)) + return false; + + m_moves[m_moveNumber++] = move; + + return true; +} + + +bool Game::doMove(SimpleMove &smove) +{ + Move move(smove); + + return doMove(move); +} + + +// Take back the last move. +// +// Note: The removed move is not remembered, so a redo is not possible. +// + +bool Game::undoMove() +{ + if (m_moveNumber == 0) + return false; + +#if 0 + m_position.setupStartPosition(); + m_moveNumber--; + for (uint i = 0; i < m_moveNumber; i++) + m_position.doMove(m_moves[i]); +#else + m_position.undoMove(m_moves[--m_moveNumber]); +#endif + + return true; +} + + +// ---------------------------------------------------------------- +// Reversi specific methods + + +// Return true if the square at (x, y) was changed during the last move. +// + +bool Game::squareModified(uint x, uint y) const +{ + // If the move number is zero, we want to redraw all squares. + // That's why we return true here. + if (m_moveNumber == 0) + return true; + + return m_moves[m_moveNumber - 1].squareModified(x, y); +} + + +// Return true if the piece at square (x, y) was turned during the last move. +// + +bool Game::wasTurned(uint x, uint y) const +{ + // Nothing turned before the first move. + if (m_moveNumber == 0) + return false; + + Color color = m_position.color(x, y); + + if (color == Nobody) + return false; + + return m_moves[m_moveNumber - 1].wasTurned(x, y); +} diff --git a/kreversi/Game.h b/kreversi/Game.h new file mode 100644 index 00000000..c33a2096 --- /dev/null +++ b/kreversi/Game.h @@ -0,0 +1,143 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class Game represents a complete or incomplete Othello game. It uses +// the classes Score and Move (and internally Position). +// You can make moves, take back one move at a time, reset to initial position +// and get certain data on the current position. + +// Public functions: + +// public Game() +// Creates a game with the initial position. + +// public void Reset() +// Resets to the initial position. + +// public boolean MakeMove(Move m) +// Makes the move m. Returns false if the move is not legal or when called +// with a move where the player is Score.NOBODY. + +// public boolean TakeBackMove() +// Takes back a move. Returns true if not at the initial position. + +// public int GetSquare(int x, int y) +// Returns the piece at (x, y). Returns Score.NOBODY if the square is not +// occupied. + +// public int GetScore(int player) +// Returns the score for player. + +// public Move GetLastMove() +// Returns the last move. Returns null if at the initial position. + +// public boolean MoveIsLegal(Move m) +// Checks if move m is legal. + +// public boolean MoveIsPossible(int player) +// Checks if there is a legal move for player. + +// public boolean MoveIsAtAllPossible() +// Checks if there are any legal moves at all. + +// public int GetMoveNumber() +// Returns move number. + +// public int GetWhoseTurn() +// Returns the player in turn to play (if there are no legal moves +// Score.NOBODY is returned). + +// public Move[] TurnedByLastMove() +// Returns a vector of the squares that were changed by the last move. +// The move that was actually played is at index 0. At the initial +// position the length of the vector returned is zero. (Could be used +// for faster updates of a graphical board). + + +#ifndef __GAME__H__ +#define __GAME__H__ + + +#include "Score.h" +#include "Move.h" +#include "Position.h" + + +class Game { +public: + Game(); + ~Game(); + + void newGame(); + + Color color(uint x, uint y) const { return m_position.color(x, y); } + uint score(Color color) const { return m_position.score(color); } + Move lastMove() const; + Move move(uint moveNo) const; + + bool moveIsLegal(SimpleMove &move) const; + bool moveIsPossible(Color color) const; + bool moveIsAtAllPossible() const; + bool doMove(Move &move); + bool doMove(SimpleMove &move); + bool undoMove(); + + const Position &position() const { return m_position; } + uint moveNumber() const { return m_moveNumber; } + Color toMove() const { return m_position.toMove(); } + + // Reversi specific methods + bool squareModified(uint x, uint y) const; + bool wasTurned(uint x, uint y) const; + +protected: + Move m_moves[60]; + Position m_position; // The current position in the game + uint m_moveNumber; +}; + + +#endif diff --git a/kreversi/Makefile.am b/kreversi/Makefile.am new file mode 100644 index 00000000..ca13e36f --- /dev/null +++ b/kreversi/Makefile.am @@ -0,0 +1,101 @@ +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) + +bin_PROGRAMS = kreversi + +kreversi_SOURCES = \ + kzoommainwindow.cpp \ + Score.cpp \ + Move.cpp \ + Position.cpp \ + Game.cpp \ + qreversigame.cpp \ + qreversigameview.cpp \ + SuperEngine.cpp \ + Engine.cpp \ + board.cpp \ + settings.ui \ + highscores.cpp \ + kreversi.cpp \ + main.cpp \ + prefs.kcfgc +kreversi_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kreversi_LDADD = $(LIB_KDEGAMES) $(LIB_KIO) +kreversi_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +METASOURCES = AUTO + +rcdir = $(kde_datadir)/kreversi +rc_DATA = kreversiui.rc + +noinst_HEADERS = \ + kzoommainwindow.h \ + Engine.h \ + Game.h \ + qreversigame.h \ + qreversigameview.h \ + kreversi.h \ + Move.h \ + board.h \ + Position.h \ + Score.h \ + version.h \ + SuperEngine.h \ + highscores.h + +SUBDIRS = . pics sounds icons + +xdg_apps_DATA = kreversi.desktop +kde_kcfg_DATA = kreversi.kcfg + + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kreversi.pot + +# for system-wide highscore file +DESTBIN = $(DESTDIR)$(bindir)/$(bin_PROGRAMS) +DESTHIGHSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY) +DESTSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY)/$(bin_PROGRAMS).scores + +install-data-local: + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + echo "********************************************************" ;\ + echo "" ;\ + echo "This game is installed sgid \"games\" to use the" ;\ + echo "system-wide highscore file (in "$(HIGHSCORE_DIRECTORY)")." ;\ + echo "" ;\ + echo "If the system-wide highscore file does not exist, it is" ;\ + echo "created with the correct ownership and permissions. See the" ;\ + echo "INSTALL file in \"kdegames/libkdegames/highscore\" for details." ;\ + echo "" ;\ + echo "********************************************************" ;\ + fi + +install-exec-hook: + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + chown $(highscore_user):$(highscore_group) $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + mkdir -p $(DESTHIGHSCORES) && \ + chown $(highscore_user):$(highscore_group) $(DESTHIGHSCORES) \ + && chmod 750 $(DESTHIGHSCORES) \ + || echo "Error: Could not create the highscore directory with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + chown $(highscore_user):$(highscore_group) $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test ${setgid} = true; then \ + chmod 2755 $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + touch $(DESTSCORES) && chown $(highscore_user):$(highscore_group) $(DESTSCORES) \ + && chmod 0660 $(DESTSCORES) \ + || echo "Error: Could not create system-wide highscore file with correct permissions !!" ;\ + fi + diff --git a/kreversi/Move.cpp b/kreversi/Move.cpp new file mode 100644 index 00000000..3a616647 --- /dev/null +++ b/kreversi/Move.cpp @@ -0,0 +1,118 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#include "Move.h" + + +SimpleMove::SimpleMove(Color color, int x, int y) +{ + m_color = color; + m_x = x; + m_y = y; +} + + +SimpleMove::SimpleMove(const SimpleMove &move) +{ + *this = move; +} + + +QString SimpleMove::asString() const +{ + if (m_x == -1) + return QString("pass"); + else + return QString("%1%2").arg(" ABCDEFGH"[m_x]).arg(" 12345678"[m_y]); +} + + +// ================================================================ + + +Move::Move() + : SimpleMove() +{ + m_turnedPieces.clear(); +} + + +Move::Move(Color color, int x, int y) + : SimpleMove(color, x, y) +{ + m_turnedPieces.clear(); +} + + +Move::Move(const Move &move) + : SimpleMove((SimpleMove&) move) +{ + m_turnedPieces.clear(); +} + + +Move::Move(const SimpleMove &move) + : SimpleMove(move) +{ + m_turnedPieces.clear(); +} + + +// ---------------------------------------------------------------- + + +bool Move::squareModified(uint x, uint y) const +{ + return (m_x == (int) x && m_y == (int) y) || wasTurned(x, y); +} + + +bool Move::wasTurned(uint x, uint y) const +{ + // findIndex returns the first index where the item is found, or -1 + // if not found. + return (m_turnedPieces.findIndex(10 * x + y) != -1); +} diff --git a/kreversi/Move.h b/kreversi/Move.h new file mode 100644 index 00000000..e205f279 --- /dev/null +++ b/kreversi/Move.h @@ -0,0 +1,124 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// This file defines the two classes SimpleMove and Move. +// +// The class Move is used to represent an Othello move with a player value +// (see class Score) and a pair of coordinates on an 8x8 Othello board. +// Each coordinate can have values between 1 and 8, inclusive. +// +// The difference between a Move and a SimpleMove is that a SimpleMove +// can be done (performed) in a Position, but a Move can be both done +// and undone. In addition to the info in SimpleMove, the class Move +// stores information that is used in undoing the move and visualizing +// it. +// +// The reason for the class SimpleMove is that it saves memory. The +// class Game stores an array of Moves, since the BoardView needs +// information about which pieces were turned by the move. +// + + +#ifndef __MOVE__H__ +#define __MOVE__H__ + + +#include "qvaluelist.h" +#include "qstring.h" + +#include "Score.h" + + +class Position; + + +class SimpleMove +{ +public: + SimpleMove() { m_color = Nobody; m_x = -1; m_y = -1; } + SimpleMove(Color color, int x, int y); + SimpleMove(const SimpleMove &move); + + //Move &operator=(Move &move); + + Color color() const { return m_color; } + int x() const { return m_x; } + int y() const { return m_y; } + + QString asString() const; + +protected: + Color m_color; + int m_x; + int m_y; +}; + + +// Note: This class is not memory optimized. The list of turned +// pieces can surely be made much smaller. + +class Move : public SimpleMove +{ + friend class Position; + +public: + Move(); + Move(Color color, int x, int y); + Move(const Move &move); + Move(const SimpleMove &move); + + bool squareModified(uint x, uint y) const; + bool wasTurned(uint x, uint y) const; + +private: + QValueList m_turnedPieces; +}; + + +typedef QValueList MoveList; + + +#endif diff --git a/kreversi/NEWS b/kreversi/NEWS new file mode 100644 index 00000000..8e815ffa --- /dev/null +++ b/kreversi/NEWS @@ -0,0 +1,20 @@ +v0.1pl168: Initial release + +06/14/97 released version 0.1.2 + +06/16/97 released version 0.2 + +06/25/97 released version 0.2.1 + +07/30/97 released version 0.3 + +???????? released version 0.4 (unware about date) + +07/10/97 released version 0.5 + +10/09/97 released 0.6 + +10/12/97 released 0.6.1 (bugfix release) + +see ChangeLog for details + diff --git a/kreversi/Position.cpp b/kreversi/Position.cpp new file mode 100644 index 00000000..22ffb3cf --- /dev/null +++ b/kreversi/Position.cpp @@ -0,0 +1,366 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class Position is used to represent an Othello position as white and +// black pieces and empty squares (see class Score) on an 8x8 Othello board. + + +#include "kdebug.h" + +#include "Position.h" +#include + + +Position::Position() +{ + setupStartPosition(); +} + + +Position::Position(Position &pos, SimpleMove &move) +{ + constrCopy(pos, move); +} + + +Position &Position::operator=(Position &pos) +{ + // Copy the position itself. + for (uint row = 0; row < 10; row++) + for (uint col = 0; col < 10; col++) + m_board[row][col] = pos.m_board[row][col]; + m_toMove = pos.m_toMove; + + m_score = pos.m_score; + + return *this; +} + + +// ---------------------------------------------------------------- +// Helpers to the constructors + + +// Construct a Position by copying another one and then make a move. +// + +void Position::constrCopy(Position &pos, SimpleMove &move) +{ + *this = pos; + doMove(move); +} + + +// Setup the Position by setting it to the initial Reversi position. + +void Position::setupStartPosition() +{ + // Initialize the real board + for (uint i = 0; i < 10; i++) + for (uint j = 0; j < 10; j++) + m_board[i][j] = Nobody; + + // The initial position + m_board[4][4] = White; + m_board[5][5] = White; + m_board[5][4] = Black; + m_board[4][5] = Black; + + // Black always starts the game in Reversi. + m_toMove = Black; + + // Each side starts out with two pieces. + m_score.set(White, 2); + m_score.set(Black, 2); +} + + +// ---------------------------------------------------------------- +// Access methods + + +Color Position::color(uint x, uint y) const +{ + if (x < 1 || x > 8 || y < 1 || y > 8) + return Nobody; + + return m_board[x][y]; +} + + +// ---------------------------------------------------------------- +// Moves in the position + + +// Return true if the move is legal. +// +// NOTE: This function does *NOT* test wether the move is done +// by the color to move. That must be checked separately. +// + +bool Position::moveIsLegal(SimpleMove &move) const +{ + if (m_board[move.x()][move.y()] != Nobody) + return false; + + Color color = move.color(); + Color opponent = ::opponent(color); + + // Check in all directions and see if there is a turnable row of + // opponent pieces. If there is at least one such row, then the + // move is legal. + for (int xinc = -1; xinc <= 1; xinc++) { + for (int yinc = -1; yinc <= 1; yinc++) { + int x, y; + + if (xinc == 0 && yinc == 0) + continue; + + // Find the end of such a row of pieces. + for (x = move.x()+xinc, y = move.y()+yinc; m_board[x][y] == opponent; + x += xinc, y += yinc) + ; + + // If the row ends with a piece of our own and there was at + // least one opponent piece between it and the move point, then + // we have found a turnable row. + if (m_board[x][y] == color + && (x - xinc != move.x() || y - yinc != move.y())) + return true; + } + } + + return false; +} + + +// See if 'color' can make a move in the current position. This is +// independent of wether it is 'color's turn to move or not. + +bool Position::moveIsPossible(Color color) const +{ + // Make it simple: Step through all squares and see if it is a legal move. + for (uint i = 1; i < 9; i++) + for (uint j = 1; j < 9; j++) { + SimpleMove move(color, i, j); + + if (moveIsLegal(move)) + return true; + } + + return false; +} + + +// Return true if any side can move. (If not, the game is over.) + +bool Position::moveIsAtAllPossible() const +{ + return (moveIsPossible(White) || moveIsPossible(Black)); +} + + +// Make a move in the position. +// +// Return true if the move was legal, otherwise return false. +// +bool Position::doMove(SimpleMove &move, QValueList *turned) +{ + if (move.color() == Nobody) + return false; + + Color color = move.color(); + Color opponent = ::opponent(color); + + // Put the piece on the board + m_board[move.x()][move.y()] = color; + m_score.inc(color); + + // Turn pieces. + uint scoreBefore = m_score.score(color); + for (int xinc = -1; xinc <= 1; xinc++) { + for (int yinc = -1; yinc <= 1; yinc++) { + int x, y; + + // Skip the case where both xinc and yinc == 0, since then we + // won't move in any direction at all. + if (xinc == 0 && yinc == 0) + continue; + + // Find the end point (x, y) of a possible row of turnable pieces. + for (x = move.x()+xinc, y = move.y()+yinc; m_board[x][y] == opponent; + x += xinc, y += yinc) + ; + + // If the row was indeed turnable, then do it. + if (m_board[x][y] == color) { + for (x -= xinc, y -= yinc; x != move.x() || y != move.y(); + x -= xinc, y -= yinc) { + // Turn the piece. + m_board[x][y] = color; + if (turned) + turned->append(10 * x + y); + + // Make the piece count correct again. + m_score.inc(color); + m_score.dec(opponent); + } + } + } + } + + // If nothing was turned, the move wasn't legal. + if (m_score.score(color) == scoreBefore) { + m_board[move.x()][move.y()] = Nobody; + m_score.dec(color); + + return false; + } + + // Set the next color to move. + if (moveIsPossible(opponent)) + m_toMove = opponent; + else if (moveIsPossible(color)) + m_toMove = color; + else + m_toMove = Nobody; + + return true; +} + + +bool Position::doMove(Move &move) +{ + move.m_turnedPieces.clear(); + return doMove((SimpleMove &) move, &move.m_turnedPieces); +} + + +bool Position::undoMove(Move &move) +{ + Color color = move.color(); + Color other = opponent(color); + + // Sanity checks + // 1. The move must be on the board and be of the right color. + if (color != m_board[move.x()][move.y()]) { + //kdDebug() << "move on the board is wrong color: " << (int) color << "[" + // << move.x() << "," << move.y() << "]" << endl; + return false; + } + + // 2. All turned pieces must be on the board anb be of the right color. + QValueList::iterator it; + for (it = move.m_turnedPieces.begin(); + it != move.m_turnedPieces.end(); + ++it) { + int sq = *it; + + if (m_board[sq / 10][sq % 10] != color) { + //kdDebug() << "turned piece the board is wrong color: [" + // << sq / 10 << "," << sq % 10 << "]" << endl; + return false; + } + } + + // Ok, everything seems allright. Let's do it! + // 1. Unturn all the turned pieces. + for (it = move.m_turnedPieces.begin(); + it != move.m_turnedPieces.end(); + ++it) { + int sq = *it; + + m_board[sq / 10][sq % 10] = other; + m_score.dec(color); + m_score.inc(other); + } + + // 2. Remove the move itself. + m_score.dec(color); + m_board[move.x()][move.y()] = Nobody; + + + return true; +} + + +MoveList Position::generateMoves(Color color) const +{ + MoveList moves; + + // Make it simple: Step through all squares and see if it is a legal move. + for (uint i = 1; i < 9; i++) { + for (uint j = 1; j < 9; j++) { + Move move(color, i, j); + + if (moveIsLegal(move)) + moves.append(move); + } + } + + return moves; +} + + +QString Position::asString() const +{ + QString result; + + for (uint y = 1; y < 9; ++y) { + for (uint x = 1; x < 9; ++x) { + switch (m_board[x][y]) { + case Nobody: result.append(' '); break; + case Black: result.append('*'); break; + case White: result.append('o'); break; + default: result.append('?'); break; + } + } + + result.append('\n'); + } + + return result; +} diff --git a/kreversi/Position.h b/kreversi/Position.h new file mode 100644 index 00000000..7269c2e6 --- /dev/null +++ b/kreversi/Position.h @@ -0,0 +1,98 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class Position is used to represent an Othello position as white and +// black pieces and empty squares (see class Score) on an 8x8 Othello board. + +#ifndef __POSITION__H__ +#define __POSITION__H__ + + +#include "Move.h" +#include "Score.h" + + +class Position +{ +public: + Position(); + Position(Position &pos, SimpleMove &move); + Position(Position &pos, Move &move); + + Position &operator=(Position &pos); + + void constrCopy(Position &pos, SimpleMove &move); + + void setupStartPosition(); + + // Access methods + Color toMove() const { return m_toMove; } + Color color(uint x, uint y) const; + uint score(Color color) const { return m_score.score(color); } + + // Moves in the current position. + bool moveIsPossible(Color color) const; + bool moveIsAtAllPossible() const; + bool moveIsLegal(SimpleMove &move) const; + bool doMove(SimpleMove &move, QValueList *turned = 0); + bool doMove(Move &move); + bool undoMove(Move &move); + + MoveList generateMoves(Color color) const; + + QString asString() const; + +private: + // The actual position itself. Use the simplest representation possible. + Color m_board[10][10]; + Color m_toMove; + + // Some extra data + Score m_score; // The number of pieces for each side. +}; + + +#endif diff --git a/kreversi/Score.cpp b/kreversi/Score.cpp new file mode 100644 index 00000000..f508152b --- /dev/null +++ b/kreversi/Score.cpp @@ -0,0 +1,70 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#include "Score.h" + + +Score::Score() +{ + m_score[White] = 0; + m_score[Black] = 0; +} + + +/* Return the opponent color for 'color'. + */ + +Color opponent(Color color) +{ + switch (color) { + case White: return Black; + case Black: return White; + case Nobody: break; + } + + return Nobody; +} diff --git a/kreversi/Score.h b/kreversi/Score.h new file mode 100644 index 00000000..947272dc --- /dev/null +++ b/kreversi/Score.h @@ -0,0 +1,78 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#ifndef __SCORE__H__ +#define __SCORE__H__ + +#include + +enum Color { White = 0, Black = 1, NbColors = 2, Nobody = NbColors }; + +Color opponent(Color color); + + +/* This class keeps track of the score for both colors. Such a score + * could be either the number of pieces, the score from the evaluation + * function or anything similar. + */ + +class Score { +public: + Score(); + + uint score(Color color) const { return m_score[color]; } + + void set(Color color, uint score) { m_score[color] = score; } + void inc(Color color) { m_score[color]++; } + void dec(Color color) { m_score[color]--; } + void add(Color color, uint s) { m_score[color] += s; } + void sub(Color color, uint s) { m_score[color] -= s; } + +private: + uint m_score[NbColors]; +}; + +#endif diff --git a/kreversi/SuperEngine.cpp b/kreversi/SuperEngine.cpp new file mode 100644 index 00000000..3da7326f --- /dev/null +++ b/kreversi/SuperEngine.cpp @@ -0,0 +1,133 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class SuperEngine is a super class for engines that produce moves. +// It implements functionality that move engines have in common, which is +// useful if you want to use several different engines in the same program +// (for instance when you are test playing different strategies against each +// other). +// +// SuperEngine implements: +// +// Random number handling (engines should not play exactly the same games +// repeatedly). +// +// Setting playing strength level. +// +// Functionality for telling the engine to interrupt calculation. +// + +// Public and protected functions: + +// public SuperEngine(int st) +// Creates an engine playing at level st. All integers greater than 0 +// should be possible levels. There need not be any actual difference in +// playing strength between levels, but if there is, higher number should +// mean greater playing strength. + +// public SuperEngine(int st, int sd) +// The same as above, but uses sd as the seed for the random generator. +// This means that the engine always behaves in exactly the same way +// (practical when testing). + +// public synchronized final void SetInterrupt(boolean intr) +// This function could be called when ComputeMove() (see below) is +// executing. It tells the engine to interrupt calculation as +// soon as possible and return null from ComputeMove(). + +// protected synchronized final boolean GetInterrupt() +// Returns true when SetInterrupt() has been called. Should be called +// with short intervals from ComputeMove(). + +// public void SetStrength(int st) +// Sets playing strength level. + +// public int GetStrength() +// Gets playing strength level. + +// public void SetSeed(int sd) +// Changes the random seed. + +// public abstract Move ComputeMove(Game g) +// This function should produce a move. If SetInterrupt() is called +// during its execution it should return null as soon as possible. + +// Protected members: + +// protected int m_strength +// Is set and read by SetStrength() and GetStrength(). + +// protected Random m_random +// Could (and should in a good engine) be used to prevent the engine from +// repeating itself, always playing the same moves in the same positions. +// If this is not done, winning once would make it possible to play the +// same moves and win every time against the program. + + +#include "SuperEngine.h" + + +SuperEngine::SuperEngine(int st) +{ + m_strength = st; + m_random.setSeed(0); + m_interrupt = false; +} + + +SuperEngine::SuperEngine(int st, int sd) +{ + m_strength = st; + m_random.setSeed(sd); + m_interrupt = false; +} + + +void SuperEngine::setSeed(int sd) +{ + m_random.setSeed(sd); +} + diff --git a/kreversi/SuperEngine.h b/kreversi/SuperEngine.h new file mode 100644 index 00000000..d3faf6a9 --- /dev/null +++ b/kreversi/SuperEngine.h @@ -0,0 +1,142 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class SuperEngine is a super class for engines that produce moves. +// It implements functionality that move engines have in common, which is +// useful if you want to use several different engines in the same program +// (for instance when you are test playing different strategies against each +// other). +// +// SuperEngine implements: +// +// Random number handling (engines should not play exactly the same games +// repeatedly). +// +// Setting playing strength level. +// +// Functionality for telling the engine to interrupt calculation. +// + +// Public and protected functions: + +// public SuperEngine(int st) +// Creates an engine playing at level st. All integers greater than 0 +// should be possible levels. There need not be any actual difference in +// playing strength between levels, but if there is, higher number should +// mean greater playing strength. + +// public SuperEngine(int st, int sd) +// The same as above, but uses sd as the seed for the random generator. +// This means that the engine always behaves in exactly the same way +// (practical when testing). + +// public synchronized final void SetInterrupt(boolean intr) +// This function could be called when ComputeMove() (see below) is +// executing. It tells the engine to interrupt calculation as +// soon as possible and return null from ComputeMove(). + +// protected synchronized final boolean GetInterrupt() +// Returns true when SetInterrupt() has been called. Should be called +// with short intervals from ComputeMove(). + +// public void SetStrength(int st) +// Sets playing strength level. + +// public int GetStrength() +// Gets playing strength level. + +// public void SetSeed(int sd) +// Changes the random seed. + +// public abstract Move ComputeMove(Game g) +// This function should produce a move. If SetInterrupt() is called +// during its execution it should return null as soon as possible. + +// Protected members: + +// protected int m_strength +// Is set and read by SetStrength() and GetStrength(). + +// protected Random m_random +// Could (and should in a good engine) be used to prevent the engine from +// repeating itself, always playing the same moves in the same positions. +// If this is not done, winning once would make it possible to play the +// same moves and win every time against the program. + +#ifndef __SUPERENGINE__H__ +#define __SUPERENGINE__H__ + +#include "Game.h" + +#include + +class SuperEngine { +public: + SuperEngine(int st); + SuperEngine(int st, int sd); + virtual ~SuperEngine() {} + + void setInterrupt(bool intr) { m_interrupt = intr; } + bool interrupted() const { return m_interrupt; } + + enum Strength { MinStrength = 1, MaxStrength = 7, + NbStrengths = MaxStrength }; + void setStrength(uint st) { m_strength = st; } + uint strength() const { return m_strength; } + + void setSeed(int sd); + + virtual Move computeMove(Game *game, bool competitive) = 0; + +protected: + uint m_strength; + KRandomSequence m_random; + +private: + bool m_interrupt; +}; + +#endif diff --git a/kreversi/TODO b/kreversi/TODO new file mode 100644 index 00000000..4e81f5d1 --- /dev/null +++ b/kreversi/TODO @@ -0,0 +1,87 @@ +TODO-list for KREVERSI +====================== + +Next +---- + +* + + +================================================================ + + +* Implement the plans in DESIGN + + Implement the QReversiGameView class DONE + - Move board view to it done + - Move movelist to it done + - Move status widgets to it done + + Move all showing of legal moves into the BoardView class DONE + + Implement the QEngineView class ---- + +* More cleaning / refactoring + + class KReversi is still a bit of a mess. Separate it more DONE + +* Enhancements to the view + + Letters A-H and figures 1-8 on the board view. DONE + + + Show possible moves in the current position DONE + - Actually show them on the board done + - Create a toggle action to toggle it on/off done + - Make an icon for the toggle action -- + - Bug: legal moves don't get updated if one side has to pass done + - Bug: legal moves don't work together with hint. done + + Show moves made during the game DONE + + + Navigate in the list of moves ---- + + + Wish 102813: Should be able to show last move DONE + - Make an icon for the toggle action -- + - Bug: When turned on, should show last move immediately done + - Bug: When turned off, should unshow last move immediately done + + + Save settings of toggleactions in config file. ---- + +* Convert KReversi to use KGame / KPlayer + I. Convert KReversi to a proper Model/View program. + 1. Fix a ReversiGame (formerly known as Game) DONE + - Clean it up. (Only store the moves). + - Add a few necessary methods. + 2. Move all the slots for KActions to kreversi.cpp DONE + 3. Move the ownership of the engine and the game to kreversi. DONE + 4. Create a new class QReversiGame, that inherits ReversiGame DONE + and sends a lot of signals. + - Split out a lot of methods from the current class Board. done + 5. Create QReversiBoardView from the rest of the current Board DONE + - Clean it done + + II. Introduce a class ReversiPlayer + + III. Convert everything to KGame + 1. Let KReversiGame inherit from KGame + 2. Let ReversiPlayer inherit from KPlayer. + + IV. ... + + V. Profit! + + + + +Old TODO items, partially done/not done +======================================= + +* undo/redo + undo works, but I'll probably do not make a redo function + +* Sound support: + I'm not happy with + the sound files I have so if + you have better sounds, mail them to me + (uuencoded). I need sounds for the following actions: + - game won + - game lost + - game drawn + - turning a piece + - putting a piece + - something for the hall of fame (trumpets???) + diff --git a/kreversi/board.cpp b/kreversi/board.cpp new file mode 100644 index 00000000..9d367a38 --- /dev/null +++ b/kreversi/board.cpp @@ -0,0 +1,576 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "board.h" +#include "prefs.h" +#include "Engine.h" +#include "qreversigame.h" + + +#ifndef PICDATA +#define PICDATA(x) KGlobal::dirs()->findResource("appdata", QString("pics/")+ x) +#endif + +const uint HINT_BLINKRATE = 250000; +const uint ANIMATION_DELAY = 3000; +const uint CHIP_OFFSET[NbColors] = { 24, 1 }; +const uint CHIP_SIZE = 36; + +#define OFFSET() (zoomedSize() * 3 / 4) + + +// ================================================================ +// class KReversiBoardView + + +QReversiBoardView::QReversiBoardView(QWidget *parent, QReversiGame *krgame) + : QWidget(parent, "board"), + chiptype(Unloaded) +{ + m_krgame = krgame; + + m_marksShowing = true; + m_legalMovesShowing = false; + m_legalMoves.clear(); + + m_showLastMove = false; + m_lastMoveShown = SimpleMove(); +} + + +QReversiBoardView::~QReversiBoardView() +{ +} + + +// ---------------------------------------------------------------- + + +// Start it all up. +// + +void QReversiBoardView::start() +{ + updateBoard(true); + adjustSize(); +} + + +void QReversiBoardView::loadChips(ChipType type) +{ + QString name("pics/"); + name += (type==Colored ? "chips.png" : "chips_mono.png"); + + QString s = KGlobal::dirs()->findResource("appdata", name); + bool ok = allchips.load(s); + + Q_ASSERT( ok && allchips.width()==CHIP_SIZE*5 + && allchips.height()==CHIP_SIZE*5 ); + chiptype = type; + update(); +} + + +// Negative speed is allowed. If speed is negative, +// no animations are displayed. +// + +void QReversiBoardView::setAnimationSpeed(uint speed) +{ + if (speed <= 10) + anim_speed = speed; +} + + +// Handle mouse clicks. +// + +void QReversiBoardView::mousePressEvent(QMouseEvent *e) +{ + // Only handle left button. No context menu. + if ( e->button() != LeftButton ) { + e->ignore(); + return; + } + + int offset = m_marksShowing ? OFFSET() : 0; + int px = e->pos().x()- 1 - offset; + int py = e->pos().y()- 1 - offset; + + if (px < 0 || px >= 8 * (int) zoomedSize() + || py < 0 || py >= 8 * (int) zoomedSize()) { + e->ignore(); + return; + } + + emit signalSquareClicked(py / zoomedSize(), px / zoomedSize()); +} + + +void QReversiBoardView::showHint(Move move) +{ + // Only show a hint if there is a move to show. + if (move.x() == -1) + return; + + // Blink with a piece at the location where the hint move points. + // + // The isVisible condition has been added so that when the player + // was viewing a hint and quits the game window, the game doesn't + // still have to do all this looping and directly ends. + QPainter p(this); + p.setPen(black); + m_hintShowing = true; + for (int flash = 0; + flash < 100 && m_hintShowing && isVisible(); + flash++) + { + if (flash & 1) { + // FIXME: Draw small circle if showLegalMoves is turned on. + drawPiece(move.y() - 1, move.x() - 1, Nobody); + if (m_legalMovesShowing) + drawSmallCircle(move.x(), move.y(), p); + } + else + drawPiece(move.y() - 1, move.x() - 1, m_krgame->toMove()); + + // keep GUI alive while waiting + for (int dummy = 0; dummy < 5; dummy++) { + usleep(HINT_BLINKRATE / 5); + qApp->processEvents(); + } + } + m_hintShowing = false; + + // Draw the empty square again. + drawPiece(move.y() - 1, move.x() - 1, m_krgame->color(move.x(), move.y())); + if (m_legalMovesShowing) + drawSmallCircle(move.x(), move.y(), p); +} + + +// Set the member m_hintShowing to false. This will make showHint() +// end, if it is running. +// + +void QReversiBoardView::quitHint() +{ + m_hintShowing = false; +} + + +void QReversiBoardView::setShowLegalMoves(bool show) +{ + m_legalMovesShowing = show; + updateBoard(true); +} + +void QReversiBoardView::setShowMarks(bool show) +{ + m_marksShowing = show; + updateBoard(true); +} + + +void QReversiBoardView::setShowLastMove(bool show) +{ + m_showLastMove = show; + updateBoard(true); +} + + +// ================================================================ +// Functions related to drawing/painting + + +// Flash all pieces which are turned. +// +// NOTE: This code is quite a hack. Should make it better. +// + +void QReversiBoardView::animateChanged(Move move) +{ + if (anim_speed == 0) + return; + + // Draw the new piece. + drawPiece(move.y() - 1, move.x() - 1, move.color()); + + // Animate row by row in all directions. + for (int dx = -1; dx < 2; dx++) + for (int dy = -1; dy < 2; dy++) + if ((dx != 0) || (dy != 0)) + animateChangedRow(move.y() - 1, move.x() - 1, dy, dx); +} + + +bool QReversiBoardView::isField(int row, int col) const +{ + return ((0 <= row) && (row < 8) && (0 <= col) && (col < 8)); +} + + +void QReversiBoardView::animateChangedRow(int row, int col, int dy, int dx) +{ + row = row + dy; + col = col + dx; + while (isField(row, col)) { + if (m_krgame->wasTurned(col+1, row+1)) { + KNotifyClient::event(winId(), "click", i18n("Click")); + rotateChip(row, col); + } else + return; + + col += dx; + row += dy; + } +} + + +void QReversiBoardView::rotateChip(uint row, uint col) +{ + // Check which direction the chip has to be rotated. If the new + // chip is white, the chip was black first, so lets begin at index + // 1, otherwise it was white. + Color color = m_krgame->color(col+1, row+1); + uint from = CHIP_OFFSET[opponent(color)]; + uint end = CHIP_OFFSET[color]; + int delta = (color==White ? 1 : -1); + + from += delta; + end -= delta; + + for (uint i = from; i != end; i += delta) { + drawOnePiece(row, col, i); + kapp->flushX(); // FIXME: use QCanvas to avoid flicker... + usleep(ANIMATION_DELAY * anim_speed); + } +} + + +// Redraw the board. If 'force' is true, redraw everything, otherwise +// only redraw those squares that have changed (marked by +// m_krgame->squareModified(col, row)). +// + +void QReversiBoardView::updateBoard (bool force) +{ + QPainter p(this); + p.setPen(black); + + // If we are showing legal moves, we have to erase the old ones + // before we can show the new ones. The easiest way to do that is + // to repaint everything. + // + // FIXME: A better way, perhaps, is to do the repainting in + // drawPiece (which should be renamed drawSquare). + if (m_legalMovesShowing) + force = true; + + // Draw the squares of the board. + for (uint row = 0; row < 8; row++) + for (uint col = 0; col < 8; col++) + if ( force || m_krgame->squareModified(col + 1, row + 1) ) { + Color color = m_krgame->color(col + 1, row + 1); + drawPiece(row, col, color); + } + + // Draw a border around the board. + int offset = m_marksShowing ? OFFSET() : 0; + p.drawRect(0 + offset, 0 + offset, + 8 * zoomedSize() + 2, 8 * zoomedSize() + 2); + + // Draw letters and numbers if appropriate. + if (m_marksShowing) { + QFont font("Sans Serif", zoomedSize() / 2 - 6); + font.setWeight(QFont::DemiBold); + QFontMetrics metrics(font); + + p.setFont(font); + uint charHeight = metrics.ascent(); + for (uint i = 0; i < 8; i++) { + QChar letter = "ABCDEFGH"[i]; + QChar number = "12345678"[i]; + uint charWidth = metrics.charWidth("ABCDEFGH", i); + + // The horizontal letters + p.drawText(offset + i * zoomedSize() + (zoomedSize() - charWidth) / 2, + offset - charHeight / 2 + 2, + QString(letter)); + p.drawText(offset + i * zoomedSize() + (zoomedSize() - charWidth) / 2, + offset + 8 * zoomedSize() + offset - charHeight / 2 + 2, + QString(letter)); + + // The vertical numbers + p.drawText((offset - charWidth) / 2 + 2, + offset + (i + 1) * zoomedSize() - charHeight / 2 + 2, + QString(number)); + p.drawText(offset + 8 * zoomedSize() + (offset - charWidth) / 2 + 2, + offset + (i + 1) * zoomedSize() - charHeight / 2 + 2, + QString(number)); + } + } + + // Show legal moves. + if (m_legalMovesShowing) + showLegalMoves(); + + // Show last move + int ellipseSize = zoomedSize() / 3; + SimpleMove lastMove = m_krgame->lastMove(); + if (m_showLastMove && lastMove.x() != -1) { + // Remove the last shown last move. + int col = m_lastMoveShown.x(); + int row = m_lastMoveShown.y(); + if (col != -1 && row != -1) { + if (lastMove.x() != col || lastMove.y() != row) { + //kdDebug() << "Redrawing piece at [" << col << "," << row + // << "] with color " << m_krgame->color(col, row) + // << endl; + drawPiece(row - 1, col - 1, m_krgame->color(col, row)); + } + } + + p.setPen(yellow); + p.setBackgroundColor(yellow); + p.setBrush(SolidPattern); + + //kdDebug() << "Marking last move at [" + // << lastMove.x() << "," << lastMove.y() << "]" + // << endl; + int px = offset + (lastMove.x() - 1) * zoomedSize() + zoomedSize() / 2; + int py = offset + (lastMove.y() - 1) * zoomedSize() + zoomedSize() / 2; + p.drawEllipse(px - ellipseSize / 2 + 1, py - ellipseSize / 2 + 1, + ellipseSize - 1, ellipseSize - 1); + + m_lastMoveShown = lastMove; + + p.setPen(black); + p.setBackgroundColor(black); + p.setBrush(NoBrush); + } +} + + +// Show legal moves on the board. + +void QReversiBoardView::showLegalMoves() +{ + QPainter p(this); + p.setPen(black); + + // Get the legal moves in the current position. + Color toMove = m_krgame->toMove(); + MoveList moves = m_krgame->position().generateMoves(toMove); + + // Show the moves on the board. + MoveList::iterator it; + for (it = moves.begin(); it != moves.end(); ++it) + drawSmallCircle((*it).x(), (*it).y(), p); +} + + +void QReversiBoardView::drawSmallCircle(int x, int y, QPainter &p) +{ + int offset = m_marksShowing ? OFFSET() : 0; + int ellipseSize = zoomedSize() / 3; + + int px = offset + (x - 1) * zoomedSize() + zoomedSize() / 2; + int py = offset + (y - 1) * zoomedSize() + zoomedSize() / 2; + + p.drawEllipse(px - ellipseSize / 2, py - ellipseSize / 2, + ellipseSize, ellipseSize); +} + + + +QPixmap QReversiBoardView::chipPixmap(Color color, uint size) const +{ + return chipPixmap(CHIP_OFFSET[color], size); +} + + +// Get a pixmap for the chip 'i' at size 'size'. +// + +QPixmap QReversiBoardView::chipPixmap(uint i, uint size) const +{ + // Get the part of the 'allchips' pixmap that contains exactly that + // chip that we want to use. + QPixmap pix(CHIP_SIZE, CHIP_SIZE); + copyBlt(&pix, 0, 0, &allchips, (i%5) * CHIP_SIZE, (i/5) * CHIP_SIZE, + CHIP_SIZE, CHIP_SIZE); + + // Resize (scale) the pixmap to the desired size. + QWMatrix wm3; + wm3.scale(float(size)/CHIP_SIZE, float(size)/CHIP_SIZE); + + return pix.xForm(wm3); +} + + +uint QReversiBoardView::zoomedSize() const +{ + return qRound(float(CHIP_SIZE) * Prefs::zoom() / 100); +} + + +void QReversiBoardView::drawPiece(uint row, uint col, Color color) +{ + int i = (color == Nobody ? -1 : int(CHIP_OFFSET[color])); + drawOnePiece(row, col, i); +} + + +void QReversiBoardView::drawOnePiece(uint row, uint col, int i) +{ + int px = col * zoomedSize() + 1; + int py = row * zoomedSize() + 1; + QPainter p(this); + + // Draw either a background pixmap or a background color to the square. + int offset = m_marksShowing ? OFFSET() : 0; + if (bg.width()) + p.drawTiledPixmap(px + offset, py + offset, + zoomedSize(), zoomedSize(), bg, px, py); + else + p.fillRect(px + offset, py + offset, + zoomedSize(), zoomedSize(), bgColor); + + // Draw a black border around the square. + p.setPen(black); + p.drawRect(px + offset, py + offset, zoomedSize(), zoomedSize()); + + // If no piece on the square, i.e. only the background, then return here... + if ( i == -1 ) + return; + + // ...otherwise finally draw the piece on the square. + p.drawPixmap(px + offset, py + offset, chipPixmap(i, zoomedSize())); +} + + +// We got a repaint event. We make it easy for us and redraw the +// entire board. +// + +void QReversiBoardView::paintEvent(QPaintEvent *) +{ + updateBoard(true); +} + + +void QReversiBoardView::adjustSize() +{ + int w = 8 * zoomedSize(); + + if (m_marksShowing) + w += 2 * OFFSET(); + + setFixedSize(w + 2, w + 2); +} + + +void QReversiBoardView::setPixmap(QPixmap &pm) +{ + if ( pm.width() == 0 ) + return; + + bg = pm; + update(); + setErasePixmap(pm); +} + + +void QReversiBoardView::setColor(const QColor &c) +{ + bgColor = c; + bg = QPixmap(); + update(); + setEraseColor(c); +} + + +// Load all settings that have to do with the board view, such as +// piece colors, background, animation speed, an so on. + +void QReversiBoardView::loadSettings() +{ + // Colors of the pieces (red/blue or black/white) + if ( Prefs::grayscale() ) { + if (chiptype != Grayscale) + loadChips(Grayscale); + } + else { + if (chiptype != Colored) + loadChips(Colored); + } + + // Animation speed. + if ( !Prefs::animation() ) + setAnimationSpeed(0); + else + setAnimationSpeed(10 - Prefs::animationSpeed()); + + // Background + if ( Prefs::backgroundImageChoice() ) { + QPixmap pm( Prefs::backgroundImage() ); + if (!pm.isNull()) + setPixmap(pm); + } else { + setColor( Prefs::backgroundColor() ); + } +} + + +#include "board.moc" + diff --git a/kreversi/board.h b/kreversi/board.h new file mode 100644 index 00000000..4f9d1603 --- /dev/null +++ b/kreversi/board.h @@ -0,0 +1,150 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#ifndef __BOARD__H__ +#define __BOARD__H__ + +#include +#include + +#include "Position.h" +//#include "Game.h" +#include "Move.h" + + +class KConfig; + +class QReversiGame; + +// The class Board is the visible Reversi Board widget. +// + +class QReversiBoardView : public QWidget { + Q_OBJECT + +public: + + QReversiBoardView(QWidget *parent, QReversiGame *game); + ~QReversiBoardView(); + + // starts all: emits some signal, so it can't be called from + // constructor + void start(); + + // Used by the outer KZoomMainWindow class. + void adjustSize(); + + // Show a hint to the user. + void showHint(Move move); + void quitHint(); + + // Turn on or off some special features. + void setShowLegalMoves(bool show); + void setShowMarks(bool show); + void setShowLastMove(bool show); + + // View methods called from the outside. + void updateBoard(bool force = FALSE); + void animateChanged(Move move); + void setAnimationSpeed(uint); + + void loadSettings(); + + // To get the pixmap for the status view + QPixmap chipPixmap(Color color, uint size) const; + + +signals: + void signalSquareClicked(int, int); + + +protected: + + // event stuff + void paintEvent(QPaintEvent *); + void mousePressEvent(QMouseEvent *); + + +private: + uint zoomedSize() const; + void drawPiece(uint row, uint col, Color); + void drawOnePiece(uint row, uint col, int i); + void animateChangedRow(int row, int col, int dy, int dx); + void rotateChip(uint row, uint col); + bool isField(int row, int col) const; + + void setColor(const QColor &); + QColor color() const { return bgColor; } + void setPixmap(QPixmap &); + + // Methods for handling images of pieces. + enum ChipType { Unloaded, Colored, Grayscale }; + void loadChips(ChipType); + ChipType chipType() const { return chiptype; } + QPixmap chipPixmap(uint i, uint size) const; + + // Private drawing methods. + void showLegalMoves(); + void drawSmallCircle(int x, int y, QPainter &p); + + +private: + QReversiGame *m_krgame; // Pointer to the game object (not owner). + + // The background of the board - a color and a pixmap. + QColor bgColor; + QPixmap bg; + + // the pieces + ChipType chiptype; + QPixmap allchips; + uint anim_speed; + + // Special stuff used only in smaller areas. + bool m_hintShowing; + MoveList m_legalMoves; + bool m_legalMovesShowing; + bool m_marksShowing; + + bool m_showLastMove; + SimpleMove m_lastMoveShown; +}; + + +#endif + diff --git a/kreversi/highscores.cpp b/kreversi/highscores.cpp new file mode 100644 index 00000000..88317a87 --- /dev/null +++ b/kreversi/highscores.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2004 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include "highscores.h" + +#include +#include +#include + + +namespace KExtHighscore +{ + +const ExtManager::Data ExtManager::DATA[SuperEngine::NbStrengths] = { + { I18N_NOOP("1 (Beginner)"), "beginner" }, + { I18N_NOOP("2"), 0 }, + { I18N_NOOP("3"), 0 }, + { I18N_NOOP("4 (Average)"), "average" }, + { I18N_NOOP("5"), 0 }, + { I18N_NOOP("6"), 0 }, + { I18N_NOOP("7 (Expert)"), "expert" } +}; + + +ExtManager::ExtManager() + : Manager(SuperEngine::NbStrengths) +{ + setShowStatistics(true); + setShowDrawGamesStatistic(true); + + const uint RANGE[6] = { 0, 32, 40, 48, 56, 64 }; + QMemArray s; + s.duplicate(RANGE, 6); + setScoreHistogram(s, ScoreBound); +} + + +QString ExtManager::gameTypeLabel(uint gameType, LabelType type) const +{ + const Data &data = DATA[gameType]; + switch (type) { + case Icon: return data.icon; + case Standard: return QString::number(gameType); + case I18N: return i18n(data.label); + case WW: break; + } + + return QString::null; +} + + +void ExtManager::convertLegacy(uint gameType) +{ + // Since there is no information about the skill level + // in the legacy highscore list, consider they are + // for beginner skill ... + qDebug("convert legacy %i", gameType); + + if ( gameType!=0 ) + return; + + KConfigGroupSaver cg(kapp->config(), "High Score"); + + for (uint i = 1; i <= 10; i++) { + QString key = "Pos" + QString::number(i); + QString name = cg.config()->readEntry(key + "Name", QString::null); + + if ( name.isEmpty() ) + name = i18n("anonymous"); + + uint score = cg.config()->readUnsignedNumEntry(key + "NumChips", 0); + if ( score==0 ) + continue; + + QString sdate = cg.config()->readEntry(key + "Date", QString::null); + QDateTime date = QDateTime::fromString(sdate); + Score s(Won); + + s.setScore(score); + s.setData("name", name); + if ( date.isValid() ) + s.setData("date", date); + submitLegacyScore(s); + } +} + + +} // Namespace diff --git a/kreversi/highscores.h b/kreversi/highscores.h new file mode 100644 index 00000000..a599d12d --- /dev/null +++ b/kreversi/highscores.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2004 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef HIGHSCORES_H +#define HIGHSCORES_H + + +#include +#include +#include "SuperEngine.h" + + +namespace KExtHighscore +{ + +class KDE_EXPORT ExtManager : public Manager +{ + public: + ExtManager(); + + private: + QString gameTypeLabel(uint gameTye, LabelType) const; + void convertLegacy(uint gameType); + + struct Data { + const char *label; + const char *icon; + }; + static const Data DATA[SuperEngine::NbStrengths]; +}; + +} + +#endif diff --git a/kreversi/icons/Makefile.am b/kreversi/icons/Makefile.am new file mode 100644 index 00000000..da1319dc --- /dev/null +++ b/kreversi/icons/Makefile.am @@ -0,0 +1 @@ +KDE_ICON = AUTO \ No newline at end of file diff --git a/kreversi/icons/cr16-action-lastmoves.png b/kreversi/icons/cr16-action-lastmoves.png new file mode 100644 index 00000000..5543a104 Binary files /dev/null and b/kreversi/icons/cr16-action-lastmoves.png differ diff --git a/kreversi/icons/cr16-action-legalmoves.png b/kreversi/icons/cr16-action-legalmoves.png new file mode 100644 index 00000000..5ef60f95 Binary files /dev/null and b/kreversi/icons/cr16-action-legalmoves.png differ diff --git a/kreversi/icons/cr22-action-lastmoves.png b/kreversi/icons/cr22-action-lastmoves.png new file mode 100644 index 00000000..29d0f962 Binary files /dev/null and b/kreversi/icons/cr22-action-lastmoves.png differ diff --git a/kreversi/icons/cr22-action-legalmoves.png b/kreversi/icons/cr22-action-legalmoves.png new file mode 100644 index 00000000..3158b678 Binary files /dev/null and b/kreversi/icons/cr22-action-legalmoves.png differ diff --git a/kreversi/icons/cr32-action-lastmoves.png b/kreversi/icons/cr32-action-lastmoves.png new file mode 100644 index 00000000..47166dd7 Binary files /dev/null and b/kreversi/icons/cr32-action-lastmoves.png differ diff --git a/kreversi/icons/cr32-action-legalmoves.png b/kreversi/icons/cr32-action-legalmoves.png new file mode 100644 index 00000000..c5a0e304 Binary files /dev/null and b/kreversi/icons/cr32-action-legalmoves.png differ diff --git a/kreversi/icons/cr48-action-lastmoves.png b/kreversi/icons/cr48-action-lastmoves.png new file mode 100644 index 00000000..47a3e0da Binary files /dev/null and b/kreversi/icons/cr48-action-lastmoves.png differ diff --git a/kreversi/icons/cr48-action-legalmoves.png b/kreversi/icons/cr48-action-legalmoves.png new file mode 100644 index 00000000..5742a01d Binary files /dev/null and b/kreversi/icons/cr48-action-legalmoves.png differ diff --git a/kreversi/icons/crsc-action-lastmoves.svgz b/kreversi/icons/crsc-action-lastmoves.svgz new file mode 100644 index 00000000..c2b1c4bb Binary files /dev/null and b/kreversi/icons/crsc-action-lastmoves.svgz differ diff --git a/kreversi/icons/crsc-action-legalmoves.svgz b/kreversi/icons/crsc-action-legalmoves.svgz new file mode 100644 index 00000000..db2acad9 Binary files /dev/null and b/kreversi/icons/crsc-action-legalmoves.svgz differ diff --git a/kreversi/icons/hi128-app-kreversi.png b/kreversi/icons/hi128-app-kreversi.png new file mode 100644 index 00000000..26c22bc8 Binary files /dev/null and b/kreversi/icons/hi128-app-kreversi.png differ diff --git a/kreversi/icons/hi16-app-kreversi.png b/kreversi/icons/hi16-app-kreversi.png new file mode 100644 index 00000000..9dda53b2 Binary files /dev/null and b/kreversi/icons/hi16-app-kreversi.png differ diff --git a/kreversi/icons/hi22-app-kreversi.png b/kreversi/icons/hi22-app-kreversi.png new file mode 100644 index 00000000..6f77f5a9 Binary files /dev/null and b/kreversi/icons/hi22-app-kreversi.png differ diff --git a/kreversi/icons/hi32-app-kreversi.png b/kreversi/icons/hi32-app-kreversi.png new file mode 100644 index 00000000..360f1bda Binary files /dev/null and b/kreversi/icons/hi32-app-kreversi.png differ diff --git a/kreversi/icons/hi48-app-kreversi.png b/kreversi/icons/hi48-app-kreversi.png new file mode 100644 index 00000000..b310d52f Binary files /dev/null and b/kreversi/icons/hi48-app-kreversi.png differ diff --git a/kreversi/icons/hi64-app-kreversi.png b/kreversi/icons/hi64-app-kreversi.png new file mode 100644 index 00000000..5ca5cb5d Binary files /dev/null and b/kreversi/icons/hi64-app-kreversi.png differ diff --git a/kreversi/kreversi.cpp b/kreversi/kreversi.cpp new file mode 100644 index 00000000..b0a5ddc8 --- /dev/null +++ b/kreversi/kreversi.cpp @@ -0,0 +1,841 @@ +/* Yo Emacs, this is -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Score.h" +#include "kreversi.h" + +// Automatically generated headers +#include "prefs.h" +#include "settings.h" + +#include "kreversi.moc" + + +// ================================================================ +// class KReversi + + +#ifndef PICDATA +#define PICDATA(x) \ + KGlobal::dirs()->findResource("appdata", QString("pics/") + x) +#endif + + +KReversi::KReversi() + : KZoomMainWindow(10, 300, 5, "kreversi"), + m_gameOver(false) +{ + QWidget *w; + QGridLayout *top; + + KNotifyClient::startDaemon(); + + // The game. + m_game = new QReversiGame(); + m_cheating = false; + m_gameOver = false; + m_humanColor = Black; + + // The Engine + m_engine = new Engine(); + setStrength(1); + + // The visual stuff + w = new QWidget(this); + setCentralWidget(w); + + top = new QGridLayout(w, 2, 2); + + // The reversi game view. + m_gameView = new QReversiGameView(w, m_game); + top->addMultiCellWidget(m_gameView, 0, 1, 0, 0); + + // Populate the GUI. + createKActions(); + addWidget(m_gameView); + + // Connect the signals from the game with slots of the view + // + // The only part of the view that is left in this class is the + // indicator of whose turn it is in the status bar. The rest is + // in the game view. + connect(m_game, SIGNAL(sig_newGame()), this, SLOT(showTurn())); + connect(m_game, SIGNAL(sig_move(uint, Move&)), + this, SLOT(handleMove(uint, Move&))); // Calls showTurn(). + connect(m_game, SIGNAL(sig_update()), this, SLOT(showTurn())); + connect(m_game, SIGNAL(sig_gameOver()), this, SLOT(slotGameOver())); + + // Signal that is sent when the user clicks on the board. + connect(m_gameView, SIGNAL(signalSquareClicked(int, int)), + this, SLOT(slotSquareClicked(int, int))); + + loadSettings(); + + setupGUI(); + init("popup"); + m_gameView->start(); + + slotNewGame(); +} + + +KReversi::~KReversi() +{ + delete m_game; + delete m_engine; +} + + + +// Create all KActions used in KReversi. +// + +void KReversi::createKActions() +{ + // Standard Game Actions. + KStdGameAction::gameNew(this, SLOT(slotNewGame()), actionCollection(), + "game_new"); + KStdGameAction::load(this, SLOT(slotOpenGame()), actionCollection()); + KStdGameAction::save(this, SLOT(slotSave()), actionCollection()); + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + KStdGameAction::hint(this, SLOT(slotHint()), actionCollection(), + "game_hint"); + KStdGameAction::undo(this, SLOT(slotUndo()), actionCollection(), + "game_undo"); + + // Non-standard Game Actions: Stop, Continue, Switch sides + stopAction = new KAction(i18n("&Stop Thinking"), "game_stop", Qt::Key_Escape, + this, SLOT(slotInterrupt()), actionCollection(), + "game_stop"); + continueAction = new KAction(i18n("&Continue Thinking"), "reload", 0, + this, SLOT(slotContinue()), actionCollection(), + "game_continue"); + new KAction(i18n("S&witch Sides"), 0, 0, + this, SLOT(slotSwitchSides()), actionCollection(), + "game_switch_sides"); + + // Some more standard game actions: Highscores, Settings. + KStdGameAction::highscores(this, SLOT(showHighScoreDialog()), actionCollection()); + KStdAction::preferences(this, SLOT(slotEditSettings()), actionCollection()); + + // Actions for the view(s). + showLastMoveAction = new KToggleAction(i18n("Show Last Move"), "lastmoves", 0, + this, SLOT(slotShowLastMove()), + actionCollection(), + "show_last_move"); + showLegalMovesAction = new KToggleAction(i18n("Show Legal Moves"), "legalmoves", 0, + this, SLOT(slotShowLegalMoves()), + actionCollection(), + "show_legal_moves"); +} + + +// ---------------------------------------------------------------- +// Methods for the engine + + +// Set the strength for the engine. We keep track of the lowest +// strength that was used during a game and if the user wins, this is +// the strength that we will store in the highscore file. + +void KReversi::setStrength(uint strength) +{ + // FIXME: 7 should be MAXSTRENGTH or something similar. + Q_ASSERT( 1 <= strength && strength <= 7 ); + + strength = QMAX(QMIN(strength, 7), 1); + m_engine->setStrength(strength); + if (m_lowestStrength < strength) + m_lowestStrength = strength; + KExtHighscore::setGameType(m_lowestStrength-1); +} + + +// ---------------------------------------------------------------- +// Slots for KActions + + +// A slot that is called when the user wants a new game. +// + +void KReversi::slotNewGame() +{ + // If already playing, ask the player if he wants to abort the old game. + if ( isPlaying() ) { + if (KMessageBox + ::warningYesNo(0, + i18n("You are already running an unfinished game. " + "If you abort the old game to start a new one, " + "the old game will be registered as a loss in " + "the highscore file.\n" + "What do you want to do?"), + i18n("Abort Current Game?"), + i18n("Abort Old Game"), + i18n("Continue Old Game")) == KMessageBox::No) + return; + + KExtHighscore::submitScore(KExtHighscore::Lost, this); + } + + m_gameOver = false; + m_cheating = false; + + m_game->newGame(); + m_competitiveGame = Prefs::competitiveGameChoice(); + m_lowestStrength = strength(); + //kdDebug() << "Competitive: " << m_competitiveGame << endl; + + // Set the state to waiting for the humans move. + setState(Ready); + + // Black always makes first move. + if (m_humanColor == White) + computerMakeMove(); +} + + +// Open an earlier saved game from the config file. +// +// FIXME: Should give a choice to load from an ordinary file (SGF?) +// + +void KReversi::slotOpenGame() +{ + KConfig *config = kapp->config(); + config->setGroup("Savegame"); + + if (loadGame(config)) + Prefs::setSkill(m_engine->strength()); + m_gameView->setHumanColor(humanColor()); +} + + +// Save a game to the config file. +// +// FIXME: Should give a choice to save as an ordinary file (SGF?) +// + +void KReversi::slotSave() +{ + KConfig *config = kapp->config(); + config->setGroup("Savegame"); + saveGame(config); + + KMessageBox::information(this, i18n("Game saved.")); +} + + +void KReversi::slotHint() +{ + Move move; + + if (state() != Ready) + return; + + setState(Thinking); + move = m_engine->computeMove(m_game, m_competitiveGame); + + setState(Hint); + m_gameView->showHint(move); + + setState(Ready); +} + + +// Takes back last set of moves +// + +void KReversi::slotUndo() +{ + if (state() != Ready) + return; + + // Can't undo anything if no moves are made. + if (m_game->moveNumber() == 0) + return; + + // Undo all moves of the same color as the last one. + Color last_color = m_game->lastMove().color(); + while (m_game->moveNumber() != 0 + && last_color == m_game->lastMove().color()) { + m_game->undoMove(); + m_gameView->removeMove(m_game->moveNumber()); + } + + // Take back one more move. + if (m_game->moveNumber() > 0) { + m_game->undoMove(); + m_gameView->removeMove(m_game->moveNumber()); + + // FIXME: Call some method in m_gameView. + m_gameView->setCurrentMove(m_game->moveNumber() - 1); + } + + if (m_game->toMove() == computerColor()) { + // Must repaint so that the new move is not shown before the old + // one is removed on the screen. + m_gameView->repaint(); + computerMakeMove(); + } + else + m_gameView->update(); +} + + +// Interrupt thinking of game engine. +// + +void KReversi::slotInterrupt() +{ + m_engine->setInterrupt(TRUE); + + // Indicate that the computer was interrupted. + showTurn(); +} + + +// Continues a move if it was interrupted earlier. +// + +void KReversi::slotContinue() +{ + if (interrupted()) + computerMakeMove(); +} + + +// Turn on or off showing of legal moves in the board view. + +void KReversi::slotShowLastMove() +{ + m_gameView->setShowLastMove(showLastMoveAction->isChecked()); +} + + +// Turn on or off showing of legal moves in the board view. + +void KReversi::slotShowLegalMoves() +{ + m_gameView->setShowLegalMoves(showLegalMovesAction->isChecked()); +} + + +void KReversi::slotSwitchSides() +{ + if (state() != Ready) + return; + + if (interrupted()) { + KMessageBox::information(this, i18n("You cannot switch sides in the middle of the computer's move."), + i18n("Notice")); + return; + } + + // It's ok to change sides before the first move. + if (m_game->moveNumber() != 0) { + int res = KMessageBox::warningContinueCancel(this, + i18n("If you switch side, your score will not be added to the highscores."), + QString::null, QString::null, "switch_side_warning"); + if ( res==KMessageBox::Cancel ) + return; + + m_cheating = true; + } + + m_humanColor = opponent(m_humanColor); + + // Update the human color in the window. + m_gameView->setHumanColor(m_humanColor); + + kapp->processEvents(); + computerMakeMove(); +} + + +// ---------------------------------------------------------------- +// Slots for the game IO + + +// Handle mouse clicks. +// + +void KReversi::slotSquareClicked(int row, int col) +{ + // Can't move when it is the computers turn. + if ( interrupted() ) { + illegalMove(); + return; + } + + if (state() == Ready) + humanMakeMove(row, col); + else if (state() == Hint) { + m_gameView->quitHint(); + setState(Ready); + } + else + illegalMove(); +} + + +// ---------------------------------------------------------------- +// Slots for the game view + + +// Show the move in the move view. +// FIXME: Move this to the gameview. + +void KReversi::handleMove(uint /*moveno*/, Move& /*move*/) +{ + showTurn(); +} + + +// A slot that is called when it is time to show whose turn it is. +// + +void KReversi::showTurn() +{ + showTurn(m_game->toMove()); +} + +void KReversi::showTurn(Color color) +{ + // If we are not playing, do nothing. + if (m_gameOver) + return; + + if (color == humanColor()) + statusBar()->message(i18n("Your turn")); + else if (color == computerColor()) { + QString message = i18n("Computer's turn"); + + // We can't use the interrupted() test here since we might be in a + // middle state when called from slotInterrupt(). + if (m_state == Thinking) + message += i18n(" (interrupted)"); + statusBar()->message(message); + } + else + statusBar()->clear(); +} + + +// A slot that is called when the game ends. +// + +void KReversi::slotGameOver() +{ + uint black = m_game->score(Black); + uint white = m_game->score(White); + + setState(Ready); + + if (black > white) + showGameOver(Black); + else if (black < white) + showGameOver(White); + else + showGameOver(Nobody); + + showTurn(Nobody); +} + + +// ---------------------------------------------------------------- +// Private methods + + +// Handle the humans move. +// + +void KReversi::humanMakeMove(int row, int col) +{ + if (state() != Ready) + return; + + Color color = m_game->toMove(); + + // Create a move from the mouse click and see if it is legal. + // If it is, then make a human move. + Move move(color, col + 1, row + 1); + if (m_game->moveIsLegal(move)) { + // Do the move. The view is automatically updated. + m_game->doMove(move); + + if (!m_game->moveIsAtAllPossible()) { + setState(Ready); + slotGameOver(); + return; + } + + if (color != m_game->toMove()) + computerMakeMove(); + } else + illegalMove(); +} + + +// Make a computer move. +// + +void KReversi::computerMakeMove() +{ + MoveList moves; + + // Check if the computer can move. + Color color = m_game->toMove(); + Color opponent = ::opponent(color); + + if (!m_game->moveIsPossible(color)) + return; + + // Make computer moves until the human can play or until the game is over. + setState(Thinking); + do { + Move move; + + if (!m_game->moveIsAtAllPossible()) { + setState(Ready); + slotGameOver(); + return; + } + + move = m_engine->computeMove(m_game, m_competitiveGame); + if (move.x() == -1) { + setState(Ready); + return; + } + usleep(300000); // Pretend we have to think hard. + + // Do the move on the board. The view is automatically updated. + //playSound("click.wav"); + m_game->doMove(move); + } while (!m_game->moveIsPossible(opponent)); + + setState(Ready); + + if (!m_game->moveIsAtAllPossible()) { + slotGameOver(); + return; + } +} + + +// Handle an attempt to make an illegal move by the human. + +void KReversi::illegalMove() +{ + KNotifyClient::event(winId(), "illegal_move", i18n("Illegal move")); +} + + +// Show things when the game is over. +// + +void KReversi::showGameOver(Color color) +{ + // If the game already was over, do nothing. + if (m_gameOver) + return; + + statusBar()->message(i18n("End of game")); + + // Get the scores. + uint human = m_game->score(humanColor()); + uint computer = m_game->score(computerColor()); + + KExtHighscore::Score score; + score.setScore(m_game->score(humanColor())); + + // Show the winner in a messagebox. + if ( color == Nobody ) { + KNotifyClient::event(winId(), "draw", i18n("Draw!")); + QString s = i18n("Game is drawn!\n\nYou : %1\nComputer: %2") + .arg(human).arg(computer); + KMessageBox::information(this, s, i18n("Game Ended")); + score.setType(KExtHighscore::Draw); + } + else if ( humanColor() == color ) { + KNotifyClient::event(winId(), "won", i18n("Game won!")); + QString s = i18n("Congratulations, you have won!\n\nYou : %1\nComputer: %2") + .arg(human).arg(computer); + KMessageBox::information(this, s, i18n("Game Ended")); + score.setType(KExtHighscore::Won); + } + else { + KNotifyClient::event(winId(), "lost", i18n("Game lost!")); + QString s = i18n("You have lost the game!\n\nYou : %1\nComputer: %2") + .arg(human).arg(computer); + KMessageBox::information(this, s, i18n("Game Ended")); + score.setType(KExtHighscore::Lost); + } + + // Store the result in the highscore file if no cheating was done, + // and only if the game was competitive. + if (!m_cheating && m_competitiveGame) { + KExtHighscore::submitScore(score, this); + } + + m_gameOver = true; +} + + +// Saves the game in the config file. +// +// Only one game at a time can be saved. +// + +void KReversi::saveGame(KConfig *config) +{ + // Stop thinking. + slotInterrupt(); + + // Write the data to the config file. + config->writeEntry("State", state()); + config->writeEntry("Strength", strength()); + config->writeEntry("Competitive", (int) m_competitiveGame); + config->writeEntry("HumanColor", (int) m_humanColor); + + // Write the moves of the game to the config object. This object + // saves itself all at once so we don't have to write the moves + // to the file ourselves. + config->writeEntry("NumberOfMoves", m_game->moveNumber()); + for (uint i = 0; i < m_game->moveNumber(); i++) { + Move move = m_game->move(i); + + QString moveString; + QString idx; + + moveString.sprintf("%d %d %d", move.x(), move.y(), (int) move.color()); + idx.sprintf("Move_%d", i + 1); + config->writeEntry(idx, moveString); + } + + // Actually write the data to file. + config->sync(); + + // Continue with the move if applicable. + slotContinue(); +} + + +// Loads the game. Only one game at a time can be saved. + +bool KReversi::loadGame(KConfig *config) +{ + slotInterrupt(); // stop thinking + + uint nmoves = config->readNumEntry("NumberOfMoves", 0); + if (nmoves==0) + return false; + + m_game->newGame(); + uint movenumber = 1; + while (nmoves--) { + // Read one move. + QString idx; + idx.sprintf("Move_%d", movenumber++); + + QStringList s = config->readListEntry(idx, ' '); + uint x = (*s.at(0)).toUInt(); + uint y = (*s.at(1)).toUInt(); + Color color = (Color)(*s.at(2)).toInt(); + + Move move(color, x, y); + m_game->doMove(move); + } + + m_humanColor = (Color) config->readNumEntry("HumanColor"); + m_competitiveGame = (bool) config->readNumEntry("Competitive"); + + m_gameView->updateBoard(TRUE); + setState(State(config->readNumEntry("State"))); + setStrength(config->readNumEntry("Strength", 1)); + + if (interrupted()) + slotContinue(); + else { + // Computer makes first move. + if (m_humanColor != m_game->toMove()) + computerMakeMove(); + } + + return true; +} + + +// ---------------------------------------------------------------- + + +void KReversi::saveProperties(KConfig *c) +{ + saveGame(c); +} + + +void KReversi::readProperties(KConfig *config) { + loadGame(config); + m_gameOver = false; + m_cheating = false; // FIXME: Is this true? It isn't saved. +} + + +void KReversi::showHighScoreDialog() +{ + KExtHighscore::show(this); +} + + +void KReversi::slotEditSettings() +{ + // If we are already editing the settings, then do nothing. + if (KConfigDialog::showDialog("settings")) + return; + + KConfigDialog *dialog = new KConfigDialog(this, "settings", Prefs::self(), + KDialogBase::Swallow); + Settings *general = new Settings(0, "General"); + + dialog->addPage(general, i18n("General"), "package_settings"); + connect(dialog, SIGNAL(settingsChanged()), this, SLOT(loadSettings())); + dialog->show(); +} + + +void KReversi::configureNotifications() +{ + KNotifyDialog::configure(this); +} + + +void KReversi::loadSettings() +{ + m_humanColor = (Color) Prefs::humanColor(); + setStrength(Prefs::skill()); + + // m_competitiveGame is set at the start of a game and can only be + // downgraded during the game, never upgraded. + if ( !Prefs::competitiveGameChoice() ) + m_competitiveGame = false; + + m_gameView->loadSettings(); + + // Update the color of the human and the computer. + m_gameView->setHumanColor(humanColor()); +} + + +bool KReversi::isPlaying() const +{ + return ( m_game->moveNumber() != 0 && !m_gameOver ); +} + + +void KReversi::setState(State newState) +{ + m_state = newState; + + if (m_state == Thinking){ + kapp->setOverrideCursor(waitCursor); + stopAction->setEnabled(true); + } + else { + kapp->restoreOverrideCursor(); + stopAction->setEnabled(false); + } + + continueAction->setEnabled(interrupted()); +} + + +bool KReversi::queryExit() +{ + if ( isPlaying() ) + KExtHighscore::submitScore(KExtHighscore::Lost, this); + + return KZoomMainWindow::queryExit(); +} + + +void KReversi::writeZoomSetting(uint zoom) +{ + Prefs::setZoom(zoom); + Prefs::writeConfig(); +} + + +uint KReversi::readZoomSetting() const +{ + return Prefs::zoom(); +} + + +void KReversi::writeMenubarVisibleSetting(bool visible) +{ + Prefs::setMenubarVisible(visible); + Prefs::writeConfig(); +} + + +bool KReversi::menubarVisibleSetting() const +{ + return Prefs::menubarVisible(); +} diff --git a/kreversi/kreversi.desktop b/kreversi/kreversi.desktop new file mode 100644 index 00000000..e32a3bc4 --- /dev/null +++ b/kreversi/kreversi.desktop @@ -0,0 +1,74 @@ +[Desktop Entry] +Exec=kreversi %i %m -caption "%c" +Name=KReversi +Name[af]=Kreversi +Name[be]=РÑверÑÑ– +Name[bn]=কে-রিভারà§à¦¸à¦¿ +Name[ca]=Reversi +Name[eo]=Renverso +Name[hi]=के-रिवरà¥à¤¸à¥€ +Name[is]=Viðsnúningur +Name[ne]=केडीई रिभरà¥à¤¸à¥€ +Name[pa]=ਕੇ-ਰੀਵਰਸੀ +Name[pl]=Reversi +Name[sv]=Kreversi +Name[ta]=Kரிவரà¯à®¸à®¿ +Name[tg]=KРеверÑи +Name[th]=หมาà¸à¸«à¸™à¸µà¸š - K +Name[zh_TW]=KReversi 黑白棋 +Type=Application +DocPath=kreversi/index.html +GenericName=Reversi Board Game +GenericName[be]=ÐаÑÑ‚Ð¾Ð»ÑŒÐ½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ Ñž Ñ€ÑверÑÑ– +GenericName[bg]=Игра на дъÑка +GenericName[bn]=ছককেনà§à¦¦à§à¦°à¦¿à¦• খেলা রিভারà§à¦¸à¦¿ +GenericName[br]=Ur c'hoari taolenn Reversi +GenericName[bs]=Igra s ploÄom +GenericName[ca]=Joc de taula Reversi +GenericName[cs]=Desková hra Reversi +GenericName[cy]=Gêm Fwrdd Reversi +GenericName[da]=Reversi brætspil +GenericName[de]=Reversi Brettspiel +GenericName[el]=ΕπιτÏαπέζιο παιχνίδι Reversi +GenericName[eo]="Renverso"-Tabuloludo +GenericName[es]=Juego de tablero Reversi +GenericName[et]=Lauamäng Reversi +GenericName[eu]=Reversi mahai-jokoa +GenericName[fa]=بازی Reversi Board +GenericName[fi]=Othello +GenericName[fr]=Jeu de plateau Reversi +GenericName[he]=רברסי, משחק לוח +GenericName[hr]=Reversi igra na ploÄi +GenericName[hu]=Reversi +GenericName[is]=Reversi borðleikur +GenericName[it]=Reversi, gioco da tavolo +GenericName[ja]=リãƒãƒ¼ã‚·ã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​ក្ដារ Reversi +GenericName[ko]=리버시 ë³´ë“œ 게임 +GenericName[lt]=Reversi stalo žaidimas +GenericName[lv]=ReversÄ galda spÄ“le +GenericName[mk]=Игра на табла Reversi +GenericName[nb]=Brettspillet reversi +GenericName[nds]=Reversi-Brettspeel +GenericName[ne]=रिभरà¥à¤¸à¥€ बोरà¥à¤¡ खेल +GenericName[nl]=Reversi-bordspel +GenericName[nn]=Brettspelet reversi +GenericName[pl]=Gra planszowa Reversi +GenericName[pt]=Jogo de Tabuleiro Reversi +GenericName[pt_BR]=Jogo de tabuleiro como Reversi +GenericName[ru]=РеверÑи +GenericName[se]=Duolbbášspeallu reversi +GenericName[sk]=Stolová hra Reversi +GenericName[sl]=Namizna igra Reversi +GenericName[sr]=Игра на табли Reversi +GenericName[sr@Latn]=Igra na tabli Reversi +GenericName[sv]=Othello brädspel +GenericName[ta]=ரிவரà¯à®šà®¿ பலகை விளையாடà¯à®Ÿà¯ +GenericName[uk]=Гра на дошці (реверÑÑ–) +GenericName[wa]=Djeu d' platea Reversi +GenericName[zh_TW]=黑白棋棋盤éŠæˆ² +Terminal=false +Icon=kreversi +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;BoardGame; diff --git a/kreversi/kreversi.h b/kreversi/kreversi.h new file mode 100644 index 00000000..17599fa2 --- /dev/null +++ b/kreversi/kreversi.h @@ -0,0 +1,178 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#ifndef KREVERSI_H +#define KREVERSI_H + + +#include "kzoommainwindow.h" + +#include "Score.h" +#include "Game.h" +#include "Engine.h" +//#include "board.h" +#include "qreversigame.h" +#include "qreversigameview.h" + + +class QLabel; + +class KAction; + + +class KReversi : public KZoomMainWindow +{ + Q_OBJECT + +public: + + enum State { Ready, Thinking, Hint}; + + KReversi(); + ~KReversi(); + + bool isPlaying() const; + + // Methods that deal with the game + Color toMove() const { return m_game->toMove(); } + Color humanColor() const { return m_humanColor; } + Color computerColor() const { return opponent(m_humanColor); } + + // Methods that deal with the engine. + void setStrength(uint); + uint strength() const { return m_engine->strength(); } + void interrupt() { m_engine->setInterrupt(TRUE); } + bool interrupted() const { return (m_game->toMove() == computerColor() + && m_state == Ready); } + + // State of the program (Hint, Ready, Thinking, etc). + void setState(State); + State state() const { return m_state; } + +private: + // Initialisation + void createKActions(); + + // View functions. + QString getPlayerName(); + + virtual void writeZoomSetting(uint zoom); + virtual uint readZoomSetting() const; + virtual void writeMenubarVisibleSetting(bool visible); + virtual bool menubarVisibleSetting() const; + + virtual void saveProperties(KConfig *); + virtual void readProperties(KConfig *); + virtual bool queryExit(); + + +private slots: + + // Slots for KActions. + void slotNewGame(); + void slotOpenGame(); + void slotSave(); + void slotHint(); + void slotUndo(); + void slotSwitchSides(); + + // Interrupt and continue the engines thinking (also KActions). + void slotInterrupt(); + void slotContinue(); + void slotShowLastMove(); + void slotShowLegalMoves(); + + // Slots for game IO + void slotSquareClicked(int, int); + + // Misc slots. + void configureNotifications(); + + // Some dialogs and other misc stuff. + void showHighScoreDialog(); + void slotEditSettings(); + void loadSettings(); + + public slots: + // Slots for the view. + void handleMove(uint moveno, Move &move); + void showTurn(); + void showTurn(Color color); + void slotGameOver(); + +private: + + // Private methods + void humanMakeMove(int row, int col); + void computerMakeMove(); + void illegalMove(); + void showGameOver(Color); + + void saveGame(KConfig *); + bool loadGame(KConfig *); + + +private: + // Some Actions that need to be manipulated. + KAction *stopAction; + KAction *continueAction; + + KToggleAction *showLastMoveAction; + KToggleAction *showLegalMovesAction; + + // The game itself and game properties + QReversiGame *m_game; // The main document - the game + + Color m_humanColor; // The Color of the human player. + bool m_gameOver; // True if the game is over + bool m_cheating; // True if the user has changed sides + uint m_lowestStrength; // Lowest strength during the game. + bool m_competitiveGame;// True if the game has been + // competitive during all moves so far. + + State m_state; // Ready, Thinking, Hint + Engine *m_engine; // The AI that creates the computers moves. + + // Widgets + QReversiGameView *m_gameView; // The board widget. +}; + + +#endif + diff --git a/kreversi/kreversi.kcfg b/kreversi/kreversi.kcfg new file mode 100644 index 00000000..c1dc6e52 --- /dev/null +++ b/kreversi/kreversi.kcfg @@ -0,0 +1,66 @@ + + + kstandarddirs.h + kglobal.h + + + + + false + + + + 1 + + + + 0 + + + + true + + + + 4 + 1 + 10 + + + + 100 + 10 + 300 + + + + true + + + + 1 + 1 + 7 + + + + true + + + + #ffffff + + + + #define PICDATA(x) KGlobal::dirs()->findResource("appdata", QString("pics/")+ x) + PICDATA("background/Light_Wood.png") + + + + true + + + diff --git a/kreversi/kreversiui.rc b/kreversi/kreversiui.rc new file mode 100644 index 00000000..304b87b2 --- /dev/null +++ b/kreversi/kreversiui.rc @@ -0,0 +1,45 @@ + + + + + + + + + &Game + + + &Move + + + + + + + + + + +Main Toolbar + + + + + + +View Toolbar + + + + + + + + + + + + + + + diff --git a/kreversi/kzoommainwindow.cpp b/kreversi/kzoommainwindow.cpp new file mode 100644 index 00000000..4da50935 --- /dev/null +++ b/kreversi/kzoommainwindow.cpp @@ -0,0 +1,135 @@ +/* + This file is part of the KDE games library + Copyright (C) 2004 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#include "kzoommainwindow.h" +#include "kzoommainwindow.moc" + +#include +#include +#include +#include + + +KZoomMainWindow::KZoomMainWindow(uint min, uint max, uint step, + const char *name) + : KMainWindow(0, name), m_zoomStep(step), m_minZoom(min), m_maxZoom(max) +{ + installEventFilter(this); + + m_zoomInAction = + KStdAction::zoomIn(this, SLOT(zoomIn()), actionCollection()); + m_zoomOutAction = + KStdAction::zoomOut(this, SLOT(zoomOut()), actionCollection()); + m_menu = + KStdAction::showMenubar(this, SLOT(toggleMenubar()), actionCollection()); +} + + +void KZoomMainWindow::init(const char *popupName) +{ + // zoom + setZoom(readZoomSetting()); + + // menubar + m_menu->setChecked( menubarVisibleSetting() ); + toggleMenubar(); + + // context popup + if (popupName) { + QPopupMenu *popup = + static_cast(factory()->container(popupName, this)); + Q_ASSERT(popup); + if (popup) + KContextMenuManager::insert(this, popup); + } +} + +void KZoomMainWindow::addWidget(QWidget *widget) +{ + widget->adjustSize(); + + QWidget *tlw = widget->topLevelWidget(); + KZoomMainWindow *zm = + static_cast(tlw->qt_cast("KZoomMainWindow")); + + Q_ASSERT(zm); + zm->m_widgets.append(widget); + connect(widget, SIGNAL(destroyed()), zm, SLOT(widgetDestroyed())); +} + + +void KZoomMainWindow::widgetDestroyed() +{ + m_widgets.remove(static_cast(sender())); +} + + +bool KZoomMainWindow::eventFilter(QObject *o, QEvent *e) +{ + if ( e->type()==QEvent::LayoutHint ) + setFixedSize(minimumSize()); // because K/QMainWindow + // does not manage fixed central widget + // with hidden menubar... + return KMainWindow::eventFilter(o, e); +} + + +void KZoomMainWindow::setZoom(uint zoom) +{ + m_zoom = zoom; + writeZoomSetting(m_zoom); + + QPtrListIterator it(m_widgets); + for (; it.current(); ++it) + (*it)->adjustSize(); + + m_zoomOutAction->setEnabled( m_zoom > m_minZoom ); + m_zoomInAction->setEnabled( m_zoom < m_maxZoom ); +} + + +void KZoomMainWindow::zoomIn() +{ + setZoom(m_zoom + m_zoomStep); +} + + +void KZoomMainWindow::zoomOut() +{ + Q_ASSERT( m_zoom >= m_zoomStep ); + setZoom(m_zoom - m_zoomStep); +} + + +void KZoomMainWindow::toggleMenubar() +{ + if ( m_menu->isChecked() ) + menuBar()->show(); + else + menuBar()->hide(); +} + + +bool KZoomMainWindow::queryExit() +{ + writeMenubarVisibleSetting(m_menu->isChecked()); + + return KMainWindow::queryExit(); +} diff --git a/kreversi/kzoommainwindow.h b/kreversi/kzoommainwindow.h new file mode 100644 index 00000000..dee04139 --- /dev/null +++ b/kreversi/kzoommainwindow.h @@ -0,0 +1,138 @@ +/* + This file is part of the KDE games library + Copyright (C) 2004 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#ifndef KZOOMMAINWINDOW_H +#define KZOOMMAINWINDOW_H + + +#include + + +class KToggleAction; + + +/** + * KZoomMainWindow is a main window of fixed size. Its size can be + * modified with the "zoom in"/"zoom out" actions. + * + * It manages one or several widgets: their adjustSize() method is + * called whenever the zoom level is changed. + * The usual implementation for those widget is to redefine adjustSize() + * with code like: + * /code + * setFixedSize(newsize); + * /endcode + * + * This class also has a "show/hide menubar" action and allows the use + * of a context popup menu (useful to restore the menubar when hidden). + */ + +class KZoomMainWindow : public KMainWindow +{ + Q_OBJECT +public: + /** Constructor. */ + KZoomMainWindow(uint minZoom, uint maxZoom, uint zoomStep, + const char *name = 0); + + /** Add a widget to be managed i.e. the adjustSize() method of the + * widget is called whenever the zoom is changed. + * This function assumes that the topLevelWidget() is the KZoomMainWindow. + */ + static void addWidget(QWidget *widget); + + uint zoom() const { return m_zoom; } + +public slots: + void zoomIn(); + void zoomOut(); + void toggleMenubar(); + +protected: + /** You need to call this after the createGUI or setupGUI method + * is called. + * @param popupName is the name of the context popup menu as defined in + * the ui.rc file. + */ + void init(const char *popupName = 0); + + virtual void setZoom(uint zoom); + virtual bool eventFilter(QObject *o, QEvent *e); + virtual bool queryExit(); + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::setZoom(zoom); + * Settings::writeConfig(); + * /endcode + */ + virtual void writeZoomSetting(uint zoom) = 0; + + /** Youneed to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * return Settings::zoom(); + * /endcode + */ + virtual uint readZoomSetting() const = 0; + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::setMenubarVisible(visible); + * Settings::writeConfig(); + * /endcode + */ + virtual void writeMenubarVisibleSetting(bool visible) = 0; + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::menubarVisible(); + * /endcode + */ + virtual bool menubarVisibleSetting() const = 0; + +private slots: + void widgetDestroyed(); + +private: + uint m_zoom; + uint m_zoomStep; + uint m_minZoom; + uint m_maxZoom; + + QPtrList m_widgets; + + KAction *m_zoomInAction; + KAction *m_zoomOutAction; + KToggleAction *m_menu; + + class KZoomMainWindowPrivate; + KZoomMainWindowPrivate *d; +}; + + +#endif diff --git a/kreversi/main.cpp b/kreversi/main.cpp new file mode 100644 index 00000000..345e97da --- /dev/null +++ b/kreversi/main.cpp @@ -0,0 +1,87 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#include +#include +#include +#include +#include + +#include "version.h" +#include "kreversi.h" +#include "highscores.h" + + +static const char description[] = I18N_NOOP("KDE Board Game"); + +int main(int argc, char **argv) +{ + KHighscore::init("kreversi"); + + KAboutData aboutData( "kreversi", I18N_NOOP("KReversi"), + KREVERSI_VERSION, description, KAboutData::License_GPL, + "(c) 1997-2000, Mario Weilguni"); + aboutData.addAuthor("Mario Weilguni",0, "mweilguni@sime.com"); + aboutData.addAuthor("Benjamin Meyer",0, "ben+kreversi@meyerhome.net"); + aboutData.addCredit("Mats Luthman", I18N_NOOP("Game engine, ported from his JAVA applet."), 0); + aboutData.addCredit("Stephan Kulow", I18N_NOOP("Comments and bugfixes."), 0); + aboutData.addCredit("Arne Klaassen", I18N_NOOP("Raytraced chips."), 0); + aboutData.addCredit("Inge Wallin", I18N_NOOP("Cleaning, bugfixes, some enhancements."), 0); + + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + + // used for loading background pixmaps + KImageIO::registerFormats(); + KExtHighscore::ExtManager highscores; + + if (a.isRestored()){ + RESTORE(KReversi) + } + else { + KReversi *kreversi = new KReversi; + a.setMainWidget(kreversi); + kreversi->show(); + } + + return a.exec(); +} + diff --git a/kreversi/pics/Makefile.am b/kreversi/pics/Makefile.am new file mode 100644 index 00000000..e962996b --- /dev/null +++ b/kreversi/pics/Makefile.am @@ -0,0 +1,8 @@ +pics_DATA = chips.png chips_mono.png + +picsdir = $(kde_datadir)/kreversi/pics + +SUBDIRS = background + +EXTRA_DIST = $(pics_DATA) + diff --git a/kreversi/pics/background/Dark_Wood.png b/kreversi/pics/background/Dark_Wood.png new file mode 100644 index 00000000..cf487169 Binary files /dev/null and b/kreversi/pics/background/Dark_Wood.png differ diff --git a/kreversi/pics/background/Earth.png b/kreversi/pics/background/Earth.png new file mode 100644 index 00000000..fd9d4f82 Binary files /dev/null and b/kreversi/pics/background/Earth.png differ diff --git a/kreversi/pics/background/Granite.png b/kreversi/pics/background/Granite.png new file mode 100644 index 00000000..6fd04e93 Binary files /dev/null and b/kreversi/pics/background/Granite.png differ diff --git a/kreversi/pics/background/Hexagon.png b/kreversi/pics/background/Hexagon.png new file mode 100644 index 00000000..4a7b3de1 Binary files /dev/null and b/kreversi/pics/background/Hexagon.png differ diff --git a/kreversi/pics/background/Light_Wood.png b/kreversi/pics/background/Light_Wood.png new file mode 100644 index 00000000..04877a47 Binary files /dev/null and b/kreversi/pics/background/Light_Wood.png differ diff --git a/kreversi/pics/background/Makefile.am b/kreversi/pics/background/Makefile.am new file mode 100644 index 00000000..e106a1f7 --- /dev/null +++ b/kreversi/pics/background/Makefile.am @@ -0,0 +1,8 @@ + +bg_DATA = Dark_Wood.png Granite.png Light_Wood.png Ocean.png Puzzle.png \ +Earth.png Hexagon.png Mystique.png Pipes.png Stones.png + +bgdir = $(kde_datadir)/kreversi/pics/background + +EXTRA_DIST = $(bg_DATA) + diff --git a/kreversi/pics/background/Mystique.png b/kreversi/pics/background/Mystique.png new file mode 100644 index 00000000..55b9b960 Binary files /dev/null and b/kreversi/pics/background/Mystique.png differ diff --git a/kreversi/pics/background/Ocean.png b/kreversi/pics/background/Ocean.png new file mode 100644 index 00000000..6e420d6e Binary files /dev/null and b/kreversi/pics/background/Ocean.png differ diff --git a/kreversi/pics/background/Pipes.png b/kreversi/pics/background/Pipes.png new file mode 100644 index 00000000..111edef0 Binary files /dev/null and b/kreversi/pics/background/Pipes.png differ diff --git a/kreversi/pics/background/Puzzle.png b/kreversi/pics/background/Puzzle.png new file mode 100644 index 00000000..4033fd80 Binary files /dev/null and b/kreversi/pics/background/Puzzle.png differ diff --git a/kreversi/pics/background/Stones.png b/kreversi/pics/background/Stones.png new file mode 100644 index 00000000..4ba7dc7f Binary files /dev/null and b/kreversi/pics/background/Stones.png differ diff --git a/kreversi/pics/chips.png b/kreversi/pics/chips.png new file mode 100644 index 00000000..5a6ffda6 Binary files /dev/null and b/kreversi/pics/chips.png differ diff --git a/kreversi/pics/chips_mono.png b/kreversi/pics/chips_mono.png new file mode 100644 index 00000000..049d8c66 Binary files /dev/null and b/kreversi/pics/chips_mono.png differ diff --git a/kreversi/prefs.kcfgc b/kreversi/prefs.kcfgc new file mode 100644 index 00000000..173c0e55 --- /dev/null +++ b/kreversi/prefs.kcfgc @@ -0,0 +1,7 @@ +# Code generation options for kconfig_compiler +File=kreversi.kcfg +#IncludeFiles=defines.h +ClassName=Prefs +Singleton=true +CustomAdditions=false +Mutators=skill,Zoom,MenubarVisible diff --git a/kreversi/qreversigame.cpp b/kreversi/qreversigame.cpp new file mode 100644 index 00000000..d31bac4a --- /dev/null +++ b/kreversi/qreversigame.cpp @@ -0,0 +1,93 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#include "qreversigame.h" + + +// ================================================================ +// class QReversiGame + + +QReversiGame::QReversiGame(QObject *parent) + : QObject(parent), Game() +{ +} + + +QReversiGame::~QReversiGame() +{ +} + + +void QReversiGame::newGame() +{ + Game::newGame(); + + emit sig_newGame(); +} + + +bool QReversiGame::doMove(Move move) +{ + bool retval = Game::doMove(move); + if (!retval) + return false; + + emit sig_move(m_moveNumber, move); + + if (!Game::moveIsAtAllPossible()) + emit sig_gameOver(); + + return retval; +} + + +bool QReversiGame::undoMove() +{ + bool retval = Game::undoMove(); + + // Update all views. + emit sig_update(); + + return retval; +} + + +#include "qreversigame.moc" + diff --git a/kreversi/qreversigame.h b/kreversi/qreversigame.h new file mode 100644 index 00000000..d1712832 --- /dev/null +++ b/kreversi/qreversigame.h @@ -0,0 +1,101 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 2005 by Inge Wallin + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#ifndef __QREVERSIGAME__H__ +#define __QREVERSIGAME__H__ + +#include +#include + +#include "Position.h" +#include "Game.h" +#include "Move.h" + + +class KConfig; + + + +// The main document class in the reversi program. The thing that +// makes this a QReversiGame instead of just a ReversiGame is that it +// emits signals that can be used to update a view. +// +// Signals: +// updateBoard() +// score() +// turn(Color) +// gameOver() +// + +class QReversiGame : public QObject, public Game { + Q_OBJECT + + public: + QReversiGame(QObject *parent = 0); + ~QReversiGame(); + + // Methods dealing with the game + void newGame(); + bool doMove(Move move); + bool undoMove(); +#if 0 + void loadSettings(); + + bool loadGame(KConfig *, bool noupdate = FALSE); + void saveGame(KConfig *); +#endif + + signals: + void sig_newGame(); + void sig_move(uint, Move&); // A move has just been done. + void sig_update(); // Some other change than a move has been done + // Example: loadFile(), undoMove(); + void sig_gameOver(); // The game is over. + + // FIXME: To be removed: + //void updateBoard(); + //void sig_score(); + //void turn(Color); + +private: + // No members. +}; + + +#endif + diff --git a/kreversi/qreversigameview.cpp b/kreversi/qreversigameview.cpp new file mode 100644 index 00000000..92812657 --- /dev/null +++ b/kreversi/qreversigameview.cpp @@ -0,0 +1,299 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 2005 by Inge Wallin + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#include +#include +#include + +#include +#include + +#if 0 +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "board.h" +#include "Engine.h" +#endif +#include "prefs.h" + +#include "qreversigame.h" +#include "qreversigameview.h" + + +// ================================================================ +// class StatusWidget + + +StatusWidget::StatusWidget(const QString &text, QWidget *parent) + : QWidget(parent, "status_widget") +{ + QHBoxLayout *hbox = new QHBoxLayout(this, 0, KDialog::spacingHint()); + QLabel *label; + + m_textLabel = new QLabel(text, this); + hbox->addWidget(m_textLabel); + + m_pixLabel = new QLabel(this); + hbox->addWidget(m_pixLabel); + + label = new QLabel(":", this); + hbox->addWidget(label); + + m_scoreLabel = new QLabel(this); + hbox->addWidget(m_scoreLabel); +} + + +// Set the text label +// + +void StatusWidget::setText(const QString &string) +{ + m_textLabel->setText(string); +} + + +// Set the pixel label - used to show the color. +// + +void StatusWidget::setPixmap(const QPixmap &pixmap) +{ + m_pixLabel->setPixmap(pixmap); +} + + +// Set the score label - used to write the number of pieces. +// + +void StatusWidget::setScore(uint s) +{ + m_scoreLabel->setText(QString::number(s)); +} + + +// ================================================================ +// class QReversiGameView + + +QReversiGameView::QReversiGameView(QWidget *parent, QReversiGame *game) + : QWidget(parent, "gameview") +{ + // Store a pointer to the game. + m_game = game; + + // The widget stuff + createView(); + + // Other initializations. + m_humanColor = Nobody; + + // Connect the game to the view. + connect(m_game, SIGNAL(sig_newGame()), this, SLOT(newGame())); + connect(m_game, SIGNAL(sig_move(uint, Move&)), + this, SLOT(moveMade(uint, Move&))); + connect(m_game, SIGNAL(sig_update()), this, SLOT(updateView())); + // The sig_gameOver signal is not used by the view. + + // Reemit the signal from the board. + connect(m_boardView, SIGNAL(signalSquareClicked(int, int)), + this, SLOT(squareClicked(int, int))); +} + + +QReversiGameView::~QReversiGameView() +{ +} + + +// Create the entire view. Only called once from the constructor. + +void QReversiGameView::createView() +{ + QGridLayout *layout = new QGridLayout(this, 4, 2); + + // The board + m_boardView = new QReversiBoardView(this, m_game); + m_boardView->loadSettings(); // Load the pixmaps used in the status widgets. + layout->addMultiCellWidget(m_boardView, 0, 3, 0, 0); + + // The status widgets + m_blackStatus = new StatusWidget(QString::null, this); + m_blackStatus->setPixmap(m_boardView->chipPixmap(Black, 20)); + layout->addWidget(m_blackStatus, 0, 1); + m_whiteStatus = new StatusWidget(QString::null, this); + m_whiteStatus->setPixmap(m_boardView->chipPixmap(White, 20)); + layout->addWidget(m_whiteStatus, 1, 1); + + // The "Moves" label + QLabel *movesLabel = new QLabel( i18n("Moves"), this); + movesLabel->setAlignment(AlignCenter); + layout->addWidget(movesLabel, 2, 1); + + // The list of moves. + m_movesView = new QListBox(this, "moves"); + m_movesView->setMinimumWidth(150); + layout->addWidget(m_movesView, 3, 1); +} + + +// ---------------------------------------------------------------- +// Slots + + +// Recieves the sig_newGame signal from the game. + +void QReversiGameView::newGame() +{ + m_boardView->updateBoard(true); + m_movesView->clear(); + updateStatus(); +} + + +// Recieves the sig_move signal from the game. + +void QReversiGameView::moveMade(uint moveNum, Move &move) +{ + //FIXME: Error checks. + QString colorsWB[] = { + i18n("White"), + i18n("Black") + }; + QString colorsRB[] = { + i18n("Red"), + i18n("Blue") + }; + + // Insert the new move in the listbox and mark it as the current one. + m_movesView->insertItem(QString("%1. %2 %3") + .arg(moveNum) + .arg(Prefs::grayscale() ? colorsWB[move.color()] + : colorsRB[move.color()]) + .arg(move.asString())); + m_movesView->setCurrentItem(moveNum - 1); + m_movesView->ensureCurrentVisible(); + + // Animate all changed pieces. + m_boardView->animateChanged(move); + m_boardView->updateBoard(); + + // Update the score. + updateStatus(); +} + + +// Recieves the sig_update signal from the game, and can be called +// whenever a total update of the view is required. + +void QReversiGameView::updateView() +{ + m_boardView->updateBoard(true); + updateMovelist(); + updateStatus(); +} + + +// Only updates the status widgets (score). + +void QReversiGameView::updateStatus() +{ + m_blackStatus->setScore(m_game->score(Black)); + m_whiteStatus->setScore(m_game->score(White)); +} + + +// Only updates the status board. + +void QReversiGameView::updateBoard(bool force) +{ + m_boardView->updateBoard(force); +} + + +// Only updates the movelist. This method regenerates the list from +// scratch. + +void QReversiGameView::updateMovelist() +{ + // FIXME: NYI +} + + +// This special slot is just because the external program doesn't have +// access to the internal board view. +// + +void QReversiGameView::squareClicked(int row, int col) +{ + emit signalSquareClicked(row, col); +} + + +// ---------------------------------------------------------------- +// Other public methods. + + +void QReversiGameView::setHumanColor(Color color) +{ + m_humanColor = color; + + if (color == Black) { + m_blackStatus->setText(i18n("You")); + m_whiteStatus->setText(""); + } + else { + m_blackStatus->setText(""); + m_whiteStatus->setText(i18n("You")); + } +} + + +#include "qreversigameview.moc" + diff --git a/kreversi/qreversigameview.h b/kreversi/qreversigameview.h new file mode 100644 index 00000000..a3059a25 --- /dev/null +++ b/kreversi/qreversigameview.h @@ -0,0 +1,160 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 2005 by Inge Wallin + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#ifndef __QREVERSIGAMEVIEW__H__ +#define __QREVERSIGAMEVIEW__H__ + + +#include + +#include "Score.h" +#include "Move.h" +#include "board.h" + + +class KConfig; + + +class QLabel; + +class QReversiGame; + + +class StatusWidget : public QWidget +{ + Q_OBJECT + +public: + StatusWidget(const QString &text, QWidget *parent); + + void setText(const QString &string); + void setPixmap(const QPixmap &pixmap); + void setScore(uint score); + +private: + QLabel *m_textLabel; + QLabel *m_pixLabel; + QLabel *m_scoreLabel; +}; + + +// The main game view + +class QReversiGameView : public QWidget { + Q_OBJECT + +public: + + QReversiGameView(QWidget *parent, QReversiGame *game); + ~QReversiGameView(); + + // Proxy methods for the board view + void showHint(Move move) { m_boardView->showHint(move); } + void quitHint() { m_boardView->quitHint(); } + + void setShowLegalMoves(bool show){ m_boardView->setShowLegalMoves(show); } + void setShowMarks(bool show) { m_boardView->setShowMarks(show); } + void setShowLastMove(bool show){ m_boardView->setShowLastMove(show); } + + void setAnimationSpeed(uint speed){m_boardView->setAnimationSpeed(speed);} + + // To get the pixmap for the status view + QPixmap chipPixmap(Color color, uint size) const + { return m_boardView->chipPixmap(color, size); } + + // Proxy methods for the movelist + // FIXME: Not all of these need to be externally reachable + void insertMove(QString moveString) { m_movesView->insertItem(moveString); } + void removeMove(int moveNum) { + m_movesView->removeItem(moveNum); + updateStatus(); + } + void setCurrentMove(int moveNum) { + m_movesView->setCurrentItem(moveNum); + m_movesView->ensureCurrentVisible(); + } + + // The status widgets. + void setHumanColor(Color color); + + // Starts all: emits some signal, so it can't be called from + // constructor + void start() { m_boardView->start(); } + + // Used by the outer KZoomMainWindow class. + void adjustSize() { m_boardView->adjustSize(); } + + void loadSettings() { m_boardView->loadSettings(); } + + +public slots: + void newGame(); + void moveMade(uint moveNum, Move &move); + + void updateView(); // Update the entire view. + void updateStatus(); // Update the status widgets (score) + void updateBoard(bool force = FALSE); // Update the board. + void updateMovelist(); // Update the move list. + +signals: + void signalSquareClicked(int, int); + +private slots: + // Internal slot to reemit the boards signal. + void squareClicked(int, int); + +private: + void createView(); + +private: + + // Pointer to the game we are displaying + QReversiGame *m_game; // Pointer to the game object (not owner). + + Color m_humanColor; + + // Widgets in the view. + QReversiBoardView *m_boardView; + QListBox *m_movesView; + StatusWidget *m_blackStatus; + StatusWidget *m_whiteStatus; +}; + + +#endif + diff --git a/kreversi/settings.ui b/kreversi/settings.ui new file mode 100644 index 00000000..b7c59ff5 --- /dev/null +++ b/kreversi/settings.ui @@ -0,0 +1,329 @@ + +Settings + + + Settings + + + + 0 + 0 + 431 + 323 + + + + Settings + + + + unnamed + + + 0 + + + 0 + + + + frame3 + + + StyledPanel + + + Raised + + + 0 + + + + unnamed + + + + spacer2 + + + Vertical + + + Expanding + + + + 20 + 20 + + + + + + kcfg_Grayscale + + + &Grayscale chips + + + + + kcfg_CompetitiveGameChoice + + + Play Game + + + + unnamed + + + + CasualGameChoice + + + Casually + + + + + + true + + + + + CompetitiveGameChoice + + + Competitively + + + + + + + + + + skillGroup + + + &Computer Skill + + + + unnamed + + + + kcfg_skill + + + 1 + + + 7 + + + 1 + + + Horizontal + + + NoMarks + + + + + Beginner + + + Beginner + + + AlignVCenter|AlignLeft + + + + + Expert + + + Expert + + + AlignVCenter|AlignRight + + + + + Average + + + Average + + + AlignCenter + + + + + + + groupBox2 + + + Animation Speed + + + + unnamed + + + + textLabel1 + + + Slow + + + AlignVCenter|AlignLeft + + + + + textLabel3 + + + Fast + + + AlignVCenter|AlignRight + + + + + kcfg_AnimationSpeed + + + 1 + + + 10 + + + 1 + + + 1 + + + Horizontal + + + NoMarks + + + + + + + kcfg_Animation + + + &Animation + + + true + + + + + kcfg_BackgroundImageChoice + + + &Background + + + + unnamed + + + + BackgroundColorChoice + + + Color: + + + + + kcfg_BackgroundImage + + + + + BackgroundImageChoice + + + &Image: + + + true + + + + + kcfg_BackgroundColor + + + false + + + + + + + 255 + 255 + 255 + + + + + + + + + + + + kcfg_Animation + toggled(bool) + groupBox2 + setEnabled(bool) + + + BackgroundColorChoice + toggled(bool) + kcfg_BackgroundColor + setEnabled(bool) + + + BackgroundImageChoice + toggled(bool) + kcfg_BackgroundImage + setEnabled(bool) + + + + + kurlrequester.h + klineedit.h + kpushbutton.h + kcolorbutton.h + + diff --git a/kreversi/sounds/Makefile.am b/kreversi/sounds/Makefile.am new file mode 100644 index 00000000..83348417 --- /dev/null +++ b/kreversi/sounds/Makefile.am @@ -0,0 +1,7 @@ +soundsdir = $(kde_datadir)/kreversi/sounds +sounds_DATA = reversi-click.wav reversi-won.wav + +EXTRA_DIST = $(sounds_DATA) + +appdatadir = $(kde_datadir)/kreversi +appdata_DATA = eventsrc \ No newline at end of file diff --git a/kreversi/sounds/eventsrc b/kreversi/sounds/eventsrc new file mode 100644 index 00000000..cc525204 --- /dev/null +++ b/kreversi/sounds/eventsrc @@ -0,0 +1,566 @@ +[!Global!] +IconName=kreversi +Comment=KReversi +Comment[be]=РÑверÑÑ– +Comment[bn]=কে-রিভারà§à¦¸à¦¿ +Comment[ca]=Reversi +Comment[is]=Viðsnúningur +Comment[ne]=केडीई रिभरà¥à¤¸à¥€ +Comment[sv]=Othello +Comment[zh_TW]=KReversi 黑白棋 + +[click] +Name=Click +Name[be]=ПÑтрычка +Name[bg]=Щракване +Name[bn]=কà§à¦²à¦¿à¦• +Name[br]=Klik +Name[bs]=Klikni +Name[ca]=Clic +Name[cs]=Kliknutí +Name[cy]=Clicio +Name[da]=Klik +Name[de]=Klick +Name[el]=Κλικ +Name[eo]=Kliki +Name[es]=Clic +Name[et]=Klõps +Name[eu]=Klikatu +Name[fa]=Ùشار +Name[fi]=Napsauta +Name[fr]=Clic +Name[ga]=Cliceáil +Name[he]=לחיצה +Name[hr]=Klik +Name[hu]=Kattintás +Name[is]=Smella +Name[it]=Clic +Name[ja]=クリック +Name[km]=ចុច +Name[ko]=í´ë¦­ +Name[lt]=Paspausti +Name[lv]=KlikÅ¡Ä·is +Name[mk]=Кликање +Name[nb]=Klikk +Name[nds]=Klick +Name[ne]=कà¥à¤²à¤¿à¤• गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Klik +Name[nn]=Klikk +Name[pa]=ਦਬਾਓ +Name[pl]=Dobierz +Name[pt]=Carregar +Name[pt_BR]=Clicar +Name[ru]=Щелчок +Name[se]=Coahkkal +Name[sk]=Kliknutie +Name[sl]=Klik +Name[sr]=Кликни +Name[sr@Latn]=Klikni +Name[sv]=Klick +Name[ta]=சொடà¯à®•à¯à®•à¯ +Name[tg]=Ðнгуштзанӣ +Name[tr]=Tık +Name[uk]=Клац +Name[zh_CN]=å•å‡» +Name[zh_TW]=é»žé¸ +Comment=Click +Comment[be]=ПÑтрычка +Comment[bg]=Щракване +Comment[bn]=কà§à¦²à¦¿à¦• +Comment[br]=Klik +Comment[bs]=Klik +Comment[ca]=Clic +Comment[cs]=Kliknutí +Comment[cy]=Clicio +Comment[da]=Klik +Comment[de]=Klick +Comment[el]=Κλικ +Comment[eo]=Kliki +Comment[es]=Pulsación +Comment[et]=Klõps +Comment[eu]=Klikatu +Comment[fa]=Ùشار +Comment[fi]=Napsauta +Comment[fr]=Clic +Comment[ga]=Cliceáil +Comment[he]=לחיצה +Comment[hr]=Klik +Comment[hu]=Kattintás +Comment[is]=Smella +Comment[it]=Clic +Comment[ja]=クリック +Comment[km]=ចុច +Comment[lt]=Paspausti +Comment[lv]=KlikÅ¡Ä·is +Comment[mk]=Вртење +Comment[nb]=Klikk +Comment[nds]=Klick +Comment[ne]=कà¥à¤²à¤¿à¤• गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Klik +Comment[nn]=Klikk +Comment[pa]=ਦਬਾਓ +Comment[pl]=KlikniÄ™cie +Comment[pt]=Carregar +Comment[pt_BR]=Clicar +Comment[ru]=Щелчок +Comment[se]=Coahkkal +Comment[sk]=Kliknutie +Comment[sl]=Klik +Comment[sr]=Кликни +Comment[sr@Latn]=Klikni +Comment[sv]=Klick +Comment[ta]=சொடà¯à®•à¯à®•à¯ +Comment[tg]=Ðнгушт задан +Comment[tr]=Tık +Comment[uk]=Клац +Comment[zh_CN]=å•å‡» +Comment[zh_TW]=é»žé¸ +default_presentation=1 +default_sound=reversi-click.wav + +[won] +Name=Game won +Name[ar]=ربحت اللعبة +Name[be]=Перамога +Name[bg]=Спечелихте +Name[bn]=খেলা জিতেছেন +Name[br]=Gounezet eo ar c'hoari +Name[bs]=Pobjeda +Name[ca]=Partida guanyada +Name[cs]=Vyhraná hra +Name[cy]=Gêm wedi ei ennill +Name[da]=Spillet vundet +Name[de]=Spiel gewonnen +Name[el]=Παιχνίδι κεÏδήθηκε +Name[eo]=Ludo venkita +Name[es]=Partida ganada +Name[et]=Mäng läbi, sina võitsid +Name[eu]=Jokoa irabazi da +Name[fa]=برد بازی +Name[fi]=Peli voitettu +Name[fr]=Partie gagnée +Name[gl]=Xogo gañado +Name[he]=ניצחת! +Name[hi]=खेल में जीत हà¥à¤ˆ +Name[hr]=Igra je dobivena +Name[hu]=GyÅ‘zelem +Name[is]=Leikur unninn +Name[it]=Partita vinta +Name[ja]=ゲームã«å‹ã¡ +Name[km]=ល្បែង​បាន​ឈ្នះ +Name[ko]=게임ì—ì„œ ì´ê¹€ +Name[lt]=Žaidimas laimÄ—tas +Name[lv]=SpÄ“le uzvarÄ“ta +Name[mk]=Играта е добиена +Name[nb]=Du vant +Name[nds]=Speel wunnen +Name[ne]=खेल जितà¥à¤¨à¥ भयो +Name[nl]=Spel gewonnen +Name[nn]=Du vann +Name[pa]=ਖੇਡ ਜਿੱਤੀ +Name[pl]=Gra wygrana +Name[pt]=Jogo ganho +Name[pt_BR]=Jogo ganho +Name[ro]=Joc cîştigat +Name[ru]=Победа +Name[se]=Don vuitet +Name[sk]=Vyhraná hra +Name[sl]=Igra je dobljena +Name[sr]=Игра је добијена +Name[sr@Latn]=Igra je dobijena +Name[sv]=Du vann spelet +Name[ta]=ஆடà¯à®Ÿà®®à¯ ஜெயிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Name[tg]=Дар бозӣ ғолиб омадед +Name[tr]=Oyun kazanıldı +Name[uk]=Гру виграно +Name[wa]=Djeu wangnî +Name[zh_CN]=æ‚¨èµ¢äº†æ¸¸æˆ +Name[zh_TW]=éŠæˆ²ç²å‹ +Comment=Game won +Comment[ar]=ربحت اللعبة +Comment[be]=Перамога +Comment[bg]=Спечелихте +Comment[bn]=খেল খতম +Comment[br]=Gounezet eo ar c'hoari +Comment[bs]=Pobjeda +Comment[ca]=Partida guanyada +Comment[cs]=Vyhraná hra +Comment[cy]=Gêm wedi ei ennill +Comment[da]=Spil vundet +Comment[de]=Spiel gewonnen +Comment[el]=Παιχνίδι κεÏδήθηκε +Comment[eo]=Ludo venkita +Comment[es]=Partida ganada +Comment[et]=Mäng läbi, sina võitsid +Comment[eu]=Jokoa irabazi da +Comment[fa]=برد بازی +Comment[fi]=Peli voitettu +Comment[fr]=Partie gagnée +Comment[gl]=Xogo gañado +Comment[he]=ניצחת! +Comment[hi]=खेल में जीत हà¥à¤ˆ +Comment[hr]=Igra je dobivena +Comment[hu]=GyÅ‘zelem +Comment[is]=Leikur unninn +Comment[it]=Partita vinta +Comment[ja]=ゲームã«å‹ã¡ +Comment[km]=ល្បែង​បាន​ឈ្នះ +Comment[ko]=게임ì—ì„œ ì´ê¹€ +Comment[lt]=Žaidimas laimÄ—tas +Comment[lv]=SpÄ“le ir uzvarÄ“ta +Comment[mk]=Играта е добиена +Comment[nb]=Du vant! +Comment[nds]=Speel wunnen +Comment[ne]=खेल जितà¥à¤¨à¥ भयो +Comment[nl]=Spel gewonnen +Comment[nn]=Du vann +Comment[pa]=ਖੇਡ ਜਿੱਤੀ +Comment[pl]=Gra wygrana +Comment[pt]=Jogo ganho +Comment[pt_BR]=Jogo ganho +Comment[ro]=Joc cîştigat +Comment[ru]=Победа +Comment[se]=Don vuitet +Comment[sk]=Vyhraná hra +Comment[sl]=Igra je dobljena +Comment[sr]=Игра је добијена +Comment[sr@Latn]=Igra je dobijena +Comment[sv]=Du vann spelet +Comment[ta]=ஆடà¯à®Ÿà®®à¯ ஜெயிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=Дар бозӣ ғолиб омадед +Comment[tr]=Oyun kazanıldı +Comment[uk]=Гру виграно +Comment[wa]=Djeu wangnî +Comment[zh_CN]=æ‚¨èµ¢äº†æ¸¸æˆ +Comment[zh_TW]=éŠæˆ²ç²å‹ +default_presentation=1 +default_sound=reversi-won.wav + +[lost] +Name=Game lost +Name[ar]=خسرت اللعبة +Name[be]=Параза +Name[bg]=Загубихте +Name[bn]=খেলায় হেরে গিয়েছেন +Name[br]=Kollet eo ar c'hoari +Name[bs]=Poraz +Name[ca]=Partida perduda +Name[cs]=Prohraná hra +Name[cy]=Gêm wedi ei golli +Name[da]=Spil tabt +Name[de]=Spiel verloren +Name[el]=Παιχνίδι χάθηκε +Name[eo]=Ludo malvenkita +Name[es]=Partida perdida +Name[et]=Mäng läbi, sina kaotasid +Name[eu]=Jokoa galdu da +Name[fa]=باخت بازی +Name[fi]=Peli hävitty +Name[fr]=Partie perdue +Name[gl]=Xogo perdido +Name[he]=המשחק הסתיי×, הפסדת +Name[hi]=खेल में हार हà¥à¤ˆ +Name[hr]=Igra je izgubljena +Name[hu]=Vereség +Name[is]=Leik tapað +Name[it]=Partita persa +Name[ja]=ゲームã«è² ã‘ +Name[km]=ល្បែង​បាន​ចាញ់ +Name[ko]=게임ì—ì„œ ì§ +Name[lt]=Žaidimas pralaimÄ—tas +Name[lv]=SpÄ“le zaudÄ“ta +Name[mk]=Играта е изгубена +Name[nb]=Du tapte +Name[nds]=Speel verloren +Name[ne]=खेल हारà¥à¤¨à¥ भयो +Name[nl]=Spel verloren +Name[nn]=Du tapte +Name[pa]=ਖੇਡ ਹਾਰੀ +Name[pl]=Koniec gry, przegraÅ‚eÅ› +Name[pt]=Jogo perdido +Name[pt_BR]=Jogo perdido +Name[ro]=Joc pierdut +Name[ru]=Поражение +Name[se]=Don vuoittohallet +Name[sk]=Prehraná hra +Name[sl]=Igra je izgubljena +Name[sr]=Игра је изгубљена +Name[sr@Latn]=Igra je izgubljena +Name[sv]=Du förlorade spelet +Name[ta]=ஆடà¯à®Ÿà®®à¯ இழகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Name[tg]=Дар бозӣ мағлуб шудед +Name[tr]=Oyun kaybedildi +Name[uk]=Гра програна +Name[wa]=Djeu pierdou +Name[zh_CN]=æ‚¨è¾“äº†æ¸¸æˆ +Name[zh_TW]=éŠæˆ²å¤±æ•— +Comment=Game lost +Comment[ar]=خسرت اللعبة +Comment[be]=Параза +Comment[bg]=Загубихте +Comment[bn]=খেলায় হেরে গিয়েছেন +Comment[br]=Koll eo ar c'hoari +Comment[bs]=Poraz +Comment[ca]=Partida perduda +Comment[cs]=Prohraná hra +Comment[cy]=Gêm wedi ei golli +Comment[da]=Spil tabt +Comment[de]=Spiel verloren +Comment[el]=Παιχνίδι χάθηκε +Comment[eo]=Ludo malvenkita +Comment[es]=Partida perdida +Comment[et]=Mäng läbi, sina kaotasid +Comment[eu]=Jokoa galdu da +Comment[fa]=باخت بازی +Comment[fi]=Peli hävitty +Comment[fr]=Partie perdue +Comment[gl]=Xogo perdido +Comment[he]=המשחק הסתיי×, הפסדת +Comment[hi]=खेल में हार हà¥à¤ˆ +Comment[hr]=Igra je izgubljena +Comment[hu]=Vereség +Comment[is]=Leik tapað +Comment[it]=Partita persa +Comment[ja]=ゲームã«è² ã‘ +Comment[km]=ល្បែង​បាន​ចាញ់ +Comment[ko]=게임ì—ì„œ ì§ +Comment[lt]=Žaidimas pralaimÄ—tas +Comment[lv]=SpÄ“le ir zaudÄ“ta +Comment[mk]=Играта е изубена +Comment[nb]=Du tapte +Comment[nds]=Speel verloren +Comment[ne]=खेल हारà¥à¤¨à¥ भयो +Comment[nl]=Spel verloren +Comment[nn]=Du tapte +Comment[pa]=ਖੇਡ ਹਾਰੀ +Comment[pl]=Koniec gry, przegraÅ‚eÅ› +Comment[pt]=Jogo perdido +Comment[pt_BR]=Jogo perdido +Comment[ro]=Joc pierdut +Comment[ru]=Поражение +Comment[se]=Don vuoittohallet +Comment[sk]=Prehraná hra +Comment[sl]=Igra je izgubljena +Comment[sr]=Игра је изгубљена +Comment[sr@Latn]=Igra je izgubljena +Comment[sv]=Du förlorade spelet +Comment[ta]=ஆடà¯à®Ÿà®®à¯ இழகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=Дар бозӣ мағлуб шудед +Comment[tr]=Oyun kaybedildi +Comment[uk]=Гра програна +Comment[wa]=Djeu pierdou +Comment[zh_CN]=æ‚¨è¾“äº†æ¸¸æˆ +Comment[zh_TW]=éŠæˆ²å¤±æ•— +default_presentation=0 + +[draw] +Name=Draw +Name[be]=ÐÑ–Ñ‡Ñ‹Ñ +Name[bg]=РавенÑтво +Name[bn]=অমীমাংসিত +Name[br]=Tresañ +Name[bs]=Crtaj +Name[ca]=Empat +Name[cs]=Remíza +Name[cy]=Arlunio +Name[da]=Træk +Name[de]=Ziehen +Name[el]=Ισοπαλία +Name[eo]=EgaliÄo +Name[es]=Dibujar +Name[et]=Viik +Name[eu]=Berdinketa +Name[fa]=قرعه‌کشی +Name[fi]=Tasapeli +Name[fr]=Égalité +Name[ga]=Tarraing +Name[he]=תיקו +Name[hr]=Crtanje +Name[hu]=Döntetlen +Name[is]=Jafntefli +Name[it]=Pari +Name[ja]=引ã分㑠+Name[km]=គូស +Name[ko]=비김 +Name[lt]=PieÅ¡ti +Name[lv]=Vilkt +Name[mk]=Ðерешено +Name[nb]=Uavgjort +Name[nds]=Trecken +Name[ne]=कोरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Tekenen +Name[nn]=Uavgjort +Name[pa]=ਬਰਾਬਰ +Name[pl]=Rysuj +Name[pt]=Empate +Name[pt_BR]=Empate +Name[ru]=ÐÐ¸Ñ‡ÑŒÑ +Name[sk]=Remíza +Name[sl]=Poteg +Name[sr]=Вуци +Name[sr@Latn]=Vuci +Name[sv]=Oavgjort +Name[ta]=வரை +Name[tg]=Кашидан +Name[tr]=Berabere +Name[uk]=ÐÑ–Ñ‡Ð¸Ñ +Name[zh_CN]=平局 +Name[zh_TW]=平手 +Comment=Draw +Comment[be]=ÐÑ–Ñ‡Ñ‹Ñ +Comment[bg]=РавенÑтво +Comment[bn]=অমীমাংসিত +Comment[br]=Tresañ +Comment[bs]=Crtaj +Comment[ca]=Empat +Comment[cs]=Remíza +Comment[cy]=Arlunio +Comment[da]=Træk +Comment[de]=Ziehen +Comment[el]=Ισοπαλία +Comment[eo]=EgaliÄo +Comment[es]=Dibujar +Comment[et]=Viik +Comment[eu]=Berdinketa +Comment[fa]=قرعه‌کشی +Comment[fi]=Tasapeli +Comment[fr]=Égalité +Comment[ga]=Tarraing +Comment[he]=תיקו +Comment[hr]=Crtanje +Comment[hu]=Döntetlen +Comment[is]=Jafntefli +Comment[it]=Pari +Comment[ja]=引ã分㑠+Comment[km]=គូស +Comment[lt]=PieÅ¡ti +Comment[lv]=Vilkt +Comment[mk]=Ðерешено +Comment[nb]=Uavgjort +Comment[nds]=Trecken +Comment[ne]=कोरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Tekenen +Comment[nn]=Uavgjort +Comment[pa]=ਬਰਾਬਰ +Comment[pl]=Dobranie karty +Comment[pt]=Empate +Comment[pt_BR]=Empate +Comment[ru]=ÐÐ¸Ñ‡ÑŒÑ +Comment[sk]=Remíza +Comment[sl]=Poteg +Comment[sr]=Вуци +Comment[sr@Latn]=Vuci +Comment[sv]=Oavgjort +Comment[ta]=வரை +Comment[tg]=Кашидан +Comment[tr]=Beraberlik +Comment[uk]=Ð½Ñ–Ñ‡Ð¸Ñ +Comment[zh_CN]=平局 +Comment[zh_TW]=平手 +default_presentation=0 + +[illegal_move] +Name=Illegal Move +Name[be]=ÐÑправільны ход +Name[bg]=Ðевалиден ход +Name[bn]=অবৈধ চাল +Name[bs]=Nedozvoljen potez +Name[ca]=Moviment il·legal +Name[cs]=Neplatný tah +Name[cy]=Symudiad Angyfreithlon +Name[da]=Ulovligt træk +Name[de]=Unerlaubter Zug +Name[el]=Μη έγκυÏη κίνηση +Name[eo]=Nepermesita movo +Name[es]=Movimiento ilegal +Name[et]=Lubamatu käik +Name[eu]=Legez kanpoko mugimendua +Name[fa]=حرکت غیر مجاز +Name[fi]=Laiton siirto +Name[fr]=Déplacement interdit +Name[ga]=Beart Neamhcheadaithe +Name[he]=מהלך ×œ× ×—×•×§×™ +Name[hr]=Nepravilan potez +Name[hu]=Szabálytalan lépés +Name[is]=Ógildur leikur +Name[it]=Mossa non lecita +Name[ja]=ä¸æ­£ãªç§»å‹• +Name[km]=ការ​ផ្លាស់ទី​មិន​ážáŸ’រឹមážáŸ’រូវ +Name[ko]=ìž˜ëª»ëœ ì›€ì§ìž„ +Name[lt]=Negalimas Ä—jimas +Name[lv]=NelegÄls gÄjiens +Name[mk]=Ðедозволен потег +Name[nb]=Ugyldig trekk +Name[nds]=Tog nich verlöövt +Name[ne]=अवैध चाल +Name[nl]=Foutieve zet +Name[nn]=Ugyldig trekk +Name[pa]=ਗਲਤ ਚਾਲ +Name[pl]=NieprawidÅ‚owy ruch +Name[pt]=Jogada Inválida +Name[pt_BR]=Movimento Ilegal +Name[ru]=Ðеправильный ход +Name[se]=Gustohis sirdin +Name[sk]=Neplatný Å¥ah +Name[sl]=Neveljavna poteza +Name[sr]=Ðедозвољен потез +Name[sr@Latn]=Nedozvoljen potez +Name[sv]=Felaktigt drag +Name[ta]=நிகழà¯à®šà¯à®šà®¿à®¯à¯ˆ வெளிபà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯ +Name[tg]=Ҳаракати ÐодуруÑÑ‚ +Name[tr]=Gecersiz Hamle +Name[uk]=Ðедозволений хід +Name[zh_CN]=无效移动 +Name[zh_TW]=ä¸åˆæ³•çš„移動 +Comment=Illegal move +Comment[be]=ÐÑправільны ход +Comment[bg]=Ðевалиден ход +Comment[bn]=অবৈধ চাল +Comment[bs]=Nedozvoljen potez +Comment[ca]=Moviment il·legal +Comment[cs]=Neplatný tah +Comment[cy]=Symudiad angyfreithlon +Comment[da]=Ulovligt træk +Comment[de]=Unerlaubter Zug +Comment[el]=Μη έγκυÏη κίνηση +Comment[eo]=Nepermesita movo +Comment[es]=Movimiento ilegal +Comment[et]=Lubamatu käik +Comment[eu]=Legez kanpoko mugimendua +Comment[fa]=حرکت غیر مجاز +Comment[fi]=Laiton siirto +Comment[fr]=Déplacement interdit +Comment[ga]=Beart neamhcheadaithe +Comment[he]=מהלך ×œ× ×—×•×§×™ +Comment[hr]=Nepravilan potez +Comment[hu]=Szabálytalan lépés +Comment[is]=Ógildur leikur +Comment[it]=Mossa non lecita +Comment[ja]=ä¸æ­£ãªç§»å‹• +Comment[km]=ការ​ផ្លាស់ទី​មិន​ážáŸ’រឹមážáŸ’រូវ +Comment[ko]=ìž˜ëª»ëœ ì›€ì§ìž„ +Comment[lt]=Negalimas Ä—jimas +Comment[lv]=Å Äds gÄjiens nav atļauts +Comment[mk]=Ðедозволен потег +Comment[nb]=Ugyldig trekk +Comment[nds]=Tog nich verlöövt +Comment[ne]=अवैध चाल +Comment[nl]=Foutieve zet +Comment[nn]=Ugyldig trekk +Comment[pa]=ਗਲਤ ਚਾਲ +Comment[pl]=NieprawidÅ‚owy ruch +Comment[pt]=Jogada inválida +Comment[pt_BR]=Movimento ilegal +Comment[ru]=Ðеправильный ход +Comment[se]=Gustohis sirdin +Comment[sk]=Neplatný Å¥ah +Comment[sl]=Neveljavna poteza +Comment[sr]=Ðедозвољен потез +Comment[sr@Latn]=Nedozvoljen potez +Comment[sv]=Felaktigt drag +Comment[ta]=தவறான நகரà¯à®¤à¯à®¤à®²à¯ +Comment[tr]=Geçersiz hamle +Comment[uk]=Ðедозволений хід +Comment[zh_CN]=无效移动 +Comment[zh_TW]=ä¸åˆæ³•çš„移動 +default_presentation=0 diff --git a/kreversi/sounds/reversi-click.wav b/kreversi/sounds/reversi-click.wav new file mode 100644 index 00000000..0b3a2964 Binary files /dev/null and b/kreversi/sounds/reversi-click.wav differ diff --git a/kreversi/sounds/reversi-won.wav b/kreversi/sounds/reversi-won.wav new file mode 100644 index 00000000..8f5c0703 Binary files /dev/null and b/kreversi/sounds/reversi-won.wav differ diff --git a/kreversi/version.h b/kreversi/version.h new file mode 100644 index 00000000..d95b83ed --- /dev/null +++ b/kreversi/version.h @@ -0,0 +1 @@ +#define KREVERSI_VERSION "1.7.1" diff --git a/ksame/ChangeLog b/ksame/ChangeLog new file mode 100644 index 00000000..bb66b357 --- /dev/null +++ b/ksame/ChangeLog @@ -0,0 +1,17 @@ +1999-06-20 Mario Weilguni + + * made it work again with Qt-2 + * fixed some old-style (K&R-C) declartions to C++ decl. + * removed debug messages when compiling with --enable-final + +1999-06-07 Petter Reinholdtsen + * knotes.cpp: Changed text Quit to Exit to match KDE style guide. + +0.5: Mario Weilguni + + * fixes for Qt 2.0 (fixed highscore list, added layout) + +0.2.1 + * [Robert Williams] Add -caption "%c" to ksame.kdelnk + * [Robert Williams] getHelpMenu(true, 0) + * [Robert Williams] Added version.h and ChangeLog diff --git a/ksame/KSameWidget.cpp b/ksame/KSameWidget.cpp new file mode 100644 index 00000000..e9f2d730 --- /dev/null +++ b/ksame/KSameWidget.cpp @@ -0,0 +1,246 @@ +/* Yo Emacs, this is -*- C++ -*- */ +/* + * ksame 0.4 - simple Game + * Copyright (C) 1997,1998 Marcus Kreutzberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "KSameWidget.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "StoneWidget.h" + +static int default_colors=3; + +#define Board KScoreDialog::Custom1 + +KSameWidget::KSameWidget(QWidget *parent, const char* name, WFlags fl) : + KMainWindow(parent,name,fl) +{ + KStdGameAction::gameNew(this, SLOT(m_new()), actionCollection(), "game_new"); + restart = new KAction(i18n("&Restart This Board"), CTRL+Key_R, this, + SLOT(m_restart()), actionCollection(), "game_restart"); + KStdGameAction::highscores(this, SLOT(m_showhs()), actionCollection(), "game_highscores"); + KStdGameAction::quit(this, SLOT(close()), actionCollection(), "game_quit"); + undo = KStdGameAction::undo(this, SLOT(m_undo()), actionCollection(), "edit_undo"); + + random = new KToggleAction(i18n("&Random Board"), 0, 0, 0, actionCollection(), "random_board"); + showNumberRemaining = new KToggleAction(i18n("&Show Number Remaining"), 0, this, SLOT(showNumberRemainingToggled()), actionCollection(), "showNumberRemaining"); + + KStdAction::configureNotifications(this, SLOT(configureNotifications()), + actionCollection()); + + status=statusBar(); + status->insertItem(i18n("Colors: XX"),1,1); + status->insertItem(i18n("Board: XXXXXX"),2,1); + status->insertItem(i18n("Marked: XXXXXX"),3,1); + status->insertItem(i18n("Score: XXXXXX"),4,1); + + stone = new StoneWidget(this,15,10); + + connect( stone, SIGNAL(s_gameover()), this, SLOT(gameover())); + connect( stone, SIGNAL(s_colors(int)), this, SLOT(setColors(int))); + connect( stone, SIGNAL(s_board(int)), this, SLOT(setBoard(int))); + connect( stone, SIGNAL(s_marked(int)), this, SLOT(setMarked(int))); + connect( stone, SIGNAL(s_score(int)), this, SLOT(setScore(int))); + connect( stone, SIGNAL(s_remove(int,int)), this, SLOT(stonesRemoved(int,int))); + + connect(stone, SIGNAL(s_sizechanged()), this, SLOT(sizeChanged())); + + sizeChanged(); + setCentralWidget(stone); + + // Once the main view can scale then use defualt setupGUI() and remove the + // noMerge="1" from the uirc file + // StatusBar | ToolBar + setupGUI(KMainWindow::Save | Keys | Create ); + + random->setChecked(true); + setScore(0); + + if (!kapp->isRestored()) + newGame(kapp->random(),default_colors); + + KConfig *cfg = kapp->config(); + if (cfg->readBoolEntry("showRemaining")) + { + showNumberRemaining->setChecked(true); + showNumberRemainingToggled(); + } +} + +void KSameWidget::readProperties(KConfig *conf) { + Q_ASSERT(conf); + stone->readProperties(conf); +} + +void KSameWidget::saveProperties(KConfig *conf) { + Q_ASSERT(conf); + stone->saveProperties(conf); + conf->sync(); +} + +void KSameWidget::sizeChanged() { + stone->setFixedSize(stone->sizeHint()); +} + +void KSameWidget::showNumberRemainingToggled() +{ + if(showNumberRemaining->isChecked()){ + QStringList list; + for(int i=1;i<=stone->colors();i++) + list.append(QString("%1").arg(stone->count(i))); + QString count = QString(" (%1)").arg(list.join(",")); + status->changeItem(i18n("%1 Colors%2").arg(stone->colors()).arg(count),1); + } + else status->changeItem(i18n("%1 Colors").arg(stone->colors()),1); + + KConfig *cfg = kapp->config(); + cfg->writeEntry("showRemaining", showNumberRemaining->isChecked()); + cfg->sync(); +} + +void KSameWidget::newGame(unsigned int board,int colors) { + while (board>=1000000) board-=1000000; + // kdDebug() << "newgame board " << board << " colors " << colors << endl; + stone->newGame(board,colors); + setScore(0); +} + +bool KSameWidget::confirmAbort() { + return stone->isGameover() || + stone->isOriginalBoard() || + (KMessageBox::questionYesNo(this, i18n("Do you want to resign?"), + i18n("New Game"),i18n("Resign"),KStdGuiItem::cancel()) == KMessageBox::Yes); +} + +void KSameWidget::m_new() { + if (random->isChecked()) { + if (confirmAbort()) + newGame(kapp->random(),default_colors); + } else { + KDialogBase dlg(this, "boardchooser", true, + i18n("Select Board"), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok); + + QVBox *page = dlg.makeVBoxMainWidget(); + + KIntNumInput bno(0, page); + bno.setRange(0, 1000000, 1); + bno.setLabel(i18n("Select a board:")); + bno.setFocus(); + bno.setFixedSize(bno.sizeHint()); + bno.setValue(stone->board()); + + if (dlg.exec()) newGame(bno.value(),default_colors); + } +} + +void KSameWidget::m_restart() { + if (confirmAbort()) + newGame(stone->board(),default_colors); +} + +void KSameWidget::m_undo() { + Q_ASSERT(stone); + stone->undo(); +} + +void KSameWidget::m_showhs() { + Q_ASSERT(stone); + stone->unmark(); + KScoreDialog d(KScoreDialog::Name | KScoreDialog::Score, this); + d.addField(Board, i18n("Board"), "Board"); + d.exec(); +} + +void KSameWidget::setColors(int colors) { + status->changeItem(i18n("%1 Colors").arg(colors),1); +} + +void KSameWidget::setBoard(int board) { + status->changeItem(i18n("Board: %1").arg(board, 6), 2); +} + +void KSameWidget::setMarked(int m) { + status->changeItem(i18n("Marked: %1").arg(m, 6),3); + m_markedStones=m; +} + +void KSameWidget::stonesRemoved(int,int) { + KNotifyClient::event(winId(),"stones removed", + i18n("One stone removed.","%n stones removed.",m_markedStones)); +} + +void KSameWidget::setScore(int score) { + if(showNumberRemaining->isChecked()){ + QStringList list; + for(int i=1;i<=stone->colors();i++) + list.append(QString("%1").arg(stone->count(i))); + QString count = QString(" (%1)").arg(list.join(",")); + status->changeItem(i18n("%1 Colors%2").arg(stone->colors()).arg(count),1); + } + status->changeItem(i18n("Score: %1").arg(score, 6),4); + undo->setEnabled(stone->undoPossible()); + restart->setEnabled(!stone->isOriginalBoard()); +} + +void KSameWidget::gameover() { + if (stone->hasBonus()) { + KNotifyClient::event(winId(), "game won", + i18n("You even removed the last stone, great job! " + "This gave you a score of %1 in total.").arg(stone->score())); + } else { + KNotifyClient::event(winId(), "game over", + i18n("There are no more removeable stones. " + "You got a score of %1 in total.").arg(stone->score())); + } + stone->unmark(); + KScoreDialog d(KScoreDialog::Name | KScoreDialog::Score, this); + d.addField(Board, i18n("Board"), "Board"); + + KScoreDialog::FieldInfo scoreInfo; + scoreInfo[Board].setNum(stone->board()); + + if (d.addScore(stone->score(), scoreInfo)) + d.exec(); +} + +void KSameWidget::configureNotifications() { + KNotifyDialog::configure(this); +} + +#include "KSameWidget.moc" diff --git a/ksame/KSameWidget.h b/ksame/KSameWidget.h new file mode 100644 index 00000000..ff02e1c4 --- /dev/null +++ b/ksame/KSameWidget.h @@ -0,0 +1,79 @@ +/* Yo Emacs, this is -*- C++ -*- */ +/* + * ksame 0.4 - simple Game + * Copyright (C) 1997,1998 Marcus Kreutzberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _KSAMEWIDGET +#define _KSAMEWIDGET + +#include + +class KToggleAction; +class StoneWidget; + +class KSameWidget: public KMainWindow { + Q_OBJECT + +public: + KSameWidget(QWidget *parent=0, const char* name=0, WFlags fl=0); + +private slots: + /* File Menu */ + void m_new(); + void m_restart(); + void m_showhs(); + void m_undo(); + + void configureNotifications(); + + void sizeChanged(); + void gameover(); + void setColors(int colors); + void setBoard(int board); + void setScore(int score); + void setMarked(int m); + void stonesRemoved(int, int); + + void showNumberRemainingToggled(); + +protected: + void newGame(unsigned int board, int colors); + + virtual void saveProperties(KConfig *conf); + virtual void readProperties(KConfig *conf); + + bool confirmAbort(); + +private: + // Remember how many stones were last marked, since when + // the stones are removed, board->marked() suddenly becomes + // 0 (or doesn't otherwise reflect the number of stones just removed). + int m_markedStones; + + StoneWidget *stone; + KStatusBar *status; + KToggleAction *random; + KToggleAction *showNumberRemaining; + KAction *restart; + KAction *undo; + +}; + +#endif // _KSAMEWIDGET + diff --git a/ksame/Makefile.am b/ksame/Makefile.am new file mode 100644 index 00000000..593a88f4 --- /dev/null +++ b/ksame/Makefile.am @@ -0,0 +1,24 @@ + +bin_PROGRAMS = ksame +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) + +ksame_SOURCES= main.cpp StoneField.cpp StoneWidget.cpp KSameWidget.cpp +ksame_LDFLAGS = $(all_libraries) $(KDE_RPATH) +ksame_LDADD = $(LIB_KDEGAMES) $(LIB_KIO) +ksame_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +METASOURCES = AUTO + +noinst_HEADERS = StoneField.h StoneWidget.h KSameWidget.h version.h + +xdg_apps_DATA = ksame.desktop + +appdir = $(kde_datadir)/ksame +app_DATA = stones.png ksameui.rc eventsrc + +KDE_ICON = ksame + +EXTRA_DIST = $(app_DATA) + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/ksame.pot diff --git a/ksame/StoneField.cpp b/ksame/StoneField.cpp new file mode 100644 index 00000000..3862d7fb --- /dev/null +++ b/ksame/StoneField.cpp @@ -0,0 +1,399 @@ +/* + * ksame 0.4 - simple Game + * Copyright (C) 1997,1998 Marcus Kreutzberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "StoneField.h" +#include +#include + +StoneFieldState::StoneFieldState(const StoneField &stonefield) +{ + field=new unsigned char[stonefield.maxstone]; + for (int i=0;i0); + Q_ASSERT(height>0); + + if (undoenabled) undolist=new QPtrList; + else undolist=0; + + sizex=width; + sizey=height; + maxstone=sizex*sizey; + field=new Stone[maxstone]; + newGame(board,colors); + m_gotBonus= false; +} + +StoneField::~StoneField() { + delete[] field; + delete undolist; + kdDebug() << "~StoneField\n" << endl; +} + +int StoneField::count(int color){ + int c = 0; + Stone *stone=field; + for (int i=0;icolor == color) + c++; + } + return c; +} + +int +StoneField::width() const { + return sizex; +} + +int +StoneField::height() const { + return sizey; +} + +void +StoneField::newGame(unsigned int board,int colors) { + kdDebug() << "StoneField::newgame board " + << board << " colors " << colors << endl; + if (colors<1) colors=3; + if (colors>7) colors=7; + this->colors=colors; + this->board=board; + reset(); +} + +void +StoneField::reset() { + random.setSeed(board); + + Stone *stone=field; + for (int i=0;icolor=1+random.getLong(colors); + stone->marked=false; + stone->changed=true; + } + + gameover=-1; + score=0; + marked=0; + + if (undolist) { + undolist->setAutoDelete(true); + undolist->clear(); + } + + int c[7]; + int j; + for (j=0;j<7;j++) c[j]=0; + + for (j=0,stone=field;jcolor]++; + } + kdDebug() << "red " << c[1] << endl; + kdDebug() << "blue " << c[2] << endl; + kdDebug() << "yellow " << c[3] << endl; + kdDebug() << "green " << c[4] << endl; +} + +int +StoneField::map(int x,int y) { + assert (!(x<0||y<0||x>=sizex||y>=sizey)); + return x+y*sizex; +} + +int +StoneField::mark(int x,int y,bool force) { + int index=map(x,y); + + if (index<0) { + unmark(); + return 0; + } + + if (field[index].marked) return -1; + unmark(); + + mark(index,field[index].color); + + if (marked==1&&!force) { + field[index].marked=false; + marked=0; + } + return marked; +} + +void +StoneField::mark(int index,unsigned char color) { + if ( index<0 || index>=maxstone ) return; + + Stone &stone=field[index]; + + if (stone.marked) return; + + if (!stone.color || stone.color!=color) return; + + stone.changed=true; + stone.marked=true; + marked++; + + // mark left + if ((index%sizex)!=0) mark(index-1,color); + // mark right + if (((index+1)%sizex)!=0) mark(index+1,color); + // mark upward + if (index>=sizex) mark(index-sizex,color); + // mark downward + if (index<(sizex-1)*sizey) mark(index+sizex,color); +} + +void +StoneField::unmark() { + if (!marked) return; + + Stone *stone=field; + for (int i=0;imarked=false; + stone->changed=true; + } + marked=0; +} + +int +StoneField::remove(int x,int y,bool force) { + int index=map(x,y); + + if (index<0) return 0; + + if (!field[index].marked) { + mark(x,y,force); + } + + if (!marked) return 0; + + // remove a single stone?? + if (marked==1&&!force) return 0; + + // add current field to undolist + if (undolist) + undolist->append(new StoneFieldState(*this)); + + // increase score + if (marked>2) + score+=(marked-2)*(marked-2); + + // remove marked stones + Stone *stone=field; + for (int i=0;imarked) { + stone->color=0; + stone->changed=true; + stone->marked=false; + } + } + int removed=marked; + marked=0; + + for (int col=0;col=0 && field[i1].color ) i1-=sizex; + int i2=i1; + while (i2>=0) { + while ( i2>=0 && !field[i2].color ) i2-=sizex; + while ( i2>=0 && field[i2].color ) { + field[i1].color=field[i2].color; + field[i1].changed=true; + field[i2].color=0; + field[i2].changed=true; + i1-=sizex; + i2-=sizex; + } + } + } + + // find the last column that has something + int lastcol = sizex; + while (lastcol > 0 && !field[map(lastcol-1, sizey-1)].color) { + lastcol--; + } + + for (int col=0;col sizex - 1) + break; // we're ready + + for (int row=0; row < sizey; row++) { + int source = map(nextfullcol, row); + int dest = map(col, row); + field[dest].color=field[source].color; + field[dest].changed=true; + field[source].color=0; + field[source].changed=true; + } + } + + // add a bonus, if field is empty + if (!field[map(0, sizey-1)].color) { + score+=1000; + m_gotBonus= true; + } + + // gameover is undefined + gameover=-1; + return removed; +} + +bool StoneField::undoPossible() const { + return !(!undolist||undolist->isEmpty()); +} + +int +StoneField::undo(int count) { + if (!undoPossible()) + return 0; + if (count <= 0) + return 0; + int undocount=1; + StoneFieldState *state=0; + undolist->setAutoDelete(true); + while (--count>0) { + if (undolist->count()==1) break; + undolist->removeLast(); + undocount++; + } + state=undolist->getLast(); + Q_ASSERT(state); + state->restore(*this); + undolist->removeLast(); + return undocount; +} + +bool +StoneField::isGameover() const { + register int i=maxstone-1;; + register unsigned char color; + + if (gameover>=0) return (bool)gameover; + // kdDebug() << "-->gameover" << endl; + + while (i>=0) { + // kdDebug() << i << " " << field[i].color << endl; + // ignore empty fields + while ( i>=0 && field[i].color==0 ) i--; + // Wenn Stein gefunden, + // dann die Nachbarn auf gleiche Farbe pruefen. + while ( i>=0 && (color=field[i].color) ) { + // check left + if ( (i%sizex)!=0 && field[i-1].color==color) + goto check_gameover; + // check upward + if ( i>=sizex && field[i-sizex].color==color) + goto check_gameover; + i--; + } + } + check_gameover: + gameover=(i<0); + // kdDebug() << "<--gameover" << endl; + return (bool)gameover; +} + +bool StoneField::gotBonus() const { + return m_gotBonus; +} + +int +StoneField::getBoard() const { + return board; +} + +int +StoneField::getScore() const { + return score; +} + +int +StoneField::getColors() const { + return colors; +} + +int +StoneField::getMarked() const { + return marked; +} + +int +StoneField::getFieldSize() const { + return maxstone; +} + +struct Stone * +StoneField::getField() const { + return field; +} + + + + + + + diff --git a/ksame/StoneField.h b/ksame/StoneField.h new file mode 100644 index 00000000..df199605 --- /dev/null +++ b/ksame/StoneField.h @@ -0,0 +1,111 @@ +/* Yo Emacs, this is -*- C++ -*- */ +/* + * ksame 0.4 - simple Game + * Copyright (C) 1997,1998 Marcus Kreutzberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _STONEFIELD +#define _STONEFIELD + +#include +#include + +struct Stone { + unsigned char color; + bool changed; + bool marked; +}; + +class StoneField; +class StoneWidget; + +class StoneFieldState { +private: + unsigned char *field; + + int colors; + unsigned int board; + unsigned int score; + int gameover; + +public: + StoneFieldState(const StoneField &stonefield); + ~StoneFieldState(); + void restore(StoneField &stonefield) const; +}; + + +class StoneField { + friend class StoneFieldState; + friend class StoneWidget; +private: + + int sizex; + int sizey; + int maxstone; + + struct Stone *field; + + int colors; + unsigned int board; + unsigned int score; + mutable int gameover; + bool m_gotBonus; + int marked; + + KRandomSequence random; + QPtrList *undolist; +public: + StoneField(int width=15,int height=10, + int colors=3,unsigned int board=0, + bool undoenabled=true); + ~StoneField(); + + int width() const; + int height() const; + + void newGame(unsigned int board,int colors); + + void reset(); + + int mark(int x,int y,bool force=false); + void unmark(); + + int remove(int x,int y,bool force=false); + + int undo(int count=1); + + bool isGameover() const; + bool gotBonus() const; + bool undoPossible() const; + int getBoard() const; + int getScore() const; + int getColors() const; + int getMarked() const; + + int count(int color); +protected: + int getFieldSize() const; + struct Stone *getField() const; + + int map(int x,int y); + void mark(int index,unsigned char color); +}; + +#endif // _STONEFIELD + diff --git a/ksame/StoneWidget.cpp b/ksame/StoneWidget.cpp new file mode 100644 index 00000000..3d4114de --- /dev/null +++ b/ksame/StoneWidget.cpp @@ -0,0 +1,345 @@ +/* + * ksame 0.4 - simple Game + * Copyright (C) 1997,1998 Marcus Kreutzberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "StoneWidget.moc" + + +struct StoneSlice { + QPixmap stone; +}; + + +StoneWidget::StoneWidget( QWidget *parent, int x, int y ) + : QWidget(parent,"StoneWidget"), stonefield(x,y) +{ + setBackgroundPixmap(QPixmap(locate("wallpaper", "Time-For-Lunch-2.jpg"))); + QPixmap stonemap(locate("appdata", "stones.png")); + + assert(!stonemap.isNull()); + + slice=0; + maxslices=30; + maxcolors=4; + + sizex=x; + sizey=y; + + stone_width=stonemap.width()/(maxslices+1); + stone_height=stonemap.height()/maxcolors; + + map = new StoneSlice*[maxcolors]; + QBitmap mask; + for (int c = 0; c < maxcolors; c++) { + map[c] = new StoneSlice[maxslices]; + + for (int s = 0; s < maxslices; s++) { + map[c][s].stone.resize(stone_width, stone_height); + assert(!map[c][s].stone.isNull()); + bitBlt(&map[c][s].stone, 0, 0, + &stonemap, stone_width * s, + c*stone_height, + stone_width,stone_height,CopyROP,false); + QImage im = map[c][s].stone.convertToImage(); + mask = im.createHeuristicMask(); + map[c][s].stone.setMask(mask); + } + } + + field_height=stone_height*sizey; + field_width=stone_width*sizex; + + setMouseTracking(true); + + // QColor c(115,115,115); + // setBackgroundColor(c); + + // emit s_sizechanged(); + startTimer( 100 ); + history.setAutoDelete(true); +} + +StoneWidget::~StoneWidget() { + for (int c = 0; c < maxcolors; c++) { + delete [] map[c]; + } + delete [] map; + + setMouseTracking(false); + killTimers(); +} + +unsigned int +StoneWidget::board() { + return stonefield.getBoard(); +} + +int +StoneWidget::score() { + return stonefield.getScore(); +} + +int +StoneWidget::count(int color) { + return stonefield.count(color); +} + +int +StoneWidget::marked() { + return stonefield.getMarked(); +} + +QSize +StoneWidget::size() { + return QSize(sizex,sizey); +} + +int +StoneWidget::colors() { + return stonefield.getColors(); +} + +QSize +StoneWidget::sizeHint () const { + return QSize(field_width,field_height); +} + +void +StoneWidget::newGame(unsigned int board,int colors) { + stonefield.newGame(board,colors); + history.clear(); + modified= false; + emit s_newgame(); + emit s_colors(stonefield.getColors()); + emit s_board(stonefield.getBoard()); +} + +void +StoneWidget::reset() { + stonefield.reset(); + history.clear(); + emit s_newgame(); +} + +void +StoneWidget::unmark() { + stonefield.unmark(); + emit s_marked(0); +} + +bool StoneWidget::undoPossible() const { + if (stonefield.isGameover()) return false; + return stonefield.undoPossible(); +} + +int +StoneWidget::undo(int count) { + if (stonefield.isGameover()) return 0; + + int ret_val=stonefield.undo(count); + + QPoint p=mapFromGlobal(cursor().pos()); + int x=p.x(); + int y=p.y(); + if (x<0||y<0||x>=field_width||y>=field_height) { + emit s_score(stonefield.getMarked()); + return ret_val; + } + + int marked=stonefield.mark(x/stone_width,y/stone_height); + emit s_marked(marked); + slice=0; + emit s_score(stonefield.getScore()); + modified= (stonefield.getScore()>0); + return ret_val; +} + +bool StoneWidget::isGameover() { + return stonefield.isGameover(); +} + +bool StoneWidget::hasBonus() { + return stonefield.gotBonus(); // don't ask me why the names differ... ;-| [hlm] +} + +bool StoneWidget::isOriginalBoard() { + return !modified; +} + +void StoneWidget::readProperties(KConfig *conf) { + Q_ASSERT(conf); + + history.clear(); + + if (!conf->hasKey("Board")|| + !conf->hasKey("Colors")|| + !conf->hasKey("Stones")) { + return; + } + newGame(conf->readNumEntry("Board"),conf->readNumEntry("Colors")); + + QStrList list; + conf->readListEntry("Stones",list); + + for (const char *item=list.first();item;item=list.next()) { + int x=-1,y=-1; + if (sscanf(item,"%02X%02X",&x,&y)!=2) break; + history.append(new QPoint(x,y)); + stonefield.remove(x,y); + } +} + + +void +StoneWidget::saveProperties(KConfig *conf) { + Q_ASSERT(conf); + + QStrList list(true); + QString tmp; + + for (QPoint *item=history.first();item;item=history.next()) { + tmp.sprintf("%02X%02X",item->x(),item->y()); + list.append(tmp.ascii()); + } + + conf->writeEntry("Stones",list); + conf->writeEntry("Board",stonefield.getBoard()); + conf->writeEntry("Colors",stonefield.getColors()); +} + +void +StoneWidget::timerEvent( QTimerEvent * ) { + QPoint p=mapFromGlobal(cursor().pos()); + int x=p.x(); + int y=p.y(); + if (x<0||y<0||x>=field_width||y>=field_height) + stonefield.unmark(); + slice=(slice+1)%maxslices; + paintEvent(0); +} + +void +StoneWidget::paintEvent( QPaintEvent *e ) { + + Stone *stone=stonefield.getField(); + + for (int y=0;ymarked||stone->changed; + + if (!redraw&&e) { + QRect r(cx,cy,stone_width,stone_height); + redraw=r.intersects(e->rect()); + } + if (redraw) { + stone->changed=false; + if (stone->color) { + + int tslice = stone->marked?slice:0; + bitBlt(this,cx,cy, + &map[stone->color-1][tslice].stone, + 0, 0, + stone_width,stone_height,CopyROP,FALSE); + + } else { + erase(cx, cy, stone_width, stone_height); + } + } + stone++; // naechster Stein. + } + } +} + +void +StoneWidget::mousePressEvent ( QMouseEvent *e) { + + if (stonefield.isGameover()) return; + + int x=e->pos().x(); + int y=e->pos().y(); + if (x<0||y<0||x>=field_width||y>=field_height) return; + + int sx=x/stone_width; + int sy=y/stone_height; + + if (stonefield.remove(sx, sy)) { + history.append(new QPoint(sx, sy)); + + emit s_remove(sx, sy); + + stonefield.mark(sx,sy); + emit s_marked(stonefield.getMarked()); + modified= true; + + emit s_score(stonefield.getScore()); + if (stonefield.isGameover()) emit s_gameover(); + } +} + +void +StoneWidget::mouseMoveEvent ( QMouseEvent *e) +{ + if (stonefield.isGameover()) { + stonefield.unmark(); + emit s_marked(0); + return; + } + + int x=e->pos().x(); + int y=e->pos().y(); + if (x<0||y<0||x>=field_width||y>=field_height) return; + + int marked=stonefield.mark(x/stone_width,y/stone_height); + if (marked>=0) { + emit s_marked(marked); + slice=0; + } +} + + + + + + + + diff --git a/ksame/StoneWidget.h b/ksame/StoneWidget.h new file mode 100644 index 00000000..05276924 --- /dev/null +++ b/ksame/StoneWidget.h @@ -0,0 +1,107 @@ +/* Yo Emacs, this is -*- C++ -*- */ +/* + * ksame 0.4 - simple Game + * Copyright (C) 1997,1998 Marcus Kreutzberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _STONEWIDGET +#define _STONEWIDGET + +#include +#include "StoneField.h" + +struct StoneSlice; + +class StoneWidget : public QWidget { + Q_OBJECT + + int modified; + // int marked; // # of marked stones + + int stones_x, stones_y; + int sizex, sizey; + int field_width, field_height; + + QPtrList history; + StoneField stonefield; + + // picture number of stonemovie + int slice; + + StoneSlice **map; + +public: + StoneWidget( QWidget *parent=0, int x=10,int y=10); + ~StoneWidget(); + + unsigned int board(); + int score(); + int marked(); + QSize size(); + int colors(); + virtual QSize sizeHint() const; + + bool undoPossible() const; + + void newGame(unsigned int board, int colors); + void reset(); + void unmark(); + int undo(int count=1); + + // test for game end + bool isGameover(); + // if isGameover(): finished with bonus? + bool hasBonus(); + // test for unchanged start position + bool isOriginalBoard(); + + virtual void readProperties(KConfig *conf); + virtual void saveProperties(KConfig *conf); + int count(int color); + +protected: + void timerEvent( QTimerEvent *e ); + void paintEvent( QPaintEvent *e ); + void mousePressEvent ( QMouseEvent *e); + void mouseMoveEvent ( QMouseEvent *e); + + // properties of the stone picture + int stone_width,stone_height; // size of one stone + int maxcolors; // number of different stones (Y direction) + int maxslices; // number of pictures per movie (X direction) + +signals: + // A new game begins + void s_newgame(); + + void s_colors(int colors); + void s_board(int board); + void s_score(int score); + void s_marked(int m); + + void s_gameover(); + + // The stone (x,y) was clicked(removed), + // all neighbor stones disappear without further signals + void s_remove(int x,int y); + + void s_sizechanged(); +}; + +#endif // _STONEWIDGET + diff --git a/ksame/eventsrc b/ksame/eventsrc new file mode 100644 index 00000000..75b222b2 --- /dev/null +++ b/ksame/eventsrc @@ -0,0 +1,391 @@ +[!Global!] +IconName=ksame +Comment=SameGame +Comment[af]=Samegame +Comment[be]=Ð“ÑƒÐ»ÑŒÐ½Ñ Same +Comment[bn]=সেইমগেম +Comment[br]=Heñvelded +Comment[ca]=Joc iguals +Comment[cs]=Hra Same +Comment[cy]=YrUnGêm +Comment[da]=Samspil (ksame) +Comment[eo]=Samludo +Comment[es]=Juego Iguales +Comment[et]=Sama mäng +Comment[fa]=همین بازی +Comment[fr]=Jeu Same +Comment[gl]=Iguais +Comment[hi]=सेमगेम +Comment[hr]=Ista Igra +Comment[hu]=KSame +Comment[ja]=ã•ã‚ãŒã‚ +Comment[lt]=Žaidimas Same +Comment[lv]=TÄ pati spÄ“le +Comment[mk]=ИÑтата игра +Comment[ne]=उसà¥à¤¤à¥ˆ खेल +Comment[nso]=Papadi yago Swana +Comment[pa]=ਸੇਮ ਖੇਡ +Comment[pl]=ToSamo +Comment[pt]=Iguais +Comment[pt_BR]=Mesmo Jogo +Comment[ro]=Identice +Comment[sk]=Hra Same +Comment[sl]=Igra istih +Comment[sv]=Samegame +Comment[ta]=அதே விளையாடà¯à®Ÿà¯ +Comment[tg]=Бозии Same +Comment[th]=เà¸à¸¡à¸šà¸­à¸¥à¸—ี่คล้ายà¸à¸±à¸™à¸‚อง K +Comment[tr]=AynıOyun +Comment[uk]=Така Ñама гра +Comment[ven]=Mutambo Wonoula +Comment[wa]=Djeu Same Game +Comment[xh]=Umdlalo OfanayoSameGame +Comment[zu]=Umdlalo ofanayo + +[stones removed] +Name=Removed some stones +Name[af]=Verwyder sommige klippe +Name[ar]=لقد أزيلت بعض الأحجار +Name[az]=Bir neçə daÅŸ É™ksildi +Name[be]=Ð’Ñ‹ выдалілі некалькі камÑнёў +Name[bg]=Премахнати Ñа топки +Name[bn]=কিছৠপাথর সরানো হয়েছে +Name[bs]=Neke strijene su uklonjene +Name[ca]=Elimina algunes pedres +Name[cs]=Byly odebrány nÄ›jaké kameny +Name[cy]=Gwaredir rhai cerrig +Name[da]=Fjernede nogle sten +Name[de]=Es wurden Steine entfernt +Name[el]=ΑφαιÏέθηκαν μεÏικές πέτÏες +Name[eo]=Forigis kelkajn Åtonojn +Name[es]=Eliminadas algunas piedras +Name[et]=Eemaldati mõned kivid +Name[eu]=Kendutako harri batzuk +Name[fa]=برخی از سنگهای حذÙ‌‌‌شده +Name[fi]=Poistettiin kiviä +Name[fr]=Des pierres ont été enlevées +Name[gl]=Algunhas pedras eliminadas +Name[he]=הסרה של מספר ××‘× ×™× +Name[hi]=कà¥à¤› पतà¥à¤¥à¤° हटाठ+Name[hr]=Neki su kamenÄići pomaknuti +Name[hu]=Levettem néhány bábut +Name[id]=Pindahkan beberapa batu +Name[is]=Fjarlægði nokkra steina +Name[it]=Rimosse alcune bilie +Name[ja]=石をå–り除ã +Name[km]=បាន​យក​ចáŸáž‰â€‹ážáŸ’ម​ážáŸ’លះ +Name[ko]=íƒ‘ì„ ì—†ì• ì‹­ì‹œì˜¤ +Name[lt]=PaÅ¡alinti kai kurie akmenys +Name[lv]=AizvÄkti daži akmeņi +Name[mk]=ОтÑтранети Ñе неколку камчиња +Name[mt]=Tneħħew xi cagħaq +Name[nb]=Fjernet noen stener +Name[nds]=En poor Steen wöörn wegdaan +Name[ne]=केही ढà¥à¤™à¥à¤—ाहरू हटाइयो +Name[nl]=Enkele stenen verwijderd +Name[nn]=Fjerna nokre steinar +Name[nso]=Tlositse maswika a mangwe +Name[pa]=ਕà©à¨ ਪੱਥਰ ਹਟਾਓ +Name[pl]=UsuniÄ™to kilka kamieni +Name[pt]=Removeu algumas pedras +Name[pt_BR]=Algumas pedras foram removidas +Name[ro]=Au fost eliminate piese +Name[ru]=Ð’Ñ‹ удалили неÑколько камней +Name[se]=Válddii moadde geaÄ‘ggi eret +Name[sk]=Odstránené niektoré kamene +Name[sl]=Odstranjenih je bilo nekaj kamnov +Name[sr]=Уклоњено је неколико каменчића +Name[sr@Latn]=Uklonjeno je nekoliko kamenÄića +Name[sv]=Tog bort nÃ¥gra stenar +Name[ta]=சில கறà¯à®•à®³à¯ நீகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®© +Name[tg]=Хориҷкунии баъзе Ñангҳо +Name[th]=ลบบอลบางลูà¸à¸­à¸­à¸ +Name[tr]=Bir kaç taÅŸ eksildi +Name[uk]=Вилучено декілька каменів +Name[ven]=Ho bviswa manwe matombo +Name[vi]=Äã di chuyển vài hòn đá +Name[xh]=Susa amatye athile +Name[zh_CN]=消去了一些石头 +Name[zh_TW]=移除一些石頭 +Name[zu]=Kususwe amanye amatshe +Comment=You clicked on some stones and made them disappear +Comment[be]=Ð’Ñ‹ пÑтрыкнулі па каменьчыках, Ñ– Ñны зніклі. +Comment[bg]=Премахнати Ñа топки +Comment[bn]=আপনি কিছৠপাথরের ওপর কà§à¦²à¦¿à¦• করে তাদের অদৃশà§à¦¯ করে দিয়েছেন। +Comment[bs]=Kliknuli ste na neke stijene i uÄinili da nestanu +Comment[ca]=Heu clicat a sobre d'algunes pedres i les heu fet desaparèixer +Comment[cs]=Klikli jste na pár kamenů a ony zmizely +Comment[cy]=Naethoch chi glicio ar rai cerrig, a gwneud iddynt ddiflannu. +Comment[da]=Du klikkede pÃ¥ nogle sten og fik dem til at forsvinde +Comment[de]=Sie haben auf einige Steine geklickt und sie so verschwinden lassen +Comment[el]=Κάνατε κλικ σε μεÏικές πέτÏες και τις κάνατε να εξαφανιστοÏν +Comment[eo]=Vi klakis sur kelkaj Åtonoj kaj malaperigis ilin. +Comment[es]=Pinchó sobre algunas piedras y las hizo desaparecer +Comment[et]=Sa klõpsasid mõnel kivil ja põhjustasid nende kadumise +Comment[eu]=Harri batzutan klikatu duzu eta desagertarazi dituzu +Comment[fa]=روی برخی از سنگها Ùشار دادید Ùˆ آنها را ناپدید کردید +Comment[fi]=Napsautit joitakin kiviä ja sait ne katoamaan. +Comment[fr]=Vous avez cliqué sur des pierres et les avez fait disparaître. +Comment[he]=לחצת על מספר ××‘× ×™× ×•×’×¨×ž×ª להן ×œ×”×™×¢×œ× +Comment[hr]=Kliknuli ste neke kamenÄiće i oni su nestali +Comment[hu]=Rákattintott néhány bábura és eltüntette Å‘ket +Comment[is]=Þú smelltir á nokkra steina og þeir hurfu +Comment[it]=Hai fatto clic su alcune bilie e le hai fatte scomparire +Comment[ja]=石をクリックã™ã‚‹ã¨ãれらを消ã›ã¾ã™ +Comment[km]=អ្នក​បាន​ចុច​លើ​ážáŸ’ម​ážáŸ’លះ និង​បាន​ធ្វើ​ឲ្យ​ពួក​វា​បាážáŸ‹ +Comment[lt]=JÅ«s paspaudÄ—te ant keleto akmenų ir juos panaikinote +Comment[lv]=JÅ«s noklikÅ¡Ä·inÄjÄt uz dažiem akmeņiem un padarijÄt tos neredzamus +Comment[mk]=Кликнавте на неколку камчиња и направивте тие да иÑчезнат +Comment[nb]=Du klikket pÃ¥ noen stener og fikk de til Ã¥ forsvinne. +Comment[nds]=Du hest op en poor Steen klickt un so verswinnen laten +Comment[ne]=तपाईà¤à¤²à¥‡ केही ढà¥à¤™à¥à¤—ाहरूमा कà¥à¤²à¤¿à¤• गरेर तिनीहरूलाई अदृशà¥à¤¯ बनाउनॠभयो +Comment[nl]=U klikte op enkele stenen en liet ze verdwijnen +Comment[nn]=Du klikka pÃ¥ nokre steinar og fekk dei til Ã¥ forsvinna. +Comment[pa]=ਤà©à¨¸à©€à¨‚ ਕà©à¨ ਪੱਥਰਾਂ ਨੂੰ ਹਟਾਇਆ ਅਤੇ ਉਹ ਗà©à¨†à¨š ਗਠ+Comment[pl]=KliknÄ…Å‚eÅ› na kilka kamieni i spowodowaÅ‚eÅ› ich znikniÄ™cie +Comment[pt]=Carregou em algumas pedras e elas desapareceram +Comment[pt_BR]=Você clicou em algumas pedras e fez elas desaparecerem +Comment[ru]=Ð’Ñ‹ щёлкнули по камешкам, и они иÑчезли. +Comment[se]=Don coahkkalit moadde geaÄ‘ggi ja jávkkahit daid +Comment[sk]=Klikli ste na kamene a tie sa odstránili +Comment[sl]=Kliknili ste nekaj kamnov in ti so izginili +Comment[sr]=Кликнули Ñте на неке каменчиће и учинили да неÑтану +Comment[sr@Latn]=Kliknuli ste na neke kamenÄiće i uÄinili da nestanu +Comment[sv]=Du klickade pÃ¥ nÃ¥gra stenar och fick dem att försvinna +Comment[ta]= நீஙà¯à®•à®³à¯ சில கறà¯à®•à®³à¯ மீத௠அழà¯à®¤à¯à®¤à®¿ அதை மறைய வைதà¯à®¤à¯à®µà®¿à®Ÿà¯à®Ÿà¯€à®°à¯à®•à®³à¯ +Comment[uk]=Ви клацнули на камінцÑÑ… Ñ– вони зникли +Comment[zh_CN]=您点击了æŸäº›çŸ³å¤´å¹¶è®©å®ƒä»¬æ¶ˆå¤± +Comment[zh_TW]=您點é¸æŸäº›çŸ³é ­ä¸¦è®“它們消失。 + +[game over] +Name=Game Over +Name[af]=Speletjie Bo +Name[ar]=اللعبة انتهت +Name[az]=Oyun Qurtardı +Name[be]=Канец гульні +Name[bg]=Край на играта +Name[bn]=খেল খতম +Name[br]=Echu an abadenn +Name[bs]=Igra zavrÅ¡ena +Name[ca]=Fi de la partida +Name[cs]=Konec hry +Name[cy]=Gêm Drosodd +Name[da]=Spillet forbi +Name[de]=Spiel beendet +Name[el]=Τέλος Ï€Î±Î¹Ï‡Î½Î¹Î´Î¹Î¿Ï +Name[eo]=Ludo finita +Name[es]=Fin de la partida +Name[et]=Mäng läbi +Name[eu]=Jokoa amaitu da +Name[fa]=بازی تمام شد +Name[fi]=Peli loppu +Name[fr]=Fin de la partie +Name[ga]=Cluiche Thart +Name[gl]=Fin do Xogo +Name[he]=×¡×™×•× ×ž×©×—×§ +Name[hi]=खेल ख़तà¥à¤® +Name[hr]=Igra je zavrÅ¡ena +Name[hu]=Vége a játéknak +Name[id]=permainan berakhir +Name[is]=Leik lokið +Name[it]=Gioco terminato +Name[ja]=ゲームオーãƒãƒ¼ +Name[km]=ល្បែង​ចប់ +Name[ko]=SameGame +Name[lt]=Žaidimas baigtas +Name[lv]=SpÄ“les beigas +Name[mk]=Играта заврши +Name[mt]=Il-Logħba SpiÄ‹Ä‹at +Name[nb]=Spillet er slutt +Name[nds]=Speel vörbi +Name[ne]=खेल समापà¥à¤¤ +Name[nl]=Spel is afgelopen +Name[nn]=Spelet er slutt +Name[nso]=Papadi e Fedile +Name[pa]=ਖੇਡ ਖਤਮ +Name[pl]=Koniec gry +Name[pt]=Fim do jogo +Name[pt_BR]=Fim do jogo +Name[ro]=Joc terminat +Name[ru]=Конец игры +Name[se]=Speallu nogai +Name[sk]=Koniec hry +Name[sl]=Konec igre +Name[sr]=Крај игре +Name[sr@Latn]=Kraj igre +Name[sv]=Spelet är slut +Name[ta]=ஆடà¯à®Ÿà®®à¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯ +Name[tg]=Бозӣ ба итмом раÑид +Name[th]=จบเà¸à¸¡ +Name[tr]=Oyun Bitti +Name[uk]=Гру завершено +Name[uz]=OÊ»yin tugadi +Name[uz@cyrillic]=Ўйин тугади +Name[ven]=Muthambo wo Fhela +Name[vi]=Game kết thúc +Name[wa]=Li djeu est houte +Name[xh]=Uphelile Umdlalo +Name[zh_CN]=游æˆç»“æŸ +Name[zh_TW]=éŠæˆ²çµæŸ +Name[zu]=Umdlalo uphelile +Comment=Game over because there are no more removeable stones +Comment[be]=Канец гульні - больш нÑма каменьчыкаў, ÑÐºÑ–Ñ Ð¼Ð¾Ð¶Ð½Ð° выдалÑць +Comment[bg]=Край на играта, защото нÑма повече топки за премахване +Comment[bn]=সরিয়ে নেওয়ার মত আর কোন পাথর অবশিষà§à¦Ÿ নেই বিধায় খেল খতম। +Comment[bs]=Igra je zavrÅ¡ena jer viÅ¡e nema uklonjivih stijena +Comment[ca]=Final de la partida perque no hi ha més pedres a eliminar +Comment[cs]=Konec hry; už nelze odstranit žádné kameny +Comment[cy]=Gêm drosodd gan nad oes rhagor o gerrig i'w gwaredu +Comment[da]=Spillet er forbi da der ikke er flere sten som kan fjernes +Comment[de]=Das Spiel ist vorüber. Es gibt keine Steine mehr, die sich entfernen lassen +Comment[el]=Τέλος Ï€Î±Î¹Ï‡Î½Î¹Î´Î¹Î¿Ï Î´Î¹ÏŒÏ„Î¹ δεν υπάÏχουν άλλες μετακινήσιμες πέτÏες +Comment[eo]=Ludo finiÄis ĉar ne plu estas forigeblaj Åtonoj. +Comment[es]=Fin de la partida porque no hay más piedras que eliminar +Comment[et]=Mäng sai läbi, sest pole enam ühtegi eemaldatavat kivi +Comment[eu]=Jokoa amaitu da mugi daitezkeen harri gehiagorik ez dagoelakko +Comment[fa]=بازی تمام شد، زیرا هیچ سنگ قابل حذ٠دیگری نیست +Comment[fi]=Peli on loppu, koska ei ole enää poistettavia kiviä +Comment[fr]=La partie est terminée car aucune pierre ne peut plus être enlevée. +Comment[he]=המשחק ×”×¡×ª×™×™× ×›×™ ×œ× × ×©×רו עוד ××‘× ×™× ×©× ×™×ª×Ÿ להסיר +Comment[hr]=Igra je zavrÅ¡ena jer viÅ¡e nema kamenÄića koje je moguće pomaknuti +Comment[hu]=Vége a játéknak, mert nincs több eltávolítható bábu +Comment[is]=Leik lokið því það eru engir fleiri steinar til að fjarlægja +Comment[it]=Il gioco è terminato perché non puoi rimuovere altre bilie +Comment[ja]=ゲーム終了。å–り除ã‘る石ãŒã‚ã‚Šã¾ã›ã‚“。 +Comment[km]=ល្បែង​ចាញ់ ព្រោះ​មិន​មាន​ážáŸ’ម​ដែល​យក​ចáŸáž‰â€‹áž‡áž¶â€‹áž…្រើន +Comment[lt]=Žaidimas baigtas, nes nebÄ—ra panaikinamų akmenų +Comment[lv]=SpÄ“le beigusies, jo aizvÄcamu akmeņu vairs nav +Comment[mk]=Играта заврши бидејќи повеќе нема камчиња што можат да Ñе отÑтранат +Comment[nb]=Spillet er slutt fordi ingen flere stener kan fjernes. +Comment[nds]=Dat Speel is vörbi, wiel sik keen Steen mehr wegdoon lett +Comment[ne]=तà¥à¤¯à¤¹à¤¾à¤ हटाउनका लागि ढà¥à¤™à¥à¤—ाहरू नभà¤à¤•à¤¾à¤²à¥‡ खेल सकियो +Comment[nl]=Het spel is afgelopen omdat er geen verwijderbare stenen meer aanwezig zijn +Comment[nn]=Spelet er slutt fordi ingen fleire steinar kan fjernast. +Comment[pa]=ਖੇਡ ਖਤਮ ਹੋ ਗਈ ਹੈ, ਕਿਉਕਿ ਹੋਰ ਹਟਾਉਣਯੋਗ ਪੱਧਰ ਨਹੀਂ ਹਨ +Comment[pl]=Koniec gdy, bo nie ma już usuwalnych kamieni +Comment[pt]=Fim do jogo, dado que já não há mais peças para remover +Comment[pt_BR]=Fim do jogo porque não há mais pedras removíveis +Comment[ru]=Конец игры - больше нет камешков, которые можно удалÑÑ‚ÑŒ +Comment[se]=Speallu nogai dannego ii leat vejolaÅ¡ váldit eanet geÄ‘ggiid eret. +Comment[sk]=Koniec hry, pretože už nie sú žiadne odstrániteľné kamene +Comment[sl]=Igre je konec, saj ni veÄ kamnov, ki bi jih bilo moÄ odstraniti +Comment[sr]=Игра је завршена јер више нема каменчића које можете уклонити +Comment[sr@Latn]=Igra je zavrÅ¡ena jer viÅ¡e nema kamenÄića koje možete ukloniti +Comment[sv]=Spelet är slut för det finns inga flera stenar som kan tas bort +Comment[ta]=நீகà¯à®•à¯à®µà®¤à®±à¯à®•à¯ கறà¯à®•à®³à¯ இலà¯à®²à¯ˆà®¯à¯†à®©à¯à®ªà®¤à®¾à®²à¯ ஆடà¯à®Ÿà®®à¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯ +Comment[uk]=Кінець гри - більше немає камінців, Ñкі можна видалÑти +Comment[zh_CN]=没有å¯ç§»åŠ¨çš„石头,游æˆç»“æŸ +Comment[zh_TW]=沒有å¯ç§»å‹•çš„石頭,éŠæˆ²çµæŸã€‚ + +[game won] +Name=Excellent finish +Name[af]=Uitstekende einde +Name[ar]=نهاية ممتازة +Name[az]=GözÉ™l bir final +Name[be]=Добры фінал +Name[bg]=Край на играта +Name[bn]=চমতà§â€à¦•à¦¾à¦° সমাপà§à¦¤à¦¿ +Name[bs]=Sjajan zavrÅ¡etak +Name[ca]=Final excel·lent +Name[cs]=SkvÄ›lý závÄ›r +Name[cy]=Gorffenniad gwych +Name[da]=Glimrende afslutning +Name[de]=Hervorragender Schluss-Spurt +Name[el]=Τέλειος τεÏματισμός +Name[eo]=Grandioza fino +Name[es]=Excelente final +Name[et]=Suurepärane lõpetamine +Name[eu]=Amaiera ederra +Name[fa]=پایانی عالی +Name[fi]=Erinomainen loppu +Name[fr]=Fin de partie victorieuse +Name[gl]=Excelente final +Name[he]=×¡×™×•× ×ž×•×©×œ× +Name[hi]=शà¥à¤°à¥‡à¤·à¥à¤  समापन +Name[hr]=OdliÄan zavrÅ¡etak +Name[hu]=KitűnÅ‘ befejezés +Name[id]=penyelesaian hebat +Name[is]=Frábær endir +Name[it]=Ottima conclusione +Name[ja]=エクセレントフィニッシュ +Name[km]=បញ្ចប់​អស្ចារ្យ +Name[lt]=Puikus finiÅ¡as +Name[lv]=Lielisks finiÅ¡s +Name[mk]=Одлична завршница +Name[mt]=Tmiem eÄ‹Ä‹ellenti +Name[nb]=Fantastisk avslutning +Name[nds]=Bannig goot beendt +Name[ne]=धेरै रामà¥à¤°à¥‹ समापà¥à¤¤à¤¿ +Name[nl]=Uitstekend einde +Name[nn]=Fantastisk avslutning +Name[pa]=ਸ਼ਾਨਦਾਰ ਸਮਾਪਤੀ +Name[pl]=DoskonaÅ‚e zakoÅ„czenie +Name[pt]=Fim de jogo excelente +Name[pt_BR]=Final excelente +Name[ro]=AÅ£i terminat excelent +Name[ru]=Великолепный финал +Name[sk]=Fantastický koniec +Name[sl]=Izvrsten konec +Name[sr]=Одличан завршетак +Name[sr@Latn]=OdliÄan zavrÅ¡etak +Name[sv]=Utmärkt avslutning +Name[ta]= à®…à®°à¯à®®à¯ˆà®¯à®¾à®© à®®à¯à®Ÿà®¿à®µà¯ +Name[tg]=Интиҳои олиҷаноб +Name[th]=จบได้ยอดเยี่ยมมาภ+Name[tr]=Harika bir bitiriÅŸ +Name[uk]=Відмінний фініш +Name[ven]=Kufhedzele kwa vhudisa +Name[vi]=Hoàn thành xuất sắc! +Name[xh]=Ugqibo Oluphume emagqabini +Name[zh_CN]=精彩的结局 +Name[zh_TW]=å®Œç¾Žå®Œæˆ +Name[zu]=Isiphetho esihle +Comment=Game over, you removed even the last stone +Comment[be]=Канец гульні, не заÑталоÑÑ Ð½Ñ–Ð²Ð¾Ð´Ð½Ð°Ð³Ð° каменьчыка +Comment[bg]=Край на играта, защото вÑички топки Ñа премахнати +Comment[bn]=খেল খতম, আপনি শেষ পাথরটিও সরিয়ে ফেলেছেন। +Comment[bs]=Igra je zavrÅ¡ena, uklonili ste i posljednju stijenu +Comment[ca]=Final de la partida, heu eliminat fins a l'última pedra +Comment[cs]=Konec hry; odstranili jste vÅ¡echny kameny +Comment[cy]=Gêm drosodd, rydych wedi gwaredu'r garreg olaf! +Comment[da]=Spillet forbi, du fjernede endog den sidste sten +Comment[de]=Das Spiel ist vorüber. Sie haben den letzten Stein entfernt +Comment[el]=Τέλος παιχνιδιοÏ, αφαιÏέσατε μέχÏι και την τελευταία πέτÏα +Comment[eo]=Ludo finita, vi forigis eĉ la lastan Åtonon. +Comment[es]=Fin de la partida, eliminó hasta la última piedra +Comment[et]=Mäng sai läbi, sest sa eemaldasid viimase kivi +Comment[eu]=Jokoa amaitu da, azken harria ere kendu duzu +Comment[fa]=بازی تمام شد، شما حتی آخرین سنگ را هم حذ٠کردید +Comment[fi]=Peli loppu,poistit viimeisenkin kiven +Comment[fr]=La partie est terminée, vous avez enlevé la dernière pierre. +Comment[he]=המשחק הסתיי×, הסרת ×פילו ×ת ×”×בן ×”×חרונה +Comment[hr]=Igra je zavrÅ¡ena, uklonili ste i posljednji kamenÄić +Comment[hu]=Vége a játéknak, mert az utolsó bábut is eltávolította +Comment[is]=Leik lokið því þú fjarlægðir meira að segja síðasta steininn +Comment[it]=Gioco terminato, hai rimosso fino all'ultima bilia +Comment[ja]=ゲーム終了。最後ã®çŸ³ã‚’å–り除ãã¾ã—ãŸã€‚ +Comment[km]=ល្បែង​ចប់ អ្នក​បាន​យក​ចáŸáž‰â€‹ážŸáž¼áž˜áŸ’បី​ážáŸ‚​ážáŸ’ម​ចុង​ក្រោយ +Comment[lt]=Žaidmas baigtas, JÅ«s panaikinote net paskutinį akmenį +Comment[lv]=SpÄ“le beigusies, jÅ«s aizvÄcÄt arÄ« pÄ“dÄ“jo akmeni +Comment[mk]=Играта заврши, го отÑтранивте и поÑледното камче +Comment[nb]=Spillet er slutt, du klarte til og med Ã¥ fjerne den siste stenen. +Comment[nds]=Dat Speel is vörbi. Du hest ok den lesten Steen wegdaan +Comment[ne]=खेल समापà¥à¤¤, तपाईà¤à¤²à¥‡ अनà¥à¤¤à¤¿à¤® ढà¥à¤™à¥à¤—ा पनि हटाउनॠभयो +Comment[nl]=Het spel is afgelopen, u hebt zelfs de laatste verwijderd +Comment[nn]=Spelet er slutt, du klarte til og med Ã¥ fjerna den siste steinen. +Comment[pa]=ਖੇਡ ਖਤਮ ਹੋਈ, ਤà©à¨¸à©€à¨‚ ਆਖਰੀ ਪੱਥਰ ਵੀ ਹਟਾ ਦਿੱਤਾ ਹੈ +Comment[pl]=Koniec gry, usunÄ…Å‚eÅ› nawet ostatni kamieÅ„ +Comment[pt]=Fim do jogo, dado que removeu todas as peças +Comment[pt_BR]=Fim do jogo, você removeu até a última pedra +Comment[ru]=Конец игры, не оÑталоÑÑŒ ни единого камешка +Comment[se]=Speallu nogai, don nágadit vela maÅ‹emuÅ¡ geaÄ‘ggi váldit eret. +Comment[sk]=Koniec hry, pretože ste odstránili aj posledný kameň +Comment[sl]=Igre je konec, odstranili ste celo zadnji kamen +Comment[sr]=Крај игре, уклонили Ñте Ñве каменчиће +Comment[sr@Latn]=Kraj igre, uklonili ste sve kamenÄiće +Comment[sv]=Spelet är slut, du tog bort till och med den sista stenen +Comment[ta]=ஆடà¯à®Ÿà®®à¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯, நீஙà¯à®•à®³à¯ இறà¯à®¤à®¿à®•à¯à®•à®²à¯à®²à¯ˆà®•à¯à®•à¯‚ட நீகà¯à®•à®¿à®µà®¿à®Ÿà¯à®Ÿà¯€à®°à¯à®•à®³à¯ +Comment[tr]=Oyun bitti, son topu da kaldırdınız. +Comment[uk]=Кінець гри - ви видалили навіть оÑтанній камінь +Comment[zh_CN]=游æˆç»“æŸï¼Œæ‚¨ç”šè‡³æ¶ˆåŽ»äº†æœ€åŽä¸€ä¸ªçŸ³å¤´ +Comment[zh_TW]=éŠæˆ²çµæŸï¼Œæ‚¨ç§»èµ°äº†æ‰€æœ‰çš„石頭。 diff --git a/ksame/hi128-app-ksame.png b/ksame/hi128-app-ksame.png new file mode 100644 index 00000000..5b15fda9 Binary files /dev/null and b/ksame/hi128-app-ksame.png differ diff --git a/ksame/hi16-app-ksame.png b/ksame/hi16-app-ksame.png new file mode 100644 index 00000000..cfd17984 Binary files /dev/null and b/ksame/hi16-app-ksame.png differ diff --git a/ksame/hi22-app-ksame.png b/ksame/hi22-app-ksame.png new file mode 100644 index 00000000..26a1d2ca Binary files /dev/null and b/ksame/hi22-app-ksame.png differ diff --git a/ksame/hi32-app-ksame.png b/ksame/hi32-app-ksame.png new file mode 100644 index 00000000..56f0d5b7 Binary files /dev/null and b/ksame/hi32-app-ksame.png differ diff --git a/ksame/hi48-app-ksame.png b/ksame/hi48-app-ksame.png new file mode 100644 index 00000000..c522c031 Binary files /dev/null and b/ksame/hi48-app-ksame.png differ diff --git a/ksame/hi64-app-ksame.png b/ksame/hi64-app-ksame.png new file mode 100644 index 00000000..15dc552a Binary files /dev/null and b/ksame/hi64-app-ksame.png differ diff --git a/ksame/ksame.desktop b/ksame/ksame.desktop new file mode 100644 index 00000000..18fdb24c --- /dev/null +++ b/ksame/ksame.desktop @@ -0,0 +1,119 @@ +[Desktop Entry] +Name=SameGame +Name[af]=Samegame +Name[az]=EyniOyun +Name[be]=Ð“ÑƒÐ»ÑŒÐ½Ñ Same +Name[bn]=সেইমগেম +Name[br]=Heñvelded +Name[ca]=Joc iguals +Name[cs]=Same +Name[cy]=YrUnGêm +Name[da]=Samspil +Name[de]=Probiere +Name[eo]=Samludo +Name[es]=Juego Iguales +Name[et]=Sama mäng +Name[fa]=همین بازی +Name[fi]=KSame +Name[fr]=Jeu Same +Name[gl]=Iguais +Name[hi]=सेमगेम +Name[hr]=Ista Igra +Name[hu]=KSame +Name[is]=Sama spil +Name[ja]=ã•ã‚ãŒã‚ +Name[mt]=LogħbaStess (SameGame) +Name[ne]=उसà¥à¤¤à¥ˆ खेल +Name[nso]=Papadi yago Swana +Name[pa]=ਸੇਮ ਖੇਡ +Name[pl]=ToSamo +Name[pt]=Iguais +Name[pt_BR]=Mesmo Jogo +Name[ro]=Identice +Name[ru]=Игра Same +Name[sk]=Same +Name[sv]=Samegame +Name[ta]=அதே விளையாடà¯à®Ÿà¯ +Name[tg]=Бозии Same +Name[th]=บอลที่คล้ายà¸à¸±à¸™ - K +Name[tr]=AynıOyun +Name[ven]=Wono ula Muthambo +Name[wa]=Djeu Same Game +Name[xh]=Ikwa ngumdlalo ofanayo +Name[zu]=Umdlalo ofanayo +Exec=ksame %i %m -caption "%c" +Type=Application +DocPath=ksame/index.html +GenericName=Board Game +GenericName[af]=Bord Speletjie +GenericName[ar]=لعبة لوح +GenericName[be]=ÐаÑÑ‚Ð¾Ð»ÑŒÐ½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ +GenericName[bg]=Табла +GenericName[bn]=ছককেনà§à¦¦à§à¦°à¦¿à¦• খেলা +GenericName[br]=Ur c'hoari taolenn +GenericName[bs]=Igra s ploÄom +GenericName[ca]=Joc de taula +GenericName[cs]=Desková Same +GenericName[cy]=Gêm Fwrdd +GenericName[da]=Brætspil +GenericName[de]=Brettspiel +GenericName[el]=ΕπιτÏαπέζιο παιχνίδι +GenericName[eo]=Tabuloludo +GenericName[es]=Juego de tablero +GenericName[et]=Lauamäng +GenericName[eu]=Mahai-jokoa +GenericName[fa]=بازی تخته +GenericName[fi]=Lautapeli +GenericName[fo]=Borðspæl +GenericName[fr]=Jeu de plateau +GenericName[ga]=Cluiche Chláir +GenericName[gl]=Xogo de Taboleiro +GenericName[he]=משחק לוח +GenericName[hi]=बिसात का खेल +GenericName[hr]=Igra na ploÄi +GenericName[hu]=Táblajáték +GenericName[is]=Borðleikur +GenericName[it]=Gioco da tavolo +GenericName[ja]=ボードゲーム +GenericName[km]=ល្បែង​ក្ដារ +GenericName[ko]=리버시 ë³´ë“œ 게임 +GenericName[lt]=Stalo žaidimas +GenericName[lv]=Galda spÄ“le +GenericName[mk]=Игра на табла +GenericName[mt]=Logħba tal-bord +GenericName[nb]=Brettspill +GenericName[nds]=Brettspeel +GenericName[ne]=बोरà¥à¤¡ खेल +GenericName[nl]=Bordspel +GenericName[nn]=Brettspel +GenericName[pa]=ਬੋਰਡ ਖੇਡ +GenericName[pl]=Gra planszowa +GenericName[pt]=Jogo de Tabuleiro +GenericName[pt_BR]=Jogo de Tabuleiro +GenericName[ro]=Un joc de table +GenericName[ru]=ÐаÑÑ‚Ð¾Ð»ÑŒÐ½Ð°Ñ Ð¸Ð³Ñ€Ð° +GenericName[rw]=Umukino w'Ikibaho +GenericName[se]=Duolbbášspeallu +GenericName[sk]=Stolová hra +GenericName[sl]=Namizna igra +GenericName[sr]=Игра на табли +GenericName[sr@Latn]=Igra na tabli +GenericName[sv]=Brädspel +GenericName[ta]=பலகை விளையாடà¯à®Ÿà¯ +GenericName[tg]=Бозии Рӯи Мизӣ +GenericName[th]=เà¸à¸¡à¸à¸£à¸°à¸”าน +GenericName[tr]=Tahta Oyunu +GenericName[uk]=Гра на дошці +GenericName[uz]=Stol oÊ»yini +GenericName[uz@cyrillic]=Стол ўйини +GenericName[ven]=Mutambo wa Bodo +GenericName[vi]=Game bàn +GenericName[wa]=Djeu d' platea +GenericName[xh]=Umdlalo Webhodi +GenericName[zh_CN]=æ£‹ç±»æ¸¸æˆ +GenericName[zh_TW]=棋盤éŠæˆ² +GenericName[zu]=Umdlalo webhodi +Icon=ksame +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;StrategyGame; diff --git a/ksame/ksameui.rc b/ksame/ksameui.rc new file mode 100644 index 00000000..b9697064 --- /dev/null +++ b/ksame/ksameui.rc @@ -0,0 +1,17 @@ + + + + &Settings + + + + + + + + +Main Toolbar + + + + diff --git a/ksame/main.cpp b/ksame/main.cpp new file mode 100644 index 00000000..08c1e076 --- /dev/null +++ b/ksame/main.cpp @@ -0,0 +1,55 @@ +/* + * ksame 0.4 - simple Game + * Copyright (C) 1997,1998 Marcus Kreutzberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include + +#include + +#include +#include +#include + +#include "version.h" +#include "KSameWidget.h" +#include +#include + +static const char description[] = I18N_NOOP("Same Game - a little game about balls and how to get rid of them"); +static const char copyright[] = "(c) 1997-1998 Marcus Kreutzberger"; + +int main( int argc, char **argv ) { + KAboutData aboutData("ksame", I18N_NOOP("SameGame"), KSAME_VERSION, + description, KAboutData::License_GPL, copyright); + aboutData.addAuthor("Marcus Kreutzberger", 0, "kreutzbe@informatik.mu-luebeck.de"); + KCmdLineArgs::init(argc, argv, &aboutData); + + KApplication::setColorSpec(QApplication::ManyColor+QApplication::CustomColor); + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + + KSameWidget *w = new KSameWidget; + if (kapp->isRestored()) { + if (KSameWidget::canBeRestored(1)) + w->restore(1); + } else + w->show(); + a.setMainWidget(w); + + return a.exec(); +} diff --git a/ksame/stones.png b/ksame/stones.png new file mode 100644 index 00000000..db3caa47 Binary files /dev/null and b/ksame/stones.png differ diff --git a/ksame/version.h b/ksame/version.h new file mode 100644 index 00000000..26af5a7b --- /dev/null +++ b/ksame/version.h @@ -0,0 +1 @@ +#define KSAME_VERSION "0.51" diff --git a/kshisen/ChangeLog b/kshisen/ChangeLog new file mode 100644 index 00000000..fdc2a653 --- /dev/null +++ b/kshisen/ChangeLog @@ -0,0 +1,113 @@ +2004-05-14 Benjamin Meyer (v.1.5.1) + * Moved settings into a KConfigXT dialog. + +2003-05-23 Dave Corrie (v.1.4.90) + + * GUI cleanup + * Double-buffer painting + * Replaced paused pixmap with i18n-friendly text + * Code cleanup + * Added 'Prefer Unscaled Tiles' feature + * Removed 'Is Game Solvable?' feature (it didn't work). It + will be re-instated when a suitable algorithm is present + * Fix bug 53893: Gravity should be applied before calculating hints + +2002-11-27 Dave Corrie (v.1.4.1) + + In conjunction with Jason Lane: + + * Move tile handling (loading/scaling/highlighting etc) into + separate class (class TileSet) + * Permit resize of playing window + * TODO: game startup optimization, double buffering, remove + paused icon (not i18n friendly) + +2002-08-01 Dave Corrie (v.1.4) + + Recent changes of note. (Does not list all the changes that + happened in the last three years!) + + * [Andreas Beckermann] make keybindings configurable + * [Jason Lane] statusbar shows number of tiles remaining + * [Jason Lane] smoothScale tiles when shrinking + * [Jason Lane] remove need for separate tile mask pixmap + * [Jason Lane] reset "cheat mode" flag when changing board + size or difficulty level + * unicode names are now shown correctly in highscore table + * improved speed of layout calculation + * reduced flicker when highlighting/unhighlighting tiles + * cache current tile scale value + * FINALLY fix undo/redo in gravity mode + +1999-06-19 Mario Weilguni (v.1.3) + + * tiles now have a mask, better drawing + * pausing games is now possible, but the pixmap should + be i18n'ed by some means + * fixed undo/redo in combination with gravity + * fixed a mysterious undo bug in combination with gravity + * prepared for 2.0 (hopefully) + +1999-04-09 Mario Weilguni (v.1.2.2) + + * fixed another bug in writeHighscore + +1999-04-04 Mario Weilguni (v.1.2.1) + + * replace all locale->translate with i18n + * fixed a bug in the writeHighscore method + +1999-03-31 Mario Weilguni (v.1.2) + + * added gravity. + * TODO: gravity and the check for unsolvable games do not work + together + * fixed menu accelerator + +1999-01-03 Mario Weilguni (v.1.1) + + * some board sizes did not match the entry shown in the + menu. Fixed. + * fixed a few warnings with egcs + * removed my initial highscore + +1998-07-17 Mario Weilguni + + * moved to version 1.0 (it´s stable enough now) + +1998-04-10 Mario Weilguni + + * better highscore management. highscores are now score-based, not + time based + * board.cpp: fixed some bugs + * the game doesn´t ask anymore for a name if the score + is not good enough for the hall of fame + +0.2.1: + * [Robert Williams] Added getHelpMenu() + * [Robert Williams] Added version.h + +0.2: added this changelog + + fixed a bug in the pathfinder (reported and fixed + by Stephane Alnet ) + + fixed a bug in the Board::lighten() function on 8-bit + displays. Reported by Marc Diefenbruch + + + Added a "Hall of Fame", mostly taken from kreversi. + + Added "About Qt" to keep the Trolls happy + + Fixed that "player-has-won-and-must-go-to-the-bathroom"-bug + (the elapsed time was taken after getPlayerName() instead + of taking it before) + + Fixed that "game-starts-before-midnight-and-ends-after-midnight" + bug. This will allow games with a duration up to 68 years, + hopefully enough for the common player. The first player who + breaks this limit should contact me in the year 2065; I will send + him 20 bottles of Austrian beer ("Murauer") :-) + +0.1: initial release diff --git a/kshisen/Makefile.am b/kshisen/Makefile.am new file mode 100644 index 00000000..ae805477 --- /dev/null +++ b/kshisen/Makefile.am @@ -0,0 +1,28 @@ + + +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) + +PICDIR = $(kde_datadir)/kshisen/pics + +bin_PROGRAMS = kshisen + +METASOURCES = board.moc app.moc + +noinst_HEADERS = app.h board.h tileset.h debug.h version.h + +kshisen_SOURCES = main.cpp board.cpp app.cpp tileset.cpp settings.ui prefs.kcfgc +kshisen_LDADD = $(LIB_KDEGAMES) $(LIB_KDEUI) +kshisen_DEPENDENCIES = $(LIB_KDEGAMES_DEP) +kshisen_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +xdg_apps_DATA = kshisen.desktop +kde_kcfg_DATA = kshisen.kcfg +KDE_ICON = kshisen + +SUBDIRS = pics + +rcdir = $(kde_datadir)/kshisen +rc_DATA = kshisenui.rc + +messages: rc.cpp + $(XGETTEXT) rc.cpp $(kshisen_SOURCES) -o $(podir)/kshisen.pot diff --git a/kshisen/app.cpp b/kshisen/app.cpp new file mode 100644 index 00000000..27f8cbf2 --- /dev/null +++ b/kshisen/app.cpp @@ -0,0 +1,760 @@ +/* Yo Emacs, this is -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KSHISEN + * + * + ******************************************************************* + * + * A japanese game similar to mahjongg + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KSHISEN" + * + * KSHISEN is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KSHISEN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KSHISEN; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "app.h" +#include "prefs.h" +#include "settings.h" + +App::App(QWidget *parent, const char *name) : KMainWindow(parent, name), + cheat(false) +{ + highscoreTable = new KHighscore(this); + + // TODO? + // Would it make sense long term to have a kconfig update rather then + // havin both formats supported in the code? + if(highscoreTable->hasTable()) + readHighscore(); + else + readOldHighscore(); + + statusBar()->insertItem("", SBI_TIME); + statusBar()->insertItem("", SBI_TILES); + statusBar()->insertFixedItem(i18n(" Cheat mode "), SBI_CHEAT); + statusBar()->changeItem("", SBI_CHEAT); + + initKAction(); + + board = new Board(this, "board"); + loadSettings(); + + setCentralWidget(board); + + setupGUI(); + + connect(board, SIGNAL(changed()), this, SLOT(enableItems())); + + QTimer *t = new QTimer(this); + t->start(1000); + connect(t, SIGNAL(timeout()), this, SLOT(updateScore())); + connect(board, SIGNAL(endOfGame()), this, SLOT(slotEndOfGame())); + connect(board, SIGNAL(resized()), this, SLOT(boardResized())); + + kapp->processEvents(); + + updateScore(); + enableItems(); +} + +void App::initKAction() +{ + // Game + KStdGameAction::gameNew(this, SLOT(newGame()), actionCollection()); + KStdGameAction::restart(this, SLOT(restartGame()), actionCollection()); + KStdGameAction::pause(this, SLOT(pause()), actionCollection()); + KStdGameAction::highscores(this, SLOT(hallOfFame()), actionCollection()); + KStdGameAction::quit(this, SLOT(quitGame()), actionCollection()); + + // Move + KStdGameAction::undo(this, SLOT(undo()), actionCollection()); + KStdGameAction::redo(this, SLOT(redo()), actionCollection()); + KStdGameAction::hint(this, SLOT(hint()), actionCollection()); + //new KAction(i18n("Is Game Solvable?"), 0, this, + // SLOT(isSolvable()), actionCollection(), "move_solvable"); + +#ifdef DEBUGGING + (void)new KAction(i18n("&Finish"), 0, board, SLOT(finish()), actionCollection(), "move_finish"); +#endif + + // Settings + KStdAction::preferences(this, SLOT(showSettings()), actionCollection()); +} + +void App::hallOfFame() +{ + showHighscore(); +} + +void App::newGame() +{ + board->newGame(); + resetCheatMode(); + enableItems(); +} + +void App::quitGame() +{ + kapp->quit(); +} + +void App::restartGame() +{ + board->setUpdatesEnabled(false); + while(board->canUndo()) + board->undo(); + board->setUpdatesEnabled(true); + board->update(); + enableItems(); +} + +void App::isSolvable() +{ + if(board->solvable()) + KMessageBox::information(this, i18n("This game is solvable.")); + else + KMessageBox::information(this, i18n("This game is NOT solvable.")); +} + +void App::pause() +{ + bool paused = board->pause(); + lockMenus(paused); +} + +void App::undo() +{ + if(board->canUndo()) + { + board->undo(); + setCheatMode(); + enableItems(); + } +} + +void App::redo() +{ + if(board->canRedo()) + board->redo(); + enableItems(); +} + +void App::hint() +{ +#ifdef DEBUGGING + board->makeHintMove(); +#else + board->showHint(); + setCheatMode(); +#endif + enableItems(); +} + +void App::loadSettings() +{ + // Setting 'Prefer Unscaled Tiles' to on is known to fail in the following + // situation: The Keramik window decoration is in use AND caption bubbles + // stick out above the title bar (i.e. Keramik's 'Draw small caption + // bubbles on active windows' configuration entry is set to off) AND the + // kshisen window is maximized. + // + // The user can work-around this situation by un-maximizing the window first. + if(Prefs::unscaled()) + { + QSize s = board->unscaledSize(); + + // We would have liked to have used KMainWindow::sizeForCentralWidgetSize(), + // but this function does not seem to work when the toolbar is docked on the + // left. sizeForCentralWidgetSize() even reports a value 1 pixel too small + // when the toolbar is docked at the top... + // These bugs present in KDE: 3.1.90 (CVS >= 20030225) + //resize(sizeForCentralWidgetSize(s)); + + s += size() - board->size(); // compensate for chrome (toolbars, statusbars etc.) + resize(s); + //kdDebug() << "App::preferUnscaled() set size to: " << s.width() << " x " << s.height() << endl; + } +} + +void App::lockMenus(bool lock) +{ + // Disable all actions apart from (un)pause, quit and those that are help-related. + // (Only undo/redo and hint actually *need* to be disabled, but disabling everything + // provides a good visual hint to the user, that they need to unpause to continue. + KPopupMenu* help = dynamic_cast(child("help", "KPopupMenu", false)); + KActionPtrList actions = actionCollection()->actions(); + KActionPtrList::iterator actionIter = actions.begin(); + KActionPtrList::iterator actionIterEnd = actions.end(); + + while(actionIter != actionIterEnd) + { + KAction* a = *actionIter; + if(!a->isPlugged(help)) + a->setEnabled(!lock); + ++actionIter; + } + + actionCollection()->action(KStdGameAction::name(KStdGameAction::Pause))->setEnabled(true); + actionCollection()->action(KStdGameAction::name(KStdGameAction::Quit))->setEnabled(true); + + enableItems(); +} + +void App::enableItems() +{ + if(!board->isPaused()) + { + actionCollection()->action(KStdGameAction::name(KStdGameAction::Undo))->setEnabled(board->canUndo()); + actionCollection()->action(KStdGameAction::name(KStdGameAction::Redo))->setEnabled(board->canRedo()); + actionCollection()->action(KStdGameAction::name(KStdGameAction::Restart))->setEnabled(board->canUndo()); + } +} + +void App::boardResized() +{ + // If the board has been resized to a size that requires scaled tiles, then the + // 'Prefer Unscaled Tiles' option should be set to off. + + //kdDebug() << "App::boardResized " << b->width() << " x " << b->height() << endl; + bool unscaled = Prefs::unscaled(); + if(unscaled && board->size() != board->unscaledSize()) + Prefs::setUnscaled(false); +} + +void App::slotEndOfGame() +{ + if(board->tilesLeft() > 0) + { + KMessageBox::information(this, i18n("No more moves possible!"), i18n("End of Game")); + } + else + { + // create highscore entry + HighScore hs; + hs.seconds = board->getTimeForGame(); + hs.x = board->x_tiles(); + hs.y = board->y_tiles(); + hs.gravity = (int)board->gravityFlag(); + + // check if we made it into Top10 + bool isHighscore = false; + if(highscore.size() < HIGHSCORE_MAX) + isHighscore = true; + else if(isBetter(hs, highscore[HIGHSCORE_MAX-1])) + isHighscore = true; + + if(isHighscore && !cheat) + { + hs.name = getPlayerName(); + hs.date = time((time_t*)0); + int rank = insertHighscore(hs); + showHighscore(rank); + } + else + { + QString s = i18n("Congratulations! You made it in %1:%2:%3") + .arg(QString().sprintf("%02d", board->getTimeForGame()/3600)) + .arg(QString().sprintf("%02d", (board->getTimeForGame() / 60) % 60)) + .arg(QString().sprintf("%02d", board->getTimeForGame() % 60)); + + KMessageBox::information(this, s, i18n("End of Game")); + } + } + + resetCheatMode(); + board->newGame(); +} + +void App::updateScore() +{ + int t = board->getTimeForGame(); + QString s = i18n(" Your time: %1:%2:%3 %4") + .arg(QString().sprintf("%02d", t / 3600 )) + .arg(QString().sprintf("%02d", (t / 60) % 60 )) + .arg(QString().sprintf("%02d", t % 60 )) + .arg(board->isPaused()?i18n("(Paused) "):QString::null); + + statusBar()->changeItem(s, SBI_TIME); + + // Number of tiles + int tl = (board->x_tiles() * board->y_tiles()); + s = i18n(" Removed: %1/%2 ") + .arg(QString().sprintf("%d", tl - board->tilesLeft())) + .arg(QString().sprintf("%d", tl )); + + statusBar()->changeItem(s, SBI_TILES); +} + +void App::setCheatMode() +{ + // set the cheat mode if not set + if(!cheat) + { + cheat = true; + statusBar()->changeItem(i18n(" Cheat mode "), SBI_CHEAT); + } +} + +void App::resetCheatMode() +{ + // reset cheat mode if set + if(cheat) + { + cheat = false; + statusBar()->changeItem("", SBI_CHEAT); + } +} + +QString App::getPlayerName() +{ + QDialog *dlg = new QDialog(this, "Hall of Fame", true); + + QLabel *l1 = new QLabel(i18n("You've made it into the \"Hall Of Fame\". Type in\nyour name so mankind will always remember\nyour cool rating."), dlg); + l1->setFixedSize(l1->sizeHint()); + + QLabel *l2 = new QLabel(i18n("Your name:"), dlg); + l2->setFixedSize(l2->sizeHint()); + + QLineEdit *e = new QLineEdit(dlg); + e->setText("XXXXXXXXXXXXXXXX"); + e->setMinimumWidth(e->sizeHint().width()); + e->setFixedHeight(e->sizeHint().height()); + e->setText( lastPlayerName ); + e->setFocus(); + + QPushButton *b = new KPushButton(KStdGuiItem::ok(), dlg); + b->setDefault(true); + b->setFixedSize(b->sizeHint()); + + connect(b, SIGNAL(released()), dlg, SLOT(accept())); + connect(e, SIGNAL(returnPressed()), dlg, SLOT(accept())); + + // create layout + QVBoxLayout *tl = new QVBoxLayout(dlg, 10); + QHBoxLayout *tl1 = new QHBoxLayout(); + tl->addWidget(l1); + tl->addSpacing(5); + tl->addLayout(tl1); + tl1->addWidget(l2); + tl1->addWidget(e); + tl->addSpacing(5); + tl->addWidget(b); + tl->activate(); + tl->freeze(); + + dlg->exec(); + + lastPlayerName = e->text(); + delete dlg; + + if(lastPlayerName.isEmpty()) + return " "; + return lastPlayerName; +} + +int App::getScore(const HighScore &hs) +{ + double ntiles = hs.x*hs.y; + double tilespersec = ntiles/(double)hs.seconds; + + double sizebonus = std::sqrt(ntiles/(double)(14.0 * 6.0)); + double points = tilespersec / 0.14 * 100.0; + + if(hs.gravity) + return (int)(2.0 * points * sizebonus); + else + return (int)(points * sizebonus); +} + +bool App::isBetter(const HighScore &hs, const HighScore &than) +{ + if(getScore(hs) > getScore(than)) + return true; + else + return false; +} + +int App::insertHighscore(const HighScore &hs) +{ + int i; + + if(highscore.size() == 0) + { + highscore.resize(1); + highscore[0] = hs; + writeHighscore(); + return 0; + } + else + { + HighScore last = highscore[highscore.size() - 1]; + if(isBetter(hs, last) || (highscore.size() < HIGHSCORE_MAX)) + { + if(highscore.size() == HIGHSCORE_MAX) + { + highscore[HIGHSCORE_MAX - 1] = hs; + } + else + { + highscore.resize(highscore.size()+1); + highscore[highscore.size() - 1] = hs; + } + + // sort in new entry + int bestsofar = highscore.size() - 1; + for(i = highscore.size() - 1; i > 0; i--) + { + if(isBetter(highscore[i], highscore[i-1])) + { + // swap entries + HighScore temp = highscore[i-1]; + highscore[i-1] = highscore[i]; + highscore[i] = temp; + bestsofar = i - 1; + } + } + + writeHighscore(); + return bestsofar; + } + } + return -1; +} + +void App::readHighscore() +{ + QStringList hi_x, hi_y, hi_sec, hi_date, hi_grav, hi_name; + hi_x = highscoreTable->readList("x", HIGHSCORE_MAX); + hi_y = highscoreTable->readList("y", HIGHSCORE_MAX); + hi_sec = highscoreTable->readList("seconds", HIGHSCORE_MAX); + hi_date = highscoreTable->readList("date", HIGHSCORE_MAX); + hi_grav = highscoreTable->readList("gravity", HIGHSCORE_MAX); + hi_name = highscoreTable->readList("name", HIGHSCORE_MAX); + + highscore.resize(0); + + for (unsigned int i = 0; i < hi_x.count(); i++) + { + highscore.resize(i+1); + + HighScore hs; + + hs.x = hi_x[i].toInt(); + hs.y = hi_y[i].toInt(); + hs.seconds = hi_sec[i].toInt(); + hs.date = hi_date[i].toInt(); + hs.date = hi_date[i].toInt(); + hs.gravity = hi_grav[i].toInt(); + hs.name = hi_name[i]; + + highscore[i] = hs; + } +} + +void App::readOldHighscore() +{ + // this is for before-KHighscore-highscores + int i; + QString s, e, grp; + KConfig *conf = kapp->config(); + + highscore.resize(0); + i = 0; + bool eol = false; + grp = conf->group(); + conf->setGroup("Hall of Fame"); + while ((i < (int)HIGHSCORE_MAX) && !eol) + { + s.sprintf("Highscore_%d", i); + if(conf->hasKey(s)) + { + e = conf->readEntry(s); + highscore.resize(i+1); + + HighScore hs; + + QStringList e = conf->readListEntry(s, ' '); + int nelem = e.count(); + hs.x = (*e.at(0)).toInt(); + hs.y = (*e.at(1)).toInt(); + hs.seconds = (*e.at(2)).toInt(); + hs.date = (*e.at(3)).toInt(); + + if(nelem == 4) // old version <= 1.1 + { + hs.gravity = 0; + hs.name = *e.at(4); + } + else + { + hs.gravity = (*e.at(4)).toInt(); + hs.name = *e.at(5); + } + + highscore[i] = hs; + } + else + { + eol = true; + } + i++; + } + +// // freshly installed, add my own highscore +// if(highscore.size() == 0) +// { +// HighScore hs; +// hs.x = 28; +// hs.y = 16; +// hs.seconds = 367; +// hs.name = "Mario"; +// highscore.resize(1); +// highscore[0] = hs; +// } + + // restore old group + conf->setGroup(grp); + + // write in new KHighscore format + writeHighscore(); + // read form KHighscore format + readHighscore(); +} + +void App::writeHighscore() +{ + int i; + QStringList hi_x, hi_y, hi_sec, hi_date, hi_grav, hi_name; + for(i = 0; i < (int)highscore.size(); i++) + { + HighScore hs = highscore[i]; + hi_x.append(QString::number(hs.x)); + hi_y.append(QString::number(hs.y)); + hi_sec.append(QString::number(hs.seconds)); + hi_date.append(QString::number(hs.date)); + hi_grav.append(QString::number(hs.gravity)); + hi_name.append(hs.name); + } + highscoreTable->writeList("x", hi_x); + highscoreTable->writeList("y", hi_y); + highscoreTable->writeList("seconds", hi_sec); + highscoreTable->writeList("date", hi_date); + highscoreTable->writeList("gravity", hi_grav); + highscoreTable->writeList("name", hi_name); + highscoreTable->sync(); +} + +void App::showHighscore(int focusitem) +{ + // this may look a little bit confusing... + QDialog *dlg = new QDialog(0, "hall_Of_fame", true); + dlg->setCaption(i18n("Hall of Fame")); + + QVBoxLayout *tl = new QVBoxLayout(dlg, 10); + + QLabel *l = new QLabel(i18n("Hall of Fame"), dlg); + QFont f = font(); + f.setPointSize(24); + f.setBold(true); + l->setFont(f); + l->setFixedSize(l->sizeHint()); + l->setFixedWidth(l->width() + 32); + l->setAlignment(AlignCenter); + tl->addWidget(l); + + // insert highscores in a gridlayout + QGridLayout *table = new QGridLayout(12, 5, 5); + tl->addLayout(table, 1); + + // add a separator line + KSeparator *sep = new KSeparator(dlg); + table->addMultiCellWidget(sep, 1, 1, 0, 4); + + // add titles + f = font(); + f.setBold(true); + l = new QLabel(i18n("Rank"), dlg); + l->setFont(f); + l->setMinimumSize(l->sizeHint()); + table->addWidget(l, 0, 0); + l = new QLabel(i18n("Name"), dlg); + l->setFont(f); + l->setMinimumSize(l->sizeHint()); + table->addWidget(l, 0, 1); + l = new QLabel(i18n("Time"), dlg); + l->setFont(f); + l->setMinimumSize(l->sizeHint()); + table->addWidget(l, 0, 2); + l = new QLabel(i18n("Size"), dlg); + l->setFont(f); + l->setMinimumSize(l->sizeHint()); + table->addWidget(l, 0, 3); + l = new QLabel(i18n("Score"), dlg); + l->setFont(f); + l->setMinimumSize(l->sizeHint().width()*3, l->sizeHint().height()); + table->addWidget(l, 0, 4); + + QString s; + QLabel *e[10][5]; + unsigned i, j; + + for(i = 0; i < 10; i++) + { + HighScore hs; + if(i < highscore.size()) + hs = highscore[i]; + + // insert rank + s.sprintf("%d", i+1); + e[i][0] = new QLabel(s, dlg); + + // insert name + if(i < highscore.size()) + e[i][1] = new QLabel(hs.name, dlg); + else + e[i][1] = new QLabel("", dlg); + + // insert time + QTime ti(0,0,0); + if(i < highscore.size()) + { + ti = ti.addSecs(hs.seconds); + s.sprintf("%02d:%02d:%02d", ti.hour(), ti.minute(), ti.second()); + e[i][2] = new QLabel(s, dlg); + } + else + { + e[i][2] = new QLabel("", dlg); + } + + // insert size + if(i < highscore.size()) + s.sprintf("%d x %d", hs.x, hs.y); + else + s = ""; + + e[i][3] = new QLabel(s, dlg); + + // insert score + if(i < highscore.size()) + { + s = QString("%1 %2") + .arg(getScore(hs)) + .arg(hs.gravity ? i18n("(gravity)") : QString("")); + } + else + { + s = ""; + } + + e[i][4] = new QLabel(s, dlg); + e[i][4]->setAlignment(AlignRight); + } + + f = font(); + f.setBold(true); + f.setItalic(true); + for(i = 0; i < 10; i++) + { + for(j = 0; j < 5; j++) + { + e[i][j]->setMinimumHeight(e[i][j]->sizeHint().height()); + + if(j == 1) + e[i][j]->setMinimumWidth(std::max(e[i][j]->sizeHint().width(), 100)); + else + e[i][j]->setMinimumWidth(std::max(e[i][j]->sizeHint().width(), 60)); + + if((int)i == focusitem) + e[i][j]->setFont(f); + + table->addWidget(e[i][j], i+2, j, AlignCenter); + } + } + + QPushButton *b = new KPushButton(KStdGuiItem::close(), dlg); + + b->setFixedSize(b->sizeHint()); + + // connect the "Close"-button to done + connect(b, SIGNAL(clicked()), dlg, SLOT(accept())); + b->setDefault(true); + b->setFocus(); + + // make layout + tl->addSpacing(10); + tl->addWidget(b); + tl->activate(); + tl->freeze(); + + dlg->exec(); + delete dlg; +} + +void App::keyBindings() +{ + KKeyDialog::configure(actionCollection(), this); +} + +/** + * Show Settings dialog. + */ +void App::showSettings(){ + if(KConfigDialog::showDialog("settings")) + return; + + KConfigDialog *dialog = new KConfigDialog(this, "settings", Prefs::self(), KDialogBase::Swallow); + Settings *general = new Settings(0, "General"); + dialog->addPage(general, i18n("General"), "package_settings"); + connect(dialog, SIGNAL(settingsChanged()), this, SLOT(loadSettings())); + connect(dialog, SIGNAL(settingsChanged()), board, SLOT(loadSettings())); + dialog->show(); +} + +#include "app.moc" diff --git a/kshisen/app.h b/kshisen/app.h new file mode 100644 index 00000000..47808f9e --- /dev/null +++ b/kshisen/app.h @@ -0,0 +1,122 @@ +/* Yo Emacs, this is -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KSHISEN + * + * + ******************************************************************* + * + * A japanese game similar to mahjongg + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KSHISEN" + * + * KSHISEN is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KSHISEN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KSHISEN; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#ifndef __APP__H__ +#define __APP__H__ + +// Should this get the whole HAVE_SYS_TIME_H TIME_WITH_SYS_TIME treatment? +#include + +#include +#include "board.h" + +class KHighscore; + +struct HighScore +{ + QString name; + int seconds; + int x, y; + time_t date; + int gravity; +}; + +const unsigned HIGHSCORE_MAX = 10; + +class App : public KMainWindow +{ + Q_OBJECT + +public: + App(QWidget *parent = 0, const char *name=0); + +private slots: + void loadSettings(); + + void slotEndOfGame(); + void enableItems(); + void updateScore(); + void showSettings(); + + void newGame(); + void quitGame(); + void restartGame(); + void isSolvable(); + void pause(); + void undo(); + void redo(); + void hint(); + void hallOfFame(); + void keyBindings(); + void boardResized(); + +private: + void lockMenus(bool); + QString getPlayerName(); + + /** + * Read the old (pre- @ref KHighscore) highscore table. + * + * This reads the config file first, then saves it in the new format and + * re-reads it again as a KHighscore table. + **/ + void readOldHighscore(); + void readHighscore(); + void writeHighscore(); + int insertHighscore(const HighScore &); + int getScore(const HighScore &); + bool isBetter(const HighScore &, const HighScore &); + void showHighscore(int focusitem = -1); + + void initKAction(); + void setCheatMode(); + void resetCheatMode(); + +private: + QString lastPlayerName; + Board *board; + QValueVector highscore; + KHighscore* highscoreTable; + bool cheat; + + enum statusBarItems { SBI_TIME, SBI_TILES, SBI_CHEAT }; + +}; + +#endif diff --git a/kshisen/board.cpp b/kshisen/board.cpp new file mode 100644 index 00000000..ab61a912 --- /dev/null +++ b/kshisen/board.cpp @@ -0,0 +1,1082 @@ +/* Yo Emacs, this is -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KSHISEN + * + * + ******************************************************************* + * + * A japanese game similar to mahjongg + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KSHISEN" + * + * KSHISEN is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KSHISEN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KSHISEN; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "board.h" +#include "prefs.h" + +#define EMPTY 0 +#define DEFAULTDELAY 500 +#define DEFAULTSHUFFLE 4 + +static int size_x[5] = {14, 18, 24, 26, 30}; +static int size_y[5] = { 6, 8, 12, 14, 16}; +static int DELAY[5] = {1000, 750, 500, 250, 125}; + +Board::Board(QWidget *parent, const char *name) : + QWidget(parent, name, WResizeNoErase), field(0), + _x_tiles(0), _y_tiles(0), + _delay(125), paused(false), + gravity_flag(true), _solvable_flag(true), + grav_col_1(-1), grav_col_2(-1), highlighted_tile(-1) +{ + // Randomize + setShuffle(DEFAULTSHUFFLE); + + random.setSeed(0); + starttime = time((time_t *)0); + + setDelay(DEFAULTDELAY); + _redo.setAutoDelete(true); + _undo.setAutoDelete(true); + + QPixmap bg(KGlobal::dirs()->findResource("appdata", "kshisen_bgnd.png")); + setBackgroundPixmap(bg); + + loadSettings(); +} + +Board::~Board() +{ + delete [] field; +} + +void Board::loadSettings(){ + int index = Prefs::size(); + setSize(size_x[index], size_y[index]); + + setShuffle(Prefs::level() * 4 + 1); + setGravityFlag(Prefs::gravity()); + setSolvableFlag(Prefs::solvable()); + setDelay(DELAY[Prefs::speed()]); +} + +int Board::x_tiles() const +{ + return _x_tiles; +} + +int Board::y_tiles() const +{ + return _y_tiles; +} + +void Board::setField(int x, int y, int value) +{ + if(x < 0 || y < 0 || x >= x_tiles() || y >= y_tiles()) + { + kdFatal() << "Attempted write to invalid field position " + "(" << x << ", " << y << ")" << endl; + } + + field[y * x_tiles() + x] = value; +} + +int Board::getField(int x, int y) const +{ +#ifdef DEBUGGING + if(x < -1 || y < -1 || x > x_tiles() || y > y_tiles()) + { + kdFatal() << "Attempted read from invalid field position " + "(" << x << ", " << y << ")" << endl; + } +#endif + + if(x < 0 || y < 0 || x >= x_tiles() || y >= y_tiles()) + return EMPTY; + + return field[y * x_tiles() + x]; +} + +void Board::gravity(int col, bool update) +{ + if(gravity_flag) + { + int rptr = y_tiles()-1, wptr = y_tiles()-1; + while(rptr >= 0) + { + if(getField(col, wptr) != EMPTY) + { + rptr--; + wptr--; + } + else + { + if(getField(col, rptr) != EMPTY) + { + setField(col, wptr, getField(col, rptr)); + setField(col, rptr, EMPTY); + if(update) + { + updateField(col, rptr); + updateField(col, wptr); + } + wptr--; + rptr--; + } + else + rptr--; + } + } + } +} + +void Board::mousePressEvent(QMouseEvent *e) +{ + // Calculate field position + int pos_x = (e->pos().x() - xOffset()) / tiles.tileWidth(); + int pos_y = (e->pos().y() - yOffset()) / tiles.tileHeight(); + + if(e->pos().x() < xOffset() || e->pos().y() < yOffset() || + pos_x >= x_tiles() || pos_y >= y_tiles()) + { + pos_x = -1; + pos_y = -1; + } + + // Mark tile + if(e->button() == LeftButton) + { + clearHighlight(); + + if(pos_x != -1) + marked(pos_x, pos_y); + } + + // Assist by highlighting all tiles of same type + if(e->button() == RightButton) + { + int clicked_tile = getField(pos_x, pos_y); + + // Clear marked tile + if(mark_x != -1 && getField(mark_x, mark_y) != clicked_tile) + { + // We need to set mark_x and mark_y to -1 before calling + // updateField() to ensure the tile is redrawn as unmarked. + int oldmarkx = mark_x; + int oldmarky = mark_y; + mark_x = -1; + mark_y = -1; + updateField(oldmarkx, oldmarky, false); + } + else + { + mark_x = -1; + mark_y = -1; + } + + // Perform highlighting + if(clicked_tile != highlighted_tile) + { + int old_highlighted = highlighted_tile; + highlighted_tile = clicked_tile; + for(int i = 0; i < x_tiles(); i++) + { + for(int j = 0; j < y_tiles(); j++) + { + const int field_tile = getField(i, j); + if(field_tile != EMPTY) + { + if(field_tile == old_highlighted) + updateField(i, j, false); + else if(field_tile == clicked_tile) + updateField(i, j, false); + } + } + } + } + } +} + +// The board is centred inside the main playing area. xOffset/yOffset provide +// the coordinates of the top-left corner of the board. +int Board::xOffset() const +{ + return (width() - (tiles.tileWidth() * x_tiles())) / 2; +} + +int Board::yOffset() const +{ + return (height() - (tiles.tileHeight() * y_tiles())) / 2; +} + +void Board::setSize(int x, int y) +{ + if(x == x_tiles() && y == y_tiles()) + return; + + if(field != 0) + delete [] field; + + field = new int[ x * y ]; + _x_tiles = x; + _y_tiles = y; + for(int i = 0; i < x; i++) + for(int j = 0; j < y; j++) + setField(i, j, EMPTY); + + // set the minimum size of the scalable window + const double MINIMUM_SCALE = 0.2; + int w = qRound(tiles.unscaledTileWidth() * MINIMUM_SCALE) * x_tiles(); + int h = qRound(tiles.unscaledTileHeight() * MINIMUM_SCALE) * y_tiles(); + w += tiles.unscaledTileWidth(); + h += tiles.unscaledTileWidth(); + + setMinimumSize(w, h); + + resizeBoard(); + newGame(); + emit changed(); +} + +void Board::resizeEvent(QResizeEvent*) +{ + resizeBoard(); + emit resized(); +} + +void Board::resizeBoard() +{ + // calculate tile size required to fit all tiles in the window + int w = static_cast( static_cast(width() - tiles.unscaledTileWidth()) / x_tiles() ); + int h = static_cast( static_cast(height() - tiles.unscaledTileWidth()) / y_tiles() ); + + const double MAXIMUM_SCALE = 2.0; + w = std::min(w, static_cast((tiles.unscaledTileWidth() * MAXIMUM_SCALE) + 0.5)); + h = std::min(h, static_cast((tiles.unscaledTileHeight() * MAXIMUM_SCALE) + 0.5)); + + tiles.resizeTiles(w, h); +} + +QSize Board::unscaledSize() const +{ + int w = tiles.unscaledTileWidth() * x_tiles() + tiles.unscaledTileWidth(); + int h = tiles.unscaledTileHeight() * y_tiles() + tiles.unscaledTileWidth(); + return QSize(w, h); +} + +void Board::newGame() +{ + //kdDebug() << "NewGame" << endl; + int i, x, y, k; + + mark_x = -1; + mark_y = -1; + highlighted_tile = -1; // will clear previous highlight + + _undo.clear(); + _redo.clear(); + connection.clear(); + + // distribute all tiles on board + int cur_tile = 1; + for(y = 0; y < y_tiles(); y += 4) + { + for(x = 0; x < x_tiles(); ++x) + { + for(k = 0; k < 4 && y + k < y_tiles(); k++) + setField(x, y + k, cur_tile); + + cur_tile++; + if(cur_tile > TileSet::nTiles) + cur_tile = 1; + } + } + + if(getShuffle() == 0) + { + update(); + starttime = time((time_t *)0); + emit changed(); + return; + } + + // shuffle the field + int tx = x_tiles(); + int ty = y_tiles(); + for(i = 0; i < x_tiles() * y_tiles() * getShuffle(); i++) + { + int x1 = random.getLong(tx); + int y1 = random.getLong(ty); + int x2 = random.getLong(tx); + int y2 = random.getLong(ty); + int t = getField(x1, y1); + setField(x1, y1, getField(x2, y2)); + setField(x2, y2, t); + } + + // do not make solvable if _solvable_flag is false + if(!_solvable_flag) + { + update(); + starttime = time((time_t *)0); + emit changed(); + return; + } + + + int fsize = x_tiles() * y_tiles() * sizeof(int); + int *oldfield = new int[x_tiles() * y_tiles()]; + memcpy(oldfield, field, fsize); // save field + int *tiles = new int[x_tiles() * y_tiles()]; + int *pos = new int[x_tiles() * y_tiles()]; + + while(!solvable(true)) + { + //kdDebug() << "Not solvable" << endl; + //dumpBoard(); + + // generate a list of free tiles and positions + int num_tiles = 0; + for(i = 0; i < x_tiles() * y_tiles(); i++) + if(field[i] != EMPTY) + { + pos[num_tiles] = i; + tiles[num_tiles] = field[i]; + num_tiles++; + } + + // restore field + memcpy(field, oldfield, fsize); + + // redistribute unsolved tiles + while(num_tiles > 0) + { + // get a random tile + int r1 = random.getLong(num_tiles); + int r2 = random.getLong(num_tiles); + int tile = tiles[r1]; + int apos = pos[r2]; + + // truncate list + tiles[r1] = tiles[num_tiles-1]; + pos[r2] = pos[num_tiles-1]; + num_tiles--; + + // put this tile on the new position + field[apos] = tile; + } + + // remember field + memcpy(oldfield, field, fsize); + } + + + // restore field + memcpy(field, oldfield, fsize); + delete tiles; + delete pos; + delete oldfield; + + update(); + starttime = time((time_t *)0); + emit changed(); +} + +bool Board::isTileHighlighted(int x, int y) const +{ + if(x == mark_x && y == mark_y) + return true; + + if(getField(x, y) == highlighted_tile) + return true; + + if(!connection.empty()) + { + if(x == connection.front().x && y == connection.front().y) + return true; + + if(x == connection.back().x && y == connection.back().y) + return true; + } + + return false; +} + +void Board::updateField(int x, int y, bool erase) +{ + QRect r(xOffset() + x * tiles.tileWidth(), + yOffset() + y * tiles.tileHeight(), + tiles.tileWidth(), + tiles.tileHeight()); + + repaint(r, erase); +} + +void Board::paintEvent(QPaintEvent *e) +{ + + QRect ur = e->rect(); // rectangle to update + QPixmap pm(ur.size()); // Pixmap for double-buffering + pm.fill(this, ur.topLeft()); // fill with widget background + QPainter p(&pm); + p.translate(-ur.x(), -ur.y()); // use widget coordinate system + + if(paused) + { + p.setFont(KGlobalSettings::largeFont()); + p.drawText(rect(), Qt::AlignCenter, i18n("Game Paused")); + } + else + { + int w = tiles.tileWidth(); + int h = tiles.tileHeight(); + for(int i = 0; i < x_tiles(); i++) + { + for(int j = 0; j < y_tiles(); j++) + { + int tile = getField(i, j); + if(tile == EMPTY) + continue; + + int xpos = xOffset() + i * w; + int ypos = yOffset() + j * h; + QRect r(xpos, ypos, w, h); + if(e->rect().intersects(r)) + { + if(isTileHighlighted(i, j)) + p.drawPixmap(xpos, ypos, tiles.highlightedTile(tile-1)); + else + p.drawPixmap(xpos, ypos, tiles.tile(tile-1)); + } + } + } + } + p.end(); + bitBlt( this, ur.topLeft(), &pm ); +} + +void Board::marked(int x, int y) +{ + // make sure that the previous connection is correctly undrawn + undrawConnection(); + + if(getField(x, y) == EMPTY) + return; + + if(x == mark_x && y == mark_y) + { + // unmark the piece + mark_x = -1; + mark_y = -1; + updateField(x, y, false); + return; + } + + if(mark_x == -1) + { + mark_x = x; + mark_y = y; + updateField(x, y, false); + return; + } + + int fld1 = getField(mark_x, mark_y); + int fld2 = getField(x, y); + + // both field same? + if(fld1 != fld2) + return; + + // trace + if(findPath(mark_x, mark_y, x, y, connection)) + { + madeMove(mark_x, mark_y, x, y); + drawConnection(getDelay()); + setField(mark_x, mark_y, EMPTY); + setField(x, y, EMPTY); + grav_col_1 = x; + grav_col_2 = mark_x; + mark_x = -1; + mark_y = -1; + + // game is over? + // Must delay until after tiles fall to make this test + // See undrawConnection GP. + } + else + { + connection.clear(); + } +} + + +void Board::clearHighlight() +{ + if(highlighted_tile != -1) + { + int old_highlight = highlighted_tile; + highlighted_tile = -1; + + for(int i = 0; i < x_tiles(); i++) + for(int j = 0; j < y_tiles(); j++) + if(old_highlight == getField(i, j)) + updateField(i, j, false); + } +} + +// Can we make a path between two tiles with a single line? +bool Board::canMakePath(int x1, int y1, int x2, int y2) const +{ + if(x1 == x2) + { + for(int i = std::min(y1, y2) + 1; i < std::max(y1, y2); i++) + if(getField(x1, i) != EMPTY) + return false; + + return true; + } + + if(y1 == y2) + { + for(int i = std::min(x1, x2) + 1; i < std::max(x1, x2); i++) + if(getField(i, y1) != EMPTY) + return false; + + return true; + } + + return false; +} + +bool Board::findPath(int x1, int y1, int x2, int y2, Path& p) const +{ + p.clear(); + + if(findSimplePath(x1, y1, x2, y2, p)) + return true; + + // Find a path of 3 segments + const int dx[4] = { 1, 0, -1, 0 }; + const int dy[4] = { 0, 1, 0, -1 }; + + for(int i = 0; i < 4; i++) + { + int newx = x1 + dx[i]; + int newy = y1 + dy[i]; + while(newx >= -1 && newx <= x_tiles() && + newy >= -1 && newy <= y_tiles() && + getField(newx, newy) == EMPTY) + { + if(findSimplePath(newx, newy, x2, y2, p)) + { + p.push_front(Position(x1, y1)); + return true; + } + newx += dx[i]; + newy += dy[i]; + } + } + + return false; +} + +// Find a path of 1 or 2 segments between tiles. Returns whether +// a path was found, and if so, the path is returned via 'p'. +bool Board::findSimplePath(int x1, int y1, int x2, int y2, Path& p) const +{ + // Find direct line (path of 1 segment) + if(canMakePath(x1, y1, x2, y2)) + { + p.push_back(Position(x1, y1)); + p.push_back(Position(x2, y2)); + return true; + } + + // If the tiles are in the same row or column, then a + // a 'simple path' cannot be found between them + if(x1 == x2 || y1 == y2) + return false; + + // Find path of 2 segments (route A) + if(getField(x2, y1) == EMPTY && canMakePath(x1, y1, x2, y1) && + canMakePath(x2, y1, x2, y2)) + { + p.push_back(Position(x1, y1)); + p.push_back(Position(x2, y1)); + p.push_back(Position(x2, y2)); + return true; + } + + // Find path of 2 segments (route B) + if(getField(x1, y2) == EMPTY && canMakePath(x1, y1, x1, y2) && + canMakePath(x1, y2, x2, y2)) + { + p.push_back(Position(x1, y1)); + p.push_back(Position(x1, y2)); + p.push_back(Position(x2, y2)); + return true; + } + + return false; +} + +void Board::drawConnection(int timeout) +{ + if(connection.empty()) + return; + + // lighten the fields + updateField(connection.front().x, connection.front().y); + updateField(connection.back().x, connection.back().y); + + QPainter p; + p.begin(this); + p.setPen(QPen(QColor("red"), tiles.lineWidth())); + + // Path.size() will always be >= 2 + Path::const_iterator pathEnd = connection.end(); + Path::const_iterator pt1 = connection.begin(); + Path::const_iterator pt2 = pt1; + ++pt2; + while(pt2 != pathEnd) + { + p.drawLine( midCoord(pt1->x, pt1->y), midCoord(pt2->x, pt2->y) ); + ++pt1; + ++pt2; + } + + p.flush(); + p.end(); + + QTimer::singleShot(timeout, this, SLOT(undrawConnection())); +} + +void Board::undrawConnection() +{ + if(grav_col_1 != -1 || grav_col_2 != -1) + { + gravity(grav_col_1, true); + gravity(grav_col_2, true); + grav_col_1 = -1; + grav_col_2 = -1; + } + + // is already undrawn? + if(connection.empty()) + return; + + // Redraw all affected fields + + Path oldConnection = connection; + connection.clear(); + + // Path.size() will always be >= 2 + Path::const_iterator pathEnd = oldConnection.end(); + Path::const_iterator pt1 = oldConnection.begin(); + Path::const_iterator pt2 = pt1; + ++pt2; + while(pt2 != pathEnd) + { + if(pt1->y == pt2->y) + { + for(int i = std::min(pt1->x, pt2->x); i <= std::max(pt1->x, pt2->x); i++) + updateField(i, pt1->y); + } + else + { + for(int i = std::min(pt1->y, pt2->y); i <= std::max(pt1->y, pt2->y); i++) + updateField(pt1->x, i); + } + ++pt1; + ++pt2; + } + + Path dummyPath; + // game is over? + if(!getHint_I(dummyPath)) + { + time_for_game = (int)difftime( time((time_t)0), starttime); + emit endOfGame(); + } +} + +QPoint Board::midCoord(int x, int y) const +{ + QPoint p; + int w = tiles.tileWidth(); + int h = tiles.tileHeight(); + + if(x == -1) + p.setX(xOffset() - (w / 4)); + else if(x == x_tiles()) + p.setX(xOffset() + (w * x_tiles()) + (w / 4)); + else + p.setX(xOffset() + (w * x) + (w / 2)); + + if(y == -1) + p.setY(yOffset() - (w / 4)); + else if(y == y_tiles()) + p.setY(yOffset() + (h * y_tiles()) + (w / 4)); + else + p.setY(yOffset() + (h * y) + (h / 2)); + + return p; +} + +void Board::setDelay(int newvalue) +{ + _delay = newvalue; +} + +int Board::getDelay() const +{ + return _delay; +} + +void Board::madeMove(int x1, int y1, int x2, int y2) +{ + Move *m = new Move(x1, y1, x2, y2, getField(x1, y1)); + _undo.append(m); + while(_redo.count()) + _redo.removeFirst(); + emit changed(); +} + +bool Board::canUndo() const +{ + return !_undo.isEmpty(); +} + +bool Board::canRedo() const +{ + return !_redo.isEmpty(); +} + +void Board::undo() +{ + if(canUndo()) + { + clearHighlight(); + undrawConnection(); + Move* m = _undo.last(); + _undo.take(); + if(gravityFlag()) + { + int y; + + // When both tiles reside in the same column, the order of undo is + // significant (we must undo the lower tile first). + if(m->x1 == m->x2 && m->y1 < m->y2) + { + std::swap(m->x1, m->x2); + std::swap(m->y1, m->y2); + } + + for(y = 0; y < m->y1; y++) + { + setField(m->x1, y, getField(m->x1, y+1)); + updateField(m->x1, y); + } + + for(y = 0; y < m->y2; y++) + { + setField(m->x2, y, getField(m->x2, y+1)); + updateField(m->x2, y); + } + } + + setField(m->x1, m->y1, m->tile); + setField(m->x2, m->y2, m->tile); + updateField(m->x1, m->y1); + updateField(m->x2, m->y2); + _redo.prepend(m); + emit changed(); + } +} + +void Board::redo() +{ + if(canRedo()) + { + clearHighlight(); + undrawConnection(); + Move* m = _redo.take(0); + setField(m->x1, m->y1, EMPTY); + setField(m->x2, m->y2, EMPTY); + updateField(m->x1, m->y1); + updateField(m->x2, m->y2); + gravity(m->x1, true); + gravity(m->x2, true); + _undo.append(m); + emit changed(); + } +} + +void Board::showHint() +{ + undrawConnection(); + + if(getHint_I(connection)) + drawConnection(1000); +} + + +#ifdef DEBUGGING +void Board::makeHintMove() +{ + Path p; + + if(getHint_I(p)) + { + mark_x = -1; + mark_y = -1; + marked(p.front().x, p.front().y); + marked(p.back().x, p.back().y); + } +} + +void Board::finish() +{ + Path p; + bool ready=false; + + while(!ready && getHint_I(p)) + { + mark_x = -1; + mark_y = -1; + if(tilesLeft() == 2) + ready = true; + marked(p.front().x, p.front().y); + marked(p.back().x, p.back().y); + kapp->processEvents(); + usleep(250*1000); + } +} + +void Board::dumpBoard() const +{ + kdDebug() << "Board contents:" << endl; + for(int y = 0; y < y_tiles(); ++y) + { + QString row; + for(int x = 0; x < x_tiles(); ++x) + { + int tile = getField(x, y); + if(tile == EMPTY) + row += " --"; + else + row += QString("%1").arg(getField(x, y), 3); + } + kdDebug() << row << endl; + } +} +#endif + +bool Board::getHint_I(Path& p) const +{ + //dumpBoard(); + short done[TileSet::nTiles]; + for( short index = 0; index < TileSet::nTiles; index++ ) + done[index] = 0; + + for(int x = 0; x < x_tiles(); x++) + { + for(int y = 0; y < y_tiles(); y++) + { + int tile = getField(x, y); + if(tile != EMPTY && done[tile - 1] != 4) + { + // for all these types of tile search path's + for(int xx = 0; xx < x_tiles(); xx++) + { + for(int yy = 0; yy < y_tiles(); yy++) + { + if(xx != x || yy != y) + { + if(getField(xx, yy) == tile) + if(findPath(x, y, xx, yy, p)) + { + //kdDebug() << "path.size() == " << p.size() << endl; + //for(Path::const_iterator i = p.begin(); i != p.end(); ++i) + // kdDebug() << "pathEntry: (" << i->x << ", " << i->y + // << ") => " << getField(i->x, i->y) << endl; + return true; + } + } + } + } + done[tile - 1]++; + } + } + } + + return false; +} + +void Board::setShuffle(int newvalue) +{ + if(newvalue != _shuffle){ + _shuffle = newvalue; + newGame(); + } +} + +int Board::getShuffle() const +{ + return _shuffle; +} + +int Board::tilesLeft() const +{ + int left = 0; + + for(int i = 0; i < x_tiles(); i++) + for(int j = 0; j < y_tiles(); j++) + if(getField(i, j) != EMPTY) + left++; + + return left; +} + +int Board::getCurrentTime() const +{ + return (int)difftime(time((time_t *)0),starttime); +} + +int Board::getTimeForGame() const +{ + if(tilesLeft() == 0) + { + return time_for_game; + } + else + { + if(paused) + return (int)difftime(pause_start, starttime); + else + return (int)difftime(time((time_t *)0), starttime); + } +} + +bool Board::solvable(bool norestore) +{ + int *oldfield = 0; + + if(!norestore) + { + oldfield = new int [x_tiles() * y_tiles()]; + memcpy(oldfield, field, x_tiles() * y_tiles() * sizeof(int)); + } + + Path p; + while(getHint_I(p)) + { + kdFatal(getField(p.front().x, p.front().y) != getField(p.back().x, p.back().y)) + << "Removing unmateched tiles: (" << p.front().x << ", " << p.front().y << ") => " + << getField(p.front().x, p.front().y) << " (" << p.back().x << ", " << p.back().y << ") => " + << getField(p.back().x, p.back().y) << endl; + setField(p.front().x, p.front().y, EMPTY); + setField(p.back().x, p.back().y, EMPTY); + //if(gravityFlag()) + //{ + // gravity(p.front().x, false); + // gravity(p.back().x, false); + //} + } + + int left = tilesLeft(); + + if(!norestore) + { + memcpy(field, oldfield, x_tiles() * y_tiles() * sizeof(int)); + delete [] oldfield; + } + + return (bool)(left == 0); +} + +bool Board::getSolvableFlag() const +{ + return _solvable_flag; +} + +void Board::setSolvableFlag(bool value) +{ + if(value && !_solvable_flag && !solvable()){ + _solvable_flag = value; + newGame(); + } + else + _solvable_flag = value; +} + +bool Board::gravityFlag() const +{ + return gravity_flag; +} + +void Board::setGravityFlag(bool b) +{ + if( gravity_flag != b ){ + if(canUndo() || canRedo()) + newGame(); + gravity_flag = b; + } +} + +bool Board::pause() +{ + paused = !paused; + if(paused) + pause_start = time((time_t *)0); + else + starttime += (time_t) difftime( time((time_t *)0), pause_start); + update(); + + return paused; +} + +QSize Board::sizeHint() const +{ + int dpi = QPaintDeviceMetrics(this).logicalDpiX(); + if (dpi < 75) + dpi = 75; + return QSize(9*dpi,7*dpi); +} + +#include "board.moc" diff --git a/kshisen/board.h b/kshisen/board.h new file mode 100644 index 00000000..c38fba57 --- /dev/null +++ b/kshisen/board.h @@ -0,0 +1,189 @@ +/* Yo Emacs, this is -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KSHISEN + * + * + ******************************************************************* + * + * A japanese game similar to mahjongg + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KSHISEN" + * + * KSHISEN is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KSHISEN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KSHISEN; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#ifndef __BOARD__H__ +#define __BOARD__H__ + +// Should this get the whole HAVE_SYS_TIME_H TIME_WITH_SYS_TIME treatment? +#include + +#include +#include +#include "tileset.h" +#include "debug.h" + +struct Position +{ + Position() : x(0), y(0) { } + Position(int _x, int _y) : x(_x), y(_y) { } + int x; + int y; +}; + +typedef std::list Path; + +class Move +{ +public: + Move(int _x1, int _y1, int _x2, int _y2, int _tile) : + x1(_x1), y1(_y1), x2(_x2), y2(_y2), tile(_tile) { } + + int x1, y1, x2, y2; + int tile; +}; + +class Board : public QWidget +{ + Q_OBJECT + +public: + Board(QWidget *parent = 0, const char *name=0); + ~Board(); + + virtual void paintEvent(QPaintEvent *); + virtual void mousePressEvent(QMouseEvent *); + virtual void resizeEvent(QResizeEvent*); + + void setDelay(int); + int getDelay() const; + + bool canUndo() const; + bool canRedo() const; + void redo(); + void undo(); + + void setSize(int x, int y); + void resizeBoard(); + QSize unscaledSize() const; + void newGame(); + void setShuffle(int); + int getShuffle() const; + + void showHint(); + bool getHint_I(Path& p) const; + +#ifdef DEBUGGING + void makeHintMove(); + void finish(); + void dumpBoard() const; +#endif + + int tilesLeft() const; + int getCurrentTime() const; + int getTimeForGame() const; + + bool solvable(bool norestore = FALSE); + + bool getSolvableFlag() const; + void setSolvableFlag(bool); + bool gravityFlag() const; + void setGravityFlag(bool); + + int x_tiles() const; + int y_tiles() const; + + bool isPaused() const { return paused; } + +signals: + void markMatched(); + void changed(); + void endOfGame(); + void resized(); + +public slots: + bool pause(); + void loadSettings(); + +private slots: + void undrawConnection(); + void gravity(int, bool); + +protected: + virtual QSize sizeHint() const; + +private: // functions + void initBoard(); + + int xOffset() const; + int yOffset() const; + + void setField(int x, int y, int value); + int getField(int x, int y) const; + void updateField(int, int, bool erase = true); + void clearHighlight(); + bool canMakePath(int x1, int y1, int x2, int y2) const; + bool findPath(int x1, int y1, int x2, int y2, Path& p) const; + bool findSimplePath(int x1, int y1, int x2, int y2, Path& p) const; + bool isTileHighlighted(int x, int y) const; + void drawConnection(int timeout); + QPoint midCoord(int x, int y) const; + void marked(int x, int y); + void madeMove(int x1, int y1, int x2, int y2); + +private: + time_t starttime; + time_t time_for_game; + + TileSet tiles; + + KRandomSequence random; + + QPtrList _undo; + QPtrList _redo; + + int undraw_timer_id; + int mark_x; + int mark_y; + Path connection; + int *field; + int _x_tiles; + int _y_tiles; + int _delay; + int _shuffle; + + bool paused; + time_t pause_start; + + bool gravity_flag; + bool _solvable_flag; + int grav_col_1, grav_col_2; + + int highlighted_tile; +}; + +#endif diff --git a/kshisen/debug.h b/kshisen/debug.h new file mode 100644 index 00000000..aa79a7e5 --- /dev/null +++ b/kshisen/debug.h @@ -0,0 +1 @@ +//#define DEBUGGING diff --git a/kshisen/hi128-app-kshisen.png b/kshisen/hi128-app-kshisen.png new file mode 100644 index 00000000..8badc8f9 Binary files /dev/null and b/kshisen/hi128-app-kshisen.png differ diff --git a/kshisen/hi16-app-kshisen.png b/kshisen/hi16-app-kshisen.png new file mode 100644 index 00000000..9077c7c8 Binary files /dev/null and b/kshisen/hi16-app-kshisen.png differ diff --git a/kshisen/hi22-app-kshisen.png b/kshisen/hi22-app-kshisen.png new file mode 100644 index 00000000..237b1a66 Binary files /dev/null and b/kshisen/hi22-app-kshisen.png differ diff --git a/kshisen/hi32-app-kshisen.png b/kshisen/hi32-app-kshisen.png new file mode 100644 index 00000000..7f7ac9d9 Binary files /dev/null and b/kshisen/hi32-app-kshisen.png differ diff --git a/kshisen/hi48-app-kshisen.png b/kshisen/hi48-app-kshisen.png new file mode 100644 index 00000000..e67de754 Binary files /dev/null and b/kshisen/hi48-app-kshisen.png differ diff --git a/kshisen/hi64-app-kshisen.png b/kshisen/hi64-app-kshisen.png new file mode 100644 index 00000000..7ec56314 Binary files /dev/null and b/kshisen/hi64-app-kshisen.png differ diff --git a/kshisen/kshisen.desktop b/kshisen/kshisen.desktop new file mode 100644 index 00000000..4fa3712c --- /dev/null +++ b/kshisen/kshisen.desktop @@ -0,0 +1,76 @@ +[Desktop Entry] +Exec=kshisen %i %m -caption "%c" +Type=Application +DocPath=kshisen/index.html +Name=Shisen-Sho +Name[af]=Shisen-sho +Name[be]=Ші-Ñен-Ñёе +Name[bn]=শিসেন-শো +Name[cs]=Å isen-Å o +Name[eo]=Åœisen-Åœo +Name[fr]=Shisen-sho +Name[he]=שישן־שו +Name[hi]=शाईसेन-शो +Name[ja]=å››å·çœ +Name[ne]=सिसेन शो +Name[pa]=ਸ਼ਿਸੀਨ-ਸ਼ੋ +Name[sk]=Å isen-Å o +Name[ta]=ஷிசெனà¯-ஷோ +Name[tg]=ШиÑен-Шо +Name[th]=ชิเซน-โช - K +Name[uk]=ШіÑен-Шо +Name[zh_CN]=连连看 +Name[zu]=I-Shisen-Sho +GenericName=Shisen-Sho Mahjongg-like Tile Game +GenericName[be]=ВарыÑнт маджонга ШыÑен-ÑÑ‘ +GenericName[bg]=Игра Ñ Ð¿Ð»Ð¾Ñ‡ÐºÐ¸ +GenericName[bn]=শিসেন-শো মাহজং-জাতীয় টালির খেলা +GenericName[br]=Ur c'hoari teol a seurt gant Shisen*Sho Mahjongg +GenericName[bs]=Shisen-Sho igra nalik na Mahjongg +GenericName[ca]=Joc de mosaics Shisen-Sho a l'estil Mahjongg +GenericName[cs]=Hra s dlaždicemi Å isen-Å o +GenericName[cy]=Gêm Deiliau Shisen-Sho sy'n debyg i Mahjongg +GenericName[da]=Shisen-Sho Mahjongg-lignende flisespil +GenericName[de]=Mahjongg-ähnliches Shisen-Sho Spiel +GenericName[el]=Παιχνίδι παÏόμοιο με το Shisen-Sho Mahjongg +GenericName[eo]=Shisen-Sho Mahjongg-simila kahel-ludo +GenericName[es]=Juego de fichas similar al Shisen-Sho Mahjongg +GenericName[et]=Mahjonggi moodi klotsimäng +GenericName[eu]=Shisen-Sho Mahjongg-en antzeko fitxa-jokoa +GenericName[fa]=بازی کاشی شبیه Shisen-Sho Mahjongg +GenericName[fi]=Shisen-Sho Mahjongg--tyylinen palikkapeli +GenericName[fr]=Jeu de tuiles Shisen-Sho dans le style Mahjongg +GenericName[he]=Shisen Sho, חיקוי מה ×’'ונג, משחק ×§×œ×¤×™× +GenericName[hr]=Shisen-Sho igra s ploÄicama poput Mahjongga +GenericName[hu]=Mahjongg-változat +GenericName[is]=Shisen-Sho kubbaleikur líkur Mahjongg +GenericName[it]=Shisen-Sho, gioco di tessere simile a Mahjongg +GenericName[ja]=å››å·çœãƒžãƒ¼ã‚¸ãƒ£ãƒ³ç‰Œã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​ក្បឿង​ដូច Shisen-Sho Mahjongg +GenericName[ko]=시센-쇼 마작과 ê°™ì€ íƒ€ì¼ ê²Œìž„ +GenericName[lv]=Shisen-Sho Mahjongg lÄ«dzÄ«ga spÄ“le +GenericName[mk]=Игра Ñо плочки Ñлична на Shisen-Sho Mahjongg +GenericName[nb]=Shisen-Sho Mahjongg-lignende brikkespill +GenericName[nds]=Mahjongg-liek Speel +GenericName[ne]=सिनसेन शो माहाजोङ जसà¥à¤¤à¥ˆ टायल खेल +GenericName[nl]=Shisen-Sho Mahjongg-achtig stenenspel +GenericName[nn]=Shisen-Sho Mahjongg-liknande brikkespel +GenericName[pa]=ਸ਼ਿਸੀਨ-ਸ਼ੋ ਮਹਿਜ਼ੋਗ ਵਰਗੀ ਖੇਡ +GenericName[pl]=Gra typu Shisen-Sho Mahjongg +GenericName[pt]=Jogo de Padrões Shisen-Sho +GenericName[pt_BR]=Jogo de Ladrilhos parecido com Shisen-Sho Mahjongg +GenericName[ru]=Ши-Ñен-ÑÑ‘ +GenericName[se]=Shisen-Sho Mahjongg-lágan bihttáspeallu +GenericName[sk]=DlaždiÄková hra typu Shisen-Sho Mahjongg +GenericName[sl]=Igra s ploÅ¡Äicami Shisen-Sho, podobna Mahjonggu +GenericName[sr]=Shisen-Sho, игра Ñа пољима налик на Mahjongg +GenericName[sr@Latn]=Shisen-Sho, igra sa poljima nalik na Mahjongg +GenericName[sv]=Shisen-Sho Mahjongg-liknande brickspel +GenericName[ta]=ஷிசெநà¯à®·à¯‹ மாஹà¯à®œà¯‹à®™à¯ போனà¯à®± ஓட௠விளையாடà¯à®Ÿà¯ +GenericName[uk]=ШіÑен-Шо Махжонг-на кшталт гри Плитки +GenericName[zh_CN]=ç±»ä¼¼è¿žè¿žçœ‹çš„éº»å°†æ¸¸æˆ +GenericName[zh_TW]=å››å·çœéº»å°‡ç‰ŒéŠæˆ² +Icon=kshisen +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;BoardGame; diff --git a/kshisen/kshisen.kcfg b/kshisen/kshisen.kcfg new file mode 100644 index 00000000..9c486ee8 --- /dev/null +++ b/kshisen/kshisen.kcfg @@ -0,0 +1,35 @@ + + + + + + true + + + true + + + true + + + + + 2 + 0 + 4 + + + 2 + 0 + 4 + + + 1 + 0 + 2 + + + diff --git a/kshisen/kshisenui.rc b/kshisen/kshisenui.rc new file mode 100644 index 00000000..dcac9fdc --- /dev/null +++ b/kshisen/kshisenui.rc @@ -0,0 +1,16 @@ + + + + + + +Main Toolbar + + + + + + + diff --git a/kshisen/main.cpp b/kshisen/main.cpp new file mode 100644 index 00000000..161cffec --- /dev/null +++ b/kshisen/main.cpp @@ -0,0 +1,74 @@ +/* Yo Emacs, this is -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KSHISEN + * + * + ******************************************************************* + * + * A japanese game similar to mahjongg + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KSHISEN" + * + * KSHISEN is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KSHISEN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KSHISEN; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#include +#include +#include +#include + +#include "version.h" +#include "app.h" + +static const char description[] = I18N_NOOP("A KDE game similiar to Mahjongg"); + +// A hack to circumvent tricky i18n issue, not used later on in the code. +// Both context and contents must be exactly the same as for the entry in +// kdelibs/kdeui/ui_standards.rc +static const char dummy[] = I18N_NOOP2("Menu title", "&Move"); + +int main(int argc, char **argv) +{ + KAboutData aboutData( "kshisen", I18N_NOOP("Shisen-Sho"), + KSHISEN_VERSION, description, KAboutData::License_GPL, + "(c) 1997, Mario Weilguni"); + aboutData.addAuthor("Dave Corrie", I18N_NOOP("Current Maintainer"), "kde@davecorrie.com"); + aboutData.addAuthor("Mario Weilguni", I18N_NOOP("Original Author"), "mweilguni@sime.com"); + aboutData.addCredit("Jason Lane", I18N_NOOP("Added 'tiles removed' counter\nTile smooth-scaling and window resizing"), "jglane@btopenworld.com"); + aboutData.addCredit(0, I18N_NOOP("Thanks also to everyone who should be listed here but isn't!"), 0); + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + + App *app = new App(); + app->show(); + a.setMainWidget(app); + a.config()->sync(); + return a.exec(); +} + diff --git a/kshisen/pics/Makefile.am b/kshisen/pics/Makefile.am new file mode 100644 index 00000000..c65bf6fb --- /dev/null +++ b/kshisen/pics/Makefile.am @@ -0,0 +1,5 @@ + +pics_DATA = tileset.png kshisen_bgnd.png +picsdir = $(kde_datadir)/kshisen/ + +EXTRA_DIST = $(pics_DATA) diff --git a/kshisen/pics/kshisen_bgnd.png b/kshisen/pics/kshisen_bgnd.png new file mode 100644 index 00000000..d1a7df75 Binary files /dev/null and b/kshisen/pics/kshisen_bgnd.png differ diff --git a/kshisen/pics/tileset.png b/kshisen/pics/tileset.png new file mode 100644 index 00000000..b4286a7d Binary files /dev/null and b/kshisen/pics/tileset.png differ diff --git a/kshisen/prefs.kcfgc b/kshisen/prefs.kcfgc new file mode 100644 index 00000000..63feff7f --- /dev/null +++ b/kshisen/prefs.kcfgc @@ -0,0 +1,6 @@ +# Code generation options for kconfig_compiler +File=kshisen.kcfg +ClassName=Prefs +Singleton=true +#CustomAdditions=true +Mutators=Gravity,Unscaled diff --git a/kshisen/settings.ui b/kshisen/settings.ui new file mode 100644 index 00000000..7f9846a6 --- /dev/null +++ b/kshisen/settings.ui @@ -0,0 +1,255 @@ + +Settings + + + Settings + + + + 0 + 0 + 385 + 381 + + + + + unnamed + + + + kcfg_Gravity + + + Gravity + + + + + kcfg_Solvable + + + Allow unsolvable games + + + + + difficulty_groupBox + + + Board Difficulty + + + + unnamed + + + + kcfg_Level + + + 2 + + + 1 + + + 1 + + + Horizontal + + + Below + + + + + textLabel1 + + + Easy + + + + + textLabel3 + + + Hard + + + AlignVCenter|AlignRight + + + + + + + speed_groupBox + + + Piece Removal Speed + + + + unnamed + + + + kcfg_Speed + + + 4 + + + 1 + + + 2 + + + Horizontal + + + Below + + + + + textLabel4 + + + Slow + + + + + textLabel6 + + + Fast + + + AlignVCenter|AlignRight + + + + + + + kcfg_Unscaled + + + Prefer unscaled tiles + + + + + size_groupBox + + + Tile Size + + + + unnamed + + + + textLabel8 + + + 14x6 + + + + + textLabel9 + + + 18x8 + + + AlignCenter + + + + + textLabel11 + + + 26x14 + + + AlignCenter + + + + + textLabel12 + + + 30x16 + + + AlignVCenter|AlignRight + + + + + kcfg_Size + + + 0 + + + 4 + + + 1 + + + 2 + + + Horizontal + + + Below + + + + + textLabel10 + + + 24x12 + + + AlignCenter + + + + + + + spacer1 + + + Vertical + + + Expanding + + + + 20 + 20 + + + + + + + diff --git a/kshisen/tileset.cpp b/kshisen/tileset.cpp new file mode 100644 index 00000000..f44e2698 --- /dev/null +++ b/kshisen/tileset.cpp @@ -0,0 +1,147 @@ +/** + * tileset.cpp + * + * Copyright (c) 2002 Jason Lane + * (c) 2002 Dave Corrie + * + * This file is part of KShisen. + * + * KMail is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#include + +#include + +#include "tileset.h" + +TileSet::TileSet() : scaledTiles(nTiles) +{ + //loadTiles + QImage tileset(KGlobal::dirs()->findResource("appdata", "tileset.png")); + if(tileset.isNull()) + { + KMessageBox::sorry(0, i18n("Cannot load tiles pixmap!")); + KApplication::exit(1); + } + + // split into individual tiles + const int TILES_X = 9; + const int TILES_Y = 4; + unscaledTiles.reserve(nTiles); + + int w = tileset.width() / TILES_X; + int h = tileset.height() / TILES_Y; + for(int row = 0; row < TILES_Y; row++) + { + for(int col = 0; col < TILES_X; col++) + unscaledTiles.push_back(tileset.copy(col * w, row * h, w, h)); + } +} + +TileSet::~TileSet() +{ +} + +void TileSet::resizeTiles(int maxWidth, int maxHeight) +{ + // calculate largest tile size that will fit in maxWidth/maxHeight + // and maintain the tile's height-to-width ratio + double ratio = static_cast(unscaledTileHeight()) / unscaledTileWidth(); + if(maxWidth * ratio < maxHeight) + maxHeight = qRound(maxWidth * ratio); + else + maxWidth = qRound(maxHeight / ratio); + + if(maxHeight == tileHeight() && maxWidth == tileWidth()) + return; + + //kdDebug() << "tile size: " << maxWidth << "x" << maxHeight << endl; + + QImage img; + for(int i = 0; i < nTiles; i++) + { + if(maxHeight == unscaledTileHeight()) + img = unscaledTiles[i].copy();//.convertDepth(32); + else + img = unscaledTiles[i].smoothScale(maxWidth, maxHeight); + + scaledTiles[i].convertFromImage(img); + } +} + +const QPixmap &TileSet::tile(int n) const +{ + return scaledTiles[n]; +} + +QPixmap TileSet::highlightedTile(int n) const +{ + const double LIGHTEN_FACTOR = 1.3; + + // lighten the image + QImage img = scaledTiles[n].convertToImage().convertDepth(32); + + for(int y = 0; y < img.height(); y++) + { + uchar* p = img.scanLine(y); + for(int x = 0; x < img.width() * 4; x++) + { + *p = static_cast(std::min(255, static_cast(*p * LIGHTEN_FACTOR))); + p++; + } + } + + QPixmap highlightedTile; + highlightedTile.convertFromImage(img); + + return highlightedTile; +} + +int TileSet::lineWidth() const +{ + int width = qRound(tileHeight() / 10.0); + if(width < 3) + width = 3; + + return width; +} + +int TileSet:: tileWidth() const +{ + return scaledTiles[0].width(); +} + +int TileSet:: tileHeight() const +{ + return scaledTiles[0].height(); +} + +int TileSet:: unscaledTileHeight() const +{ + return unscaledTiles[0].height(); +} + +int TileSet:: unscaledTileWidth() const +{ + return unscaledTiles[0].width(); +} + diff --git a/kshisen/tileset.h b/kshisen/tileset.h new file mode 100644 index 00000000..02905201 --- /dev/null +++ b/kshisen/tileset.h @@ -0,0 +1,59 @@ +/** + * tileset.h + * + * Copyright (c) 2002 Jason Lane + * (c) 2002 Dave Corrie + * + * This file is part of KShisen. + * + * KMail is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __IMAGEDATA__H__ +#define __IMAGEDATA__H__ + +#include + +class TileSet +{ + +public: + + static const int nTiles = 36; + + TileSet(); + ~TileSet(); + + void resizeTiles(int maxWidth, int maxHeight); + + const QPixmap &tile(int n) const; + QPixmap highlightedTile(int n) const; + + int lineWidth() const; + + int tileWidth() const; + int tileHeight() const; + int unscaledTileHeight() const; + int unscaledTileWidth() const; + +private: + + QValueVector scaledTiles; + QValueVector unscaledTiles; + +}; + +#endif + diff --git a/kshisen/version.h b/kshisen/version.h new file mode 100644 index 00000000..2921d135 --- /dev/null +++ b/kshisen/version.h @@ -0,0 +1,14 @@ +// Explanation of KShisen version numbering system +// +// Version numbers are of the form: MAJOR.MINOR.MICRO +// +// The MAJOR number should only be incremented when really major +// changes occur. Such an event is not currently foreseeable. +// +// The MINOR number is increased for every branch of KDE from HEAD +// (i.e. it's increased for the KDE3.1 branch, KDE3.2 branch etc.). +// +// The MICRO version is increased for every bug-fix to a branch. +// MICRO numbers >= 90 are used for development milestones in HEAD. + +#define KSHISEN_VERSION "1.5.1" diff --git a/ksirtet/CHANGELOG b/ksirtet/CHANGELOG new file mode 100644 index 00000000..925a24d9 --- /dev/null +++ b/ksirtet/CHANGELOG @@ -0,0 +1,301 @@ +2.1.11b (1 September 2005) [KDE 3.4.3 stable] + * fix compilation dependencies + * "fix" bug #65488 by disabling network games + +2.1.11 (12 September 2004) [KDE 3.3.1 stable] + * fix loss of key down action after switching windows (bug #86017) + * fix removed lines being not updated at restart (bug #88990) + +2.1.10 (4 July 2004) [KDE 3.3 stable] + * use zoom in/out actions (bug #65478) + * remove arrows in two players mode + add description for the three + balls in multiplayer game (bug #65489) + +2.1.9 (31 May 2004) + * more detailed removed line display + * display level in progress bar + +2.1.8 (10 March 2004) [KDE 3.2.2 stable] + * fix bug #65490 (default with simpler removed lines display) + * fix bug #77170 (crash at startup) + +2.1.7 (24 January 2003) [KDE 3.2 stable] + * more control on drop down action by key press and release + * move to left/right column actions + * notifications + * fix crash when switching from normal to arcade game + * options for old style rotation, for direct drop down and for + simplified "removed blocks" display[idea by Whitehawk Stormchaser] + * next/previous player names removed: it was confusing imho + * clean up configuration dialog GUI + * better multiplayer highscores dialog + * removed "start" button for clients + +2.1.6b (18 December 2002) [KDE 3.1 stable] + * fix stage display + +2.1.6 (6 June 2002) + * simplify start/pause button (ugly with large texts) + * arcade mode (only 6 levels) for KSirtet [idea by Douglas Smith] + * KAccel is not used anymore... + * some code cleanup and separation of source directories + +2.1.5 (16 April 2002) + * highlight level lcd when it changes + * progress bar for level [idea by Niko Boehm] + +2.1.4c (13 May 2002) + * fix crash when pause pressed during animation [report by Christian Esken] + +2.1.4b (15 April 2002) [KDE 3.0.1 stable] + * fix background color (less flicker and no more white background for + not compliant (?) window manager) [report by Niko Boehm] + * fix T piece rotation [report by Niko Boehm] + * save key shortcuts [report by Niko Boehm] + +2.1.4 (15 January 2002) + * revamped settings + * some cleanups + * fix several problems in AI code : much better now (for ksirtet) + * several bug fixes + * fix piece rotation in ksirtet and kfouleggs [many thanks to Sebastian + Boehm] + +2.1.3 (16 December 2001) + * use the dynamic library for highscores and the new API + * fix a severe bug occurring for multiple gifts (multiplayers game in ksirtet) + * reset gifts at game start (multiplayers game) + * slow down the computer player (for ksirtet) + * fix AI configuration saving + +2.1.2 (29 November 2001) + * fix pause behaviour in menu (forward port from 2.2 branch) + * use KConfigGroupSaver everywhere + * World-wide highscores for KSirtet and KFoulEggs !! + +2.1.1 (17 October 2001) + * some more reorganization + * some code revamped and simplified + * a DESIGN file ! + * progressive fall animation added to kfouleggs + * board can be made transparent :) + * "removed" lcd now displays more details (ksirtet & kfouleggs) + * highscores updated (new dialog) + * ported to Qt 3.0 (a piece of cake :) + +2.1.0b (28 November 2001) [SF 2.2 stable] + * fix pause behaviour in menu [bug reported by Erik Hill] + +2.1.0 (17 July 2001) [KDE 2.2 stable] + +2.0.10 (03 July 2001) [SF 2.2 devel] + * reorganized sources files + * fixed level increase with initial level higher than 1 [thanks to Christof + Musik] + +2.0.9 (24 May 2001) + * configuration of the initial level added [suggested by Alain Beyrand among + others ...] + * enhancement of highscores : nicer dialogs, nb of games, mean score, ... + * use of KHighscore + * use of KStdGameAction + * fix shadow display + +2.0.8 (11 April 2001) + * fix for multiplayers game (see lib changelog) + * pause when highscores requested [suggested by David Grill Watson] + * create GenericBoard class (for future extensions) + +2.0.7 (14 March 2001) + * changed highscores accelerator to CTRL + H (more standard) + * better use of session-management + +2.0.6 (1 February 2001) [KDE 2.1 stable] + * more explanations in KFouleggs score computation. + * some fix in KFoulEggs piece drawing [I was not able to see + the problem ; thanks to Tom Colgate] + +2.0.5 (13 September 2000) [KDE 2.0 stable] + * fix for change of behaviour in QCanvas (QCanvasItems need a show). + +2.0.4 (4 September 2000) + * fixed behaviour of button in highscores dialog when entering the winner + name [proposed by Lotta Inkovaara] + * fixed repaint problems of piece shadow [some flicker added though] + * keys bindings should be now saved [due to some obscure bug in + kdelibs/kaccel ; thanks to Chris] + +2.0.3 + * use of KMainWindow + interception of LayoutHint events to fix the resize + problems ... + +2.0.2 (19 June 2000) + * versioning the XMLGUI files + * removed (unstandard) title in dialogs + * block size is configurable + +2.0.1 (28 May 2000) + * fixed highscores dialog showing empty if score too small [bug reported + by David Grill Watson] + * fixed something in the XML files ... + +2.0.0 (10 May 2000) + * stupid bug fixed (off by one error in random piece generation) + * fix segfault when exiting with the close button + +1.1.16 (14 April 2000) + * AI is now configurable + * multiplayer + AI for fouleggs !! + * XMLguified + config cleaned up + * animations can be configured on/off + +1.1.15 (3 April 2000) + * nicer "light removed" animation in ksirtet + * new "bump falling piece" animation + * new "progressive falling on remove" animation for ksirtet + +1.1.14 (2 April 2000) + * KAction + * separated in two executables (ksirtet and kfouleggs) + * use of QCanvas done + * fixed a nasty bug for kfouleggs (segfault when the piece is almost at top + and the action dropDown is activated) + +1.1.13 (7 February 2000) + * found bug in piece copying/random generation + * KAboutData + * first modifications for the use of QCanvas + +1.1.12 (14 December 1999) + * FE drawings are ok (not very cute though) + * multiplayers status line completed : players names + gift leds + * T piece starting orientation modified so it can be rotated immediately + (this is of some importance for the AI player ...) + * LCD style slightly changed + * AI customization (to optimize AI ...) [uncomplete] + * "What's This" added + * multiplayers score dialog [uncomplete] + +1.1.11 (8 November 1999) + * keys were "inverted" for two-players-game. + * Foul Eggs game !! (score + multiplayers + AI still to be done). + +1.1.10 (14 August 1999) + * big change for multiplayers game (due to heavy modification of lib) + +1.1.9 (6 July 1999) + * highscore dialog is "layouted" + * layout fixes + +1.1.8 + * fixes in AI and gift mechanism. + * gift received and gift sent shower in multiplayers game. + +1.1.7 (21 March 1999) + * "shadow" and "show next" are configurable via menu. + * fix in shadow repaint. + * fix : the removed line display is now cleaned when starting a new game. + +1.1.6 (20 March 1999) + * the main board now use QT's layout engine. + * some clean-ups for future extensions. + * asynchronous opponent gift treatement (no more funnies when gray lines + arrive at unexpected time) + * shadow of the falling piece (to limit parallax errors) ... + +1.1.5 (11 March 1999) + * fixed the layout code for Qt 2.0 (Mario Weilguni ) + +1.1.4 (6 March 1999) + * you can play against the COMPUTER !!! well its brain is rather limited + but it's better than nothing :) + * changes all over the place. + +1.1.3 (6 March 1999) + * bug fix : random generation is uniform among games in multiplayers. + * bug fix : in multiplayer : dropdown state does not persist after gameover. + +1.1.2 (2 March 1999) + * big review of generic tetris code : + - three fixes : * the falling pieces are better centered. + * the next piece is shown well centered. + * at game over the next piece is effectively the + next one. + - preparation for future extensions. + +1.1.1 (28 February 1999) + * many bug fixes for multiplayer (there are still some nasty ones around) + +1.1.0 (25 February 1999) + * big revamping : the old net code is gone ... a new one is coming : + you can now play on the same computer ... the network code may (will ?) + crash : understand "highly experimental" ;) + * well it's almost ok. still some rough ends ... + +1.0.2 (18 February 1999) + * fix a focus bug (bug report by Malte Starostik) + +1.0.1 (16 February 1999) + * bug fix : no more funnies when pausing at unexpected moments [bug report by + Malte Starostik] + * keys settings were not saved [bug report by Sven Kuenzler] + * drop pieces were not giving points anymore (thought I had already + killed this one ...) + * more clean-up (ad nauseam) + +1.0.0 (19 June 1998) [KDE 1.0 stable] + * final cleanup before 1.0 ! + +0.2.12 (10 March 1998) + * some cleanups + * blink full lines + * accelerated drop for dropped down pieces + * one more time for ace players : the main score box was too small + +0.2.11 + * [Robert Williams] Added version.h and ChangeLog + * [Robert Williams] Added getCaption() + +0.2.10 + * included in the CVS tree + * KTopLevelWidget & KMenubar + +0.2.7 + * some cleanups + international support + * net fix : use 'uname' rather than 'getenv("HOSTNAME")' + +0.2.6 + * no more NULLs (for the sake of 64bits) + * a nicer highscores dialog (there should be no more truncated score + for ace players :) + +0.2.5 + * just a minor change to allow compile in Linux/Alpha + +0.2.4 + * minor changes + * "kexample 0.31" compliance + +0.2.3 + * adapted for libkdecore 0.7 + * hide/show menubar + popup menu (ala kvt) + * more compliant to the Style Guide + +0.2.2 + * use kdehelp + * use of KConfig and clean the highscore dialog + * use of kmsgbox for about :) [it is nice] + +0.2.1 + * use of KKeyCode for the management of keys + +0.2 + * many things : network game + extension for other similar game + +0.1 + * get rid of the grab stuff from the original version + * small reorganisation + * change of the key-bindings (cf help) + * [Asger] the real scoring + * [Asger] get rid of some needless stuff in GenericTetris + * highscores diff --git a/ksirtet/DESIGN b/ksirtet/DESIGN new file mode 100644 index 00000000..0cc7d3d2 --- /dev/null +++ b/ksirtet/DESIGN @@ -0,0 +1,57 @@ +'lo, time to explain how some things are designed in the generic class +of these games + +NB: You should read this file with a fixed font editor ... + +the following set of functions is implemented in the hierarchy of classes : +GenericTetris -> BaseBoard -> Board -> specific Board class for each game + +=============================================================================== +action 'Piece Drop Down' activated + | +pieceDropped() <-| + | | +oneLineDown() -> by timer -| + +=============================================================================== +if oneLineDown() make the piece touch the ground + | +pieceDropped() + | +_beforeGlue() <-| + | | +beforeGlue() -> by timer -| // here is implemented the bump effect + | (when done) +gluePiece() + | +_afterGlue() <-| + | | +afterGlue() -> by timer -| // here kfouleggs remove holes + | (when done) +needRemoving ? no -> _afterAfterRemove() + | +_beforeRemove() <-| + | | +beforeRemove() -> by timer -| // here blocks to be removed are highlighted + | (when done) +remove() + | +_afterRemove() <-| + | | +afterRemove() -> by timer -| // here is animated the fall of piece in + | // the holes leaved by removed blocks + | if needs removing again -> _beforeRemove() + | +_afterAfterRemove() ? no gift pending -> afterAfterRemove() + | +putGift() + | +_afterGift() <-| + | | +afterGift() -> by timer -| // here kfouleggs make the gift block to land + | +_afterAfterRemove() + | +afterAfterRemove() ? gameOver -> gameOver() + | +newPiece() diff --git a/ksirtet/LICENSE b/ksirtet/LICENSE new file mode 100644 index 00000000..4f0ca177 --- /dev/null +++ b/ksirtet/LICENSE @@ -0,0 +1,19 @@ +KSIRTET +------- +Copyright (c) 1995 Eirik ENG +Copyright (c) 1996-2004 Nicolas HADACEK (hadacek@kde.org) + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. diff --git a/ksirtet/Makefile.am b/ksirtet/Makefile.am new file mode 100644 index 00000000..0d3fff71 --- /dev/null +++ b/ksirtet/Makefile.am @@ -0,0 +1,4 @@ +# Please keep ksirtet in a subdir from here. +# If moving it here, then cvs complains that ksirtet is both a local exe +# and a (deleted) cvs subdir +SUBDIRS = ksirtet diff --git a/ksirtet/README b/ksirtet/README new file mode 100644 index 00000000..c73d9a16 --- /dev/null +++ b/ksirtet/README @@ -0,0 +1,18 @@ +KSIRTET : a tetris-alike game +--------------------------------- +Copyright (c) 1995 Eirik ENG +Copyright (c) 1996-2004 Nicolas HADACEK (hadacek@kde.org) +Distributed under the GNU General Public License + +KSirtet is an adaptation of the well-known Tetris game. +It provides multiplayers functionality and "artificial +intelligence" player. + +The code links to the ksirtet libraries. + + +Note: the code is in the subdir "ksirtet" due to a CVS limitation known +as the "CVS memory effect" + + +Enjoy ! diff --git a/ksirtet/TODO b/ksirtet/TODO new file mode 100644 index 00000000..112cc096 --- /dev/null +++ b/ksirtet/TODO @@ -0,0 +1,8 @@ +see also the TODO for libksirtet + + * more varied AIs (it is now quite good) + + * action for "total drop", "to left", "to right", "half turn" + + * in multiplayer, when one player changes level: all players should go to + that level too (?) diff --git a/ksirtet/configure.in.in b/ksirtet/configure.in.in new file mode 100644 index 00000000..88bff352 --- /dev/null +++ b/ksirtet/configure.in.in @@ -0,0 +1,2 @@ +AC_CHECK_HEADERS(fcntl.h sys/time.h unistd.h sys/select.h sys/filio.h) + diff --git a/ksirtet/ksirtet/Makefile.am b/ksirtet/ksirtet/Makefile.am new file mode 100644 index 00000000..2466e023 --- /dev/null +++ b/ksirtet/ksirtet/Makefile.am @@ -0,0 +1,81 @@ +INCLUDES = -I$(top_builddir)/libksirtet -I$(top_srcdir)/libksirtet -I$(top_srcdir)/libksirtet/base -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) + +KDE_CXXFLAGS = $(KDE_USE_FPIE) + +bin_PROGRAMS = ksirtet +ksirtet_LDADD = $(top_builddir)/libksirtet/common/libksirtetcommon.la +ksirtet_DEPENDENCIES = $(top_builddir)/libksirtet/common/libksirtetcommon.la +ksirtet_LDFLAGS = $(KDE_USE_PIE) $(all_libraries) $(KDE_RPATH) +ksirtet_SOURCES = piece.cpp board.cpp ai.cpp field.cpp settings.cpp main.cpp prefs.kcfgc +METASOURCES = board.moc ai.moc field.moc settings.moc main.moc + +rcdir = $(kde_datadir)/ksirtet +rc_DATA = ksirtetui.rc + +KDE_ICON = ksirtet + +xdg_apps_DATA = ksirtet.desktop +kde_kcfg_DATA = ksirtet.kcfg + +appdatadir = $(kde_datadir)/ksirtet +appdata_DATA = eventsrc + +messages: rc.cpp + $(XGETTEXT) rc.cpp $(ksirtet_SOURCES) -o $(podir)/ksirtet.pot + +check_PROGRAMS = check_score +check_score_SOURCES = check_score.cpp +check_score_LDADD = -lkdecore +check_score_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +# for system-wide highscore file +DESTBIN = $(DESTDIR)$(bindir)/$(bin_PROGRAMS) +DESTHIGHSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY) +DESTSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY)/$(bin_PROGRAMS).scores + +install-data-local: + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && echo "********************************************************" \ + && echo "" \ + && echo "This game is installed sgid \"games\" to use the" \ + && echo "system-wide highscore file (in "$(HIGHSCORE_DIRECTORY)")." \ + && echo "" \ + && echo "If the system-wide highscore file does not exist, it is" \ + && echo "created with the correct ownership and permissions. See the" \ + && echo "INSTALL file in \"kdegames/libkdegames/highscore\" for details." \ + && echo "" \ + && echo "********************************************************" \ + ) || true + +install-exec-hook: + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((chown $(highscore_user):$(highscore_group) $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((mkdir -p $(DESTHIGHSCORES) && chown $(highscore_user):$(highscore_group) $(DESTHIGHSCORES) \ + && chmod 750 $(DESTHIGHSCORES)) \ + || echo "Error: Could not create the highscore directory with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((chown $(highscore_user):$(highscore_group) $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test ${setgid} = true \ + && ((chmod 2755 $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((touch $(DESTSCORES) && chown $(highscore_user):$(highscore_group) $(DESTSCORES) \ + && chmod 0660 $(DESTSCORES)) \ + || echo "Error: Could not create system-wide highscore file with correct permissions !!" \ + )) || true + +field.o: ../../libksirtet/common/commonprefs.h ../../libksirtet/base/baseprefs.h +main.o: ../../libksirtet/common/commonprefs.h ../../libksirtet/base/baseprefs.h +prefs.o: ../../libksirtet/common/commonprefs.h ../../libksirtet/base/baseprefs.h + diff --git a/ksirtet/ksirtet/ai.cpp b/ksirtet/ksirtet/ai.cpp new file mode 100644 index 00000000..eba477d2 --- /dev/null +++ b/ksirtet/ksirtet/ai.cpp @@ -0,0 +1,25 @@ +#include "ai.h" +#include "ai.moc" + +#include + + +const AI::Data KSAI::DATA[] = { + { "OccupiedLines", I18N_NOOP("Occupied lines:"), 0, + false, nbOccupiedLines }, + { "Holes", I18N_NOOP("Number of holes:"), 0, + false, nbHoles }, + { "Spaces", I18N_NOOP("Number of spaces:"), I18N_NOOP("Number of spaces under mean height"), + false, nbSpaces }, + { "Peak2Peak", I18N_NOOP("Peak-to-peak distance:"), 0, + false, peakToPeak }, + { "MeanHeight", I18N_NOOP("Mean height:"), 0, + false, mean }, + { "FullLines", I18N_NOOP("Number of full lines:"), 0, + true, nbRemoved }, + LastData +}; + +KSAI::KSAI() + : AI(0, 200, DATA) // #### tune or configure times... +{} diff --git a/ksirtet/ksirtet/ai.h b/ksirtet/ksirtet/ai.h new file mode 100644 index 00000000..465925ef --- /dev/null +++ b/ksirtet/ksirtet/ai.h @@ -0,0 +1,17 @@ +#ifndef KS_AI_H +#define KS_AI_H + +#include "common/ai.h" + + +class KSAI : public AI +{ + Q_OBJECT + public: + KSAI(); + + private: + static const Data DATA[]; +}; + +#endif diff --git a/ksirtet/ksirtet/board.cpp b/ksirtet/ksirtet/board.cpp new file mode 100644 index 00000000..4c084f63 --- /dev/null +++ b/ksirtet/ksirtet/board.cpp @@ -0,0 +1,181 @@ +#include "board.h" +#include "board.moc" + +#include "common/misc_ui.h" +#include "common/factory.h" +#include "piece.h" + + +using namespace KGrid2D; + +KSBoard::KSBoard(bool graphic, QWidget *parent) + : Board(graphic, new GiftPool(parent), parent), + filled(matrix().height()), linesRemoved(4) +{ + init(); +} + +void KSBoard::init() +{ + addRemoved = 0; + _lastRemoved = 0; + for (uint i=0; i<4; i++) { + linesRemoved[i] = 0; + _lastRemoved += 2<(g).filled.copy(); // deep copy +} + +void KSBoard::start(const GTInitData &data) +{ + init(); + Board::start(data); +} + +void KSBoard::computeInfos() +{ + Board::computeInfos(); + + filled.fill(0); + for (uint j=0; j1 ? addRemoved-1 : 0); + addRemoved = 0; + return g; +} + +void KSBoard::setGarbageBlock(const Coord &c) +{ + Block *gb = currentPiece()->garbageBlock(); + gb->sprite()->show(); + setBlock(c, gb); +} + +bool KSBoard::_putGift(uint nb) +{ + if ( nbClearLines()==0 ) return false; + + // lift all the cases of one line + // (nbClearLine!=0 --> firstClearLine!=height + for (uint j=firstClearLine(); j>0; j--) + for (uint i=0; icbi.baseTime / (1 + stageData().speed); +} + +void KSBoard::arcadePrepare() +{ + if ( !stageData().hasPattern ) return; + + for (uint i=0; iupdate(); +} + + diff --git a/ksirtet/ksirtet/board.h b/ksirtet/ksirtet/board.h new file mode 100644 index 00000000..90b1c231 --- /dev/null +++ b/ksirtet/ksirtet/board.h @@ -0,0 +1,50 @@ +#ifndef KS_BOARD_H +#define KS_BOARD_H + +#include "common/board.h" + + +class KSBoard : public Board +{ + Q_OBJECT + public: + KSBoard(bool graphic, QWidget *parent); + void copy(const GenericTetris &); + + void start(const GTInitData &); + enum Constants { Width = 10, Nb_Stages = 6 }; + uint nbRemovedLines(uint i) const { return linesRemoved[i]; } + uint lastRemoved() const { return _lastRemoved; } + + private: + QMemArray filled; + QMemArray linesRemoved; + uint addRemoved; + uint _lastRemoved; + + struct StageData { + uint todo, speed; + bool hasPattern; + uint pattern[Width]; + }; + static const StageData STAGE_DATA[Nb_Stages]; + + void init(); + uint gift(); + bool putGift(uint nb); + bool _putGift(uint nb); + bool needRemoving(); + void remove(); + bool toBeRemoved(const KGrid2D::Coord &) const; + bool toFall(const KGrid2D::Coord &) const; + void computeInfos(); + void setGarbageBlock(const KGrid2D::Coord &); + + const StageData &stageData() const { return STAGE_DATA[arcadeStage()]; } + uint normalTime() const; + uint arcadeTodo() const { return stageData().todo; } + uint arcadeDone() const { return nbRemoved(); } + void arcadePrepare(); +}; + +#endif diff --git a/ksirtet/ksirtet/check_score.cpp b/ksirtet/ksirtet/check_score.cpp new file mode 100644 index 00000000..71cd53be --- /dev/null +++ b/ksirtet/ksirtet/check_score.cpp @@ -0,0 +1,59 @@ +#include + +#include + + +int main(int argc, char **argv) +{ + // read argument + KCmdLineArgs::init(argc, argv, "check_score", 0, 0, true); + static KCmdLineOptions options[] = + { {"+file", "Count of removed lines", 0} }; + KCmdLineArgs::addCmdLineOptions(options); + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + if ( args->count()==0 ) KCmdLineArgs::usage(); + QString s = args->arg(0); + bool ok; + uint nb = s.toUInt(&ok); + if ( !ok ) qFatal("The argument is not an unsigned integer."); + + cout << "--------------" << endl; + + // constants + const uint width = 10; + const uint height = 22; + const uint nbLinesPerLevel = 10; + const uint blocksPerPiece = 4; + + // compute level + uint level = (nb / nbLinesPerLevel) + 1; + cout << "Level : " << level << endl << endl; + + // compute max score due to drop down + uint ddScore = + ((width*height) / blocksPerPiece) * height; // board full of pieces + ddScore += + ((nb * width) / blocksPerPiece) * height; // piece for removed lines + cout << "Max Score from drop down pieces : " << ddScore << endl; + + // compute max score due to removed lines + uint score = 0; + uint nb_tetris = 0; + while ( nb>=4 ) { + nb -= 4; + level = (nb / nbLinesPerLevel) + 1; + score += 1200 * level; + nb_tetris++; + } + if ( nb==3 ) score += 300; + else if ( nb==2 ) score += 100; + else if ( nb==1 ) score += 40; + cout << "Max score from removed lines : " << score << endl; + cout << "Max count of tetris : " << nb_tetris << endl; + + cout << endl; + cout << "Max score : " << ddScore + score << endl; + + cout << "--------------" << endl; + return 0; +} diff --git a/ksirtet/ksirtet/eventsrc b/ksirtet/ksirtet/eventsrc new file mode 100644 index 00000000..1b563e72 --- /dev/null +++ b/ksirtet/ksirtet/eventsrc @@ -0,0 +1,360 @@ +[!Global!] +IconName=ksirtet +Comment=KSirtet +Comment[be]=СіртÑÑ‚Ñ€Ñ‹Ñ +Comment[bn]=কে-সরিটটে +Comment[hi]=के-सिरà¥à¤Ÿà¥‡à¤Ÿ +Comment[mk]=КСиртет +Comment[ne]=केडीई सरटेट +Comment[pl]=Sirtet (anagram) +Comment[sv]=Ksirtet +Comment[ta]=Kசரà¯à®Ÿà¯†à®Ÿà¯ +Comment[tg]=KÐ¡Ð¸Ñ€Ñ‚ÐµÑ‚Ñ€Ð¸Ñ +Comment[tr]=Tetris +Comment[zh_TW]=俄羅斯方塊 + +[removed] +Name=Line removed +Name[ar]=لقد أزيل الخط +Name[be]=Ð›Ñ–Ð½Ñ–Ñ Ð²Ñ‹Ð´Ð°Ð»ÐµÐ½Ð°Ñ +Name[bg]=Премахната е Ð»Ð¸Ð½Ð¸Ñ +Name[bn]=লাইন সরিয়ে ফেলা হয়েছে +Name[br]=Linenn lemet +Name[bs]=Uklonjena linija +Name[ca]=Línia eliminada +Name[cs]=OdstranÄ›n řádek +Name[cy]=Gwaredwyd llinell +Name[da]=Linje fjernet +Name[de]=Zeile entfernt +Name[el]=ΓÏαμμή αφαιÏέθηκε +Name[eo]=Linio forigita +Name[es]=Línea eliminada +Name[et]=Eemaldatud rida +Name[eu]=Lerroa kendu da +Name[fa]=خط حذ٠شد +Name[fi]=Rivi poistettu +Name[fr]=Ligne supprimée +Name[ga]=Líne bainte +Name[gl]=Liña eliminada +Name[he]=שורה הוסרה +Name[hi]=पंकà¥à¤¤à¤¿ हटाठ+Name[hr]=Uklonjena linija +Name[hu]=Sor eltávolítva +Name[is]=Lína fjarlægð +Name[it]=Riga rimossa +Name[ja]=線を消ã—ã¾ã—㟠+Name[km]=បន្ទាážáŸ‹â€‹ážŠáŸ‚ល​បាន​យក​ចáŸáž‰ +Name[lt]=Linija panaikinta +Name[lv]=Rinda noņemta +Name[mk]=ОтÑтранета е линија +Name[nb]=Linje fjernet +Name[nds]=Reeg wegdaan +Name[ne]=रेखा हटाइयो +Name[nl]=Regel verwijderd +Name[nn]=Linje fjerna +Name[pa]=ਸਤਰ ਹਟਾਈ +Name[pl]=Linia usuniÄ™ta +Name[pt]=Linha removida +Name[pt_BR]=Linha removida +Name[ro]=Linie eliminată +Name[ru]=Ð›Ð¸Ð½Ð¸Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð° +Name[se]=Linnjá lea eretváldon +Name[sk]=ÄŒiara odstránená +Name[sl]=Vrstica odstranjena +Name[sr]=Уклоњена линија +Name[sr@Latn]=Uklonjena linija +Name[sv]=Rad borttagen +Name[ta]=கோட௠நீகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Name[tg]=Сатр хориҷ карда шуд +Name[tr]=Silinen Çizgiler +Name[uk]=Ð›Ñ–Ð½Ñ–Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð° +Name[zh_CN]=消去的行数 +Name[zh_TW]=消去的行 +Comment=Line removed +Comment[ar]=لقد أزيل الخط +Comment[be]=Ð›Ñ–Ð½Ñ–Ñ Ð²Ñ‹Ð´Ð°Ð»ÐµÐ½Ð°Ñ +Comment[bg]=Премахната е Ð»Ð¸Ð½Ð¸Ñ +Comment[bn]=লাইন সরিয়ে ফেলা হয়েছে +Comment[br]=Linenn lemet +Comment[bs]=Uklonjena linija +Comment[ca]=Línia eliminada +Comment[cs]=OdstranÄ›n řádek +Comment[cy]=Gwaredwyd llinell +Comment[da]=Linje fjernet +Comment[de]=Zeile entfernt +Comment[el]=ΓÏαμμή αφαιÏέθηκε +Comment[eo]=Linio forigita +Comment[es]=Línea eliminada +Comment[et]=Eemaldatud rida +Comment[eu]=Lerroa kendu da +Comment[fa]=خط حذ٠شد +Comment[fi]=Rivi poistettu +Comment[fr]=Ligne supprimée +Comment[ga]=Líne bainte +Comment[gl]=Liña eliminada +Comment[he]=שורה הוסרה +Comment[hi]=पंकà¥à¤¤à¤¿ हटाठ+Comment[hr]=Uklonjena linija +Comment[hu]=Sor eltávolítva +Comment[is]=Lína fjarlægð +Comment[it]=Linea rimossa +Comment[ja]=線を消ã—ã¾ã—㟠+Comment[km]=បន្ទាážáŸ‹â€‹ážŠáŸ‚ល​បាន​យក​ចáŸáž‰ +Comment[lt]=Linija panaikinta +Comment[lv]=Rinda ir noņemta +Comment[mk]=ОтÑтранета е линија +Comment[nb]=Linje fjernet +Comment[nds]=Reeg wegdaan +Comment[ne]=रेखा हटाइयो +Comment[nl]=Regel verwijderd +Comment[nn]=Linje fjerna +Comment[pa]=ਸਤਰ ਹਟਾਈ +Comment[pl]=Linia usuniÄ™ta +Comment[pt]=Linha removida +Comment[pt_BR]=Linha removida +Comment[ro]=Linie eliminată +Comment[ru]=Ð›Ð¸Ð½Ð¸Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð° +Comment[se]=Linnjá lea eretváldon +Comment[sk]=ÄŒiara odstránená +Comment[sl]=Odstranjena vrstica +Comment[sr]=Уклоњена линија +Comment[sr@Latn]=Uklonjena linija +Comment[sv]=Rad borttagen +Comment[ta]=கோட௠நீகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=Сатр хориҷ карда шуд +Comment[tr]=Silinen çizgiler +Comment[uk]=Ð›Ñ–Ð½Ñ–Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð° +Comment[zh_CN]=消去的行数 +Comment[zh_TW]=消去的行 +default_presentation=0 + +[game over] +Name=Game Over +Name[af]=Speletjie Bo +Name[ar]=اللعبة انتهت +Name[az]=Oyun Qurtardı +Name[be]=Канец гульні +Name[bg]=Край на играта +Name[bn]=খেল খতম +Name[br]=Echu an abadenn +Name[bs]=Igra zavrÅ¡ena +Name[ca]=Fi de la partida +Name[cs]=Konec hry +Name[cy]=Gêm Drosodd +Name[da]=Spillet forbi +Name[de]=Spiel beendet +Name[el]=Τέλος Ï€Î±Î¹Ï‡Î½Î¹Î´Î¹Î¿Ï +Name[eo]=Ludo finita +Name[es]=Fin de la partida +Name[et]=Mäng läbi +Name[eu]=Jokoa amaitu da +Name[fa]=بازی تمام شد +Name[fi]=Peli loppu +Name[fr]=Fin de la partie +Name[ga]=Cluiche Thart +Name[gl]=Fin do Xogo +Name[he]=×¡×™×•× ×ž×©×—×§ +Name[hi]=खेल ख़तà¥à¤® +Name[hr]=Igra je zavrÅ¡ena +Name[hu]=Vége a játéknak +Name[id]=permainan berakhir +Name[is]=Leik lokið +Name[it]=Gioco terminato +Name[ja]=ゲームオーãƒãƒ¼ +Name[km]=ល្បែង​ចប់ +Name[ko]=SameGame +Name[lt]=Žaidimas baigtas +Name[lv]=SpÄ“les beigas +Name[mk]=Играта заврши +Name[mt]=Il-Logħba SpiÄ‹Ä‹at +Name[nb]=Spillet er slutt +Name[nds]=Speel vörbi +Name[ne]=खेल समापà¥à¤¤ +Name[nl]=Spel is afgelopen +Name[nn]=Spelet er slutt +Name[nso]=Papadi e Fedile +Name[pa]=ਖੇਡ ਖਤਮ +Name[pl]=Koniec gry +Name[pt]=Fim do jogo +Name[pt_BR]=Fim do jogo +Name[ro]=Joc terminat +Name[ru]=Конец игры +Name[se]=Speallu nogai +Name[sk]=Koniec hry +Name[sl]=Konec igre +Name[sr]=Крај игре +Name[sr@Latn]=Kraj igre +Name[sv]=Spelet är slut +Name[ta]=ஆடà¯à®Ÿà®®à¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯ +Name[tg]=Бозӣ ба итмом раÑид +Name[th]=จบเà¸à¸¡ +Name[tr]=Oyun Bitti +Name[uk]=Гру завершено +Name[uz]=OÊ»yin tugadi +Name[uz@cyrillic]=Ўйин тугади +Name[ven]=Muthambo wo Fhela +Name[vi]=Game kết thúc +Name[wa]=Li djeu est houte +Name[xh]=Uphelile Umdlalo +Name[zh_CN]=游æˆç»“æŸ +Name[zh_TW]=éŠæˆ²çµæŸ +Name[zu]=Umdlalo uphelile +Comment=Game over +Comment[be]=Канец гульні +Comment[bg]=Край на играта +Comment[bn]=খেল খতম +Comment[br]=Echu an abadenn +Comment[bs]=Kraj igre +Comment[ca]=Fi de la partida +Comment[cs]=Hra skonÄena +Comment[cy]=Gêm drosodd +Comment[da]=Spil forbi +Comment[de]=Das Spiel ist vorbei +Comment[el]=Τέλος Ï€Î±Î¹Ï‡Î½Î¹Î´Î¹Î¿Ï +Comment[eo]=Ludo finita +Comment[es]=Fin de la partida +Comment[et]=Mäng läbi +Comment[eu]=Jokoa amaitu da +Comment[fa]=بازی تمام شد +Comment[fi]=Peli loppui +Comment[fr]=Fin de la partie +Comment[ga]=Cluiche thart +Comment[he]=×¡×™×•× ×ž×©×—×§ +Comment[hr]=Kraj igre +Comment[hu]=Vége a játéknak +Comment[is]=Leik lokið +Comment[it]=Gioco terminato +Comment[ja]=ゲームオーãƒãƒ¼ +Comment[km]=ល្បែង​ចប់ +Comment[lt]=Žaidimas baigtas +Comment[lv]=SpÄ“le beigusies +Comment[mk]=Играта заврши +Comment[nb]=Spillet er slutt +Comment[nds]=Speel vörbi +Comment[ne]=खेल समापà¥à¤¤ +Comment[nl]=Het spel is afgelopen +Comment[nn]=Spelet er slutt +Comment[pa]=ਖੇਡ ਖਤਮ +Comment[pl]=Koniec gry +Comment[pt]=Fim do jogo +Comment[pt_BR]=Fim do Jogo +Comment[ru]=Конец игры +Comment[se]=Speallu nogai +Comment[sk]=Koniec hry +Comment[sl]=Konec igre +Comment[sr]=Крај игре +Comment[sr@Latn]=Kraj igre +Comment[sv]=Spelet slut +Comment[ta]=ஆடà¯à®Ÿà®®à¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯ +Comment[tr]=Oyun bitti +Comment[uk]=Кінець гри +Comment[wa]=Li djeu est houte +Comment[zh_CN]=游æˆç»“æŸ +Comment[zh_TW]=éŠæˆ²çµæŸ +default_presentation=0 + +[glued] +Name=Piece glued +Name[ar]=لقد ألصقت القطعة +Name[be]=Кавалачак зліпнуўÑÑ +Name[bg]=Залепени Ñа парчета +Name[bn]=গà§à¦Ÿà¦¿ আটকে আছে +Name[bs]=Komad spojen +Name[ca]=Peça enganxada +Name[cs]=Blok spojen +Name[cy]=Darn wedi ei gludo +Name[da]=Brik klæbet fast +Name[de]=Zusammengefügte Klötzchen +Name[el]=Το κομμάτι κολλήθηκε +Name[eo]=Peco gluita +Name[es]=Ficha pegada +Name[et]=Kleepunud klots +Name[eu]=Pieza itsatsita +Name[fa]=قطعه چسبید +Name[fi]=Pala liimattu +Name[fr]=Morceau collé +Name[gl]=Peza pegada +Name[he]=חתיכה מודבקת +Name[hi]=टà¥à¤•à¤¡à¤¼à¥‡ चिपकाठ+Name[hr]=Zalijepljeni dio +Name[hu]=Elem lekötve +Name[is]=Hlutur límdur +Name[it]=Pezzo incollato +Name[ja]=ピースをãã£ã¤ã‘㟠+Name[km]=បំណែក​បាន​បិទ +Name[lt]=Dalis įklijuota +Name[lv]=PielÄ«mÄ“ts gabals +Name[mk]=Залепено е парче +Name[nb]=Brikke festet +Name[nds]=Steen tosamenbackt +Name[ne]=टाà¤à¤¸à¤¿à¤à¤•à¥‹ टà¥à¤•à¥à¤°à¤¾ +Name[nl]=Samengevoegde stukken +Name[nn]=Brikke festa +Name[pl]=Sklejenie elementów +Name[pt]=Peça colada +Name[pt_BR]=Peça colada +Name[ru]=Слипание Ñиц +Name[se]=Bihttá lea liibmejuvvon +Name[sk]=Kus zlepený +Name[sl]=Kos prilepljen +Name[sr]=Залепљено парче +Name[sr@Latn]=Zalepljeno parÄe +Name[sv]=Pjäs fastsatt +Name[ta]= தà¯à®£à¯à®Ÿà¯ கிலூயிட௠+Name[tg]=ҚиÑмҳои чаÑбанда +Name[tr]=Yapıştırılan parçalar +Name[uk]=Фігура приклеєна +Name[zh_CN]=粘连的å—æ•° +Name[zh_TW]=é»è‘—çš„å°ç¢Žç‰‡ +Comment=Piece glued +Comment[ar]=لقد ألصقت القطعة +Comment[be]=Кавалачак зліпнуўÑÑ +Comment[bg]=Залепени Ñа парчета +Comment[bn]=গà§à¦Ÿà¦¿ আটকে আছে +Comment[bs]=Komad spojen +Comment[ca]=Peça enganxada +Comment[cs]=Blok spojen +Comment[cy]=Darn wedi ei gludo +Comment[da]=Brik klæbet fast +Comment[de]=Zusammengefügte Klötzchen +Comment[el]=Το κομμάτι κολλήθηκε +Comment[eo]=Peco gluita +Comment[es]=Ficha pegada +Comment[et]=Paikapandud klots +Comment[eu]=Pieza itsatsita +Comment[fa]=قطعه چسبید +Comment[fi]=Pala liimattu +Comment[fr]=Morceau collé +Comment[gl]=Peza pegada +Comment[he]=חתיכה מודבקת +Comment[hi]=टà¥à¤•à¤¡à¤¼à¥‡ चिपकाठ+Comment[hr]=Zalijepljeni dio +Comment[hu]=Elem lekötve +Comment[is]=Hlutur límdur +Comment[it]=Pezzo incollato +Comment[ja]=ピースをãã£ã¤ã‘㟠+Comment[km]=បំណែក​បាន​បិទ +Comment[lt]=Dalis įklijuota +Comment[lv]=Gabals ir pielÄ«mÄ“ts +Comment[mk]=Залепено е парче +Comment[nb]=Brikke festet +Comment[nds]=Steen tosamenbackt +Comment[ne]=टाà¤à¤¸à¤¿à¤à¤•à¥‹ टà¥à¤•à¥à¤°à¤¾ +Comment[nl]=Samengevoegde stukken +Comment[nn]=Brikke festa +Comment[pl]=Sklejenie elementu +Comment[pt]=Peça colada +Comment[pt_BR]=Peça colada +Comment[ru]=Слипание Ñиц +Comment[se]=Bihttá lea liibmejuvvon +Comment[sk]=Kus zlepený +Comment[sl]=Kos prilepljen +Comment[sr]=Залепљено парче +Comment[sr@Latn]=Zalepljeno parÄe +Comment[sv]=Pjäs fastsatt +Comment[ta]=தà¯à®£à¯à®Ÿà¯ ஒடà¯à®Ÿà®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=ҚиÑмҳои чаÑбанда +Comment[tr]=Parça yapıştırıldı +Comment[uk]=Фігура приклеєна +Comment[zh_CN]=粘连的å—æ•° +default_presentation=0 diff --git a/ksirtet/ksirtet/field.cpp b/ksirtet/ksirtet/field.cpp new file mode 100644 index 00000000..e3384a9e --- /dev/null +++ b/ksirtet/ksirtet/field.cpp @@ -0,0 +1,61 @@ +#include "field.h" +#include "field.moc" + +#include + +#include +#include + +#include "board.h" +#include "prefs.h" +#include "piece.h" + +//----------------------------------------------------------------------------- +KSField::KSField(QWidget *parent) + : Field(parent) +{ + const Board *b = static_cast(board); + QWhatsThis::add(b->giftPool(), i18n("Indicate the number of garbage lines you received from your opponent.")); +} + +void KSField::removedUpdated() +{ + KGameLCD *lcd = static_cast(removedList->lcd(0)); + lcd->displayInt(board->nbRemoved()); + if ( board->nbRemoved() ) lcd->highlight(); + if ( Prefs::showDetailedRemoved() ) { + const KSBoard *ksb = static_cast(board); + for (uint i=0; i<4; i++) { + if ( !(ksb->lastRemoved() & (2<(removedList->lcd(i+1)); + lcd->displayInt(ksb->nbRemovedLines(i)); + if ( ksb->nbRemovedLines(i) ) lcd->highlight(); + } + } +} + +void KSField::settingsChanged() +{ + Field::settingsChanged(); + bool b = Prefs::oldRotationStyle(); + static_cast(Piece::info()).setOldRotationStyle(b); + + removedList->clear(); + QWhatsThis::remove(removedList); + KGameLCD *lcd = new KGameLCD(5, removedList); + QString s = (Prefs::showDetailedRemoved() ? i18n("Total:") : QString::null); + removedList->append(s, lcd); + lcd->displayInt( board->nbRemoved() ); + lcd->show(); + + if ( Prefs::showDetailedRemoved() ) { + for (uint i=0; i<4; i++) { + KGameLCD *lcd = new KGameLCD(5, removedList); + QString s = i18n("1 Line:", "%n Lines:", i+1); + removedList->append(s, lcd); + uint nb = static_cast(board)->nbRemovedLines(i); + lcd->displayInt(nb); + lcd->show(); + } + } +} diff --git a/ksirtet/ksirtet/field.h b/ksirtet/ksirtet/field.h new file mode 100644 index 00000000..4af498e9 --- /dev/null +++ b/ksirtet/ksirtet/field.h @@ -0,0 +1,19 @@ +#ifndef KS_FIELD_H +#define KS_FIELD_H + +#include "common/field.h" +#include "common/misc_ui.h" + + +class KSField : public Field +{ + Q_OBJECT + public: + KSField(QWidget *parent); + + private slots: + virtual void removedUpdated(); + virtual void settingsChanged(); +}; + +#endif diff --git a/ksirtet/ksirtet/hi128-app-ksirtet.png b/ksirtet/ksirtet/hi128-app-ksirtet.png new file mode 100644 index 00000000..4e96420b Binary files /dev/null and b/ksirtet/ksirtet/hi128-app-ksirtet.png differ diff --git a/ksirtet/ksirtet/hi16-app-ksirtet.png b/ksirtet/ksirtet/hi16-app-ksirtet.png new file mode 100644 index 00000000..ac3c6d33 Binary files /dev/null and b/ksirtet/ksirtet/hi16-app-ksirtet.png differ diff --git a/ksirtet/ksirtet/hi22-app-ksirtet.png b/ksirtet/ksirtet/hi22-app-ksirtet.png new file mode 100644 index 00000000..455fa6a8 Binary files /dev/null and b/ksirtet/ksirtet/hi22-app-ksirtet.png differ diff --git a/ksirtet/ksirtet/hi32-app-ksirtet.png b/ksirtet/ksirtet/hi32-app-ksirtet.png new file mode 100644 index 00000000..0ef40175 Binary files /dev/null and b/ksirtet/ksirtet/hi32-app-ksirtet.png differ diff --git a/ksirtet/ksirtet/hi48-app-ksirtet.png b/ksirtet/ksirtet/hi48-app-ksirtet.png new file mode 100644 index 00000000..481d8d61 Binary files /dev/null and b/ksirtet/ksirtet/hi48-app-ksirtet.png differ diff --git a/ksirtet/ksirtet/hi64-app-ksirtet.png b/ksirtet/ksirtet/hi64-app-ksirtet.png new file mode 100644 index 00000000..84d016fb Binary files /dev/null and b/ksirtet/ksirtet/hi64-app-ksirtet.png differ diff --git a/ksirtet/ksirtet/ksirtet.desktop b/ksirtet/ksirtet/ksirtet.desktop new file mode 100644 index 00000000..bfd9ef35 --- /dev/null +++ b/ksirtet/ksirtet/ksirtet.desktop @@ -0,0 +1,68 @@ +[Desktop Entry] +Name=KSirtet +Name[af]=Ksirtet +Name[be]=СіртÑÑ‚Ñ€Ñ‹Ñ +Name[bn]=কে-সরিটটে +Name[hi]=के-सिरà¥à¤Ÿà¥‡à¤Ÿ +Name[ne]=केडीई सरटेट +Name[pl]=Sirtet (anagram) +Name[sv]=Ksirtet +Name[ta]=Kசரà¯à®Ÿà¯†à®Ÿà¯ +Name[tg]=KÐ¡Ð¸Ñ€Ñ‚ÐµÑ‚Ñ€Ð¸Ñ +Name[zh_TW]=KSirtet ä¿„ç¾…æ–¯ +Name[zu]=I-KSirtet +Icon=ksirtet +Exec=ksirtet -caption "%c" %i %m +Type=Application +DocPath=ksirtet/index.html +GenericName=Tetris Game +GenericName[be]=ТÑÑ‚Ñ€Ñ‹Ñ +GenericName[bg]=Ð¢ÐµÑ‚Ñ€Ð¸Ñ +GenericName[bn]=টেটà§à¦°à¦¿à¦¸ খেলা +GenericName[bs]=Igra Tetrisa +GenericName[ca]=Joc Tetris +GenericName[cs]=Hra Tetris +GenericName[cy]=Gêm Tetris +GenericName[da]=Tetris spil +GenericName[de]=Tetris Spiel +GenericName[el]=Παιχνίδι Tetris +GenericName[eo]="Tetris"-ludo +GenericName[es]=Juego de Tetris +GenericName[et]=Tetris +GenericName[eu]=Tetris jokoa +GenericName[fa]=بازی Tetris +GenericName[fi]=Tetris +GenericName[fr]=Jeu de Tetris +GenericName[ga]=Cluiche Tetris +GenericName[he]=משחק טטריס +GenericName[hr]=Tetris +GenericName[hu]=Tetrisz +GenericName[is]=Tetrisleikur +GenericName[it]=Gioco del Tetris +GenericName[ja]=テトリス +GenericName[km]=ល្បែង Tetris +GenericName[lt]=Tetrio žaidimas +GenericName[lv]=Tetris +GenericName[mk]=Игра Ð¢ÐµÑ‚Ñ€Ð¸Ñ +GenericName[nb]=Tetrisspill +GenericName[nds]=Tetris-Speel +GenericName[ne]=टेटà¥à¤°à¤¿à¤¸ खेल +GenericName[nl]=Tetris-spel +GenericName[nn]=Tetrisspel +GenericName[pl]=Tetris +GenericName[pt]=Jogo de Tetris +GenericName[pt_BR]=Jogo de Tetris +GenericName[ru]=Ð¡Ð¸Ñ€Ñ‚ÐµÑ‚Ñ€Ð¸Ñ +GenericName[se]=Tetrisspeallu +GenericName[sk]=Hra Tetris +GenericName[sl]=Igra Tetrisa +GenericName[sr]=Игра Tetris-а +GenericName[sr@Latn]=Igra Tetris-a +GenericName[sv]=Tetrisspel +GenericName[ta]=டெடà¯à®°à®¿à®¸à¯ விளையாடà¯à®Ÿà¯ +GenericName[uk]=Ð¢ÐµÑ‚Ñ€Ñ–Ñ +GenericName[zh_CN]=ä¿„ç½—æ–¯æ–¹å— +GenericName[zh_TW]=俄羅斯方塊éŠæˆ² +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;ArcadeGame; diff --git a/ksirtet/ksirtet/ksirtet.kcfg b/ksirtet/ksirtet/ksirtet.kcfg new file mode 100644 index 00000000..d4053758 --- /dev/null +++ b/ksirtet/ksirtet/ksirtet.kcfg @@ -0,0 +1,55 @@ + + + + + + false + + + + + + -2.0 + -100.0 + 0.0 + + + + -4.0 + -100.0 + 0.0 + + + + -2.0 + -100.0 + 0.0 + + + + -2.0 + -100.0 + 0.0 + + + + 0.0 + -100.0 + 0.0 + + + + 50.0 + -0.0 + 100.0 + + + 1 + 1 + 4 + + + diff --git a/ksirtet/ksirtet/ksirtetui.rc b/ksirtet/ksirtet/ksirtetui.rc new file mode 100644 index 00000000..83a6386d --- /dev/null +++ b/ksirtet/ksirtet/ksirtetui.rc @@ -0,0 +1,29 @@ + + + + + + &Mode + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ksirtet/ksirtet/main.cpp b/ksirtet/ksirtet/main.cpp new file mode 100644 index 00000000..a186038e --- /dev/null +++ b/ksirtet/ksirtet/main.cpp @@ -0,0 +1,91 @@ +#include "main.h" +#include "main.moc" + +#include +#include +#include + +#include "common/inter.h" +#include "common/highscores.h" +#include "prefs.h" +#include "piece.h" + + +//----------------------------------------------------------------------------- +const MPGameInfo MP_GAME_INFO = { + "006", // multiplayer id (increase when incompatible changes are made) + 4, // max nb local games + 500, // interval + true, // IA allowed + 0, 0 // no setting slots +}; + +const MainData MAIN_DATA = { + "ksirtet", + I18N_NOOP("KSirtet"), + I18N_NOOP("KSirtet is an adaptation of the well-known Tetris game"), + "http://ksirtet.sourceforge.net/", + I18N_NOOP("Removed Lines"), + "2.1.11b", + "2.1.11b (1 September 2005)" +}; + +const uint HISTOGRAM_SIZE = 8; +const uint HISTOGRAM[HISTOGRAM_SIZE] = { + 1, 5000, 10000, 20000, 50000, 100000, 200000, 300000 +}; + +const BaseBoardInfo BASE_BOARD_INFO = { + KSBoard::Width, 22, // width - height + true, // with pieces + + 150, // before remove time + 10, // after remove time + 3, // nb toggles + 5, // nb partial fall stages + + KSBoard::Nb_Stages, // nb arcade stages + + HISTOGRAM, HISTOGRAM_SIZE, false, // score is not bound +}; + +const CommonBoardInfo COMMON_BOARD_INFO = { + 1000, // base time + 20, // drop down time + 10, // before glue time + 10, // after glue time + 10, // after gift time + 3, // nb bump stages + 10, // nb removed to level + 3, 3, 800, 2000 // nb leds, nb max to send, shower timeout, pool timeout +}; + +KSFactory::KSFactory() + : CommonFactory(MAIN_DATA, BASE_BOARD_INFO, COMMON_BOARD_INFO) +{} + +BaseInterface *KSFactory::createInterface(QWidget *parent) +{ + return new Interface(MP_GAME_INFO, parent); +} + +//----------------------------------------------------------------------------- +int main(int argc, char **argv) +{ + KHighscore::init(MAIN_DATA.appName); + KSFactory ksf; + ksf.init(argc, argv); + + KSPieceInfo pieceInfo; + CommonHighscores highscores; + + (void) Prefs::self(); // Create preferences + + if ( kapp->isRestored() ) RESTORE(KSMainWindow) + else { + KSMainWindow *mw = new KSMainWindow; + kapp->setMainWidget(mw); + mw->show(); + } + return kapp->exec(); +} diff --git a/ksirtet/ksirtet/main.h b/ksirtet/ksirtet/main.h new file mode 100644 index 00000000..67fe3619 --- /dev/null +++ b/ksirtet/ksirtet/main.h @@ -0,0 +1,36 @@ +#ifndef KS_MAIN_H +#define KS_MAIN_H + +#include "common/main.h" +#include "common/factory.h" +#include "ai.h" +#include "settings.h" +#include "board.h" +#include "field.h" + + +//----------------------------------------------------------------------------- +class KSFactory : public CommonFactory +{ + public: + KSFactory(); + + protected: + virtual BaseBoard *createBoard(bool graphic, QWidget *parent) + { return new KSBoard(graphic, parent); } + virtual BaseField *createField(QWidget *parent) + { return new KSField(parent); } + virtual BaseInterface *createInterface(QWidget *parent); + virtual AI *createAI() { return new KSAI; } + virtual QWidget *createGameConfig() { return new KSGameConfig; } +}; + +//----------------------------------------------------------------------------- +class KSMainWindow : public MainWindow +{ + Q_OBJECT + public: + KSMainWindow() { init(); } +}; + +#endif diff --git a/ksirtet/ksirtet/piece.cpp b/ksirtet/ksirtet/piece.cpp new file mode 100644 index 00000000..67ba2635 --- /dev/null +++ b/ksirtet/ksirtet/piece.cpp @@ -0,0 +1,82 @@ +#include "piece.h" + +#include +#include + + +const KSPieceInfo::Form KSPieceInfo::FORMS[NB_FORMS] = { + { {{-1, -1, 0, 0}, {-1, 0, 0, 1}, {-1, -1, 0, 0}, {-1, 0, 0, 1}}, + {{ 1, 0, 0, -1}, {-1, -1, 0, 0}, { 1, 0, 0, -1}, {-1, -1, 0, 0}}, + {{ 0, 0, -1, -1}, { 1, 0, 0, -1}, { 0, 0, 1, 1}, {-1, 0, 0, 1}}, + {{-1, 0, 0, 1}, { 0, 0, -1, -1}, { 1, 0, 0, -1}, { 0, 0, 1, 1}}, + 2 }, // broken line Z + { {{-1, -1, 0, 0}, {-1, 0, 0, 1}, {-1, -1, 0, 0}, {-1, 0, 0, 1}}, + {{-1, 0, 0, 1}, { 0, 0, -1, -1}, {-1, 0, 0, 1}, { 0, 0, -1, -1}}, + {{ 0, 0, 1, 1}, { 1, 0, 0, -1}, { 0, 0, -1, -1}, {-1, 0, 0, 1}}, + {{-1, 0, 0, 1}, { 0, 0, 1, 1}, { 1, 0, 0, -1}, { 0, 0, -1, -1}}, + 2 }, // broken line S + { {{ 0, 0, 0, 0}, {-1, 0, 1, 2}, { 0, 0, 0, 0}, {-1, 0, 1, 2}}, + {{-1, 0, 1, 2}, { 0, 0, 0, 0}, {-1, 0, 1, 2}, { 0, 0, 0, 0}}, + {{ 0, 0, 0, 0}, { 1, 0, -1, -2}, { 0, 0, 0, 0}, {-1, 0, 1, 2}}, + {{-1, 0, 1, 2}, { 0, 0, 0, 0}, { 1, 0, -1, -2}, { 0, 0, 0, 0}}, + 2 }, // line + { {{ 0, 0, 1, 0}, {-1, 1, 0, 0}, { 0, 0, -1, 0}, { 1, -1, 0, 0}}, + {{ 1, -1, 0, 0}, { 0, 0, 1, 0}, {-1, 1, 0, 0}, { 0, 0, -1, 0}}, + {{-1, 0, 1, 0}, { 0, 0, 0, 1}, { 1, 0, -1, 0}, { 0, 0, 0, -1}}, + {{ 0, 0, 0, -1}, {-1, 0, 1, 0}, { 0, 0, 0, 1}, { 1, 0, -1, 0}}, + 4 }, // T + { {{-1, 0, -1, 0}, {-1, 0, -1, 0}, {-1, 0, -1, 0}, {-1, 0, -1, 0}}, + {{ 0, 0, -1, -1}, { 0, 0, -1, -1}, { 0, 0, -1, -1}, { 0, 0, -1, -1}}, + {{-1, 0, -1, 0}, { 0, 0, -1, -1}, { 1, 0, 1, 0}, { 0, 0, 1, 1}}, + {{ 0, 0, 1, 1}, {-1, 0, -1, 0}, { 0, 0, -1, -1}, { 1, 0, 1, 0}}, + 1 }, // square + { {{ 1, 1, 1, 0}, {-1, 0, 1, 1}, {-1, -1, -1, 0}, { 1, 0, -1, -1}}, + {{ 1, 0, -1, -1}, { 1, 1, 1, 0}, {-1, 0, 1, 1}, {-1, -1, -1, 0}}, + {{-1, 0, 0, 0}, { 1, 1, 0, -1}, { 1, 0, 0, 0}, {-1, -1, 0, 1}}, + {{-1, -1, 0, 1}, {-1, 0, 0, 0}, { 1, 1, 0, -1}, { 1, 0, 0, 0}}, + 4 }, // backward L + { {{-1, -1, -1, 0}, {-1, 0, 1, 1}, { 1, 1, 1, 0}, { 1, 0, -1, -1}}, + {{ 1, 0, -1, -1}, {-1, -1, -1, 0}, {-1, 0, 1, 1}, { 1, 1, 1, 0}}, + {{ 1, 0, 0, 0}, { 1, 1, 0, -1}, {-1, 0, 0, 0}, {-1, -1, 0, 1}}, + {{-1, -1, 0, 1}, { 1, 0, 0, 0}, { 1, 1, 0, -1}, {-1, 0, 0, 0}}, + 4 } // L +}; + +const char *KSPieceInfo::COLOR_LABELS[NB_FORMS+1] = { + I18N_NOOP("Z piece color:"), I18N_NOOP("S piece color:"), + I18N_NOOP("I piece color:"), I18N_NOOP("T piece color:"), + I18N_NOOP("Square color:"), I18N_NOOP("Mirrored L piece color:"), + I18N_NOOP("L piece color:"), I18N_NOOP("Garbage block color:") +}; + +const char *KSPieceInfo::DEFAULT_COLORS[NB_FORMS+1] = { + "#C86464", "#64C864", "#6464C8", "#C8C864", "#C864C8","#64C8C8", "#DAAA00", + "#C8C8C8" +}; + +QColor KSPieceInfo::defaultColor(uint i) const +{ + if ( i>=nbColors() ) return QColor(); + return QColor(DEFAULT_COLORS[i]); +} + +void KSPieceInfo::draw(QPixmap *pixmap, uint blockType, uint, + bool lighted) const +{ + QColor col = color(blockType); + if (lighted) col = col.light(); + pixmap->fill(col); + + QPainter p(pixmap); + QRect r = pixmap->rect(); + + p.setPen( col.light() ); + p.moveTo(r.bottomLeft()); + p.lineTo(r.topLeft()); + p.lineTo(r.topRight()); + + p.setPen( col.dark() ); + p.moveTo(r.topRight() + QPoint(0,1)); + p.lineTo(r.bottomRight()); + p.lineTo(r.bottomLeft() + QPoint(1,0)); +} diff --git a/ksirtet/ksirtet/piece.h b/ksirtet/ksirtet/piece.h new file mode 100644 index 00000000..25d866c4 --- /dev/null +++ b/ksirtet/ksirtet/piece.h @@ -0,0 +1,63 @@ +#ifndef KS_PIECE_H +#define KS_PIECE_H + +#include +#include "base/piece.h" + + +class KSPieceInfo : public GPieceInfo +{ + public: + KSPieceInfo() : _oldRotationStyle(false) {} + + void setOldRotationStyle(bool oldStyle) { _oldRotationStyle = oldStyle; } + + virtual uint nbBlocks() const { return NB_BLOCKS; } + virtual uint nbForms() const { return NB_FORMS; } + virtual uint nbTypes() const { return NB_FORMS; } + + virtual const int *i(uint form, uint rot) const { + return (_oldRotationStyle ? FORMS[form].oi[rot] : FORMS[form].i[rot]); + } + virtual const int *j(uint form, uint rot) const { + return (_oldRotationStyle ? FORMS[form].oj[rot] : FORMS[form].j[rot]); + } + virtual uint value(uint type, uint) const { return type; } + virtual uint form(uint type) const { return type; } + virtual uint nbConfigurations(uint type) const { return FORMS[type].nbConfigs; } + + virtual uint nbNormalBlockTypes() const { return NB_FORMS; } + virtual uint nbGarbageBlockTypes() const { return 1; } + virtual uint nbBlockModes() const { return 1; } + + virtual uint nbColors() const { return NB_FORMS + 1; } + virtual QString colorLabel(uint i) const { return i18n(COLOR_LABELS[i]); } + virtual QColor defaultColor(uint i) const; + + private: + virtual void draw(QPixmap *, uint blockType, uint blockMode, + bool lighted) const; + + private: + bool _oldRotationStyle; + + enum { NB_BLOCKS = 4, + NB_FORMS = 7 }; + + struct Form { + int i[4][NB_BLOCKS]; // new rotation style + int j[4][NB_BLOCKS]; + int oi[4][NB_BLOCKS]; // old rotation style + int oj[4][NB_BLOCKS]; + uint nbConfigs; // number of unique configs + }; + static const Form FORMS[NB_FORMS]; + + static const char *COLOR_LABELS[NB_FORMS+1]; + static const char *DEFAULT_COLORS[NB_FORMS+1]; +}; + +#endif + + + diff --git a/ksirtet/ksirtet/prefs.kcfgc b/ksirtet/ksirtet/prefs.kcfgc new file mode 100644 index 00000000..1201abb3 --- /dev/null +++ b/ksirtet/ksirtet/prefs.kcfgc @@ -0,0 +1,9 @@ +# Code generation options for kconfig_compiler +File=ksirtet.kcfg +IncludeFiles=common/commonprefs.h +Inherits=CommonPrefs +ClassName=Prefs +Singleton=true +#Mutators=true +#CustomAdditions=true +#Mutators=true diff --git a/ksirtet/ksirtet/settings.cpp b/ksirtet/ksirtet/settings.cpp new file mode 100644 index 00000000..96669e78 --- /dev/null +++ b/ksirtet/ksirtet/settings.cpp @@ -0,0 +1,17 @@ +#include "settings.h" +#include "settings.moc" + +#include +#include + +#include + + +KSGameConfig::KSGameConfig() +{ + int row = _grid->numRows(); + int col = _grid->numCols(); + + QCheckBox *cb = new QCheckBox(i18n("Old rotation style"), this, "kcfg_OldRotationStyle"); + _grid->addMultiCellWidget(cb, row, row, 0, col-1); +} diff --git a/ksirtet/ksirtet/settings.h b/ksirtet/ksirtet/settings.h new file mode 100644 index 00000000..7e945db6 --- /dev/null +++ b/ksirtet/ksirtet/settings.h @@ -0,0 +1,14 @@ +#ifndef KS_SETTINGS_H +#define KS_SETTINGS_H + +#include "common/settings.h" + + +class KSGameConfig : public GameConfig +{ + Q_OBJECT + public: + KSGameConfig(); +}; + +#endif diff --git a/ksmiletris/COPYRIGHT b/ksmiletris/COPYRIGHT new file mode 100644 index 00000000..783a77c3 --- /dev/null +++ b/ksmiletris/COPYRIGHT @@ -0,0 +1,22 @@ +/**************************************************************** +Copyright (c) 1998 Sandro Sigala . +All rights reserved. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of the author not be used in +advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +The author disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall the author be liable for any +special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ diff --git a/ksmiletris/Makefile.am b/ksmiletris/Makefile.am new file mode 100644 index 00000000..2b0c0b3e --- /dev/null +++ b/ksmiletris/Makefile.am @@ -0,0 +1,21 @@ +SUBDIRS= data wav + +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) +METASOURCES = AUTO + +bin_PROGRAMS = ksmiletris +ksmiletris_SOURCES = main.cpp gamewindow.cpp gamewidget.cpp \ + screenwidget.cpp mirrorwidget.cpp npiecewidget.cpp +ksmiletris_LDFLAGS = $(all_libraries) $(KDE_RPATH) +ksmiletris_LDADD = $(LIB_KDEGAMES) +ksmiletris_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +xdg_apps_DATA = ksmiletris.desktop + +KDE_ICON = ksmiletris + +rcdir = $(kde_datadir)/ksmiletris +rc_DATA = ksmiletrisui.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/ksmiletris.pot diff --git a/ksmiletris/README b/ksmiletris/README new file mode 100644 index 00000000..7b4179b2 --- /dev/null +++ b/ksmiletris/README @@ -0,0 +1 @@ +See the HTML documentation in the doc/ subdirectory. diff --git a/ksmiletris/data/Makefile.am b/ksmiletris/data/Makefile.am new file mode 100644 index 00000000..b9b59670 --- /dev/null +++ b/ksmiletris/data/Makefile.am @@ -0,0 +1,16 @@ +AUTOMAKE_OPTIONS = foreign + +ksmiletrisdir = $(kde_datadir)/ksmiletris/data + +ksmiletris_DATA = bg1.bmp bg10.bmp bg11.bmp bg12.bmp bg13.bmp bg14.bmp \ + bg15.bmp bg16.bmp bg2.bmp bg3.bmp bg4.bmp bg5.bmp bg6.bmp \ + bg7.bmp bg8.bmp bg9.bmp block-a1.bmp block-a2.bmp \ + block-a3.bmp block-a4.bmp block-a5.bmp block-a6.bmp \ + block-a7.bmp block-a8.bmp block-b1.bmp block-b2.bmp \ + block-b3.bmp block-b4.bmp block-b5.bmp block-b6.bmp \ + block-b7.bmp block-b8.bmp block-c1.bmp block-c2.bmp \ + block-c3.bmp block-c4.bmp block-c5.bmp block-c6.bmp \ + block-c7.bmp block-c8.bmp broken-mask.bmp broken.bmp \ + pause.bmp cleared-a.bmp cleared-b.bmp cleared-c.bmp + +EXTRA_DIST = $(ksmiletris_DATA) diff --git a/ksmiletris/data/bg1.bmp b/ksmiletris/data/bg1.bmp new file mode 100644 index 00000000..c1a92f86 Binary files /dev/null and b/ksmiletris/data/bg1.bmp differ diff --git a/ksmiletris/data/bg10.bmp b/ksmiletris/data/bg10.bmp new file mode 100644 index 00000000..70f900bf Binary files /dev/null and b/ksmiletris/data/bg10.bmp differ diff --git a/ksmiletris/data/bg11.bmp b/ksmiletris/data/bg11.bmp new file mode 100644 index 00000000..927b4796 Binary files /dev/null and b/ksmiletris/data/bg11.bmp differ diff --git a/ksmiletris/data/bg12.bmp b/ksmiletris/data/bg12.bmp new file mode 100644 index 00000000..05e40ef6 Binary files /dev/null and b/ksmiletris/data/bg12.bmp differ diff --git a/ksmiletris/data/bg13.bmp b/ksmiletris/data/bg13.bmp new file mode 100644 index 00000000..d887e9ee Binary files /dev/null and b/ksmiletris/data/bg13.bmp differ diff --git a/ksmiletris/data/bg14.bmp b/ksmiletris/data/bg14.bmp new file mode 100644 index 00000000..98c70026 Binary files /dev/null and b/ksmiletris/data/bg14.bmp differ diff --git a/ksmiletris/data/bg15.bmp b/ksmiletris/data/bg15.bmp new file mode 100644 index 00000000..8757b0a0 Binary files /dev/null and b/ksmiletris/data/bg15.bmp differ diff --git a/ksmiletris/data/bg16.bmp b/ksmiletris/data/bg16.bmp new file mode 100644 index 00000000..2eb18c21 Binary files /dev/null and b/ksmiletris/data/bg16.bmp differ diff --git a/ksmiletris/data/bg2.bmp b/ksmiletris/data/bg2.bmp new file mode 100644 index 00000000..823723d5 Binary files /dev/null and b/ksmiletris/data/bg2.bmp differ diff --git a/ksmiletris/data/bg3.bmp b/ksmiletris/data/bg3.bmp new file mode 100644 index 00000000..c2ee8de1 Binary files /dev/null and b/ksmiletris/data/bg3.bmp differ diff --git a/ksmiletris/data/bg4.bmp b/ksmiletris/data/bg4.bmp new file mode 100644 index 00000000..6f39972a Binary files /dev/null and b/ksmiletris/data/bg4.bmp differ diff --git a/ksmiletris/data/bg5.bmp b/ksmiletris/data/bg5.bmp new file mode 100644 index 00000000..0e484b81 Binary files /dev/null and b/ksmiletris/data/bg5.bmp differ diff --git a/ksmiletris/data/bg6.bmp b/ksmiletris/data/bg6.bmp new file mode 100644 index 00000000..07420680 Binary files /dev/null and b/ksmiletris/data/bg6.bmp differ diff --git a/ksmiletris/data/bg7.bmp b/ksmiletris/data/bg7.bmp new file mode 100644 index 00000000..86036a44 Binary files /dev/null and b/ksmiletris/data/bg7.bmp differ diff --git a/ksmiletris/data/bg8.bmp b/ksmiletris/data/bg8.bmp new file mode 100644 index 00000000..6ce24fe1 Binary files /dev/null and b/ksmiletris/data/bg8.bmp differ diff --git a/ksmiletris/data/bg9.bmp b/ksmiletris/data/bg9.bmp new file mode 100644 index 00000000..ca3c94fc Binary files /dev/null and b/ksmiletris/data/bg9.bmp differ diff --git a/ksmiletris/data/block-a1.bmp b/ksmiletris/data/block-a1.bmp new file mode 100644 index 00000000..5841d5bd Binary files /dev/null and b/ksmiletris/data/block-a1.bmp differ diff --git a/ksmiletris/data/block-a2.bmp b/ksmiletris/data/block-a2.bmp new file mode 100644 index 00000000..1e447d36 Binary files /dev/null and b/ksmiletris/data/block-a2.bmp differ diff --git a/ksmiletris/data/block-a3.bmp b/ksmiletris/data/block-a3.bmp new file mode 100644 index 00000000..21528d77 Binary files /dev/null and b/ksmiletris/data/block-a3.bmp differ diff --git a/ksmiletris/data/block-a4.bmp b/ksmiletris/data/block-a4.bmp new file mode 100644 index 00000000..5d57813a Binary files /dev/null and b/ksmiletris/data/block-a4.bmp differ diff --git a/ksmiletris/data/block-a5.bmp b/ksmiletris/data/block-a5.bmp new file mode 100644 index 00000000..98d462c5 Binary files /dev/null and b/ksmiletris/data/block-a5.bmp differ diff --git a/ksmiletris/data/block-a6.bmp b/ksmiletris/data/block-a6.bmp new file mode 100644 index 00000000..f5850429 Binary files /dev/null and b/ksmiletris/data/block-a6.bmp differ diff --git a/ksmiletris/data/block-a7.bmp b/ksmiletris/data/block-a7.bmp new file mode 100644 index 00000000..f6684405 Binary files /dev/null and b/ksmiletris/data/block-a7.bmp differ diff --git a/ksmiletris/data/block-a8.bmp b/ksmiletris/data/block-a8.bmp new file mode 100644 index 00000000..b69d0dda Binary files /dev/null and b/ksmiletris/data/block-a8.bmp differ diff --git a/ksmiletris/data/block-b1.bmp b/ksmiletris/data/block-b1.bmp new file mode 100644 index 00000000..4b602027 Binary files /dev/null and b/ksmiletris/data/block-b1.bmp differ diff --git a/ksmiletris/data/block-b2.bmp b/ksmiletris/data/block-b2.bmp new file mode 100644 index 00000000..8b5e8a74 Binary files /dev/null and b/ksmiletris/data/block-b2.bmp differ diff --git a/ksmiletris/data/block-b3.bmp b/ksmiletris/data/block-b3.bmp new file mode 100644 index 00000000..99d7caf3 Binary files /dev/null and b/ksmiletris/data/block-b3.bmp differ diff --git a/ksmiletris/data/block-b4.bmp b/ksmiletris/data/block-b4.bmp new file mode 100644 index 00000000..c25c9031 Binary files /dev/null and b/ksmiletris/data/block-b4.bmp differ diff --git a/ksmiletris/data/block-b5.bmp b/ksmiletris/data/block-b5.bmp new file mode 100644 index 00000000..032591c6 Binary files /dev/null and b/ksmiletris/data/block-b5.bmp differ diff --git a/ksmiletris/data/block-b6.bmp b/ksmiletris/data/block-b6.bmp new file mode 100644 index 00000000..d068c51a Binary files /dev/null and b/ksmiletris/data/block-b6.bmp differ diff --git a/ksmiletris/data/block-b7.bmp b/ksmiletris/data/block-b7.bmp new file mode 100644 index 00000000..0154cf1b Binary files /dev/null and b/ksmiletris/data/block-b7.bmp differ diff --git a/ksmiletris/data/block-b8.bmp b/ksmiletris/data/block-b8.bmp new file mode 100644 index 00000000..3f60e0ad Binary files /dev/null and b/ksmiletris/data/block-b8.bmp differ diff --git a/ksmiletris/data/block-c1.bmp b/ksmiletris/data/block-c1.bmp new file mode 100644 index 00000000..f6d91227 Binary files /dev/null and b/ksmiletris/data/block-c1.bmp differ diff --git a/ksmiletris/data/block-c2.bmp b/ksmiletris/data/block-c2.bmp new file mode 100644 index 00000000..f83013bc Binary files /dev/null and b/ksmiletris/data/block-c2.bmp differ diff --git a/ksmiletris/data/block-c3.bmp b/ksmiletris/data/block-c3.bmp new file mode 100644 index 00000000..bb07ba76 Binary files /dev/null and b/ksmiletris/data/block-c3.bmp differ diff --git a/ksmiletris/data/block-c4.bmp b/ksmiletris/data/block-c4.bmp new file mode 100644 index 00000000..8f9aa21c Binary files /dev/null and b/ksmiletris/data/block-c4.bmp differ diff --git a/ksmiletris/data/block-c5.bmp b/ksmiletris/data/block-c5.bmp new file mode 100644 index 00000000..ca97d6d0 Binary files /dev/null and b/ksmiletris/data/block-c5.bmp differ diff --git a/ksmiletris/data/block-c6.bmp b/ksmiletris/data/block-c6.bmp new file mode 100644 index 00000000..2fa42edf Binary files /dev/null and b/ksmiletris/data/block-c6.bmp differ diff --git a/ksmiletris/data/block-c7.bmp b/ksmiletris/data/block-c7.bmp new file mode 100644 index 00000000..8dd189fe Binary files /dev/null and b/ksmiletris/data/block-c7.bmp differ diff --git a/ksmiletris/data/block-c8.bmp b/ksmiletris/data/block-c8.bmp new file mode 100644 index 00000000..79fbbaf0 Binary files /dev/null and b/ksmiletris/data/block-c8.bmp differ diff --git a/ksmiletris/data/broken-mask.bmp b/ksmiletris/data/broken-mask.bmp new file mode 100644 index 00000000..f84cc4c1 Binary files /dev/null and b/ksmiletris/data/broken-mask.bmp differ diff --git a/ksmiletris/data/broken.bmp b/ksmiletris/data/broken.bmp new file mode 100644 index 00000000..a575c899 Binary files /dev/null and b/ksmiletris/data/broken.bmp differ diff --git a/ksmiletris/data/cleared-a.bmp b/ksmiletris/data/cleared-a.bmp new file mode 100644 index 00000000..4d904604 Binary files /dev/null and b/ksmiletris/data/cleared-a.bmp differ diff --git a/ksmiletris/data/cleared-b.bmp b/ksmiletris/data/cleared-b.bmp new file mode 100644 index 00000000..0193c4ab Binary files /dev/null and b/ksmiletris/data/cleared-b.bmp differ diff --git a/ksmiletris/data/cleared-c.bmp b/ksmiletris/data/cleared-c.bmp new file mode 100644 index 00000000..0193c4ab Binary files /dev/null and b/ksmiletris/data/cleared-c.bmp differ diff --git a/ksmiletris/data/pause.bmp b/ksmiletris/data/pause.bmp new file mode 100644 index 00000000..75f8b8d0 Binary files /dev/null and b/ksmiletris/data/pause.bmp differ diff --git a/ksmiletris/gamewidget.cpp b/ksmiletris/gamewidget.cpp new file mode 100644 index 00000000..7bead2f7 --- /dev/null +++ b/ksmiletris/gamewidget.cpp @@ -0,0 +1,518 @@ +/**************************************************************** +Copyright (c) 1998 Sandro Sigala . +All rights reserved. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of the author not be used in +advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +The author disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall the author be liable for any +special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_USLEEP +#include +#endif + +#include "ksmiletris.h" +#include "gamewidget.h" +#include "screenwidget.h" +#include "mirrorwidget.h" +#include "npiecewidget.h" + +#include +#include + +GameWidget::GameWidget(QWidget *parent, const char *name) + : QWidget(parent, name) +{ + in_game = false; + in_pause = false; + + random.setSeed(0); + + loadSprites(); + setPieces(Pieces_Smiles); + + map = new Sprite[scr_width * scr_height]; + mirror_sprites = new Sprite[scr_width]; + + screen = new ScreenWidget(sprites, &in_game, &in_pause, this); + screen->move(10, 10); + screen->setScreenSprites(map); + + mirror = new MirrorWidget(sprites, &in_game, &in_pause, this); + mirror->move(10, 407); + mirror->setMirrorSprites(mirror_sprites); + + next = new NextPieceWidget(sprites, &in_game, &in_pause, this); + next->move(278, 10); + next->setNextPieceSprites(next_piece); + + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(timeout())); +} + +GameWidget::~GameWidget() +{ + delete [] sprites; + delete [] map; + delete [] mirror_sprites; +} + +void GameWidget::playSound(Sound s) +{ + if (!do_sounds) + return; + + QString name; + switch (s) { + case Sound_Break: + name = "break.wav"; + break; + case Sound_Clear: + name = "clear.wav"; + break; + } + + KAudioPlayer::play(locate("data", QString("ksmiletris/sounds/") + name)); +} + +void GameWidget::setPieces(PiecesType type) +{ + QString prefix; + + switch (type) { + case Pieces_Smiles: + prefix = "block-a"; + loadSprite(Sprite_Cleared, "cleared-a.bmp"); + break; + case Pieces_Symbols: + prefix = "block-b"; + loadSprite(Sprite_Cleared, "cleared-b.bmp"); + break; + case Pieces_Icons: + prefix = "block-c"; + loadSprite(Sprite_Cleared, "cleared-c.bmp"); + } + + for (int i = 0; i < num_blocks; ++i) { + QString n; + n.setNum(i + 1); + loadSprite((Sprite)(Sprite_Block1 + i), prefix + n + ".bmp"); + } + QPixmap pm(32, 32); + for (int i = 0; i < num_blocks; ++i) { + QPainter p; + p.begin(&pm); + p.drawPixmap(0, 0, sprites[Sprite_Block1 + i]); + p.drawPixmap(0, 0, sprites[Sprite_Broken]); + p.end(); + sprites[Sprite_Broken1 + i] = pm; + } + + if (in_game) repaintChilds(); +} + +void GameWidget::loadSprites() +{ + sprites = new QPixmap[num_sprites]; + loadSprite(Sprite_Bg1, "bg1.bmp"); + loadSprite(Sprite_Bg2, "bg2.bmp"); + loadSprite(Sprite_Bg3, "bg3.bmp"); + loadSprite(Sprite_Bg4, "bg4.bmp"); + loadSprite(Sprite_Bg5, "bg5.bmp"); + loadSprite(Sprite_Bg6, "bg6.bmp"); + loadSprite(Sprite_Bg7, "bg7.bmp"); + loadSprite(Sprite_Bg8, "bg8.bmp"); + loadSprite(Sprite_Bg9, "bg9.bmp"); + loadSprite(Sprite_Bg10, "bg10.bmp"); + loadSprite(Sprite_Bg11, "bg11.bmp"); + loadSprite(Sprite_Bg12, "bg12.bmp"); + loadSprite(Sprite_Bg13, "bg13.bmp"); + loadSprite(Sprite_Bg14, "bg14.bmp"); + loadSprite(Sprite_Bg15, "bg15.bmp"); + loadSprite(Sprite_Bg16, "bg16.bmp"); + loadMaskedSprite(Sprite_Broken, "broken.bmp", "broken-mask.bmp"); +} + +void GameWidget::loadSprite(Sprite spr, const QString & path) +{ + if (!sprites[spr].load(locate("appdata", QString("data/") + path))) + qFatal("Cannot open data files.\nHave you correctly installed KSmiletris?"); +} + +void GameWidget::loadMaskedSprite(Sprite spr, const QString & path1, const QString & path2) +{ + QBitmap bmp; + if (!sprites[spr].load(locate("appdata", QString("data/") + path1))) + qFatal("Cannot open data files.\nHave you correctly installed KSmiletris?"); + if (!bmp.load(locate("appdata", QString("data/") + path2))) + qFatal("Cannot open data files.\nHave you correctly installed KSmiletris?"); + sprites[spr].setMask(bmp); +} + +void GameWidget::newGame() +{ + in_pause = false; + fast_mode = false; + + num_level = 1; + num_pieces_level = 2; + num_points = 0; + + bg_sprite = (Sprite)(Sprite_Bg1 + random.getLong(num_bgs)); + screen->setBackgroundSprite(bg_sprite); + mirror->setBackgroundSprite(bg_sprite); + next->setBackgroundSprite(bg_sprite); + for (int i = 0; i < scr_width; ++i) + mirror_sprites[i] = bg_sprite; + for (int i = 0; i < 4; ++i) + next_piece[i] = bg_sprite; + for (int y = 0; y < scr_height; ++y) + for (int x = 0; x < scr_width; ++x) + ref(x, y) = bg_sprite; + + newPiece(); + nextPiece(); + updateMirror(); + + in_game = true; + repaintChilds(); + emit changedStats(num_level, num_points); + timer_interval = 700; + timer->start(timer_interval); +} + +void GameWidget::repaintChilds() +{ + screen->repaint(false); + mirror->repaint(false); + next->repaint(false); +} + +void GameWidget::putPiece() +{ + if (piece[0] != bg_sprite) ref(xpos + 0, ypos + 0) = piece[0]; + if (piece[1] != bg_sprite) ref(xpos + 1, ypos + 0) = piece[1]; + if (piece[2] != bg_sprite) ref(xpos + 0, ypos + 1) = piece[2]; + if (piece[3] != bg_sprite) ref(xpos + 1, ypos + 1) = piece[3]; + updateMirror(); + screen->repaint(false); +} + +void GameWidget::getPiece() +{ + if (piece[0] != bg_sprite) ref(xpos + 0, ypos + 0) = bg_sprite; + if (piece[1] != bg_sprite) ref(xpos + 1, ypos + 0) = bg_sprite; + if (piece[2] != bg_sprite) ref(xpos + 0, ypos + 1) = bg_sprite; + if (piece[3] != bg_sprite) ref(xpos + 1, ypos + 1) = bg_sprite; +} + +void GameWidget::newPiece() +{ + static int pieces[][4] = {{1, 0, 1, 1}, + {1, 1, 1, 0}, + {1, 1, 0, 1}, + {0, 1, 1, 1}}; + int p = random.getLong(4); + for (int i = 0; i < 4; ++i) + if (pieces[p][i]) + next_piece[i] = (Sprite)(Sprite_Block1 + random.getLong(num_pieces_level)); + else + next_piece[i] = bg_sprite; + next->repaint(false); +} + +void GameWidget::nextPiece() +{ + piece[0] = next_piece[0]; + piece[1] = next_piece[1]; + piece[2] = next_piece[2]; + piece[3] = next_piece[3]; + newPiece(); + xpos = (scr_width - 2) / 2; + ypos = 0; + if ((piece[0] != bg_sprite && ref(xpos + 0, ypos + 0) != bg_sprite) + || (piece[1] != bg_sprite && ref(xpos + 1, ypos + 0) != bg_sprite) + || (piece[2] != bg_sprite && ref(xpos + 0, ypos + 1) != bg_sprite) + || (piece[3] != bg_sprite && ref(xpos + 1, ypos + 1) != bg_sprite)) { + in_game = false; + repaintChilds(); + KMessageBox::sorry(this, i18n("Game Over")); + emit gameOver(); + } + + putPiece(); +} + +void GameWidget::updateMirror() +{ + for (int x = 0; x < scr_width; ++x) + mirror_sprites[x] = bg_sprite; + mirror_sprites[xpos] = piece[2] == bg_sprite ? piece[0] : piece[2]; + mirror_sprites[xpos+1] = piece[3] == bg_sprite ? piece[1] : piece[3]; + mirror->repaint(false); +} + +void GameWidget::keyUp() +{ + if (!in_game || in_pause) return; + + getPiece(); + if ((piece[0] == bg_sprite && ref(xpos + 0, ypos + 0) != bg_sprite) + || (piece[1] == bg_sprite && ref(xpos + 1, ypos + 0) != bg_sprite) + || (piece[2] == bg_sprite && ref(xpos + 0, ypos + 1) != bg_sprite) + || (piece[3] == bg_sprite && ref(xpos + 1, ypos + 1) != bg_sprite)) { + putPiece(); + return; + } + + Sprite npiece[4]; + npiece[2] = piece[0]; + npiece[0] = piece[1]; + npiece[3] = piece[2]; + npiece[1] = piece[3]; + for (int i = 0; i < 4; ++i) + piece[i] = npiece[i]; + putPiece(); +} + +void GameWidget::keyDown() +{ + if (!in_game || in_pause) return; + + getPiece(); + if ((piece[0] == bg_sprite && ref(xpos + 0, ypos + 0) != bg_sprite) + || (piece[1] == bg_sprite && ref(xpos + 1, ypos + 0) != bg_sprite) + || (piece[2] == bg_sprite && ref(xpos + 0, ypos + 1) != bg_sprite) + || (piece[3] == bg_sprite && ref(xpos + 1, ypos + 1) != bg_sprite)) { + putPiece(); + return; + } + + Sprite npiece[4]; + npiece[0] = piece[2]; + npiece[1] = piece[0]; + npiece[2] = piece[3]; + npiece[3] = piece[1]; + for (int i = 0; i < 4; ++i) + piece[i] = npiece[i]; + putPiece(); +} + +void GameWidget::keyLeft() +{ + if (!in_game || in_pause || xpos == 0) return; + + getPiece(); + if ((piece[0] != bg_sprite && ref(xpos - 1, ypos + 0) != bg_sprite) + || (piece[1] != bg_sprite && ref(xpos + 0, ypos + 0) != bg_sprite) + || (piece[2] != bg_sprite && ref(xpos - 1, ypos + 1) != bg_sprite) + || (piece[3] != bg_sprite && ref(xpos + 0, ypos + 1) != bg_sprite)) { + putPiece(); + return; + } + + --xpos; + putPiece(); +} + +void GameWidget::keyRight() +{ + if (!in_game || in_pause || xpos == scr_width - 2) return; + + getPiece(); + if ((piece[0] != bg_sprite && ref(xpos + 1, ypos + 0) != bg_sprite) + || (piece[1] != bg_sprite && ref(xpos + 2, ypos + 0) != bg_sprite) + || (piece[2] != bg_sprite && ref(xpos + 1, ypos + 1) != bg_sprite) + || (piece[3] != bg_sprite && ref(xpos + 2, ypos + 1) != bg_sprite)) { + putPiece(); + return; + } + + ++xpos; + putPiece(); +} + +void GameWidget::keySpace() +{ + if (!in_game || in_pause || fast_mode) return; + fast_mode = true; + timer->changeInterval(50); +} + +void GameWidget::broke(int x, int y, bool *xmap) +{ + xmap[y*scr_width + x] = true; + if (ref(x, y) >= Sprite_Broken1) { + // Clear the piece + playSound(Sound_Clear); + ref(x, y) = Sprite_Cleared; + num_points += 20; + } else { + // Break the piece + playSound(Sound_Break); + ref(x, y) = (Sprite)(Sprite_Broken1 + ref(x, y) - Sprite_Block1); + num_points += 10; + } + int level = num_points / 1000 + 1; + if (level > num_level) { + num_level = level; + num_pieces_level = (num_level/2 + 2) > num_blocks ? + num_blocks : num_level/2 + 2; + timer_interval = timer_interval - 25; + if (timer_interval < 50) + timer_interval = 50; + timer->changeInterval(timer_interval); + } + + emit changedStats(num_level, num_points); + +#ifdef HAVE_USLEEP + screen->repaint(false); + usleep(75 * 1000); +#endif +} + +void GameWidget::recBroke(int x, int y, bool *xmap) +{ + int t = type(ref(x, y)); + + broke(x, y, xmap); + // X-1, Y + if (x > 0 && !xmap[y*scr_width + x - 1] && type(ref(x-1, y)) == t) + recBroke(x-1, y, xmap); + // X+1, Y + if (x < scr_width-1 && !xmap[y*scr_width + x + 1] && type(ref(x+1, y)) == t) + recBroke(x+1, y, xmap); + // X, Y-1 + if (y > 0 && !xmap[(y-1)*scr_width + x] && type(ref(x, y-1)) == t) + recBroke(x, y-1, xmap); + // X, Y+1 + if (y < scr_height-1 && !xmap[(y+1)*scr_width + x] && type(ref(x, y+1)) == t) + recBroke(x, y+1, xmap); + + // X-1, Y+1 + if (x > 0 && y < scr_height-1 && !xmap[(y+1)*scr_width + x - 1] && type(ref(x-1, y+1)) == t) + recBroke(x-1, y+1, xmap); + // X+1, Y+1 + if (x < scr_width-1 && y < scr_height-1 && !xmap[(y+1)*scr_width + x + 1] && type(ref(x+1, y+1)) == t) + recBroke(x+1, y+1, xmap); + // X-1, Y-1 + if (x > 0 && y > 0 && !xmap[(y-1)*scr_width + x - 1] && type(ref(x-1, y-1)) == t) + recBroke(x-1, y-1, xmap); + // X+1, Y-1 + if (x < scr_width-1 && y > 0 && !xmap[(y-1)*scr_width + x + 1] && type(ref(x+1, y-1)) == t) + recBroke(x+1, y-1, xmap); +} + +void GameWidget::checkSolePiece(int x, int y, bool *xmap) +{ + if (y < scr_height-1 && type(ref(x, y+1)) == type(ref(x, y))) + recBroke(x, y, xmap); +} + +void GameWidget::checkPiece(int x, int y) +{ + bool xmap[scr_width * scr_height]; + for (int yy = 0; yy < scr_height; ++yy) + for (int x = 0; x < scr_width; ++x) + xmap[yy*scr_width + x] = false; + checkSolePiece(x, y, xmap); +} + +void GameWidget::compact() +{ + recheck: + bool moved = true; + while (moved) { + moved = false; + for (int x = 0; x < scr_width; ++x) + for (int y = scr_height - 1; y > 0; --y) + if (ref(x, y) == bg_sprite + || ref(x, y) == Sprite_Cleared) { + int i; + for (i = y-1; i >= 0; --i) + if (ref(x, i) != bg_sprite + && ref(x, i) != Sprite_Cleared) { + ref(x, y) = ref(x, i); + ref(x, i) = bg_sprite; + moved = true; + checkPiece(x, y); + goto recheck; + break; + } + if (i < 0) + ref(x, y) = bg_sprite; + } + } +} + +void GameWidget::blockPiece() +{ + if (fast_mode) { + timer->changeInterval(timer_interval); + fast_mode = false; + } + putPiece(); + bool xmap[scr_width * scr_height]; + for (int yy = 0; yy < scr_height; ++yy) + for (int x = 0; x < scr_width; ++x) + xmap[yy*scr_width + x] = false; + if (piece[2] != bg_sprite) + checkSolePiece(xpos, ypos+1, xmap); + if (piece[3] != bg_sprite) + checkSolePiece(xpos+1, ypos+1, xmap); + if (piece[2] == bg_sprite) + checkSolePiece(xpos, ypos, xmap); + if (piece[3] == bg_sprite) + checkSolePiece(xpos+1, ypos, xmap); + compact(); + nextPiece(); +} + +void GameWidget::timeout() +{ + if (!in_game || in_pause) + return; + + getPiece(); + if (ypos == scr_height - 2) { + blockPiece(); + return; + } + + if ((piece[0] != bg_sprite && ref(xpos + 0, ypos + 1) != bg_sprite) + || (piece[1] != bg_sprite && ref(xpos + 1, ypos + 1) != bg_sprite) + || (piece[2] != bg_sprite && ref(xpos + 0, ypos + 2) != bg_sprite) + || (piece[3] != bg_sprite && ref(xpos + 1, ypos + 2) != bg_sprite)) + blockPiece(); + else { + ++ypos; + putPiece(); + } + +} + +#include "gamewidget.moc" diff --git a/ksmiletris/gamewidget.h b/ksmiletris/gamewidget.h new file mode 100644 index 00000000..49901650 --- /dev/null +++ b/ksmiletris/gamewidget.h @@ -0,0 +1,112 @@ +/**************************************************************** +Copyright (c) 1998 Sandro Sigala . +All rights reserved. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of the author not be used in +advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +The author disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall the author be liable for any +special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#ifndef GAMEWIDGET_H +#define GAMEWIDGET_H + +#include +#include + +#include "ksmiletris.h" + +class ScreenWidget; +class MirrorWidget; +class NextPieceWidget; +class QTimer; + +class GameWidget : public QWidget { + Q_OBJECT + +signals: + void changedStats(int, int); + void gameOver(); + +public: + bool in_game, in_pause; + bool do_sounds; + int num_level; + int num_points; + + GameWidget(QWidget *parent=0, const char *name=0); + ~GameWidget(); + + void setPieces(PiecesType type); + + Sprite& ref(int x, int y) { + return map[y*scr_width + x]; + } + + Sprite type(Sprite s) { + if (s >= Sprite_Broken1 && s < Sprite_Broken1 + num_blocks) + return (Sprite)(Sprite_Block1 + s - Sprite_Broken1); + return s; + } + + void repaintChilds(); + +private: + QPixmap *sprites; + ScreenWidget *screen; + MirrorWidget *mirror; + NextPieceWidget *next; + int xpos, ypos; + Sprite piece[4]; + Sprite *map; + Sprite *mirror_sprites; + Sprite next_piece[4]; + Sprite bg_sprite; + int timer_interval; + bool fast_mode; + QTimer *timer; + KRandomSequence random; + + int num_pieces_level; + + void playSound(Sound s); + void loadSprites(); + void loadSprite(Sprite spr, const QString & path); + void loadMaskedSprite(Sprite spr, const QString & path1, const QString & path2); + void newBlock(); + void putPiece(); + void getPiece(); + void newPiece(); + void nextPiece(); + void compact(); + void broke(int x, int y, bool *xmap); + void recBroke(int x, int y, bool *xmap); + void checkSolePiece(int x, int y, bool *xmap); + void checkPiece(int x, int y); + void blockPiece(); + void updateMirror(); + +public slots: + void newGame(); + void keyUp(); + void keyDown(); + void keyLeft(); + void keyRight(); + void keySpace(); + void timeout(); +}; + +#endif // !GAMEWIDGET_H diff --git a/ksmiletris/gamewindow.cpp b/ksmiletris/gamewindow.cpp new file mode 100644 index 00000000..60973530 --- /dev/null +++ b/ksmiletris/gamewindow.cpp @@ -0,0 +1,204 @@ +/**************************************************************** +Copyright (c) 1998 Sandro Sigala . +All rights reserved. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of the author not be used in +advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +The author disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall the author be liable for any +special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "ksmiletris.h" +#include "gamewindow.h" +#include "gamewidget.h" +#include "kscoredialog.h" + +#include +#include + + + +const int default_width = 362; +const int default_height = 460; + +GameWindow::GameWindow(QWidget *, const char *name) + : KMainWindow(0, name) +{ + //New Games + (void)KStdGameAction::gameNew(this, + SLOT(menu_newGame()), + actionCollection()); + + //Pause Game + (void)KStdGameAction::pause(this, + SLOT(menu_pause()), + actionCollection()); + + //End Game + (void)KStdGameAction::end(this, + SLOT(menu_endGame()), + actionCollection()); + + //Highscores + (void)KStdGameAction::highscores(this, + SLOT(menu_highScores()), + actionCollection()); + + //Quit + (void)KStdGameAction::quit(this, + SLOT(close()), + actionCollection()); + + QStringList list; + KSelectAction* piecesAct = + new KSelectAction(i18n("&Pieces"), 0, this, SLOT(menu_pieces()), + actionCollection(), "settings_pieces"); + list.append(i18n("&Smiles")); + list.append(i18n("S&ymbols")); + list.append(i18n("&Icons")); + piecesAct->setItems(list); + + (void)new KToggleAction(i18n("&Sounds"), 0, this, + SLOT(menu_sounds()), actionCollection(), "settings_sounds"); + + + + + + //connect(menu, SIGNAL(moved(menuPosition)), + // this, SLOT(movedMenu(menuPosition))); ? + + status = new KStatusBar(this); + status->insertItem(i18n("Level: 99"), 1); + status->insertItem(i18n("Score: 999999"), 2); + status->changeItem("", 1); + status->changeItem("", 2); + + game = new GameWidget(this); + setCentralWidget(game); + connect(game, SIGNAL(changedStats(int, int)), + this, SLOT(updateStats(int, int))); + connect(game, SIGNAL(gameOver()), this, SLOT(gameOver())); + + //keys + (void)new KAction(i18n("Move Left"), Key_Left, game, SLOT(keyLeft()), actionCollection(), "left"); + (void)new KAction(i18n("Move Right"), Key_Right, game, SLOT(keyRight()), actionCollection(), "right"); + (void)new KAction(i18n("Rotate Left"), Key_Up, game, SLOT(keyUp()), actionCollection(), "up"); + (void)new KAction(i18n("Rotate Right"), Key_Down, game, SLOT(keyDown()), actionCollection(), "down"); + (void)new KAction(i18n("Drop Down"), Key_Space, game, SLOT(keySpace()), actionCollection(), "space"); + + game->setFixedSize(default_width, default_height); + adjustSize(); + setFixedSize(size()); + + // Read configuration + KConfig *config = kapp->config(); + config->setGroup("Options"); + PiecesType pieces_type = (PiecesType)config->readNumEntry("Pieces", static_cast(Pieces_Smiles)); + game->setPieces(pieces_type); + ((KSelectAction*)actionCollection()->action("settings_pieces"))->setCurrentItem((int)pieces_type); + + + game->do_sounds = config->readBoolEntry("Sounds", true); + ((KToggleAction*)actionCollection()->action("settings_sounds"))->setChecked(game->do_sounds); + + setupGUI(KMainWindow::Save | Keys | StatusBar | Create); +} + +void GameWindow::menu_newGame() +{ + ((KToggleAction*)actionCollection()->action(KStdGameAction::stdName(KStdGameAction::Pause)))->setChecked(false); + game->newGame(); +} + +void GameWindow::menu_pause() +{ + if (game->in_game) { + game->in_pause = !game->in_pause; + ((KToggleAction*)actionCollection()->action(KStdGameAction::stdName(KStdGameAction::Pause)))->setChecked(game->in_pause); + game->repaintChilds(); + } +} + +void GameWindow::menu_endGame() +{ + if (game->in_game) { + game->in_game = false; + game->repaintChilds(); + ((KToggleAction*)actionCollection()->action(KStdGameAction::stdName(KStdGameAction::Pause)))->setChecked(false); + gameOver(); + } +} + +void GameWindow::menu_highScores() +{ + KScoreDialog d(KScoreDialog::Name | KScoreDialog::Level | KScoreDialog::Score, this); + d.exec(); +} + +void GameWindow::menu_pieces() +{ + int index = ((KSelectAction*)actionCollection()->action("settings_pieces"))->currentItem(); + game->setPieces((PiecesType)index); + + KConfig *config = kapp->config(); + config->setGroup("Options"); + config->writeEntry("Pieces", index); +} + +void GameWindow::menu_sounds() +{ + game->do_sounds = !game->do_sounds; + ((KToggleAction*)actionCollection()->action("settings_sounds"))->setChecked(game->do_sounds); + + KConfig *config = kapp->config(); + config->setGroup("Options"); + config->writeEntry("Sounds", game->do_sounds); +} + +void GameWindow::updateStats(int level, int points) +{ + QString l, p; + l.setNum(level); + p.setNum(points); + status->changeItem(i18n("Level: %1").arg(l), 1); + status->changeItem(i18n("Score: %1").arg(p), 2); +} + +void GameWindow::gameOver() +{ + KScoreDialog d(KScoreDialog::Name | KScoreDialog::Level | KScoreDialog::Score, this); + + KScoreDialog::FieldInfo scoreInfo; + + scoreInfo[KScoreDialog::Level].setNum(game->num_level); + + if (!d.addScore(game->num_points, scoreInfo)) + return; + + // Show highscore & ask for name. + d.exec(); +} + +#include "gamewindow.moc" diff --git a/ksmiletris/gamewindow.h b/ksmiletris/gamewindow.h new file mode 100644 index 00000000..54e637a0 --- /dev/null +++ b/ksmiletris/gamewindow.h @@ -0,0 +1,63 @@ +/**************************************************************** +Copyright (c) 1998 Sandro Sigala . +All rights reserved. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of the author not be used in +advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +The author disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall the author be liable for any +special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#ifndef GAMEWINDOW_H +#define GAMEWINDOW_H + +#include + +#include "ksmiletris.h" + +class KMenuBar; +class QPopupMenu; +class GameWidget; +class KStatusBar; + +class GameWindow : public KMainWindow { + Q_OBJECT + +public: + GameWindow(QWidget *parent=0, const char *name=0); + +public slots: + void menu_newGame(); + void menu_pause(); + void menu_endGame(); + void menu_highScores(); + void menu_pieces(); + void menu_sounds(); + + void updateStats(int level, int points); + void gameOver(); + //void movedMenu(menuPosition); + +private: + //enum menuPosition { Top, Bottom, Floating }; // hack + //KMenuBar *menu; + + KStatusBar *status; + GameWidget *game; + QString m_player; +}; + +#endif // !GAMEWINDOW_H diff --git a/ksmiletris/hi128-app-ksmiletris.png b/ksmiletris/hi128-app-ksmiletris.png new file mode 100644 index 00000000..a7789b74 Binary files /dev/null and b/ksmiletris/hi128-app-ksmiletris.png differ diff --git a/ksmiletris/hi16-app-ksmiletris.png b/ksmiletris/hi16-app-ksmiletris.png new file mode 100644 index 00000000..c0883958 Binary files /dev/null and b/ksmiletris/hi16-app-ksmiletris.png differ diff --git a/ksmiletris/hi22-app-ksmiletris.png b/ksmiletris/hi22-app-ksmiletris.png new file mode 100644 index 00000000..e4b46fea Binary files /dev/null and b/ksmiletris/hi22-app-ksmiletris.png differ diff --git a/ksmiletris/hi32-app-ksmiletris.png b/ksmiletris/hi32-app-ksmiletris.png new file mode 100644 index 00000000..2c38e3ce Binary files /dev/null and b/ksmiletris/hi32-app-ksmiletris.png differ diff --git a/ksmiletris/hi48-app-ksmiletris.png b/ksmiletris/hi48-app-ksmiletris.png new file mode 100644 index 00000000..17696b2c Binary files /dev/null and b/ksmiletris/hi48-app-ksmiletris.png differ diff --git a/ksmiletris/hi64-app-ksmiletris.png b/ksmiletris/hi64-app-ksmiletris.png new file mode 100644 index 00000000..6c9b6263 Binary files /dev/null and b/ksmiletris/hi64-app-ksmiletris.png differ diff --git a/ksmiletris/ksmiletris.desktop b/ksmiletris/ksmiletris.desktop new file mode 100644 index 00000000..59d6034f --- /dev/null +++ b/ksmiletris/ksmiletris.desktop @@ -0,0 +1,78 @@ +[Desktop Entry] +MimeType= +GenericName=Tetris-like Game +GenericName[be]=Ð“ÑƒÐ»ÑŒÐ½Ñ Ñž Ñ‚ÑÑ‚Ñ€Ñ‹Ñ +GenericName[bg]=Ð¢ÐµÑ‚Ñ€Ð¸Ñ +GenericName[bn]=টেটà§à¦°à¦¿à¦¸-জাতীয় টালির খেলা +GenericName[br]=Ur c'hoari a seurt gant Tetris +GenericName[bs]=Igra nalik na Tetris +GenericName[ca]=Joc a l'estil Tetris +GenericName[cs]=Hra podobná Tetris +GenericName[cy]=Gêm sy'n debyg i Tetris +GenericName[da]=Tetris-lignende spil +GenericName[de]=Tetris-ähnliches Spiel +GenericName[el]=Παιχνίδι παÏόμοιο με το Tetris +GenericName[eo]="Tetris"-simila ludo +GenericName[es]=Juego similar al Tetris +GenericName[et]=Tetrise moodi mäng +GenericName[eu]=Tetris-en antzeko jokoa +GenericName[fa]=بازی شبیه Tetris +GenericName[fi]=Tetris-tyylinen peli +GenericName[fr]=Jeu dans le style de Tetris +GenericName[ga]=Cluiche Mar Tetris +GenericName[he]=חיקוי טטריס +GenericName[hr]=Igra poput Tetrisa +GenericName[hu]=Tetrisz-szerű +GenericName[is]=Leikur líkur Tetris +GenericName[it]=Gioco simile al Tetris +GenericName[ja]=テトリスã®ã‚ˆã†ãªã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​ដូច Tetris +GenericName[ko]=지뢰찾기 게임 +GenericName[lt]=Tetrio tipo žaidimas +GenericName[lv]=Tetrim lÄ«dzÄ«ga spÄ“le +GenericName[mk]=Игра Ñлична на Ð¢ÐµÑ‚Ñ€Ð¸Ñ +GenericName[nb]=Tetrislignende spill +GenericName[nds]=Tetris-liek Speel +GenericName[ne]=टेटà¥à¤°à¤¿à¤¸ जसà¥à¤¤à¥ˆ खेल +GenericName[nl]=Tetris-achtig spel +GenericName[nn]=Tetrisliknande spel +GenericName[pl]=Gra typu Tetris +GenericName[pt]=Jogo tipo Tetris +GenericName[pt_BR]=Jogo como Tetris +GenericName[ru]=Игра в Ñ‚ÐµÑ‚Ñ€Ð¸Ñ +GenericName[se]=Tetrislágan speallu +GenericName[sk]=Hra typu Tetris +GenericName[sl]=Igra podobna Tetrisu +GenericName[sr]=Игра налик на Tetris +GenericName[sr@Latn]=Igra nalik na Tetris +GenericName[sv]=Tetrisliknande spel +GenericName[ta]=டெடà¯à®°à®¿à®¸à¯ போனà¯à®± விளையாடà¯à®Ÿà¯ +GenericName[uk]=Гра Ñхожа на Ñ‚ÐµÑ‚Ñ€Ñ–Ñ +GenericName[zh_CN]=ä¿„ç½—æ–¯æ–¹å— +GenericName[zh_TW]=類似俄羅斯方塊éŠæˆ² +Exec=ksmiletris -caption "%c" %i %m +Icon=ksmiletris +Path= +Type=Application +Terminal=false +Name=KSmileTris +Name[af]=K-glimlag-tetris +Name[be]=Смайл-Ñ‚ÑÑ‚Ñ€Ñ‹Ñ +Name[bn]=কে-সà§à¦®à¦¾à¦‡à¦²à¦Ÿà§à¦°à¦¿à¦¸ +Name[eo]=Ridultetriso +Name[hi]=के-सà¥à¤®à¤¾à¤ˆà¤² टà¥à¤°à¤¿à¤¸ +Name[is]=Brostris +Name[ne]=केडीई सà¥à¤®à¤¾à¤‡à¤² टà¥à¤°à¤¿à¤¸ +Name[pt_BR]=KSorrisoTris +Name[ru]=Смайлик-Ñ‚ÐµÑ‚Ñ€Ð¸Ñ +Name[sv]=Ksmiletris +Name[ta]= Kஸà¯à®®à¯ˆà®²à¯ டà¯à®°à®¿à®¸à¯ +Name[tg]=KТетриÑи ханда +Name[tr]=KSmiletris +Name[zh_CN]=KSmiletris +Name[zh_TW]=KSmileTris 笑臉俄羅斯 +Name[zu]=I-KSmileTris +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;ArcadeGame; +DocPath=ksmiletris/index.html diff --git a/ksmiletris/ksmiletris.h b/ksmiletris/ksmiletris.h new file mode 100644 index 00000000..c141d601 --- /dev/null +++ b/ksmiletris/ksmiletris.h @@ -0,0 +1,86 @@ +/**************************************************************** +Copyright (c) 1998 Sandro Sigala . +All rights reserved. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of the author not be used in +advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +The author disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall the author be liable for any +special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#ifndef KSMILETRIS_H +#define KSMILETRIS_H + +const int sprite_width = 32; +const int sprite_height = 32; + +enum Sprite { + Sprite_Bg1, + Sprite_Bg2, + Sprite_Bg3, + Sprite_Bg4, + Sprite_Bg5, + Sprite_Bg6, + Sprite_Bg7, + Sprite_Bg8, + Sprite_Bg9, + Sprite_Bg10, + Sprite_Bg11, + Sprite_Bg12, + Sprite_Bg13, + Sprite_Bg14, + Sprite_Bg15, + Sprite_Bg16, + Sprite_Block1, + Sprite_Block2, + Sprite_Block3, + Sprite_Block4, + Sprite_Block5, + Sprite_Block6, + Sprite_Block7, + Sprite_Block8, + Sprite_Broken, + Sprite_Broken1, + Sprite_Broken2, + Sprite_Broken3, + Sprite_Broken4, + Sprite_Broken5, + Sprite_Broken6, + Sprite_Broken7, + Sprite_Broken8, + Sprite_Cleared, + NUM_SPRITES +}; + +const int num_sprites = NUM_SPRITES; +const int num_blocks = 8; +const int num_bgs = 16; + +const int scr_width = 8; +const int scr_height = 12; + +enum PiecesType { + Pieces_Smiles=0, + Pieces_Symbols=1, + Pieces_Icons=2 +}; + +enum Sound { + Sound_Break, + Sound_Clear +}; + +#endif // !KSMILETRIS_H diff --git a/ksmiletris/ksmiletrisui.rc b/ksmiletris/ksmiletrisui.rc new file mode 100644 index 00000000..66c29179 --- /dev/null +++ b/ksmiletris/ksmiletrisui.rc @@ -0,0 +1,12 @@ + + + + &Game + + &Settings + + + + + + diff --git a/ksmiletris/main.cpp b/ksmiletris/main.cpp new file mode 100644 index 00000000..ebb51ee2 --- /dev/null +++ b/ksmiletris/main.cpp @@ -0,0 +1,53 @@ +/**************************************************************** +Copyright (c) 1998 Sandro Sigala . +All rights reserved. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of the author not be used in +advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +The author disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall the author be liable for any +special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "config.h" + +#include +#include +#include +#include + +#include "gamewindow.h" + + +static const char description[] = I18N_NOOP("KDE SmileTris"); + + +int main(int argc, char **argv) +{ + KAboutData aboutData( "ksmiletris", I18N_NOOP("KSmileTris"), + VERSION, description, KAboutData::License_GPL, + "(c) 1998, Sandro Sigala"); + aboutData.addAuthor("Sandro Sigala",0, "ssigala@globalnet.it"); + KCmdLineArgs::init( argc, argv, &aboutData ); + + + KApplication app; + KGlobal::locale()->insertCatalogue("libkdegames"); + + GameWindow *w = new GameWindow; + app.setMainWidget(w); + w->show(); + return app.exec(); +} diff --git a/ksmiletris/mirrorwidget.cpp b/ksmiletris/mirrorwidget.cpp new file mode 100644 index 00000000..43ae0175 --- /dev/null +++ b/ksmiletris/mirrorwidget.cpp @@ -0,0 +1,58 @@ +/**************************************************************** +Copyright (c) 1998 Sandro Sigala . +All rights reserved. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of the author not be used in +advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +The author disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall the author be liable for any +special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "config.h" + +#include +#include +#include + +#include "ksmiletris.h" +#include "mirrorwidget.h" + +MirrorWidget::MirrorWidget(QPixmap *s, bool *game, bool *pause, + QWidget *parent, const char *name) + : QFrame(parent, name) +{ + in_game = game; + in_pause = pause; + sprites = s; + setFrameStyle(QFrame::Box | QFrame::Raised); + setLineWidth(2); + setMidLineWidth(1); + resize(scr_width * sprite_width + 10, sprite_height + 10); +} + +void MirrorWidget::drawContents(QPainter *p) +{ + QRect r = contentsRect(); + + if (!*in_game) { + p->fillRect(r, black); + return; + } + + for (int x = 0; x < scr_width; ++x) + p->drawPixmap(r.x() + x*sprite_width, r.y(), + sprites[mirror_sprites[x]]); +} diff --git a/ksmiletris/mirrorwidget.h b/ksmiletris/mirrorwidget.h new file mode 100644 index 00000000..4b0c7fa0 --- /dev/null +++ b/ksmiletris/mirrorwidget.h @@ -0,0 +1,49 @@ +/**************************************************************** +Copyright (c) 1998 Sandro Sigala . +All rights reserved. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of the author not be used in +advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +The author disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall the author be liable for any +special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#ifndef MIRRORWIDGET_H +#define MIRRORWIDGET_H + +#include + +#include "ksmiletris.h" + +class MirrorWidget : public QFrame { +public: + MirrorWidget(QPixmap *s, bool *game, bool *pause, + QWidget *parent=0, const char *name=0); + + void setBackgroundSprite(Sprite s) { bg_sprite = s; } + void setMirrorSprites(Sprite *s) { mirror_sprites = s; } + +private: + QPixmap *sprites; + bool *in_game, *in_pause; + Sprite bg_sprite; + Sprite *mirror_sprites; + +protected: + void drawContents(QPainter *p); +}; + +#endif // !MIRRORWIDGET_H diff --git a/ksmiletris/npiecewidget.cpp b/ksmiletris/npiecewidget.cpp new file mode 100644 index 00000000..5df68f8e --- /dev/null +++ b/ksmiletris/npiecewidget.cpp @@ -0,0 +1,63 @@ +/**************************************************************** +Copyright (c) 1998 Sandro Sigala . +All rights reserved. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of the author not be used in +advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +The author disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall the author be liable for any +special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "config.h" + +#include +#include +#include + +#include "ksmiletris.h" +#include "npiecewidget.h" + +NextPieceWidget::NextPieceWidget(QPixmap *s, bool *game, bool *pause, + QWidget *parent, const char *name) + : QFrame(parent, name) +{ + in_game = game; + in_pause = pause; + sprites = s; + setFrameStyle(QFrame::Box | QFrame::Raised); + setLineWidth(2); + setMidLineWidth(1); + resize(2 * sprite_width + 10, 2 * sprite_height + 10); +} + +void NextPieceWidget::drawContents(QPainter *p) +{ + QRect r = contentsRect(); + + if (!*in_game) { + p->fillRect(r, black); + return; + } + + p->drawPixmap(r.x(), r.y(), + sprites[next_piece_sprites[0]]); + p->drawPixmap(r.x()+sprite_width, r.y(), + sprites[next_piece_sprites[1]]); + p->drawPixmap(r.x(), r.y()+sprite_height, + sprites[next_piece_sprites[2]]); + p->drawPixmap(r.x()+sprite_width, r.y()+sprite_height, + sprites[next_piece_sprites[3]]); +} diff --git a/ksmiletris/npiecewidget.h b/ksmiletris/npiecewidget.h new file mode 100644 index 00000000..66095e31 --- /dev/null +++ b/ksmiletris/npiecewidget.h @@ -0,0 +1,49 @@ +/**************************************************************** +Copyright (c) 1998 Sandro Sigala . +All rights reserved. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of the author not be used in +advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +The author disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall the author be liable for any +special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#ifndef NPIECEWIDGET_H +#define NPIECEWIDGET_H + +#include + +#include "ksmiletris.h" + +class NextPieceWidget : public QFrame { +public: + NextPieceWidget(QPixmap *s, bool *game, bool *pause, + QWidget *parent=0, const char *name=0); + + void setBackgroundSprite(Sprite s) { bg_sprite = s; } + void setNextPieceSprites(Sprite *s) { next_piece_sprites = s; } + +private: + QPixmap *sprites; + bool *in_game, *in_pause; + Sprite bg_sprite; + Sprite *next_piece_sprites; + +protected: + void drawContents(QPainter *p); +}; + +#endif // !NPIECEWIDGET_H diff --git a/ksmiletris/screenwidget.cpp b/ksmiletris/screenwidget.cpp new file mode 100644 index 00000000..0080aadd --- /dev/null +++ b/ksmiletris/screenwidget.cpp @@ -0,0 +1,68 @@ +/**************************************************************** +Copyright (c) 1998 Sandro Sigala . +All rights reserved. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of the author not be used in +advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +The author disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall the author be liable for any +special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "ksmiletris.h" +#include "screenwidget.h" + +ScreenWidget::ScreenWidget(QPixmap *s, bool *game, bool *pause, + QWidget *parent, const char *name) + : QFrame(parent, name) +{ + in_game = game; + in_pause = pause; + sprites = s; + setFrameStyle(QFrame::Box | QFrame::Raised); + setLineWidth(2); + setMidLineWidth(1); + resize(scr_width * sprite_width + 10, scr_height * sprite_height + 10); +} + +void ScreenWidget::drawContents(QPainter *p) +{ + QRect r = contentsRect(); + + if (!*in_game) { + p->fillRect(r, black); + return; + } + + for (int y = 0; y < scr_height; ++y) + for (int x = 0; x < scr_width; ++x) + p->drawPixmap(r.x() + x*sprite_width, + r.y() + y*sprite_height, + sprites[screen_sprites[y*scr_width + x]]); + + if (*in_pause) { + QPixmap pause(locate("appdata", "data/pause.bmp")); + p->drawPixmap((width()-pause.width())/2, + (height()-pause.height())/2, pause); + } +} diff --git a/ksmiletris/screenwidget.h b/ksmiletris/screenwidget.h new file mode 100644 index 00000000..5643d7ed --- /dev/null +++ b/ksmiletris/screenwidget.h @@ -0,0 +1,49 @@ +/**************************************************************** +Copyright (c) 1998 Sandro Sigala . +All rights reserved. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of the author not be used in +advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +The author disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall the author be liable for any +special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#ifndef SCREENWIDGET_H +#define SCREENWIDGET_H + +#include + +#include "ksmiletris.h" + +class ScreenWidget : public QFrame { +public: + ScreenWidget(QPixmap *s, bool *game, bool *pause, + QWidget *parent=0, const char *name=0); + + void setBackgroundSprite(Sprite s) { bg_sprite = s; } + void setScreenSprites(Sprite *s) { screen_sprites = s; } + +private: + QPixmap *sprites; + bool *in_game, *in_pause; + Sprite bg_sprite; + Sprite *screen_sprites; + +protected: + void drawContents(QPainter *p); +}; + +#endif // !SCREENWIDGET_H diff --git a/ksmiletris/wav/Makefile.am b/ksmiletris/wav/Makefile.am new file mode 100644 index 00000000..1eed07ad --- /dev/null +++ b/ksmiletris/wav/Makefile.am @@ -0,0 +1,7 @@ +AUTOMAKE_OPTIONS = foreign + +sound_DATA = break.wav clear.wav + +sounddir = $(kde_datadir)/ksmiletris/sounds + +EXTRA_DIST = $(sound_DATA) diff --git a/ksmiletris/wav/break.wav b/ksmiletris/wav/break.wav new file mode 100644 index 00000000..a3596a20 Binary files /dev/null and b/ksmiletris/wav/break.wav differ diff --git a/ksmiletris/wav/clear.wav b/ksmiletris/wav/clear.wav new file mode 100644 index 00000000..dec7f26d Binary files /dev/null and b/ksmiletris/wav/clear.wav differ diff --git a/ksnake/ChangeLog b/ksnake/ChangeLog new file mode 100644 index 00000000..58991c06 --- /dev/null +++ b/ksnake/ChangeLog @@ -0,0 +1,21 @@ +Version 0.4.0: + * [Benjamin Meyer] Changed to use KDE highscore widget. + * [Benjamin Meyer] Changed to use KConfigXT and a configure dialog. + * [Benjamin Meyer] Removed unnecessary classes. + * [Benjamin Meyer] Lots of general code cleanup (headers/indentation/variable naming). + * [Benjamin Meyer] Added copyright headers to all of the source files. + +Version 0.3.1: + * [Andrew Chant] Cleaned up a lot of code + Fixed Start new game dialogue behavior + Fixed fonts on High Scores list. + Made 'Beginner' mode actually somewhat Easy!! +Version 0.3.0: + * [Andrew Chant] Added Window Resizing capability, so now works on + almost all resoloutions! +Version 0.2.1: + * [Robert Williams] Added -caption "%c" to ksnake.kdelnk + * [Robert Williams] Added getCaption() + * [Robert Williams] Added version.h + * [Robert Williams] Added getHelpMenu() + diff --git a/ksnake/Makefile.am b/ksnake/Makefile.am new file mode 100644 index 00000000..b46794a6 --- /dev/null +++ b/ksnake/Makefile.am @@ -0,0 +1,34 @@ + +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore/ $(all_includes) + +noinst_HEADERS = game.h rattler.h board.h bitmaps.h \ + level.h ball.h snake.h basket.h startroom.h \ + pixServer.h progress.h levels.h view.h version.h + +ksnake_SOURCES = game.cpp rattler.cpp board.cpp level.cpp ball.cpp \ + snake.cpp basket.cpp startroom.cpp \ + pixServer.cpp progress.cpp levels.cpp\ + view.cpp main.cpp settings.kcfgc appearance.ui general.ui + +ksnake_LDFLAGS = $(all_libraries) $(KDE_RPATH) +ksnake_LDADD = $(LIB_KDEUI) $(LIB_KDEGAMES) -lm +ksnake_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +METASOURCES = AUTO + +bin_PROGRAMS = ksnake + +######### build rules ################ + +xdg_apps_DATA = ksnake.desktop +kde_kcfg_DATA = ksnake.kcfg + +KDE_ICON = ksnake + +SUBDIRS = data + +rcdir = $(kde_datadir)/ksnake +rc_DATA = ksnakeui.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/ksnake.pot diff --git a/ksnake/appearance.ui b/ksnake/appearance.ui new file mode 100644 index 00000000..0f328f04 --- /dev/null +++ b/ksnake/appearance.ui @@ -0,0 +1,87 @@ + +Appearance + + + Appearance + + + + 0 + 0 + 334 + 285 + + + + + unnamed + + + + buttonGroup1 + + + Background + + + + unnamed + + + + kcfg_bgimage + + + + + kcfg_bgcolor + + + + + + + + kcfg_bgcolor_enabled + + + Color: + + + true + + + + + kcfg_bgimage_enabled + + + Image: + + + + + + + spacer2 + + + Vertical + + + Expanding + + + + 21 + 40 + + + + + + + + kcolorbutton.h + + diff --git a/ksnake/ball.cpp b/ksnake/ball.cpp new file mode 100644 index 00000000..2881beb9 --- /dev/null +++ b/ksnake/ball.cpp @@ -0,0 +1,128 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "ball.h" +#include "board.h" + +int bounce[8][8]={ + { NE, NW, SE, SW, N, E, S, W }, + { SE, SW, NE, NW, S, E, N, W }, + { NW, NE, SW, SE, N, W, S, E }, + { SW, SE, NW, NE, S, W, N, E }, + { NE, NW, SE, SW, N, E, S, W }, + { SE, SW, NE, NW, S, E, N, W }, + { NW, NE, SW, SE, N, W, S, E }, + { SW, SE, NW, NE, S, W, N, E } +}; + +Ball::Ball(Board *b, PixServer *p) +{ + board = b; + pixServer = p; + + int i = BoardWidth+1; + while( !board->isEmpty(i) ) i++; + hold = index = i; + board->set(index, Balle); + next = SE; +} + +void Ball::zero() +{ + board->set(index, empty); + pixServer->erase(index); +} + +void Ball::nextMove() +{ + hold = index; + board->set(hold, empty); + + for ( int x = 0; x < 8 ; x++) { + int d = bounce[next][x]; + int nextSq = board->getNext(d, index); + + if (board->isHead(nextSq) || board->isEmpty(nextSq)) { + next = d; + index = nextSq; + board->set(index, Balle); + break; + } + } +} + +void Ball::repaint() +{ + static int i = 0; + static bool rotate = true; + + pixServer->erase(hold); + pixServer->draw(index, BallPix, i); + + if (rotate) + if (++i > 3) i=0; + + rotate = !rotate; +} + +void KillerBall::nextMove() +{ + hold = index; + board->set(hold, empty); + + // Find the snake. + int sn = board->samyHeadIndex(); + if(board->isHead(sn)) { + int nextSq = getNextSquare(); + if(nextSq != -1) { + next = board->direction(index, nextSq); + index = nextSq; + board->set(index, Balle); + return; + } + } + + for ( int x = 0; x < 8 ; x++) { + int d = bounce[next][x]; + int nextSq = board->getNext(d, index); + + if (board->isHead(nextSq) || board->isEmpty(nextSq)) { + next = d; + index = nextSq; + board->set(index, Balle); + break; + } + } +} + +int KillerBall::getNextSquare() +{ + return board->getNextCloseTo(index, board->samyHeadIndex(), true); +} + +int DumbKillerBall::getNextSquare() +{ + return board->getNextCloseToDumb(index, board->samyHeadIndex()); +} diff --git a/ksnake/ball.h b/ksnake/ball.h new file mode 100644 index 00000000..e7796354 --- /dev/null +++ b/ksnake/ball.h @@ -0,0 +1,62 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef BALL_H +#define BALL_H + +#include "pixServer.h" + +class Ball { +public: + Ball(Board *b, PixServer *p); + virtual ~Ball(){} + virtual void nextMove(); + void repaint(); + void zero(); +protected: + Board *board; + PixServer *pixServer; + int index; + int hold; + int next; +}; + +class KillerBall : public Ball { +public: + KillerBall(Board *b, PixServer *p) : Ball(b, p) {} + virtual ~KillerBall(){} + void nextMove(); +protected: + virtual int getNextSquare(); +}; + +class DumbKillerBall : public KillerBall { +public: + DumbKillerBall(Board *b, PixServer *p) : KillerBall(b, p) {} +protected: + int getNextSquare(); +}; + +#endif // BALL_H diff --git a/ksnake/basket.cpp b/ksnake/basket.cpp new file mode 100644 index 00000000..c03b1deb --- /dev/null +++ b/ksnake/basket.cpp @@ -0,0 +1,118 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include "board.h" +#include "basket.h" +#include "pixServer.h" + +Kaffee::Kaffee(int pos, int r1, int r2) +{ + p = pos; + t = Red; + r = r2; + QTimer::singleShot( r1, this, SLOT(golden()) ); + dirty = true; +} + +void Kaffee::golden() +{ + dirty = true; + t = (t == Red ? Golden : Red); + QTimer::singleShot( r, this, SLOT(golden()) ); +} + +Basket::Basket(Board *b, PixServer *p) +{ + board = b; + pixServer = p; + list = new QPtrList; + list->setAutoDelete( true ); +} + +Basket::~Basket() +{ + delete list; +} + +void Basket::clear() +{ + if( !list->isEmpty()) + list->clear(); +} + +void Basket::newApples() +{ + int x; + int i = 0; + + while(i < 10) { + x = random.getLong(board->size()); + if ((unsigned)x < board->size() && board->isEmpty(x) && x > BoardWidth+4) { + Kaffee *g = new Kaffee(x, random.getLong(40000), random.getLong(40000)); + board->set(x, Apple); + list->append(g); + i++; + } + } +} + +void Basket::repaint(bool dirty ) +{ + Kaffee *g; + for ( g = list->first(); g != 0; g = list->next()) { + if (dirty) { + pixServer->draw(g->position(), ApplePix, (int)g->type()); + g->dirty = false; + } + else if (g->dirty) { + pixServer->draw(g->position(), ApplePix, (int)g->type()); + g->dirty = false; + } + } +} + +Fruits Basket::eaten(int i) +{ + Kaffee *g; + Fruits f = Red; + + for (g = list->first(); g != 0; g = list->next() ) + { + if (g->position() == i) { + f = g->type(); + list->remove(g); + break; + } + } + if (list->isEmpty()) + emit openGate(); + + return f; +} + +#include "basket.moc" diff --git a/ksnake/basket.h b/ksnake/basket.h new file mode 100644 index 00000000..eb965e4a --- /dev/null +++ b/ksnake/basket.h @@ -0,0 +1,70 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef BASKET_H +#define BASKET_H + +#include + +class PixServer; + +enum Fruits { Red, Golden }; + +class Kaffee : public QObject +{ + Q_OBJECT +public: + Kaffee(int pos, int r1, int r2); + int position() { return p;} + Fruits type() { return t;} + bool dirty; +private slots: + void golden(); +private: + int p; + int r; + Fruits t; +}; + +class Basket : public QObject +{ + Q_OBJECT +public: + Basket(Board *b, PixServer *p); + ~Basket(); + void repaint(bool); + void newApples(); + void clear(); + Fruits eaten( int i); +signals: + void openGate(); +private: + Board *board; + PixServer *pixServer; + QPtrList *list; + KRandomSequence random; +}; + +#endif // BASKET_H diff --git a/ksnake/bitmaps.h b/ksnake/bitmaps.h new file mode 100644 index 00000000..95b17ed4 --- /dev/null +++ b/ksnake/bitmaps.h @@ -0,0 +1,106 @@ +#define zero_width 7 +#define zero_height 9 +static unsigned char zero_bits[] = { + 0x1e, 0x33, 0x3b, 0x3b, 0x33, 0x37, 0x37, 0x33, 0x1e}; + +#define one_width 7 +#define one_height 9 +static unsigned char one_bits[] = { + 0x18, 0x1c, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}; + +#define two_width 7 +#define two_height 9 +static unsigned char two_bits[] = { + 0x1e, 0x3f, 0x33, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x3f}; + +#define three_width 7 +#define three_height 9 +static unsigned char three_bits[] = { + 0x1e, 0x3f, 0x33, 0x30, 0x1c, 0x30, 0x33, 0x33, 0x1e}; + +#define four_width 7 +#define four_height 9 +static unsigned char four_bits[] = { + 0x06, 0x06, 0x36, 0x36, 0x36, 0x33, 0x7f, 0x30, 0x30}; + +#define five_width 7 +#define five_height 9 +static unsigned char five_bits[] = { + 0x3f, 0x03, 0x03, 0x03, 0x1f, 0x30, 0x30, 0x18, 0x0f}; + +#define six_width 7 +#define six_height 9 +static unsigned char six_bits[] = { + 0x1c, 0x0c, 0x06, 0x1f, 0x33, 0x33, 0x33, 0x33, 0x1e}; + +#define seven_width 7 +#define seven_height 9 +static unsigned char seven_bits[] = { + 0x7f, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x0c}; + +#define eight_width 7 +#define eight_height 9 +static unsigned char eight_bits[] = { + 0x1e, 0x33, 0x33, 0x37, 0x1e, 0x3b, 0x33, 0x33, 0x1e}; + +#define nine_width 7 +#define nine_height 9 +static unsigned char nine_bits[] = { + 0x1e, 0x33, 0x33, 0x33, 0x33, 0x3e, 0x18, 0x0c, 0x0e}; + +#define intro_width 35 +#define intro_height 35 +static unsigned char intro_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x71, 0x00, 0x00, 0x00, 0x04, 0xf9, 0x00, 0xc0, 0x00, + 0x04, 0x8d, 0x01, 0xc0, 0x00, 0x04, 0x8d, 0x01, 0xc0, 0xe4, 0x04, 0x8d, + 0x0d, 0xce, 0xf2, 0x05, 0x0d, 0x7c, 0xdb, 0x9a, 0x05, 0x19, 0x6c, 0xdb, + 0x99, 0x05, 0x31, 0x6c, 0xd8, 0x98, 0x05, 0x61, 0x6c, 0xde, 0xf9, 0x05, + 0xc1, 0x6c, 0xdb, 0x12, 0x04, 0x8d, 0x6d, 0xdf, 0xe6, 0x05, 0xf9, 0x00, + 0xd8, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xf9, + 0x00, 0x00, 0x00, 0x04, 0x19, 0x03, 0x00, 0x00, 0x04, 0x19, 0x02, 0x00, + 0x00, 0x04, 0x19, 0x06, 0x00, 0x38, 0x04, 0x19, 0x06, 0x00, 0x7c, 0x04, + 0x19, 0xe3, 0x70, 0x66, 0x04, 0x99, 0xb1, 0xd9, 0x66, 0x04, 0xf9, 0xb0, + 0xd9, 0x66, 0x04, 0x99, 0x81, 0xd9, 0x7e, 0x04, 0x19, 0xe1, 0x19, 0x04, + 0x04, 0x19, 0xb2, 0x99, 0x78, 0x04, 0x19, 0xf0, 0x71, 0x00, 0x04, 0x01, + 0x80, 0x01, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; + +#define level_width 35 +#define level_height 35 +static unsigned char level_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x0d, 0x00, 0x00, 0xf0, + 0x04, 0x0d, 0x00, 0x00, 0xc0, 0x04, 0x0d, 0x00, 0x00, 0xc0, 0x04, 0x0d, + 0x9e, 0x99, 0xc7, 0x04, 0x0d, 0xb3, 0xd9, 0xcc, 0x04, 0x0d, 0xb3, 0xd9, + 0xcc, 0x04, 0x0d, 0xbf, 0xd9, 0xcf, 0x04, 0x0d, 0x83, 0xd9, 0xc0, 0x04, + 0x0d, 0x03, 0xcf, 0xc0, 0x04, 0x7d, 0x1e, 0x86, 0xf7, 0x05, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; + +#define gameover_width 35 +#define gameover_height 35 +static unsigned char gameover_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x79, 0x00, 0x00, 0x00, 0x04, 0xcd, + 0x00, 0x00, 0x00, 0x04, 0xcd, 0x78, 0xfc, 0x78, 0x04, 0x0d, 0xc0, 0xac, + 0xcd, 0x04, 0x0d, 0xc0, 0xac, 0xcd, 0x04, 0xed, 0xf8, 0xac, 0xfd, 0x04, + 0xcd, 0xcc, 0xac, 0x0d, 0x04, 0xcd, 0xcc, 0xac, 0x0d, 0x04, 0xf9, 0xf8, + 0x8c, 0x79, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xf1, + 0x00, 0x00, 0x00, 0x04, 0x99, 0x01, 0x00, 0x00, 0x04, 0x99, 0x99, 0xf1, + 0x98, 0x05, 0x99, 0x99, 0x99, 0xd9, 0x05, 0x99, 0x99, 0x99, 0x39, 0x04, + 0x99, 0x99, 0xf9, 0x19, 0x04, 0x99, 0x99, 0x19, 0x18, 0x04, 0x99, 0xf1, + 0x18, 0x18, 0x04, 0xf1, 0x60, 0xf0, 0x18, 0x04, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/board.cpp b/ksnake/board.cpp new file mode 100644 index 00000000..9179dfb1 --- /dev/null +++ b/ksnake/board.cpp @@ -0,0 +1,330 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include "board.h" + +Pos::Pos(Pos *p, int i, int r) { + _parent = p; + _index = i; + _price = r; + left = right = next = fnext = 0; + inList = false; +} +Pos::~Pos() { + delete fnext; +} +int Pos::index() const { + return(_index); +} +void Pos::setPrice(int p) { + _price = p; +} +int Pos::price() const { + return(_price); +} +void Pos::setParent(Pos *p) { + _parent = p; +} +Pos *Pos::parent() const { + return(_parent); +} +Pos *Pos::listNext() { + inList = false; + return(next); +} +void Pos::addBTree(Pos *np) { + // Check direction np is going to. + Pos **p = 0; + if(np->index() < index()) + p = &left; + else if(np->index() > index()) + p = &right; + else { + qFatal("Repeated nodes on btree should never happens"); + } + + if(! *p) { + *p = np; + } + else { + (*p)->addBTree(np); + } +} +Pos *Pos::searchBTree(int i) { + if(i == index()) { + return(this); + } + else if(i < index() && left) { + return(left->searchBTree(i)); + } + else if(right) { + return(right->searchBTree(i)); + } + // Node not found. + return(0); +} +void Pos::addFList(Pos *np) { + np->fnext = fnext; + fnext = np; +} +void Pos::addList(Pos *np) { + if(np->inList) + return; // We're already in list. + + np->inList = true; + Pos *p, *n; + for(p = this; p; p = n) { + // Check if the node next to p has a higher price. + n = p->next; + if(! n) { + // The new node go to tail. + np->next = 0; + p->next = np; + return; + } + + if(np->price() <= n->price()) { + // Add new node after p. + np->next = p->next; + p->next = np; + return; + } + } + qFatal("Shouldn't reach this point"); +} + +Board::Board(int s) + :QMemArray (s) +{ + sz = s; + samyIndex = -1; +} + +void Board::index(int i) +{ + row = i/BoardWidth; + col = i-(row*BoardWidth); +} + +bool Board::inBounds(int i) +{ + return ( i < 0 || i > sz-1 ? false : true); +} + +void Board::set(int i, Square sq) +{ + if (inBounds(i)) + at(i) = sq; + if(sq == head) + samyIndex = i; +} + +QRect Board::rect(int i) +{ + index(i); + return (QRect(col*BRICKSIZE, row*BRICKSIZE, BRICKSIZE, BRICKSIZE)); +} + +bool Board::isEmpty(int i) +{ + if (inBounds(i)) + return (at(i) == empty ? true : false); + return true; +} + +bool Board::isBrick(int i) +{ + if (inBounds(i)) + return (at(i) == brick ? true : false); + return false; +} + +bool Board::isApple(int i) +{ + if (inBounds(i)) + return (at(i) == Apple ? true : false); + return false; +} + +bool Board::isHead(int i) +{ + if (inBounds(i)) + return (at(i) == head ? true : false); + return false; +} + +bool Board::isSnake(int i) +{ + if (inBounds(i)) + return (at(i) == snake ? true : false); + return false; +} + +int Board::getNext(int n, int i) +{ + index(i); + + switch(n) + { + case NW: + return( i >= BoardWidth && col > 0 ? (i-BoardWidth)-1 : OUT); + case N: + return( i >= BoardWidth ? i-BoardWidth : OUT ); + case NE: + return( i >= BoardWidth && col < BoardWidth-1 ? (i-BoardWidth)+1 : OUT); + case W: + return(col > 0 ? i-1 : OUT ); + case E: + return(col < BoardWidth-1 ? i+1 : OUT ); + case SW: + return( row < sz-BoardWidth && col > 0 ? (i+BoardWidth)-1 : OUT); + case S: + return( row < sz-BoardWidth ? i+BoardWidth : OUT ); + case SE: + return( row < sz-BoardWidth && col < BoardWidth-1 ? (i+BoardWidth)+1 : OUT); + default: + return OUT; + } +} + +int Board::getNextCloseToDumb(int s, int d) +{ + if(s == d) + return(-1); // What can I say, we're here! ;o) + + int nextSq = getNext(direction(s, d), s); + + if(! isEmpty(nextSq)) + return(-1); + + return(nextSq); +} + +int Board::getNextCloseTo(int s, int d, bool diag, int lastIndex) +{ + if(s == d) + return(-1); // What can I say, we're here! ;o) + + const int firstN = diag ? 4 : 0; + const int lastN = diag ? 8 : 4; + + Pos *root = new Pos(0, s, 0), *list = root; + + // List of indexes. + for(; list; list = list->listNext()) { + Pos *p; + + // Check if current list node is the destination position. + if(list->index() == d) { + // Find first movement after root. + for(; ; list = p) { + p = list->parent(); + if(p == root) { + // This is our move. + int nextSq = list->index(); + delete root; + index(nextSq); + return(nextSq); + } + } + qFatal("Never here"); + } + + // Make possible moves. + for(int n = firstN; n < lastN; n ++) { + int i = getNext(n, list->index()); + int pri = list->price() + 1; + + // getNext returned valid place? + if(! inBounds(i) || (! isEmpty(i) && i != d)) { + // Or place is out of map or it's not empty, + // so go to the next possible move. + continue; + } + + int pi = list->parent() ? list->parent()->index() : lastIndex; + if(pi != -1 && direction(pi, list->index()) != + direction(list->index(), i)) { + pri += 10; + } + + // Check if position wasn't processed yet. + if( (p = root->searchBTree(i))) { + // Position already processed. + // Check price of found position with current one. + if(p->price() > pri) { + // We found a cheapear way to reach the same + // place, so let's change the parent and price of p. + p->setPrice(list->price() + 1); + p->setParent(list); + list->addList(p); + } + continue; + } + + // Create new Pos class instance. + p = new Pos(list, i, pri); + + // Add. + list->addList(p); + root->addFList(p); + root->addBTree(p); + } + } + + // Solution not found. + delete root; + return(-1); +} + +int Board::direction(int s, int d) +{ + index(s); + int scol = col, srow = row; + index(d); + if(scol > col) { // Left. + if(srow < row) + return(SW); + else if(srow > row) + return(NW); + else + return(W); + } + else if(scol < col) { // Right. + if(srow < row) + return(SE); + else if(srow > row) + return(NE); + else + return(E); + } + else { // X's the same. + if(srow < row) + return(S); + else + return(N); + } +} diff --git a/ksnake/board.h b/ksnake/board.h new file mode 100644 index 00000000..0fa43d7e --- /dev/null +++ b/ksnake/board.h @@ -0,0 +1,100 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef BOARD_H +#define BOARD_H + +enum Square {empty, brick, Apple, Balle, snake, head}; +enum Directions {N=0,E=2,S=1,W=3,NE,SE,NW,SW}; + +#define BoardWidth 35 +extern int BRICKSIZE; +extern int MAPWIDTH; +extern int MAPHEIGHT; +#define OUT -1 + +typedef int Gate; +const int NORTH_GATE = BoardWidth/2; +const int SOUTH_GATE =(BoardWidth*BoardWidth)-(NORTH_GATE+1); + +class Pos { +public: + Pos(Pos *p, int i, int r); + ~Pos(); + int index() const; + void setPrice(int p); + int price() const; + void setParent(Pos *p); + Pos *parent() const; + Pos *listNext(); + void addBTree(Pos *np); + Pos *searchBTree(int i); + void addFList(Pos *); + void addList(Pos *np); +private: + int _index; + int _price; // Price to reach this position. + Pos *_parent; // Way to reach destination. + Pos *next; // Single linked list. + Pos *fnext; // Link all Pos instances, so we can delete them all. + Pos *left, *right; // BTree. + bool inList; +}; + +class Board : public QMemArray +{ +public: + Board (int s); + ~Board() {} + QRect rect(int i); + + void set(int i, Square sq); + + bool isBrick(int i); + bool isEmpty(int i); + bool isApple(int i); + bool isHead(int i); + bool isSnake(int i); + + int getNext(int, int); + int getNextCloseToDumb(int, int); + int getNextCloseTo(int, int, bool, int = -1); + int direction(int, int); + + int samyHeadIndex() const { + return(samyIndex); + } + +private: + void index(int i); + bool inBounds(int i); + int row; + int col; + int sz; + + int samyIndex; +}; + +#endif // BOARD_H diff --git a/ksnake/data/Makefile.am b/ksnake/data/Makefile.am new file mode 100644 index 00000000..42d15ec2 --- /dev/null +++ b/ksnake/data/Makefile.am @@ -0,0 +1,9 @@ + + +highscoredir = $(kde_datadir)/ksnake +highscore_DATA = highScores + +SUBDIRS = levels pixmaps backgrounds + +EXTRA_DIST = $(highscore_DATA) + diff --git a/ksnake/data/backgrounds/Bark.png b/ksnake/data/backgrounds/Bark.png new file mode 100644 index 00000000..68180cdd Binary files /dev/null and b/ksnake/data/backgrounds/Bark.png differ diff --git a/ksnake/data/backgrounds/Blue_Carpet.png b/ksnake/data/backgrounds/Blue_Carpet.png new file mode 100644 index 00000000..92c8c8f2 Binary files /dev/null and b/ksnake/data/backgrounds/Blue_Carpet.png differ diff --git a/ksnake/data/backgrounds/Dark_Wood.png b/ksnake/data/backgrounds/Dark_Wood.png new file mode 100644 index 00000000..9767ec63 Binary files /dev/null and b/ksnake/data/backgrounds/Dark_Wood.png differ diff --git a/ksnake/data/backgrounds/Granite.png b/ksnake/data/backgrounds/Granite.png new file mode 100644 index 00000000..2dc1c2a9 Binary files /dev/null and b/ksnake/data/backgrounds/Granite.png differ diff --git a/ksnake/data/backgrounds/Green_Carpet.png b/ksnake/data/backgrounds/Green_Carpet.png new file mode 100644 index 00000000..ba18e54c Binary files /dev/null and b/ksnake/data/backgrounds/Green_Carpet.png differ diff --git a/ksnake/data/backgrounds/Makefile.am b/ksnake/data/backgrounds/Makefile.am new file mode 100644 index 00000000..597ab5a8 --- /dev/null +++ b/ksnake/data/backgrounds/Makefile.am @@ -0,0 +1,8 @@ + +bgdir = $(kde_datadir)/ksnake/backgrounds + +bg_DATA = Bark.png Blue_Carpet.png Dark_Wood.png Granite.png \ + Green_Carpet.png Mystique.png Rope_Weave.png Volcanic.png \ + Wood.png + +EXTRA_DIST = $(bg_DATA) diff --git a/ksnake/data/backgrounds/Mystique.png b/ksnake/data/backgrounds/Mystique.png new file mode 100644 index 00000000..92f59620 Binary files /dev/null and b/ksnake/data/backgrounds/Mystique.png differ diff --git a/ksnake/data/backgrounds/Rope_Weave.png b/ksnake/data/backgrounds/Rope_Weave.png new file mode 100644 index 00000000..70903bbc Binary files /dev/null and b/ksnake/data/backgrounds/Rope_Weave.png differ diff --git a/ksnake/data/backgrounds/Volcanic.png b/ksnake/data/backgrounds/Volcanic.png new file mode 100644 index 00000000..90d185fc Binary files /dev/null and b/ksnake/data/backgrounds/Volcanic.png differ diff --git a/ksnake/data/backgrounds/Wood.png b/ksnake/data/backgrounds/Wood.png new file mode 100644 index 00000000..c317f8df Binary files /dev/null and b/ksnake/data/backgrounds/Wood.png differ diff --git a/ksnake/data/highScores b/ksnake/data/highScores new file mode 100644 index 00000000..41c812e8 Binary files /dev/null and b/ksnake/data/highScores differ diff --git a/ksnake/data/levels/Makefile.am b/ksnake/data/levels/Makefile.am new file mode 100644 index 00000000..6918477d --- /dev/null +++ b/ksnake/data/levels/Makefile.am @@ -0,0 +1,9 @@ + +room_DATA = room01 room02 room03 room04 room05 room06 room07 \ + room08 room09 room10 room11 room12 room13 room14 room15 \ + room16 room17 room18 room19 room20 room21 room22 room23 \ + room24 room25 + +roomdir = $(kde_datadir)/ksnake/levels + +EXTRA_DIST = $(room_DATA) diff --git a/ksnake/data/levels/room01 b/ksnake/data/levels/room01 new file mode 100644 index 00000000..de31fc6a --- /dev/null +++ b/ksnake/data/levels/room01 @@ -0,0 +1,18 @@ +#define room01_width 35 +#define room01_height 35 +static unsigned char room01_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x01, 0x01, 0x00, 0x04, 0x04, 0x01, 0x01, 0x00, 0x04, 0x04, 0x01, + 0x01, 0x00, 0x04, 0x04, 0x01, 0x01, 0x00, 0x04, 0x04, 0x01, 0x01, 0x00, + 0x04, 0x04, 0x01, 0x01, 0x00, 0x04, 0x04, 0x01, 0x01, 0x00, 0x04, 0x04, + 0x01, 0x01, 0x00, 0x04, 0x04, 0x01, 0x01, 0x00, 0x04, 0x04, 0x01, 0x01, + 0x00, 0x04, 0x04, 0x01, 0x01, 0x00, 0x04, 0x04, 0x01, 0x01, 0x00, 0x04, + 0x04, 0x01, 0x01, 0x00, 0x04, 0x04, 0x01, 0x01, 0x00, 0x04, 0x04, 0x01, + 0x01, 0x00, 0x04, 0x04, 0x01, 0x01, 0x00, 0x04, 0x04, 0x01, 0x01, 0x00, + 0x04, 0x04, 0x01, 0x01, 0x00, 0x04, 0x04, 0x01, 0x01, 0x00, 0x04, 0x04, + 0x01, 0x01, 0x00, 0x04, 0x04, 0x01, 0x01, 0x00, 0x04, 0x04, 0x01, 0x01, + 0x00, 0x04, 0x04, 0x01, 0x01, 0x00, 0x04, 0x04, 0x01, 0x01, 0x00, 0x04, + 0x04, 0x01, 0x01, 0x00, 0x04, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room02 b/ksnake/data/levels/room02 new file mode 100644 index 00000000..4c73afef --- /dev/null +++ b/ksnake/data/levels/room02 @@ -0,0 +1,18 @@ +#define room2_width 35 +#define room2_height 35 +static unsigned char room2_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xc1, 0xff, 0xff, 0x1f, 0x04, 0x01, + 0x00, 0x02, 0x00, 0x04, 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0x00, 0x02, + 0x00, 0x04, 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0x00, 0x02, 0x00, 0x04, + 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0x00, + 0x02, 0x00, 0x04, 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0x00, 0x02, 0x00, + 0x04, 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, + 0x00, 0x02, 0x00, 0x04, 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0x00, 0x02, + 0x00, 0x04, 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0x00, 0x02, 0x00, 0x04, + 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0x00, + 0x02, 0x00, 0x04, 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room03 b/ksnake/data/levels/room03 new file mode 100644 index 00000000..c7738cc8 --- /dev/null +++ b/ksnake/data/levels/room03 @@ -0,0 +1,18 @@ +#define room3_width 35 +#define room3_height 35 +static unsigned char room3_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xc1, 0xff, 0xff, 0x1f, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, + 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, + 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, + 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, + 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, + 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, + 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, + 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, + 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, + 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, + 0x10, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room04 b/ksnake/data/levels/room04 new file mode 100644 index 00000000..0085400a --- /dev/null +++ b/ksnake/data/levels/room04 @@ -0,0 +1,18 @@ +#define room4_width 35 +#define room4_height 35 +static unsigned char room4_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xc1, 0x1f, 0xc0, 0x1f, 0x04, 0x41, + 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, + 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, + 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, + 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, + 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, + 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, + 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, + 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, + 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0xc1, 0x1f, 0xc0, 0x1f, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room05 b/ksnake/data/levels/room05 new file mode 100644 index 00000000..814e56d9 --- /dev/null +++ b/ksnake/data/levels/room05 @@ -0,0 +1,18 @@ +#define room5_width 35 +#define room5_height 35 +static unsigned char room5_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x41, 0x00, 0xfe, 0x1f, 0x04, 0x41, + 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, + 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, + 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, + 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, + 0x04, 0x41, 0x00, 0x02, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, + 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, + 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, + 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0x41, 0x00, + 0x00, 0x10, 0x04, 0x41, 0x00, 0x00, 0x10, 0x04, 0xc1, 0xff, 0x03, 0x10, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room06 b/ksnake/data/levels/room06 new file mode 100644 index 00000000..bd5c36f8 --- /dev/null +++ b/ksnake/data/levels/room06 @@ -0,0 +1,18 @@ +#define room6_width 35 +#define room6_height 35 +static unsigned char room6_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x41, 0x04, 0x00, 0x11, 0x04, 0x41, + 0x04, 0x00, 0x11, 0x04, 0x41, 0x04, 0x00, 0x11, 0x04, 0x41, 0x04, 0x00, + 0x11, 0x04, 0x41, 0x04, 0x00, 0x11, 0x04, 0x41, 0x04, 0x00, 0x11, 0x04, + 0x41, 0x04, 0x00, 0x11, 0x04, 0x41, 0x04, 0x00, 0x11, 0x04, 0x41, 0x04, + 0x00, 0x11, 0x04, 0x41, 0x04, 0x00, 0x11, 0x04, 0x41, 0x04, 0x00, 0x11, + 0x04, 0x41, 0x04, 0x00, 0x11, 0x04, 0x41, 0x04, 0x00, 0x11, 0x04, 0x41, + 0x04, 0x00, 0x11, 0x04, 0x41, 0x04, 0x00, 0x11, 0x04, 0x41, 0x04, 0x00, + 0x11, 0x04, 0x41, 0x04, 0x00, 0x11, 0x04, 0x41, 0x04, 0x00, 0x11, 0x04, + 0x41, 0x04, 0x00, 0x11, 0x04, 0x41, 0x04, 0x00, 0x11, 0x04, 0x41, 0x04, + 0x00, 0x11, 0x04, 0x41, 0x04, 0x00, 0x11, 0x04, 0x41, 0x04, 0x00, 0x11, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room07 b/ksnake/data/levels/room07 new file mode 100644 index 00000000..3330a24b --- /dev/null +++ b/ksnake/data/levels/room07 @@ -0,0 +1,18 @@ +#define room7_width 35 +#define room7_height 35 +static unsigned char room7_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, + 0x04, 0xe1, 0xff, 0xff, 0x3f, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0xe1, 0xff, 0xf8, 0x3f, 0x04, 0x01, 0x80, 0x08, + 0x00, 0x04, 0x01, 0x80, 0x08, 0x00, 0x04, 0x01, 0x80, 0x08, 0x00, 0x04, + 0x01, 0x80, 0x08, 0x00, 0x04, 0x01, 0x80, 0x08, 0x00, 0x04, 0x01, 0x80, + 0x08, 0x00, 0x04, 0x01, 0x80, 0x08, 0x00, 0x04, 0x01, 0x80, 0x08, 0x00, + 0x04, 0x01, 0x80, 0x08, 0x00, 0x04, 0x01, 0x80, 0x08, 0x00, 0x04, 0x01, + 0x80, 0x08, 0x00, 0x04, 0x01, 0x80, 0x08, 0x00, 0x04, 0x01, 0x80, 0x08, + 0x00, 0x04, 0x01, 0x80, 0x08, 0x00, 0x04, 0x01, 0x80, 0x08, 0x00, 0x04, + 0x01, 0x80, 0x08, 0x00, 0x04, 0x01, 0x80, 0x08, 0x00, 0x04, 0x01, 0x80, + 0x08, 0x00, 0x04, 0x01, 0x80, 0x08, 0x00, 0x04, 0x01, 0x80, 0x08, 0x00, + 0x04, 0x01, 0x80, 0x08, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room08 b/ksnake/data/levels/room08 new file mode 100644 index 00000000..00ae3b91 --- /dev/null +++ b/ksnake/data/levels/room08 @@ -0,0 +1,18 @@ +#define room8_width 35 +#define room8_height 35 +static unsigned char room8_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, + 0x04, 0xe1, 0xff, 0xff, 0x3f, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0xe1, 0xff, 0xff, 0x3f, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x21, 0x01, 0x00, 0x24, 0x04, + 0x21, 0x01, 0x00, 0x24, 0x04, 0x21, 0x01, 0x00, 0x24, 0x04, 0x21, 0x01, + 0x00, 0x24, 0x04, 0x21, 0x01, 0x00, 0x24, 0x04, 0x21, 0x01, 0x00, 0x24, + 0x04, 0x21, 0x01, 0x00, 0x24, 0x04, 0x21, 0x01, 0x00, 0x24, 0x04, 0x21, + 0x01, 0x00, 0x24, 0x04, 0x21, 0x01, 0x00, 0x24, 0x04, 0x21, 0x01, 0x00, + 0x24, 0x04, 0x21, 0x01, 0x00, 0x24, 0x04, 0x21, 0x01, 0x00, 0x24, 0x04, + 0x21, 0x01, 0x00, 0x24, 0x04, 0x21, 0x01, 0x00, 0x24, 0x04, 0x21, 0x01, + 0x00, 0x24, 0x04, 0x21, 0x01, 0x00, 0x24, 0x04, 0x21, 0x01, 0x00, 0x24, + 0x04, 0x21, 0x01, 0x00, 0x24, 0x04, 0x21, 0x01, 0x00, 0x24, 0x04, 0x21, + 0x01, 0x00, 0x24, 0x04, 0x21, 0x01, 0x00, 0x24, 0x04, 0x21, 0x01, 0x00, + 0x24, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room09 b/ksnake/data/levels/room09 new file mode 100644 index 00000000..8fe76e00 --- /dev/null +++ b/ksnake/data/levels/room09 @@ -0,0 +1,18 @@ +#define room9_width 35 +#define room9_height 35 +static unsigned char room9_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xf1, 0x7f, 0xf0, 0x7f, + 0x04, 0x11, 0x00, 0x00, 0x40, 0x04, 0x11, 0x00, 0x00, 0x40, 0x04, 0x11, + 0x00, 0x00, 0x40, 0x04, 0x11, 0x7f, 0xf0, 0x47, 0x04, 0x11, 0x01, 0x00, + 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, + 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, + 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, + 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, + 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, + 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, + 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x7f, + 0xf0, 0x47, 0x04, 0x11, 0x00, 0x00, 0x40, 0x04, 0x11, 0x00, 0x00, 0x40, + 0x04, 0x11, 0x00, 0x00, 0x40, 0x04, 0xf1, 0x7f, 0xf0, 0x7f, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room10 b/ksnake/data/levels/room10 new file mode 100644 index 00000000..a50f3f39 --- /dev/null +++ b/ksnake/data/levels/room10 @@ -0,0 +1,18 @@ +#define room10_width 35 +#define room10_height 35 +static unsigned char room10_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x11, 0x01, 0xfe, 0x7f, + 0x04, 0x11, 0x01, 0x00, 0x40, 0x04, 0x11, 0x01, 0x00, 0x40, 0x04, 0x11, + 0x01, 0x00, 0x40, 0x04, 0x11, 0x01, 0xfe, 0x47, 0x04, 0x11, 0x01, 0x00, + 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, + 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, + 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x05, 0x44, + 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x05, 0x44, 0x04, 0x11, + 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, + 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, + 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x7f, + 0x00, 0x44, 0x04, 0x11, 0x00, 0x00, 0x44, 0x04, 0x11, 0x00, 0x00, 0x44, + 0x04, 0x11, 0x00, 0x00, 0x44, 0x04, 0xf1, 0x7f, 0x00, 0x44, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room11 b/ksnake/data/levels/room11 new file mode 100644 index 00000000..6a0c1a1d --- /dev/null +++ b/ksnake/data/levels/room11 @@ -0,0 +1,18 @@ +#define room11_width 35 +#define room11_height 35 +static unsigned char room11_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x91, 0x04, 0x00, 0x49, + 0x04, 0x91, 0x04, 0x00, 0x49, 0x04, 0x91, 0x04, 0x00, 0x49, 0x04, 0x91, + 0x04, 0x00, 0x49, 0x04, 0x91, 0x04, 0x00, 0x49, 0x04, 0x91, 0x04, 0x00, + 0x49, 0x04, 0x91, 0x04, 0x00, 0x49, 0x04, 0x91, 0x04, 0x00, 0x49, 0x04, + 0x91, 0x04, 0x00, 0x49, 0x04, 0x91, 0x04, 0x00, 0x49, 0x04, 0x91, 0x04, + 0x00, 0x49, 0x04, 0x91, 0x04, 0x00, 0x49, 0x04, 0x91, 0x04, 0x00, 0x49, + 0x04, 0x91, 0x04, 0x00, 0x49, 0x04, 0x91, 0x04, 0x00, 0x49, 0x04, 0x91, + 0x04, 0x00, 0x49, 0x04, 0x91, 0x04, 0x00, 0x49, 0x04, 0x91, 0x04, 0x00, + 0x49, 0x04, 0x91, 0x04, 0x00, 0x49, 0x04, 0x91, 0x04, 0x00, 0x49, 0x04, + 0x91, 0x04, 0x00, 0x49, 0x04, 0x91, 0x04, 0x00, 0x49, 0x04, 0x91, 0x04, + 0x00, 0x49, 0x04, 0x91, 0x04, 0x00, 0x49, 0x04, 0x91, 0x04, 0x00, 0x49, + 0x04, 0x91, 0x04, 0x00, 0x49, 0x04, 0x91, 0x04, 0x00, 0x49, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room12 b/ksnake/data/levels/room12 new file mode 100644 index 00000000..ce609936 --- /dev/null +++ b/ksnake/data/levels/room12 @@ -0,0 +1,18 @@ +#define room12_width 35 +#define room12_height 35 +static unsigned char room12_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xf1, 0xff, 0xff, 0x7f, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xf1, + 0xff, 0xf8, 0x7f, 0x04, 0x01, 0x80, 0x08, 0x00, 0x04, 0x01, 0x80, 0x08, + 0x00, 0x04, 0xf1, 0x8f, 0x88, 0x7f, 0x04, 0x01, 0x88, 0x88, 0x00, 0x04, + 0x01, 0x88, 0x88, 0x00, 0x04, 0x01, 0x88, 0x88, 0x00, 0x04, 0x01, 0x88, + 0x88, 0x00, 0x04, 0x01, 0x88, 0x88, 0x00, 0x04, 0x01, 0x88, 0x88, 0x00, + 0x04, 0x01, 0x88, 0x88, 0x00, 0x04, 0x01, 0x88, 0x88, 0x00, 0x04, 0x01, + 0x88, 0x88, 0x00, 0x04, 0x01, 0x88, 0x88, 0x00, 0x04, 0x01, 0x88, 0x88, + 0x00, 0x04, 0x01, 0x88, 0x88, 0x00, 0x04, 0x01, 0x88, 0x88, 0x00, 0x04, + 0x01, 0x88, 0x88, 0x00, 0x04, 0x01, 0x88, 0x88, 0x00, 0x04, 0x01, 0x88, + 0x88, 0x00, 0x04, 0x01, 0x88, 0x88, 0x00, 0x04, 0x01, 0x88, 0x88, 0x00, + 0x04, 0x01, 0x88, 0x88, 0x00, 0x04, 0x01, 0x88, 0x88, 0x00, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room13 b/ksnake/data/levels/room13 new file mode 100644 index 00000000..ad3b3879 --- /dev/null +++ b/ksnake/data/levels/room13 @@ -0,0 +1,18 @@ +#define room13_width 35 +#define room13_height 35 +static unsigned char room13_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xf1, 0xff, 0xff, 0x7f, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xf1, + 0xff, 0xff, 0x7f, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, + 0x51, 0x01, 0x00, 0x54, 0x04, 0x51, 0x01, 0x00, 0x54, 0x04, 0x51, 0x01, + 0x00, 0x54, 0x04, 0x51, 0x01, 0x00, 0x54, 0x04, 0x51, 0x01, 0x00, 0x54, + 0x04, 0x51, 0x01, 0x00, 0x54, 0x04, 0x51, 0x01, 0x00, 0x54, 0x04, 0x51, + 0x01, 0x00, 0x54, 0x04, 0x51, 0x01, 0x00, 0x54, 0x04, 0x51, 0x01, 0x00, + 0x54, 0x04, 0x51, 0x01, 0x00, 0x54, 0x04, 0x51, 0x01, 0x00, 0x54, 0x04, + 0x51, 0x01, 0x00, 0x54, 0x04, 0x51, 0x01, 0x00, 0x54, 0x04, 0x51, 0x01, + 0x00, 0x54, 0x04, 0x51, 0x01, 0x00, 0x54, 0x04, 0x51, 0x01, 0x00, 0x54, + 0x04, 0x51, 0x01, 0x00, 0x54, 0x04, 0x51, 0x01, 0x00, 0x54, 0x04, 0x11, + 0x01, 0x00, 0x44, 0x04, 0x51, 0x01, 0x00, 0x54, 0x04, 0x51, 0x01, 0x00, + 0x54, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room14 b/ksnake/data/levels/room14 new file mode 100644 index 00000000..7d682c7e --- /dev/null +++ b/ksnake/data/levels/room14 @@ -0,0 +1,18 @@ +#define room14_width 35 +#define room14_height 35 +static unsigned char room14_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xf1, 0xff, 0xf8, 0x7f, + 0x04, 0x11, 0x00, 0x00, 0x40, 0x04, 0x11, 0x00, 0x00, 0x40, 0x04, 0x11, + 0x00, 0x00, 0x40, 0x04, 0x11, 0xff, 0xf8, 0x47, 0x04, 0x11, 0x01, 0x00, + 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, + 0x11, 0xf1, 0x7f, 0x44, 0x04, 0x11, 0x11, 0x40, 0x44, 0x04, 0x11, 0x11, + 0x40, 0x44, 0x04, 0x11, 0x11, 0x40, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, + 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, + 0x11, 0x40, 0x44, 0x04, 0x11, 0x11, 0x40, 0x44, 0x04, 0x11, 0x11, 0x40, + 0x44, 0x04, 0x11, 0xf1, 0x7f, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, + 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0xff, + 0xf8, 0x47, 0x04, 0x11, 0x00, 0x00, 0x40, 0x04, 0x11, 0x00, 0x00, 0x40, + 0x04, 0x11, 0x00, 0x00, 0x40, 0x04, 0xf1, 0xff, 0xf8, 0x7f, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room15 b/ksnake/data/levels/room15 new file mode 100644 index 00000000..004ad67e --- /dev/null +++ b/ksnake/data/levels/room15 @@ -0,0 +1,18 @@ +#define room15_width 35 +#define room15_height 35 +static unsigned char room15_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x11, 0xf1, 0xff, 0x7f, + 0x04, 0x11, 0x01, 0x00, 0x40, 0x04, 0x11, 0x01, 0x00, 0x40, 0x04, 0x11, + 0x01, 0x00, 0x40, 0x04, 0x11, 0xf1, 0xff, 0x47, 0x04, 0x11, 0x01, 0x00, + 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, + 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0xc1, + 0x1d, 0x44, 0x04, 0x11, 0xc1, 0x1d, 0x44, 0x04, 0x11, 0xc1, 0x18, 0x44, + 0x04, 0x11, 0x01, 0x02, 0x44, 0x04, 0x11, 0xc1, 0x18, 0x44, 0x04, 0x11, + 0xc1, 0x1d, 0x44, 0x04, 0x11, 0xc1, 0x1d, 0x44, 0x04, 0x11, 0x01, 0x00, + 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, + 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0xff, + 0x7f, 0x44, 0x04, 0x11, 0x00, 0x00, 0x44, 0x04, 0x11, 0x00, 0x00, 0x44, + 0x04, 0x11, 0x00, 0x00, 0x44, 0x04, 0xf1, 0xff, 0x7f, 0x44, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room16 b/ksnake/data/levels/room16 new file mode 100644 index 00000000..59dde122 --- /dev/null +++ b/ksnake/data/levels/room16 @@ -0,0 +1,18 @@ +#define room16_width 35 +#define room16_height 35 +static unsigned char room16_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x91, 0x24, 0x40, 0x92, 0x04, 0x91, 0x24, 0x40, 0x92, + 0x04, 0x91, 0x24, 0x40, 0x92, 0x04, 0x91, 0x24, 0x40, 0x92, 0x04, 0x91, + 0x24, 0x40, 0x92, 0x04, 0x91, 0x24, 0x40, 0x92, 0x04, 0x91, 0x24, 0x40, + 0x92, 0x04, 0x91, 0x24, 0x40, 0x92, 0x04, 0x91, 0x24, 0x40, 0x92, 0x04, + 0x91, 0x24, 0x40, 0x92, 0x04, 0x91, 0x24, 0x40, 0x92, 0x04, 0x91, 0x24, + 0x40, 0x92, 0x04, 0x91, 0x24, 0x40, 0x92, 0x04, 0x91, 0x24, 0x40, 0x92, + 0x04, 0x91, 0x24, 0x40, 0x92, 0x04, 0x91, 0x24, 0x40, 0x92, 0x04, 0x91, + 0x24, 0x40, 0x92, 0x04, 0x91, 0x24, 0x40, 0x92, 0x04, 0x91, 0x24, 0x40, + 0x92, 0x04, 0x91, 0x24, 0x40, 0x92, 0x04, 0x91, 0x24, 0x40, 0x92, 0x04, + 0x91, 0x24, 0x40, 0x92, 0x04, 0x91, 0x24, 0x40, 0x92, 0x04, 0x91, 0x24, + 0x40, 0x92, 0x04, 0x91, 0x24, 0x40, 0x92, 0x04, 0x91, 0x24, 0x40, 0x92, + 0x04, 0x91, 0x24, 0x40, 0x92, 0x04, 0x91, 0x24, 0x40, 0x92, 0x04, 0x91, + 0x24, 0x40, 0x92, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room17 b/ksnake/data/levels/room17 new file mode 100644 index 00000000..1237cfb7 --- /dev/null +++ b/ksnake/data/levels/room17 @@ -0,0 +1,18 @@ +#define room17_width 35 +#define room17_height 35 +static unsigned char room17_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xf1, 0xff, 0xff, 0x7f, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xf1, + 0xff, 0xf8, 0x7f, 0x04, 0x01, 0x80, 0x08, 0x00, 0x04, 0x01, 0x80, 0x08, + 0x00, 0x04, 0xf1, 0x8f, 0x88, 0x7f, 0x04, 0x01, 0x88, 0x88, 0x00, 0x04, + 0x01, 0x88, 0x88, 0x00, 0x04, 0xf1, 0x88, 0x88, 0x78, 0x04, 0x81, 0x88, + 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, + 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, 0x81, + 0x88, 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, + 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, + 0x81, 0x88, 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, 0x81, 0x88, + 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, + 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room18 b/ksnake/data/levels/room18 new file mode 100644 index 00000000..2c1949f5 --- /dev/null +++ b/ksnake/data/levels/room18 @@ -0,0 +1,18 @@ +#define room18_width 35 +#define room18_height 35 +static unsigned char room18_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xf1, 0xff, 0xff, 0x7f, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xf1, + 0xff, 0xff, 0x7f, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0x11, 0xe1, 0x3f, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, + 0x55, 0x01, 0x00, 0x54, 0x05, 0x55, 0xe1, 0x3f, 0x54, 0x05, 0x55, 0x01, + 0x00, 0x54, 0x05, 0x55, 0x01, 0x00, 0x54, 0x05, 0x55, 0xe1, 0x3f, 0x54, + 0x05, 0x55, 0x01, 0x00, 0x54, 0x05, 0x55, 0x01, 0x00, 0x54, 0x05, 0x55, + 0xe1, 0x3f, 0x54, 0x05, 0x55, 0x01, 0x00, 0x54, 0x05, 0x55, 0x01, 0x00, + 0x54, 0x05, 0x55, 0x01, 0x00, 0x54, 0x05, 0x55, 0x01, 0x00, 0x54, 0x05, + 0x55, 0x01, 0x00, 0x54, 0x05, 0x55, 0x01, 0x00, 0x54, 0x05, 0x55, 0x01, + 0x00, 0x54, 0x05, 0x55, 0x01, 0x00, 0x54, 0x05, 0x55, 0x01, 0x00, 0x54, + 0x05, 0x55, 0x01, 0x00, 0x54, 0x05, 0x55, 0x01, 0x00, 0x54, 0x05, 0x55, + 0x01, 0x00, 0x54, 0x05, 0x11, 0x01, 0x00, 0x44, 0x04, 0x55, 0x01, 0x00, + 0x54, 0x05, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room19 b/ksnake/data/levels/room19 new file mode 100644 index 00000000..39a1d698 --- /dev/null +++ b/ksnake/data/levels/room19 @@ -0,0 +1,18 @@ +#define room19_width 35 +#define room19_height 35 +static unsigned char room19_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xf1, 0xff, 0xf8, 0x7f, + 0x04, 0x11, 0x00, 0x00, 0x40, 0x04, 0x11, 0x00, 0x00, 0x40, 0x04, 0x11, + 0x00, 0x00, 0x40, 0x04, 0x11, 0xff, 0xff, 0x47, 0x04, 0x11, 0x01, 0x00, + 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, + 0x11, 0xf1, 0x78, 0x44, 0x04, 0x11, 0x11, 0x40, 0x44, 0x04, 0x11, 0x11, + 0x40, 0x44, 0x04, 0x11, 0x11, 0x42, 0x44, 0x04, 0x11, 0x10, 0x40, 0x40, + 0x04, 0x11, 0x90, 0x48, 0x40, 0x04, 0x11, 0x10, 0x40, 0x40, 0x04, 0x11, + 0x11, 0x42, 0x44, 0x04, 0x11, 0x11, 0x40, 0x44, 0x04, 0x11, 0x11, 0x40, + 0x44, 0x04, 0x11, 0xf1, 0x78, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, + 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0xff, + 0xff, 0x47, 0x04, 0x11, 0x00, 0x00, 0x40, 0x04, 0x11, 0x00, 0x00, 0x40, + 0x04, 0x11, 0x00, 0x00, 0x40, 0x04, 0xf1, 0xff, 0xf8, 0x7f, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room20 b/ksnake/data/levels/room20 new file mode 100644 index 00000000..11765e1c --- /dev/null +++ b/ksnake/data/levels/room20 @@ -0,0 +1,18 @@ +#define room20_width 35 +#define room20_height 35 +static unsigned char room20_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x01, 0x00, 0x00, 0x04, 0x01, 0x01, + 0x00, 0x00, 0x04, 0x01, 0x01, 0x00, 0x00, 0x04, 0x11, 0xf1, 0xff, 0x7f, + 0x04, 0x11, 0x01, 0x00, 0x40, 0x04, 0x11, 0x01, 0x00, 0x40, 0x04, 0x11, + 0x01, 0x00, 0x40, 0x04, 0x11, 0xf1, 0xff, 0x47, 0x04, 0x11, 0x01, 0x00, + 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, + 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0xc1, + 0x15, 0x44, 0x04, 0x11, 0x01, 0x14, 0x44, 0x04, 0x11, 0xc1, 0x10, 0x44, + 0x04, 0x11, 0x01, 0x02, 0x44, 0x04, 0x11, 0x41, 0x18, 0x44, 0x04, 0x11, + 0x41, 0x01, 0x44, 0x04, 0x11, 0x41, 0x1d, 0x44, 0x04, 0x11, 0x01, 0x00, + 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, + 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0xff, + 0x0f, 0x44, 0x04, 0x11, 0x00, 0x00, 0x44, 0x04, 0x11, 0x00, 0x00, 0x44, + 0x04, 0x11, 0x00, 0x00, 0x44, 0x04, 0xf1, 0xff, 0x0f, 0x44, 0x04, 0x01, + 0x00, 0x00, 0x04, 0x04, 0x01, 0x00, 0x00, 0x04, 0x04, 0x01, 0x00, 0x00, + 0x04, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room21 b/ksnake/data/levels/room21 new file mode 100644 index 00000000..3ce582b6 --- /dev/null +++ b/ksnake/data/levels/room21 @@ -0,0 +1,18 @@ +#define room21_width 35 +#define room21_height 35 +static unsigned char room21_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x49, 0x92, 0x48, 0x92, 0x04, 0x49, 0x92, 0x48, 0x92, + 0x04, 0x49, 0x92, 0x48, 0x92, 0x04, 0x49, 0x92, 0x48, 0x92, 0x04, 0x49, + 0x92, 0x48, 0x92, 0x04, 0x49, 0x92, 0x48, 0x92, 0x04, 0x49, 0x92, 0x48, + 0x92, 0x04, 0x49, 0x92, 0x48, 0x92, 0x04, 0x49, 0x92, 0x48, 0x92, 0x04, + 0x49, 0x92, 0x48, 0x92, 0x04, 0x49, 0x92, 0x48, 0x92, 0x04, 0x49, 0x92, + 0x48, 0x92, 0x04, 0x49, 0x92, 0x48, 0x92, 0x04, 0x49, 0x92, 0x48, 0x92, + 0x04, 0x49, 0x92, 0x48, 0x92, 0x04, 0x49, 0x92, 0x48, 0x92, 0x04, 0x49, + 0x92, 0x48, 0x92, 0x04, 0x49, 0x92, 0x48, 0x92, 0x04, 0x49, 0x92, 0x48, + 0x92, 0x04, 0x49, 0x92, 0x48, 0x92, 0x04, 0x49, 0x92, 0x48, 0x92, 0x04, + 0x49, 0x92, 0x48, 0x92, 0x04, 0x49, 0x92, 0x48, 0x92, 0x04, 0x49, 0x92, + 0x48, 0x92, 0x04, 0x49, 0x92, 0x48, 0x92, 0x04, 0x49, 0x92, 0x48, 0x92, + 0x04, 0x49, 0x92, 0x48, 0x92, 0x04, 0x49, 0x92, 0x48, 0x92, 0x04, 0x49, + 0x92, 0x48, 0x92, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room22 b/ksnake/data/levels/room22 new file mode 100644 index 00000000..104557c9 --- /dev/null +++ b/ksnake/data/levels/room22 @@ -0,0 +1,18 @@ +#define room22_width 35 +#define room22_height 35 +static unsigned char room22_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xf1, 0xff, 0xff, 0x7f, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xff, + 0xff, 0xf8, 0xff, 0x07, 0x01, 0x80, 0x08, 0x00, 0x04, 0x01, 0x80, 0x08, + 0x00, 0x04, 0xf1, 0x8f, 0x88, 0x7f, 0x04, 0x01, 0x88, 0x88, 0x00, 0x04, + 0x01, 0x88, 0x88, 0x00, 0x04, 0xf1, 0x88, 0x88, 0x78, 0x04, 0x81, 0x88, + 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, + 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, 0x81, + 0x88, 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, + 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, + 0x81, 0x88, 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, 0x81, 0x88, + 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, + 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, 0x81, 0x88, 0x88, 0x08, 0x04, 0x81, + 0x80, 0x08, 0x08, 0x04, 0x81, 0x00, 0x00, 0x08, 0x04, 0x81, 0x00, 0x00, + 0x08, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room23 b/ksnake/data/levels/room23 new file mode 100644 index 00000000..e3091764 --- /dev/null +++ b/ksnake/data/levels/room23 @@ -0,0 +1,18 @@ +#define room23_width 35 +#define room23_height 35 +static unsigned char room23_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0xf1, 0xff, 0xff, 0x7f, 0x04, 0x01, 0x00, 0x00, 0x00, + 0x04, 0xc1, 0xff, 0xff, 0x1f, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xf1, + 0xff, 0xff, 0x7f, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0x11, 0xe1, 0x3f, 0x44, 0x04, 0x11, 0x01, 0x02, 0x44, 0x04, + 0x11, 0x01, 0x00, 0x44, 0x04, 0x55, 0xe5, 0x3f, 0x55, 0x05, 0x55, 0x05, + 0x02, 0x55, 0x05, 0x55, 0x05, 0x00, 0x55, 0x05, 0x55, 0xe5, 0x3f, 0x55, + 0x05, 0x55, 0x05, 0x02, 0x55, 0x05, 0x55, 0x05, 0x00, 0x55, 0x05, 0x55, + 0xe5, 0x3f, 0x55, 0x05, 0x55, 0x05, 0x02, 0x55, 0x05, 0x55, 0x05, 0x00, + 0x55, 0x05, 0x11, 0xe1, 0x3f, 0x44, 0x04, 0x55, 0x05, 0x02, 0x55, 0x05, + 0x55, 0x05, 0x00, 0x55, 0x05, 0x55, 0x05, 0x00, 0x55, 0x05, 0x55, 0x05, + 0x00, 0x55, 0x05, 0x55, 0x05, 0x00, 0x55, 0x05, 0x55, 0x05, 0x00, 0x55, + 0x05, 0x55, 0x05, 0x00, 0x55, 0x05, 0x55, 0x05, 0x00, 0x55, 0x05, 0x55, + 0x05, 0x00, 0x55, 0x05, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x01, 0x00, + 0x44, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room24 b/ksnake/data/levels/room24 new file mode 100644 index 00000000..29d5e2a1 --- /dev/null +++ b/ksnake/data/levels/room24 @@ -0,0 +1,18 @@ +#define room24_width 35 +#define room24_height 35 +static unsigned char room24_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xc1, 0xff, 0xff, 0x1f, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x11, 0x00, 0x00, 0x40, 0x04, 0x11, + 0x00, 0x00, 0x40, 0x04, 0x11, 0xfc, 0xff, 0x41, 0x04, 0x11, 0x00, 0x00, + 0x40, 0x04, 0x11, 0x01, 0x00, 0x40, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, + 0x11, 0xc1, 0x1f, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x11, + 0x40, 0x44, 0x04, 0x11, 0x91, 0x4d, 0x44, 0x04, 0x11, 0x91, 0x4d, 0x44, + 0x04, 0x11, 0x11, 0x40, 0x44, 0x04, 0x11, 0x91, 0x4d, 0x44, 0x04, 0x11, + 0x91, 0x4d, 0x44, 0x04, 0x11, 0x11, 0x40, 0x44, 0x04, 0x11, 0x01, 0x00, + 0x44, 0x04, 0x11, 0xc1, 0x1f, 0x44, 0x04, 0x11, 0x01, 0x00, 0x44, 0x04, + 0x11, 0x01, 0x00, 0x44, 0x04, 0x11, 0x00, 0x00, 0x40, 0x04, 0x11, 0xfc, + 0xff, 0x41, 0x04, 0x11, 0x00, 0x00, 0x40, 0x04, 0x11, 0x00, 0x00, 0x40, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xc1, 0xff, 0xff, 0x1f, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/levels/room25 b/ksnake/data/levels/room25 new file mode 100644 index 00000000..e52f722a --- /dev/null +++ b/ksnake/data/levels/room25 @@ -0,0 +1,18 @@ +#define room25_width 35 +#define room25_height 35 +static unsigned char room25_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0xf1, 0xff, 0xff, 0x47, + 0x04, 0x11, 0x00, 0x00, 0x44, 0x04, 0x11, 0x00, 0x00, 0x44, 0x04, 0x11, + 0xff, 0x03, 0x44, 0x04, 0x11, 0x01, 0x02, 0x44, 0x04, 0x11, 0x01, 0x02, + 0x44, 0x04, 0x11, 0x49, 0x02, 0x44, 0x04, 0x11, 0x91, 0x02, 0x44, 0x04, + 0x11, 0x01, 0x02, 0x44, 0x04, 0x11, 0x01, 0x02, 0x44, 0x04, 0x11, 0x01, + 0x02, 0x44, 0x04, 0x11, 0x01, 0x02, 0x44, 0x04, 0x11, 0x01, 0x02, 0x44, + 0x04, 0x11, 0x01, 0x02, 0x44, 0x04, 0x11, 0x01, 0x02, 0x44, 0x04, 0x11, + 0x01, 0x02, 0x44, 0x04, 0x11, 0x01, 0x02, 0x44, 0x04, 0x11, 0x01, 0x02, + 0x44, 0x04, 0x11, 0x01, 0x02, 0x44, 0x04, 0x11, 0x01, 0x02, 0x44, 0x04, + 0x11, 0x01, 0x92, 0x44, 0x04, 0x11, 0x01, 0x22, 0x45, 0x04, 0x11, 0x01, + 0x02, 0x44, 0x04, 0x11, 0x01, 0xfe, 0x47, 0x04, 0x11, 0x01, 0x00, 0x40, + 0x04, 0x11, 0x01, 0x00, 0x40, 0x04, 0x11, 0xff, 0xff, 0x7f, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x07}; diff --git a/ksnake/data/pixmaps/Makefile.am b/ksnake/data/pixmaps/Makefile.am new file mode 100644 index 00000000..67ea560b --- /dev/null +++ b/ksnake/data/pixmaps/Makefile.am @@ -0,0 +1,5 @@ + +pixmapsdir = $(kde_datadir)/ksnake/pics +pixmaps_DATA = apples.png ball.png brick.png snake1.png snake2.png + +EXTRA_DIST = $(pixmaps_DATA) diff --git a/ksnake/data/pixmaps/apples.png b/ksnake/data/pixmaps/apples.png new file mode 100644 index 00000000..f90552a7 Binary files /dev/null and b/ksnake/data/pixmaps/apples.png differ diff --git a/ksnake/data/pixmaps/ball.png b/ksnake/data/pixmaps/ball.png new file mode 100644 index 00000000..98ecc115 Binary files /dev/null and b/ksnake/data/pixmaps/ball.png differ diff --git a/ksnake/data/pixmaps/brick.png b/ksnake/data/pixmaps/brick.png new file mode 100644 index 00000000..a2b264af Binary files /dev/null and b/ksnake/data/pixmaps/brick.png differ diff --git a/ksnake/data/pixmaps/snake1.png b/ksnake/data/pixmaps/snake1.png new file mode 100644 index 00000000..c95367ef Binary files /dev/null and b/ksnake/data/pixmaps/snake1.png differ diff --git a/ksnake/data/pixmaps/snake2.png b/ksnake/data/pixmaps/snake2.png new file mode 100644 index 00000000..154b889a Binary files /dev/null and b/ksnake/data/pixmaps/snake2.png differ diff --git a/ksnake/game.cpp b/ksnake/game.cpp new file mode 100644 index 00000000..0fa18df2 --- /dev/null +++ b/ksnake/game.cpp @@ -0,0 +1,180 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* + note: the code to lookup and insert the pixmaps + into the Options menu was copied and adapted + from KReversi. + thanks. + */ +#include +#include + + +#include +#include + +#include +#include +#include +#include + +#include "rattler.h" +#include "game.h" +#include "startroom.h" +#include "levels.h" +#include "progress.h" +#include "view.h" +#include "general.h" +#include "appearance.h" +#include "settings.h" + +#define SCORE 1 +#define LIVES 2 + +Game::Game(QWidget *parent, const char *name) : KMainWindow(parent,name) +{ + // create statusbar + statusBar()->insertItem(i18n("Score: 0"), SCORE); + statusBar()->insertItem(i18n("Lives: 0"), LIVES); + + levels = new Levels(); + + view = new View(this, "View"); + rattler = view->rattler; + rattler->reloadRoomPixmap(); + rattler->setFocus(); + + connect(rattler, SIGNAL(setPoints(int)), this, SLOT(scoreChanged(int))); + connect(rattler, SIGNAL(setTrys(int)), this, SLOT(setTrys(int))); + connect(rattler, SIGNAL(rewind()), view->progress, SLOT(rewind())); + connect(rattler, SIGNAL(advance()), view->progress, SLOT(advance())); + connect(view->progress, SIGNAL(restart()), rattler, SLOT(restartTimer())); + + connect(rattler, SIGNAL(togglePaused()), this, SLOT(togglePaused())); + connect(rattler, SIGNAL(setScore(int)), this, SLOT(gameEnd(int))); + + setCentralWidget(view); + + createMenu(); + setupGUI(KMainWindow::Save | StatusBar | Create ); +} + +Game::~Game() +{ + delete levels; +} + +void Game::scoreChanged(int score){ + statusBar()->changeItem(i18n("Score: %1").arg(score), SCORE); +} + +void Game::setTrys(int tries){ + statusBar()->changeItem(i18n("Lives: %1").arg(tries), LIVES); +} + +void Game::gameEnd(int score){ + KScoreDialog di(KScoreDialog::Name | KScoreDialog::Score | KScoreDialog::Date, this); + KScoreDialog::FieldInfo scoreInfo; + QString date = QDateTime::currentDateTime().toString(); + scoreInfo.insert(KScoreDialog::Date, date); + if (di.addScore(score, scoreInfo, true)) + di.exec(); +} + +void Game::showHighScores() +{ + KScoreDialog d(KScoreDialog::Name | KScoreDialog::Score | KScoreDialog::Date, this); + d.exec(); +} + +void Game::createMenu() +{ + actionCollection()->setAutoConnectShortcuts(false); + (void)new KAction(i18n("Move Up"), Key_Up, 0, 0, actionCollection(), "Pl1Up"); + (void)new KAction(i18n("Move Down"), Key_Down, 0, 0, actionCollection(), "Pl1Down"); + (void)new KAction(i18n("Move Right"), Key_Right, 0, 0, actionCollection(), "Pl1Right"); + (void)new KAction(i18n("Move Left"), Key_Left, 0, 0, actionCollection(), "Pl1Left"); + actionCollection()->setAutoConnectShortcuts(true); + rattler->setActionCollection(actionCollection()); + + KStdGameAction::gameNew(rattler, SLOT(restart()), actionCollection()); + pause = KStdGameAction::pause(rattler, SLOT(pause()), actionCollection()); + KStdGameAction::highscores(this, SLOT(showHighScores()), actionCollection()); + KStdGameAction::quit( this, SLOT(close()), actionCollection()); + + KStdAction::preferences(this, SLOT(showSettings()), actionCollection()); + + // TODO change and make custom function that pauses game or + // modify widget to pause when loosing focus and remove this + KStdAction::keyBindings(guiFactory(), SLOT(configureShortcuts()), +actionCollection()); +} + +void Game::togglePaused() +{ + static bool checked = false; + checked = !checked; + pause->setEnabled(checked); +} + +/** + * Show Settings dialog. + */ +void Game::showSettings(){ + if(KConfigDialog::showDialog("settings")) + return; + + KConfigDialog *dialog = new KConfigDialog(this, "settings", Settings::self()); + dialog->addPage(new General(0, "General"), i18n("General"), "package_settings"); + + Appearance *a = new Appearance(0, "Appearance"); + + + QStringList list; + + if (rattler->backgroundPixmaps.count() == 0) { + list.append(i18n("none")); + } else { + QStringList::ConstIterator it = rattler->backgroundPixmaps.begin(); + for(unsigned i = 0; it != rattler->backgroundPixmaps.end(); i++, it++) { + // since the filename may contain underscore, they + // are replaced with spaces in the menu entry + QString s = QFileInfo( *it ).baseName(); + s = s.replace(QRegExp("_"), " "); + list.append(s); + } + } + + a->kcfg_bgimage->insertStringList(list); + + dialog->addPage(a, i18n("Appearance"), "style"); + + dialog->addPage(new StartRoom(0, "StartRoom"), i18n("First Level"), "folder_home"); + connect(dialog, SIGNAL(settingsChanged()), rattler, SLOT(loadSettings())); + dialog->show(); +} + +#include "game.moc" diff --git a/ksnake/game.h b/ksnake/game.h new file mode 100644 index 00000000..f25ef127 --- /dev/null +++ b/ksnake/game.h @@ -0,0 +1,64 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santo + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef GAME_H +#define GAME_H + +#include + +class KAction; +class Rattler; +class Levels; +class View; + +class Game : public KMainWindow { + +Q_OBJECT + +public: + Game(QWidget *parent=0, const char *name=0); + ~Game(); + +private slots: + void gameEnd(int score); + void showHighScores(); + + void showSettings(); + + void togglePaused(); + void scoreChanged(int newScore); + void setTrys(int); + +private: + View *view; + Rattler *rattler; + Levels *levels; + KAction *pause; + + void createMenu(); +}; + +#endif // GAME_H + diff --git a/ksnake/general.ui b/ksnake/general.ui new file mode 100644 index 00000000..de9ef8cb --- /dev/null +++ b/ksnake/general.ui @@ -0,0 +1,202 @@ + +General + + + General + + + + 0 + 0 + 333 + 409 + + + + + unnamed + + + + groupBox4 + + + Speed + + + + unnamed + + + + kcfg_Skill + + + 3 + + + 1 + + + Horizontal + + + Below + + + + + textLabel10 + + + Slow + + + + + textLabel12 + + + Fast + + + AlignVCenter|AlignRight + + + + + + + groupBox6 + + + Snakes + + + + unnamed + + + + textLabel7 + + + Snake behavior: + + + + + + Random + + + + + Eater + + + + + Killer + + + + kcfg_SnakesAI + + + + + kcfg_ComputerSnakes + + + 1 + + + + + textLabel8 + + + Number of snakes: + + + + + + + groupBox5 + + + Balls + + + + unnamed + + + + textLabel9 + + + Number of balls: + + + + + kcfg_Balls + + + 1 + + + + + + Dumb + + + + + Average + + + + + Killer + + + + kcfg_BallsAI + + + + + textLabel13 + + + Ball behavior: + + + + + + + spacer1 + + + Vertical + + + Expanding + + + + 20 + 70 + + + + + + + diff --git a/ksnake/hi128-app-ksnake.png b/ksnake/hi128-app-ksnake.png new file mode 100644 index 00000000..f0031391 Binary files /dev/null and b/ksnake/hi128-app-ksnake.png differ diff --git a/ksnake/hi16-app-ksnake.png b/ksnake/hi16-app-ksnake.png new file mode 100644 index 00000000..ab14f946 Binary files /dev/null and b/ksnake/hi16-app-ksnake.png differ diff --git a/ksnake/hi22-app-ksnake.png b/ksnake/hi22-app-ksnake.png new file mode 100644 index 00000000..ee9e6756 Binary files /dev/null and b/ksnake/hi22-app-ksnake.png differ diff --git a/ksnake/hi32-app-ksnake.png b/ksnake/hi32-app-ksnake.png new file mode 100644 index 00000000..1c68c053 Binary files /dev/null and b/ksnake/hi32-app-ksnake.png differ diff --git a/ksnake/hi48-app-ksnake.png b/ksnake/hi48-app-ksnake.png new file mode 100644 index 00000000..89e65255 Binary files /dev/null and b/ksnake/hi48-app-ksnake.png differ diff --git a/ksnake/hi64-app-ksnake.png b/ksnake/hi64-app-ksnake.png new file mode 100644 index 00000000..b7d4fdc0 Binary files /dev/null and b/ksnake/hi64-app-ksnake.png differ diff --git a/ksnake/ksnake.desktop b/ksnake/ksnake.desktop new file mode 100644 index 00000000..89cc2e05 --- /dev/null +++ b/ksnake/ksnake.desktop @@ -0,0 +1,82 @@ +[Desktop Entry] +Name=KSnakeRace +Name[af]=K-slang-resies +Name[ar]=لعبة سباق الأÙعى (KSnakeRace) +Name[be]=ЗмÑÑ–Ð½Ñ‹Ñ Ð³Ð¾Ð½ÐºÑ– +Name[bn]=কে-সà§à¦¨à§‡à¦•à¦°à§‡à¦¸ +Name[ca]=Cursa de serps +Name[eo]=Serpentkurado +Name[fi]=KSnake +Name[hi]=के-सà¥à¤¨à¥‡à¤•-रेस +Name[hr]=KUtrka zmija +Name[hu]=Kígyóverseny +Name[is]=Slönguleikurinn +Name[nb]=Slangeløp +Name[ne]=केडीई सरà¥à¤ª दौड +Name[nn]=Slangeløp +Name[pa]=ਕੇ-ਸੱਪ ਦੌੜ +Name[pl]=WyÅ›cig węży +Name[pt_BR]=KCorrida de Cobras +Name[se]=GearpmaÅ¡gilvu +Name[sv]=Ksnakerace +Name[ta]=Kபாமà¯à®ªà¯ பநà¯à®¤à®¯à®®à¯ +Name[tg]=KМуÑобиқаи Морчаҳо +Name[th]=à¹à¸‚่งวิ่งงู - K +Name[tr]=Yılan Yarışı +Name[zh_TW]=KSnakeRace 貪食蛇 +Name[zu]=I-KSnakeRace +Icon=ksnake +Exec=ksnake %i %m -caption "%c" +DocPath=ksnake/index.html +GenericName=Snake Race Game +GenericName[be]=ЗмÑÑ–Ð½Ñ‹Ñ Ð³Ð¾Ð½ÐºÑ– +GenericName[bg]=Змии и Ñбълки +GenericName[bn]=সà§à¦¨à§‡à¦• রেস খেলা +GenericName[bs]=Igra utrke zmija +GenericName[ca]=Joc de cursa de serps +GenericName[cs]=Závod hadů +GenericName[cy]=Gêm Ras Nadredd +GenericName[da]=Slangevæddeløb spil +GenericName[de]=Schlangenrennen +GenericName[el]=Παιχνίδι αγώνα φιδιών +GenericName[eo]=Serpent-vetkura ludo +GenericName[es]=Juego de la carrera de la serpiente +GenericName[et]=Ussiralli +GenericName[eu]=Snake Race jokoa +GenericName[fa]=بازی مسابقۀ مار +GenericName[fi]=Matokilpailupeli +GenericName[fr]=Jeu de course de serpent +GenericName[he]=משחק מירוץ × ×—×©×™× +GenericName[hr]=Igra utrke zmija +GenericName[hu]=Ãœgyességi +GenericName[is]=Snákakapphlaup +GenericName[it]=Gioco del Snake Race +GenericName[ja]=蛇ã®ãƒ¬ãƒ¼ã‚¹ã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​ប្រណាំង​ពស់ +GenericName[lv]=Čūsku skrieÅ¡anÄs spÄ“le +GenericName[mk]=Игра Ñо трки на змии +GenericName[nb]=Slangespill +GenericName[nds]=Schlangenwettloop +GenericName[ne]=सरà¥à¤ª दौड खेल +GenericName[nl]=Slangenracespel +GenericName[nn]=Slangespel +GenericName[pa]=ਸੱਪ ਦੌੜ ਖੇਡ +GenericName[pl]=WyÅ›cig węży +GenericName[pt]=Jogo de Corrida de Cobras +GenericName[pt_BR]=Jogo de corrida de cobras +GenericName[ru]=Змеиные гонки +GenericName[se]=Gearpmášspeallu +GenericName[sk]=Hra Snake Race +GenericName[sl]=Igra dirke kaÄ +GenericName[sr]=Игра змијица +GenericName[sr@Latn]=Igra zmijica +GenericName[sv]=Orm-racespel +GenericName[ta]=பாமà¯à®ªà¯ ஓடà¯à®Ÿ விளையாடà¯à®Ÿà¯ +GenericName[uk]=Гра Ñхожа на перегони пітонів +GenericName[zh_CN]=贪食蛇 +GenericName[zh_TW]=åžé£Ÿè›‡éŠæˆ² +Terminal=false +Type=Application +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;ArcadeGame; diff --git a/ksnake/ksnake.kcfg b/ksnake/ksnake.kcfg new file mode 100644 index 00000000..087be291 --- /dev/null +++ b/ksnake/ksnake.kcfg @@ -0,0 +1,50 @@ + + + + + + false + + + + white + + + true + + + + 4 + + + + + 0 + + + + 1 + + + + 0 + + + + + 1 + + + + 0 + + + + 1 + + + + diff --git a/ksnake/ksnakeui.rc b/ksnake/ksnakeui.rc new file mode 100644 index 00000000..cb3a2023 --- /dev/null +++ b/ksnake/ksnakeui.rc @@ -0,0 +1,5 @@ + + + + + diff --git a/ksnake/level.cpp b/ksnake/level.cpp new file mode 100644 index 00000000..86360818 --- /dev/null +++ b/ksnake/level.cpp @@ -0,0 +1,152 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include "level.h" +#include "board.h" +#include "bitmaps.h" +#include "levels.h" + +const uchar *numbers[10] = { zero_bits, one_bits, two_bits, + three_bits, four_bits, five_bits, + six_bits, seven_bits, eight_bits, nine_bits +}; + +Level::Level(Board *b) +{ + board = b; + create(Intro); +} + +void Level::nextLevel() +{ + if (level < leV->max()) + level++; +} + +void Level::create(Img img) +{ + switch(img){ + case Banner: + createBanner(); + break; + case Room: + createRoom(); + break; + case Intro: + makeImageFromData(intro_bits); + break; + case GameOver: + makeImageFromData(gameover_bits); + break; + } +} + +void Level::createRoom() +{ + QImage image = leV->getImage(level); + initBoard(image); +} + +void Level::makeImageFromData(const uchar *buf) +{ + QBitmap bitmap(BoardWidth, BoardWidth, buf, true); + QImage image = bitmap.convertToImage(); + initBoard (image); +} + +void Level::initBoard(const QImage &image) +{ + int index = 0; + uchar *b; + + for ( int y = 0;y < image.height();y++ ) { + b = image.scanLine(y); + for ( int x = 0;x < image.width();x++ ) { + + if ( image.bitOrder() == QImage::BigEndian ) { + if (((*b >> (7 - (x & 7))) & 1) == 1) + board->set(index, brick); + else board->set(index, empty); + } else { + if (((*b >> (x & 7)) & 1) == 1) + board->set(index, brick); + else board->set(index, empty); + } + + if ( (x & 7) == 7 ) + b++; + index++; + } + } +} + +void Level::createBanner() +{ + makeImageFromData(level_bits); + + QString num; + num.setNum(level); + if(level < 10) + num.insert(0,'0'); + + QString left = num.left(1); + QString right = num.right(1); + + drawNumber ( 606, numbers[left.toInt()] ); + drawNumber ( 614, numbers[right.toInt()] ); +} + +void Level::drawNumber(int beginAt, const uchar *buf) +{ + QBitmap bitmap(7, 9, buf, true); + QImage image = bitmap.convertToImage(); + + int index = beginAt; + uchar *b; + + for ( int y = 0;y < image.height();y++ ) + { + b = image.scanLine(y); + for ( int x = 0;x < image.width();x++ ) + { + if ( image.bitOrder() == QImage::BigEndian ) + { + if (((*b >> (7 - (x & 7))) & 1) == 1) + board->set(index, brick); + } else { + if (((*b >> (x & 7)) & 1) == 1) + board->set(index, brick); + } + if ( (x & 7) == 7 ) + b++; + index++; + } + index += 28; + } +} + diff --git a/ksnake/level.h b/ksnake/level.h new file mode 100644 index 00000000..51c86dc6 --- /dev/null +++ b/ksnake/level.h @@ -0,0 +1,55 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef LEVEL_H +#define LEVEL_H + +class Board; + +enum Img {Banner, Room, Intro, GameOver}; + +class Level { + +public: + Level (Board *); + void nextLevel(); + void setLevel(int level) { this->level = level; } + int getLevel() const { return level; } + void create(Img boardType); + +private: + Board *board; + QImage makeImage(char *); + void makeImageFromData(const uchar *buf); + void drawNumber(int beginAt, const uchar *buf); + void initBoard(const QImage &image); + void createRoom(); + void createBanner(); + + // Can level be negative? + int level; +}; + +#endif // LEVEL_H diff --git a/ksnake/levels.cpp b/ksnake/levels.cpp new file mode 100644 index 00000000..c58aa895 --- /dev/null +++ b/ksnake/levels.cpp @@ -0,0 +1,58 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include + +#include "levels.h" + +Levels *leV = 0; + +Levels::Levels() +{ + leV = this; + list = KGlobal::dirs()->findAllResources("appdata", "levels/*"); + list.prepend( "dummy" ); +} + +int Levels::max() +{ + return ( list.count() -1 ); +} + +QImage Levels::getImage(int at) +{ + QBitmap bitmap(*list.at(at)); + QImage image = bitmap.convertToImage(); + return image; +} + +QPixmap Levels::getPixmap(int at) +{ + return QPixmap(*list.at(at)); +} + diff --git a/ksnake/levels.h b/ksnake/levels.h new file mode 100644 index 00000000..e40f5602 --- /dev/null +++ b/ksnake/levels.h @@ -0,0 +1,45 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef LEVELS_H +#define LEVELS_H + +class Levels +{ +public: + Levels (); + QImage getImage(int); + QPixmap getPixmap(int); + int max(); + +private: + QStringList list; + +}; + +extern Levels *leV; + +#endif // LEVELS_H + diff --git a/ksnake/main.cpp b/ksnake/main.cpp new file mode 100644 index 00000000..0ff21c66 --- /dev/null +++ b/ksnake/main.cpp @@ -0,0 +1,54 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "game.h" +#include "version.h" +#include +#include +#include + +static const char description[] = I18N_NOOP("KDE Snake Race Game"); + +int main( int argc, char **argv ) +{ + KAboutData aboutData( "ksnake", I18N_NOOP("KSnakeRace"), + KSNAKE_VERSION, description, KAboutData::License_GPL, + I18N_NOOP("(c) 1997-2000, Your Friendly KSnake Developers")); + aboutData.addAuthor("Michel Filippi",0, "mfilippi@sade.rhein-main.de"); + aboutData.addAuthor("Robert Williams"); + aboutData.addAuthor("Andrew Chant",0, "andrew.chant@utoronto.ca"); + aboutData.addCredit("André Luiz dos Santos", I18N_NOOP("AI stuff"), "andre@netvision.com.br"); + aboutData.addCredit("Benjamin Meyer", I18N_NOOP("Improvements"), "ben+ksnake@meyerhome.net"); + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication app; + KGlobal::locale()->insertCatalogue("libkdegames"); + + Game *ksnake = new Game(); + app.setMainWidget( ksnake ); + ksnake->show(); + return app.exec(); +} + diff --git a/ksnake/pixServer.cpp b/ksnake/pixServer.cpp new file mode 100644 index 00000000..bdc281ea --- /dev/null +++ b/ksnake/pixServer.cpp @@ -0,0 +1,245 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "pixServer.h" + +#include +#include +#include + +#include +#include +#include + +#include "board.h" +#include "settings.h" + +PixServer::PixServer( Board *b) +{ + board = b; + initPixmaps(); + initBrickPixmap(); + initbackPixmaps(); + initRoomPixmap(); +} + +void PixServer::erase(int pos) +{ + if (!board->isEmpty(pos)) + return; + + QRect rect = board->rect(pos); + bitBlt( &cachePix, rect.x(), rect.y(), &backPix, + rect.x(), rect.y(), rect.width(), rect.height()); +} + +void PixServer::restore(int pos) +{ + QRect rect = board->rect(pos); + bitBlt( &cachePix, rect.x(), rect.y(), &roomPix, + rect.x(), rect.y(), rect.width(), rect.height()); +} + +void PixServer::draw(int pos, PixMap pix, int i) +{ + QPixmap p; + p.resize(BRICKSIZE, BRICKSIZE); + + QRect rect = board->rect(pos); + + if (! plainColor) + bitBlt( &p, 0, 0, &backPix, + rect.x(), rect.y(), rect.width(), rect.height()); + else + p.fill(backgroundColor); + + switch (pix) { + case SamyPix: bitBlt(&p ,0,0, &samyPix[i]); + break; + case CompuSnakePix: bitBlt(&p ,0,0, &compuSnakePix[i]); + break; + case ApplePix: bitBlt(&p ,0,0, &applePix[i]); + break; + case BallPix: bitBlt(&p ,0,0, &ballPix[i]); + break; + default: + break; + } + + bitBlt(&cachePix, rect.x(), rect.y(), &p); +} + +void PixServer::initPixmaps() +{ + + QPixmap pm = QPixmap(locate("appdata", "pics/snake1.png")); + QImage qi = pm.convertToImage(); + qi=qi.smoothScale(BRICKSIZE*18,BRICKSIZE); + pm.convertFromImage(qi,QPixmap::AvoidDither); + for (int x = 0 ; x < 18; x++){ + compuSnakePix[x].resize(BRICKSIZE, BRICKSIZE); + bitBlt(&compuSnakePix[x] ,0,0, &pm,x*BRICKSIZE, 0, BRICKSIZE, BRICKSIZE, Qt::CopyROP, true); + compuSnakePix[x].setMask(compuSnakePix[x].createHeuristicMask()); + } + + pm = QPixmap(locate("appdata", "pics/snake2.png")); + qi = pm.convertToImage(); + qi=qi.smoothScale(BRICKSIZE*18,BRICKSIZE); + pm.convertFromImage(qi,QPixmap::AvoidDither); + for (int x = 0 ; x < 18; x++){ + samyPix[x].resize(BRICKSIZE, BRICKSIZE); + bitBlt(&samyPix[x] ,0,0, &pm,x*BRICKSIZE, 0, BRICKSIZE, BRICKSIZE, Qt::CopyROP, true); + samyPix[x].setMask(samyPix[x].createHeuristicMask()); + } + + pm = QPixmap(locate("appdata", "pics/ball.png")); + qi = pm.convertToImage(); + qi=qi.smoothScale(BRICKSIZE*4,BRICKSIZE); + pm.convertFromImage(qi,QPixmap::AvoidDither); + for (int x = 0 ; x < 4; x++){ + ballPix[x].resize(BRICKSIZE, BRICKSIZE); + bitBlt(&ballPix[x] ,0,0, &pm,x*BRICKSIZE, 0, BRICKSIZE, BRICKSIZE, Qt::CopyROP, true); + ballPix[x].setMask(ballPix[x].createHeuristicMask()); + } + + pm = QPixmap(locate("appdata", "pics/apples.png")); + qi = pm.convertToImage(); + qi=qi.smoothScale(BRICKSIZE*2,BRICKSIZE); + pm.convertFromImage(qi,QPixmap::AvoidDither); + for (int x = 0 ; x < 2; x++){ + applePix[x].resize(BRICKSIZE, BRICKSIZE); + bitBlt(&applePix[x] ,0,0, &pm,x*BRICKSIZE, 0, BRICKSIZE, BRICKSIZE, Qt::CopyROP, true); + applePix[x].setMask(applePix[x].createHeuristicMask()); + } +} + +void PixServer::initbackPixmaps() +{ + QString path; + plainColor = false; + + if(Settings::bgcolor_enabled()){ + backgroundColor = Settings::bgcolor(); + plainColor = true; + } else if(Settings::bgimage_enabled()) { + // A bit of a hack. + QStringList backgroundPixmaps = + KGlobal::dirs()->findAllResources("appdata", "backgrounds/*.png"); + path = backgroundPixmaps[(Settings::bgimage())]; + } + + QPixmap PIXMAP; + int pw, ph; + + backPix.resize(MAPWIDTH, MAPHEIGHT); + + if (! plainColor) { + + PIXMAP = QPixmap(path); + + if (!PIXMAP.isNull()) { + pw = PIXMAP.width(); + ph = PIXMAP.height(); + + for (int x = 0; x <= MAPWIDTH; x+=pw) //Tile BG Pixmap onto backPix + for (int y = 0; y <= MAPHEIGHT; y+=ph) + bitBlt(&backPix, x, y, &PIXMAP); + } + else { + kdDebug() << "error loading background image :" << path << endl; + backgroundColor = (QColor("black")); + plainColor = true; + } + } + if ( plainColor) + backPix.fill(backgroundColor); +} + +void PixServer::initBrickPixmap() +{ + QPixmap pm = QPixmap(locate("appdata", "pics/brick.png")); + if (pm.isNull()) { + kdFatal() << i18n("error loading %1, aborting\n").arg("brick.png"); + } + int pw = pm.width(); + int ph = pm.height(); + + offPix.resize(MAPWIDTH, MAPHEIGHT); + for (int x = 0; x <= MAPWIDTH; x+=pw) + for (int y = 0; y <= MAPHEIGHT; y+=ph) + bitBlt(&offPix, x, y, &pm); +} + +void PixServer::initRoomPixmap() +{ + QPainter paint; + + roomPix.resize(MAPWIDTH, MAPHEIGHT); + bitBlt(&roomPix,0,0, &backPix); + paint.begin(&roomPix); + + for (unsigned int x = 0; x < board->size(); x++) { + if (board->isBrick(x)) + drawBrick(&paint, x); + } + paint.end(); + + cachePix.resize(MAPWIDTH, MAPHEIGHT); + bitBlt(&cachePix,0,0, &roomPix); +} + +void PixServer::drawBrick(QPainter *p ,int i) +{ + //Note, ROOMPIC IS OUR 'TARGET' + QColor light(180,180,180); + QColor dark(100,100,100); + + int topSq = board->getNext(N, i); //find 'address' of neighbouring squares + int botSq = board->getNext(S, i); + int rightSq = board->getNext(E ,i); + int leftSq = board->getNext(W, i); + + QRect rect = board->rect(i); //get our square's rect + + int x = rect.x(); + int y = rect.y(); //Get x,y location of square??? + + int width, height; + + int highlight = 2; //Highlighting Distance (pixels)? + + width = height = rect.width(); + + p->fillRect(x, y, width, height, light); //By default, fill square w/ Light? no. use dark!!!! + + bitBlt(&roomPix, x, y, &offPix, x, y, width, height ); //Copy the brick pattern onto the brick + + if (!board->isBrick(rightSq)) p->fillRect(x + width - highlight, y, highlight, height, dark); //highlight if its an end-brick. + if (!board->isBrick(leftSq)) p->fillRect(x, y, highlight, height, light); + if (!board->isBrick(botSq)) p->fillRect(x, y + height - highlight, width, highlight, dark); + if (!board->isBrick(topSq)) p->fillRect(x, y, width, highlight, light); + +} + diff --git a/ksnake/pixServer.h b/ksnake/pixServer.h new file mode 100644 index 00000000..783f7efa --- /dev/null +++ b/ksnake/pixServer.h @@ -0,0 +1,76 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef PIXSERVER_H +#define PIXSERVER_H + +#include + +class Board; +enum SnakePix {TailUp, TailDown, TailRight, TailLeft, + HeadUp, HeadDown, HeadRight, HeadLeft, + AngleSw, AngleSe, AngleNw, AngleNe, + BodyHz, BodyVt, + HtailUp, HtailDown, HtailRight, HtailLeft }; +enum PixMap { SamyPix, CompuSnakePix, ApplePix, BallPix }; +enum Image {Snake, ComputerSnake}; + +class PixServer +{ +public: + PixServer (Board *); + QPixmap levelPix() const { return cachePix; } + + void initRoomPixmap(); + void initBrickPixmap(); + void initPixmaps(); + void initbackPixmaps(); + + void draw(int pos, PixMap pix, int i = 0); + void erase(int pos); + void restore(int pos); + +private: + Board *board; + + void drawBrick(QPainter *, int); + + QPixmap samyPix[18]; + QPixmap compuSnakePix[18]; + QPixmap ballPix[4]; + QPixmap applePix[2]; + + QPixmap roomPix; + QPixmap cachePix; + QPixmap offPix; + QPixmap backPix; + + bool plainColor; + QColor backgroundColor; + +}; + +#endif // PIXSERVER_H + diff --git a/ksnake/progress.cpp b/ksnake/progress.cpp new file mode 100644 index 00000000..d6d88823 --- /dev/null +++ b/ksnake/progress.cpp @@ -0,0 +1,54 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "progress.h" + +Progress::Progress(QWidget *parent, const char *name) + : KGameProgress(0, 300, 300, KGameProgress::Horizontal, parent, name) +{ + setBarColor("green1"); + setTextEnabled(false); +} + +void Progress::advance() +{ + if (value() == 0) { + emit restart(); + return; + } + + if (value() == 80) + setBarColor("red1"); + + KGameProgress::advance(-1); +} + +void Progress::rewind() +{ + setBarColor("green1"); + KGameProgress::setValue(300); +} + +#include "progress.moc" diff --git a/ksnake/progress.h b/ksnake/progress.h new file mode 100644 index 00000000..4b902f26 --- /dev/null +++ b/ksnake/progress.h @@ -0,0 +1,47 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef PROGRESS_H +#define PROGRESS_H + +#include + +class Progress : public KGameProgress +{ + Q_OBJECT +public: + Progress( QWidget *parent=0, const char *name=0 ); + +public slots: + void advance(); + void rewind(); + +signals: + void restart(); + +}; + +#endif // PROGRESS_H + diff --git a/ksnake/rattler.cpp b/ksnake/rattler.cpp new file mode 100644 index 00000000..8bea1c0c --- /dev/null +++ b/ksnake/rattler.cpp @@ -0,0 +1,692 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "rattler.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "level.h" +#include "basket.h" +#include "settings.h" + +QBitArray gameState(5); +QLabel *label = 0; +int speed[4] = { 130, 95, 55, 40 }; + +Rattler::Rattler( QWidget *parent, const char *name ) + : QWidget( parent, name ) +{ + setFocusPolicy(QWidget::StrongFocus); + + numBalls = Settings::balls(); + ballsAI = Settings::ballsAI(); + numSnakes = Settings::computerSnakes(); + snakesAI = Settings::snakesAI(); + skill = Settings::skill(); + room = Settings::startingRoom(); + + board = new Board(35*35); + level = new Level(board); + pix = new PixServer(board); + basket = new Basket(board, pix); + samy = new SamySnake(board, pix); + + computerSnakes = new QPtrList; + computerSnakes->setAutoDelete( true ); + + balls = new QPtrList; + balls->setAutoDelete( true ); + + connect( samy, SIGNAL(closeGate(int)), this, SLOT(closeGate(int))); + connect( samy, SIGNAL(score(bool, int)), this, SLOT(scoring(bool,int))); + connect( samy, SIGNAL(goingOut()), this, SLOT(speedUp())); + connect( basket, SIGNAL(openGate()), this, SLOT(openGate())); + + gameState.fill(false); + gameState.setBit(Demo); + + timerCount = 0; + QTimer::singleShot( 2000, this, SLOT(demo()) ); // Why wait? + + backgroundPixmaps = + KGlobal::dirs()->findAllResources("appdata", "backgrounds/*.png"); +} + +Rattler::~Rattler() +{ + delete level; + delete balls; + delete computerSnakes; + delete basket; + delete samy; + delete pix; + delete board; +} + +/** + * One of the settings changed, load our settings + */ +void Rattler::loadSettings(){ + setBalls(Settings::balls()); + setBallsAI(Settings::ballsAI()); + setCompuSnakes(Settings::computerSnakes()); + setSnakesAI(Settings::snakesAI()); + setSkill(Settings::skill()); + setRoom(Settings::startingRoom()); + reloadRoomPixmap(); +} + +void Rattler::paintEvent( QPaintEvent *e) +{ + QRect rect = e->rect(); + + if (rect.isEmpty()) + return; + QPixmap levelPix = pix->levelPix(); + + basket->repaint(true); + + bitBlt(this, rect.x(), rect.y(), + &levelPix, rect.x(), rect.y(), rect.width(), rect.height()); +} + +void Rattler::timerEvent( QTimerEvent * ) +{ + timerCount++; + + if ( !leaving ) // advance progressBar unless Samy + emit advance(); // is going out + + for (CompuSnake *c = computerSnakes->first(); c != 0; c = computerSnakes->next()){ + if(c) { + c->nextMove(); + c->repaint(false); + } + } + + for (Ball *b = balls->first(); b != 0; b = balls->next()){ + if (b) { + b->nextMove(); + b->repaint(); + } + } + + + samyState state = ok; + + if(!gameState.testBit(Demo)) + { + state = samy->nextMove(direction); + samy->repaint( false ); + } + + basket->repaint( false); + + if (state == ko) + newTry(); + else if (state == out) + levelUp(); + + QPixmap levelPix = pix->levelPix(); + bitBlt(this, 0, 0, &levelPix, 0, 0, rect().width(), rect().height()); +} + +void Rattler::keyPressEvent( QKeyEvent *k ) +{ + if (gameState.testBit(Paused)) + return; + + KKey key(k); + if(actionCollection->action("Pl1Up")->shortcut().contains(key)) + direction = N; + else if(actionCollection->action("Pl1Down")->shortcut().contains(key)) + direction = S; + else if(actionCollection->action("Pl1Right")->shortcut().contains(key)) + direction = E; + else if(actionCollection->action("Pl1Left")->shortcut().contains(key)) + direction = W; + else if ((k->key() == Key_Return) || + (k->key() == Key_Enter) || (k->key() == Key_Space)) { + if (!gameState.testBit(Demo)) { + k->ignore(); + return; + } + restart(); + } + else { + k->ignore(); + return; + } + k->accept(); +} + +void Rattler::mousePressEvent( QMouseEvent *e ) +{ + if (gameState.testBit(Paused)) + return; + + uint button = e->button(); + if (button == Qt::RightButton) + { + switch (direction) + { + case N: direction=E; + break; + case E: direction=S; + break; + case S: direction=W; + break; + case W: direction=N; + break; + } + e->accept(); + } + else if (button == Qt::LeftButton) + { + switch (direction) + { + case N: direction=W; + break; + case E: direction=N; + break; + case S: direction=E; + break; + case W: direction=S; + break; + } + e->accept(); + } + else + e->ignore(); +} + +void Rattler::closeGate(int i) +{ + board->set(i, brick); + pix->restore(i); +} + +void Rattler::openGate() +{ + board->set(NORTH_GATE, empty); + pix->erase(NORTH_GATE); +} + +void Rattler::scoring(bool win, int i) +{ + Fruits fruit = basket->eaten(i); + + if (gameState.testBit(Demo)) + win = true; + + int p = 0; + + switch (fruit) { + + case Red: + if (win) { + if (!timerHasRunOut) + p = 1 + skill*2; + else p = 1; + } + else p = -2; + break; + + case Golden: + if (win) { + if (!timerHasRunOut) + p = 2 + (skill*2) + (numSnakes*2) + (numBalls+2); + else p = 2; + } + else p = -5; + break; + + default: + break; + + } + score(p); +} + +void Rattler::score(int p) +{ + points += p; + points = (points < 0 ? 0 : points); + emit setPoints(points); + + while (points > check*50) { + check++; + if (trys < 7 && !gameState.testBit(Demo)) + emit setTrys(++trys); + } +} + +void Rattler::killedComputerSnake() +{ + if (!gameState.testBit(Demo)) + score(20); +} + +void Rattler::pause() +{ + if (gameState.testBit(Init)) + return; + + if (gameState.testBit(Playing)) { + + gameState.toggleBit(Playing); + gameState.setBit(Paused); + stop(); + + KAction* tempPauseAction = KStdGameAction::pause(); + + label = new QLabel(this); + label->setFont( QFont( "Times", 14, QFont::Bold ) ); + label->setText(i18n("Game Paused\n Press %1 to resume\n") + .arg(tempPauseAction->shortcutText())); + label->setAlignment( AlignCenter ); + label->setFrameStyle( QFrame::Panel | QFrame::Raised ); + label->setGeometry(182, 206, 198, 80); + label->show(); + + delete tempPauseAction; + //emit togglePaused(); + } + else if (gameState.testBit(Paused)) { + gameState.toggleBit(Paused); + gameState.setBit(Playing); + start(); + cleanLabel(); + //emit togglePaused(); + } +} + +void Rattler::cleanLabel() +{ + if (label) { + delete label; + label = 0; + } +} + +void Rattler::restartDemo() +{ + if (!gameState.testBit(Demo)) + return; + + int r = 50000+ (kapp->random() % 30000); + QTimer::singleShot( r, this, SLOT(restartDemo()) ); + + stop(); + level->create(Intro); + pix->initRoomPixmap(); + init(false); + repaint(); + start(); +} + +void Rattler::demo() +{ + static bool first_time = true; + + if(gameState.testBit(Init) || gameState.testBit(Playing)) + return; + + stop(); + + QTimer::singleShot( 60000, this, SLOT(restartDemo()) ); + gameState.fill(false); + gameState.setBit(Init); + gameState.setBit(Demo); + resetFlags(); + + if(!first_time) { + level->create(Intro); + pix->initRoomPixmap(); + } + repaint(rect(), false); + init(false); + run(); + first_time = false; +} + +void Rattler::restart() +{ + if (gameState.testBit(Init)) + return; + stop(); + + if (gameState.testBit(Paused) || gameState.testBit(Playing)) { + + switch( KMessageBox::questionYesNo( this, + i18n("A game is already started.\n" + "Start a new one?\n"), i18n("Snake Race"), i18n("Start New"), i18n("Keep Playing"))) { + case KMessageBox::No: + if (!gameState.testBit(Paused)) + start(); + return; + break; + case KMessageBox::Yes: + ; + } + } + + gameState.fill(false); + gameState.setBit(Init); + gameState.setBit(Playing); + + resetFlags(); + + level->setLevel(room); + level->create(Banner); + pix->initRoomPixmap(); + + cleanLabel(); + + repaint(); + QTimer::singleShot( 2000, this, SLOT(showRoom()) ); +} + +void Rattler::newTry() +{ + stop(); + + if(trys==0) { + gameState.fill(false); + gameState.setBit(Over); + level->create(GameOver); + pix->initRoomPixmap(); + repaint(); + QTimer::singleShot( 5000, this, SLOT(demo()) ); + emit setScore(points); + return; + } + --trys; + gameState.fill(false); + gameState.setBit(Init); + gameState.setBit(Playing); + + level->create(Room); + pix->initRoomPixmap(); + init(true); + repaint(); + QTimer::singleShot( 1000, this, SLOT(run()) ); +} + +void Rattler::levelUp() +{ + stop(); + + gameState.fill(false); + gameState.setBit(Init); + gameState.setBit(Playing); + + score (2*(level->getLevel())+(2*numSnakes)+(2*numBalls)+(2*skill)); + + level->nextLevel(); + level->create(Banner); + pix->initRoomPixmap(); + repaint(); + + QTimer::singleShot( 2000, this, SLOT(showRoom()) ); +} + +/* this slot is called by the progressBar when value() == 0 +or by a compuSnake wich manages to exit */ +void Rattler::restartTimer() +{ + timerHasRunOut = true; + timerCount = 0; + emit rewind(); + + if ( board->isEmpty(NORTH_GATE) ) + closeGate(NORTH_GATE); + basket->newApples(); +} + +void Rattler::speedUp() +{ + leaving = true; + stop(); + start( 30 ); +} + +void Rattler::resetFlags() +{ + trys = 2; + check = 1; + points = 0; +} + +void Rattler::showRoom() +{ + level->create(Room); + pix->initRoomPixmap(); + init(true); + repaint(); + QTimer::singleShot( 1000, this, SLOT(run()) ); +} + +void Rattler::init(bool play) +{ + leaving = false; + timerHasRunOut = false; + timerCount = 0; + emit rewind(); + + emit setTrys(trys); + emit setPoints(points); + + basket->clear(); + basket->newApples(); + restartBalls(play); + restartComputerSnakes(play); + if(play) + samy->init(); +} + +void Rattler::run() +{ + direction = N; + gameState.toggleBit(Init); + start(); +} + +void Rattler::start() +{ + gameTimer = startTimer( speed [skill] ); +} + +void Rattler::start(int t) +{ + gameTimer = startTimer(t); +} + +void Rattler::stop() +{ + killTimers(); +} + +void Rattler::restartComputerSnakes(bool play) +{ + if( !computerSnakes->isEmpty()) + computerSnakes->clear(); + + int i = (play == false && numSnakes == 0 ? 1 : numSnakes); + + for (int x = 0; x < i; x++) { + CompuSnake *as; + switch(snakesAI) { + default: // random. + as = new CompuSnake(board, pix); + break; + case 1: // eater. + as = new EaterCompuSnake(board, pix); + break; + case 2: // killer. + as = new KillerCompuSnake(board, pix); + break; + } + connect( as, SIGNAL(closeGate(int)), this, SLOT(closeGate(int))); + connect( as, SIGNAL(restartTimer()), this, SLOT(restartTimer())); + connect( as, SIGNAL(score(bool, int)), this, SLOT(scoring(bool,int))); + connect( as, SIGNAL(killed()), this, SLOT(killedComputerSnake())); + computerSnakes->append(as); + } +} + +void Rattler::restartBalls(bool play) +{ + if( !balls->isEmpty()) + balls->clear(); + + int i = (play == false && numBalls == 0 ? 1 : numBalls); + + for (int x = 0; x < i; x++) { + Ball *b; + switch(ballsAI) { + default: // default. + b = new Ball(board, pix); + break; + case 1: // dumb. + b = new DumbKillerBall(board, pix); + break; + case 2: // smart. + b = new KillerBall(board, pix); + break; + } + balls->append(b); + } +} + +void Rattler::setBalls(int newNumBalls) +{ + numBalls = balls->count(); + + if (!(gameState.testBit(Playing) || gameState.testBit(Demo)) || numBalls == newNumBalls) + return; + + while ( newNumBalls > numBalls) { + Ball *b = new Ball(board, pix); + balls->append(b); + numBalls++; + } + while (newNumBalls < numBalls) { + Ball *b = balls->getLast(); + b->zero(); + balls->removeLast(); + numBalls--; + } +} + +void Rattler::setBallsAI(int i) +{ + ballsAI = i; +} + +void Rattler::resizeEvent( QResizeEvent * ) +{ + pix->initPixmaps(); + pix->initBrickPixmap(); + pix->initbackPixmaps(); + pix->initRoomPixmap(); +} + +void Rattler::setCompuSnakes(int i) +{ + CompuSnake *cs; + numSnakes = i; + int count = computerSnakes->count(); + + if (gameState.testBit(Playing) || gameState.testBit(Demo)) { + if ( i > count) { + while ( i > count) { + CompuSnake *as; + switch(snakesAI) { + default: // random. + as = new CompuSnake(board, pix); + break; + case 1: // eater. + as = new EaterCompuSnake(board, pix); + break; + case 2: // killer. + as = new KillerCompuSnake(board, pix); + break; + } + connect( as, SIGNAL(closeGate(int)), this, SLOT(closeGate(int))); + connect( as, SIGNAL(restartTimer()), this, SLOT(restartTimer())); + connect( as, SIGNAL(score(bool, int)), this, SLOT(scoring(bool,int))); + connect( as, SIGNAL(killed()), this, SLOT(killedComputerSnake())); + computerSnakes->append(as); + i--; + } + } + else if (i < count) { + while (i < count) { + cs = computerSnakes->getLast(); + cs->zero(); + computerSnakes->removeLast(); + i++; + } + } + } +} + +void Rattler::setSnakesAI(int i) +{ + snakesAI = i; +} + +void Rattler::setSkill(int i) +{ + skill = i; + if (gameState.testBit(Playing) || gameState.testBit(Demo)) { + stop(); + start(); + } +} + +void Rattler::setRoom(int i) +{ + room = i; +} + +void Rattler::reloadRoomPixmap() +{ + pix->initbackPixmaps(); + pix->initRoomPixmap(); + demo(); +} + +#include "rattler.moc" + diff --git a/ksnake/rattler.h b/ksnake/rattler.h new file mode 100644 index 00000000..2922dfe6 --- /dev/null +++ b/ksnake/rattler.h @@ -0,0 +1,155 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef RATTLER_H +#define RATTLER_H + +#include +#include +#include "ball.h" +#include "snake.h" + +class Board; +class PixServer; +class Level; +class Basket; +class SamySnake; + + +enum { Init, Playing, Demo, Paused, Over }; + +class Rattler : public QWidget +{ + Q_OBJECT + +public: + Rattler ( QWidget *parent=0, const char *name=0 ); + ~Rattler(); + void setActionCollection(KActionCollection *a){ actionCollection = a;} + + + void setBalls(int); + void setBallsAI(int); + void setCompuSnakes(int); + void setSnakesAI(int); + void setSkill(int); + void setRoom(int); + + void reloadRoomPixmap(); + + QStringList backgroundPixmaps; + +public slots: + void closeGate(int); + void openGate(); + void loadSettings(); + + void scoring(bool, int); + + void restart(); + void newTry(); + void levelUp(); + + void pause(); + void restartTimer(); + + void speedUp(); + + void run(); + void demo(); + + void killedComputerSnake(); + +private slots: + void start(); + void stop(); + void showRoom(); + void restartDemo(); + +signals: + void setPoints(int); + void setTrys(int); + + void setScore(int); + // Is this used? Maybe remove? + void togglePaused(); + + // progress + void rewind(); + void advance(); + +protected: + void timerEvent( QTimerEvent * ); + void paintEvent( QPaintEvent * ); + void keyPressEvent( QKeyEvent * ); + void mousePressEvent( QMouseEvent * ); + void focusOutEvent( QFocusEvent * ) { ; } + void focusInEvent( QFocusEvent * ) { ; } + KActionCollection *actionCollection; + +private: + Board *board; + PixServer *pix; + Level *level; + Basket *basket; + SamySnake *samy; + + int timerCount; + bool leaving; + + int check; + int points; + int trys; + + int direction; + + QPtrList *balls; + void restartBalls(bool); + int numBalls; + int ballsAI; + + QPtrList *computerSnakes; + void restartComputerSnakes(bool); + int numSnakes; + int snakesAI; + + int room; + int skill; + + int gameTimer; + bool timerHasRunOut; + void start(int); + void resetFlags(); + void init(bool); + + void score(int); + void cleanLabel(); + + void resizeEvent( QResizeEvent * ); +}; + + +#endif // RATTLER_H + diff --git a/ksnake/settings.kcfgc b/ksnake/settings.kcfgc new file mode 100644 index 00000000..cf8ee5e6 --- /dev/null +++ b/ksnake/settings.kcfgc @@ -0,0 +1,5 @@ +# Code generation options for kconfig_compiler +File=ksnake.kcfg +ClassName=Settings +Singleton=true +Mutators=false diff --git a/ksnake/snake.cpp b/ksnake/snake.cpp new file mode 100644 index 00000000..33b3f027 --- /dev/null +++ b/ksnake/snake.cpp @@ -0,0 +1,557 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include "snake.h" + +int opposite[4] = { S, N , W, E }; + +int emptySq[4][4]={ + { N, E, W, N }, + { S, W, E, S }, + { E, N, S, E }, + { W, S, N, W } +}; + +Snake::Snake(Board *b, PixServer *p, Gate g, PixMap x) +{ + list.setAutoDelete( true ); + pixServer = p; + board = b; + gate = g; + pixmap = x; + random.setSeed(0); +} + +void Snake::updateSamy() +{ + int x = tail(); + while ( x > 0) { + *list.at(x) = *list.at(x-1); + --x; + } +} + +void Snake::zero() +{ + for ( Samy *sam = list.first(); sam != 0; sam = list.next() ) { + board->set(sam->index, empty); + pixServer->erase(sam->index); + } +} + +void Snake::appendSamy() +{ + Samy *sam = new Samy; + list.append(sam); + + updateSamy(); + grow--; +} + +void Snake::reset(int index, int border) +{ + Samy *sam = list.first(); + + switch (border) { + case N: + sam->pixmap = (tail() == 0 ? HtailUp : HeadUp); + break; + case S: + sam->pixmap = (tail() == 0 ? HtailDown : HeadDown); + break; + case E: + sam->pixmap = (tail() == 0 ? HtailRight : HeadRight); + break; + case W: + sam->pixmap = (tail() == 0 ? HtailLeft : HeadLeft); + break; + } + + sam->index = index; + sam->direction = border; + + if (tail() > 1) { + + sam = list.next(); + + if (sam->direction == border) { + if (border == N || border == S) + sam->pixmap = BodyVt; + else + sam->pixmap = BodyHz; + } + else { + if (border == W && sam->direction == S + || border == N && sam->direction == E) + sam->pixmap = AngleNw; + if (border == E && sam->direction == S + || border == N && sam->direction == W) + sam->pixmap = AngleNe; + if(border == W && sam->direction == N + || border == S && sam->direction == E) + sam->pixmap = AngleSw; + if(border == E && sam->direction == N + || border == S && sam->direction == W) + sam->pixmap = AngleSe; + } + + + } //end if (tail() > 1) + + if (tail() > 0) { + + sam = list.last(); + + switch (list.at(tail()-1)->direction) { + case N: + sam->pixmap = TailUp; + break; + case S: + sam->pixmap = TailDown; + break; + case E: + sam->pixmap = TailRight; + break; + case W: + sam->pixmap = TailLeft; + break; + } + } +} + +void Snake::repaint( bool dirty) +{ + int x = 0; + for ( Samy *sam = list.first(); sam != 0; sam = list.next(), x++) { + if (sam->index != OUT ) { + if(!dirty && x > 1 && x < tail()) + continue; + pixServer->draw(sam->index, pixmap, sam->pixmap); + } + } + if (!growing() && hold != OUT && hold != gate) { + pixServer->erase(hold); + } +} + + +CompuSnake::CompuSnake( Board *b, PixServer *p) + : Snake( b, p, NORTH_GATE, CompuSnakePix ) +{ + init(); +} + +bool CompuSnake::init() +{ + if( !list.isEmpty()) { + list.clear(); + } + + int index = NORTH_GATE; + int length = 12; + grow = 0; + hold = OUT; + + if ( !board->isBrick(gate) ) return false; + + Samy *sam; + for ( int x = 0; x < length; x++) { + board->set(index, snake); + sam = new Samy; + sam->direction = S; + sam->index = index; + sam->pixmap = (x == 0 ? HeadDown : BodyVt); + list.append(sam); + index = -1; + } + return true; +} + +bool CompuSnake::permission() +{ + if( list.isEmpty() ){ + + if ( hold != OUT) { + emit killed(); + hold = OUT; + } + + if(board->isBrick(gate)){ + static int skip = 12; + if (skip < 12) { + skip++; + return false; + } else { + skip = 0; + return init(); + } + } + else return false; + } + else return true; +} + +void CompuSnake::nextMove() +{ + if (!permission()) + return; + + Samy *sam = list.first(); + int index = sam->index; + int dir = sam->direction; + static bool varies = false; + + + bool found = false; + + for ( int x = 0; x < 4 ; x++) { + int next = board->getNext(x, sam->index); + if (board->isApple(next)){ + index = next; + dir = x; + found = true; + grow+=6; + emit score(false, index); + break; + } + } + + if(!found) + for ( int x = 0; x < 4 ; x++) { + int sq = emptySq[sam->direction][x]; + if (varies && (x > 0 && x < 3)) + sq = opposite[sq]; + int next = board->getNext(sq, sam->index); + if (findEmpty(next, x)) { + index = next; + dir = sq; + found = true; + break; + } + } + varies = !varies; + + if(!found) { + hold = list.last()->index; + if (board->isSnake(hold)) board->set(hold, empty); + removeSamy(); + } + else + if(growing()) + appendSamy(); + else + if (!growing() && found) { + hold = list.last()->index; + if (board->isSnake(hold)) board->set(hold, empty); + updateSamy(); + } + + if( !list.isEmpty()) { + board->set(index, snake); + reset(index, dir); + } + + if ( hold == gate) + out(); +} + +void KillerCompuSnake::nextMove() +{ + if (!permission()) return; + + Samy *sam = list.first(); + int index = sam->index; + int dir = sam->direction; + static bool varies = false; + + + bool found = false; + + if(!found) { + int sn = board->samyHeadIndex(); + if(sn != -1 && board->isHead(sn)) { + int nextSq = board->getNextCloseTo(index, sn, false, lastIndex); + if(nextSq != -1) { + dir = board->direction(index, nextSq); + index = nextSq; + found = true; + } + } + } + + if(!found) + for ( int x = 0; x < 4 ; x++) { + int sq = emptySq[sam->direction][x]; + if (varies && (x > 0 && x < 3)) + sq = opposite[sq]; + int next = board->getNext(sq, sam->index); + if (findEmpty(next, x)) { + index = next; + dir = sq; + found = true; + break; + } + } + varies = !varies; + + if(!found) { + hold = list.last()->index; + if (board->isSnake(hold)) board->set(hold, empty); + removeSamy(); + } + else { + lastIndex = index; + + if(growing()) + appendSamy(); + else + if (!growing() && found) { + hold = list.last()->index; + if (board->isSnake(hold)) board->set(hold, empty); + updateSamy(); + } + } + + if( !list.isEmpty()) { + board->set(index, snake); + reset(index, dir); + } + + if ( hold == gate) + out(); +} + +void EaterCompuSnake::nextMove() +{ + if (!permission()) return; + + Samy *sam = list.first(); + int index = sam->index; + int dir = sam->direction; + static bool varies = false; + + + bool found = false; + + for ( int x = 0; x < 4 ; x++) { + int next = board->getNext(x, sam->index); + if (board->isApple(next)){ + index = next; + dir = x; + found = true; + grow+=6; + emit score(false, index); + break; + } + } + + if(!found) { + int sn; + bool apple = false; + for(sn = board->count() - 1; sn > 0; sn --) { + if(board->isApple(sn)) { + apple = true; + int nextSq = board->getNextCloseTo(index, sn, false); + if(nextSq != -1) { + dir = board->direction(index, nextSq); + index = nextSq; + found = true; + break; + } + } + } + if(!found && !apple) { + // No more apples, move snake to gate. + int nextSq = board->getNextCloseTo(index, gate, false, lastIndex); + if(nextSq != -1) { + dir = board->direction(index, nextSq); + index = nextSq; + found = true; + } + } + } + + if(!found) + for ( int x = 0; x < 4 ; x++) { + int sq = emptySq[sam->direction][x]; + if (varies && (x > 0 && x < 3)) + sq = opposite[sq]; + int next = board->getNext(sq, sam->index); + if (findEmpty(next, x)) { + index = next; + dir = sq; + found = true; + break; + } + } + varies = !varies; + + if(!found) { + hold = list.last()->index; + if (board->isSnake(hold)) board->set(hold, empty); + removeSamy(); + } + else { + lastIndex = index; + + if(growing()) + appendSamy(); + else + if (!growing() && found) { + hold = list.last()->index; + if (board->isSnake(hold)) board->set(hold, empty); + updateSamy(); + } + } + + if( !list.isEmpty()) { + board->set(index, snake); + reset(index, dir); + } + + if ( hold == gate) + out(); +} + +bool CompuSnake::findEmpty(int i, int it) +{ + bool found = false; + bool change = false; + static int s_random = random.getLong(BoardWidth/2); + static int moves = 0; + + if (moves > s_random) { + s_random = random.getLong(BoardWidth/2); + moves = 0; + change = true; + } + + found = ( ( board->isEmpty(i) && it > 0) + || ( board->isEmpty(i) && !change && it == 0) ); + + moves++; + change = false; + return found; +} + +void CompuSnake::removeSamy() +{ + list.remove(); + grow = 0; +} + +void CompuSnake::out() +{ + emit closeGate( gate ); + + if( list.isEmpty() ) + return; + + if(list.first()->index == OUT) { + emit restartTimer(); + list.clear(); + } +} + +SamySnake::SamySnake( Board *b, PixServer *p) + : Snake( b, p, SOUTH_GATE, SamyPix ) +{ + +} + +void SamySnake::init() +{ + if( !list.isEmpty()) { + list.clear(); + } + Samy *sam; + + int index = SOUTH_GATE; + int length = 12; + grow = 0; + hold = 0; + + for ( int x = 0; x < length; x++) { + board->set(index, head); + sam = new Samy; + sam->direction = N; + sam->index = index; + sam->pixmap = (x == 0 ? HeadUp : BodyVt); + list.append(sam); + index = -1; + } +} + +samyState SamySnake::nextMove(int direction) +{ + Samy *sam = list.first(); + + if(!board->isHead(sam->index) && sam->index != OUT) + return ko; + + if ( direction == opposite[sam->direction]) + direction = sam->direction; + + if(sam->index == gate || sam->index == OUT ) + direction = N; + + if (sam->index == NORTH_GATE) { + emit goingOut(); + direction = N; + } + + int index = board->getNext(direction, sam->index); + + if (board->isApple(index)) { + grow+=6; + emit score(true, index); + } + else if (!board->isEmpty(index)) + return ko; + + if(growing()) + appendSamy(); + else { + hold = list.last()->index; + board->set(hold, empty); + updateSamy(); + } + + board->set(sam->index, snake); + reset(index, direction); + board->set(index, head); + + if ( hold == gate) + emit closeGate( gate ); + else if ( hold == NORTH_GATE) + return out; + + return ok; +} + +#include "snake.moc" + diff --git a/ksnake/snake.h b/ksnake/snake.h new file mode 100644 index 00000000..863ec593 --- /dev/null +++ b/ksnake/snake.h @@ -0,0 +1,140 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef SAMY_H +#define SAMY_H + +#include +#include "pixServer.h" +#include "board.h" + +class Snake : public QObject +{ + + Q_OBJECT + +signals: + void score(bool, int); + void killed(); + void closeGate(int); + void restartTimer(); + void goingOut(); + +public: + Snake(Board *b, PixServer *p, Gate g, PixMap x); + ~Snake() {} + void repaint( bool ); + void zero(); + +protected: + Board *board; + PixServer *pixServer; + Gate gate; + PixMap pixmap; + + struct Samy { + int direction; + SnakePix pixmap; + int index; + }; + + QPtrList list; + + KRandomSequence random; + + void reset(int index, int border); + void appendSamy(); + void updateSamy(); + int tail() const { return (list.count() -1 ); } + bool growing() const { return (grow > 0 ? TRUE : FALSE); } + + int hold; + int grow; +}; + +class CompuSnake : public Snake +{ + +public: + CompuSnake(Board *b, PixServer *p); + virtual ~CompuSnake() {} + virtual void nextMove(); + +protected: + bool init(); + void removeSamy(); + bool findEmpty(int i, int it); + bool permission(); + void out(); + +}; + +/** + * Don't eat any apples. + * Try to hit samy's head. + */ +class KillerCompuSnake : public CompuSnake +{ + +public: + KillerCompuSnake(Board *b, PixServer *p) : CompuSnake(b, p) {} + virtual ~KillerCompuSnake() {} + virtual void nextMove(); + +private: + int lastIndex; + +}; + +/** + * Eat as much apples as it can, from down to up. + * When all apples are eaten, try to reach the gate. + */ +class EaterCompuSnake : public CompuSnake +{ + +public: + EaterCompuSnake(Board *b, PixServer *p) : CompuSnake(b, p) {} + virtual ~EaterCompuSnake() {} + virtual void nextMove(); + +private: + int lastIndex; + +}; + +enum samyState { ok, ko, out }; + +class SamySnake : public Snake +{ + +public: + SamySnake(Board *, PixServer *); + samyState nextMove(int direction); + void init(); + +}; + +#endif // SAMY_H diff --git a/ksnake/startroom.cpp b/ksnake/startroom.cpp new file mode 100644 index 00000000..5bb4d989 --- /dev/null +++ b/ksnake/startroom.cpp @@ -0,0 +1,81 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include "startroom.h" + +#include +#include +#include +#include +#include +#include + +#include "levels.h" + +StartRoom::StartRoom( QWidget *parent, const char *name) + : QWidget( parent, name ) +{ + QGridLayout *Form1Layout = new QGridLayout( this, 1, 1, 11, 6, "Form1Layout"); + QSpacerItem* spacer = new QSpacerItem( 20, 61, QSizePolicy::Minimum, QSizePolicy::Expanding ); + Form1Layout->addItem( spacer, 2, 1 ); + + QHBoxLayout *layout1 = new QHBoxLayout( 0, 0, 6, "layout1"); + QSpacerItem* spacer_2 = new QSpacerItem( 91, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); + layout1->addItem( spacer_2 ); + + picture = new QLabel( this, "picture" ); + layout1->addWidget( picture ); + QSpacerItem* spacer_3 = new QSpacerItem( 41, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); + layout1->addItem( spacer_3 ); + + Form1Layout->addMultiCellLayout( layout1, 0, 0, 0, 1 ); + + roomRange = new QSpinBox( this, "kcfg_StartingRoom" ); + roomRange->setMaxValue( 25 ); + roomRange->setMinValue( 1 ); + + Form1Layout->addWidget( roomRange, 1, 1 ); + + QLabel *textLabel = new QLabel( this, "textLabel" ); + textLabel->setText(i18n("First level:")); + Form1Layout->addWidget( textLabel, 1, 0 ); + + connect( roomRange, SIGNAL(valueChanged(int)), SLOT(loadLevel(int))); + loadLevel(1); +} + +void StartRoom::loadLevel(int level) +{ + if(level < 1 || level > leV->max()) + return; + + QPixmap pixmap = leV->getPixmap(level); + QWMatrix m; + m.scale( (double)7, (double)7 ); + pixmap = pixmap.xForm( m ); + picture->setPixmap(pixmap); +} + +#include "startroom.moc" + diff --git a/ksnake/startroom.h b/ksnake/startroom.h new file mode 100644 index 00000000..ef8cbbe0 --- /dev/null +++ b/ksnake/startroom.h @@ -0,0 +1,50 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef STARTROOM_H +#define STARTROOM_H + +#include + +class QLabel; +class QSpinBox; + +class StartRoom : public QWidget +{ + Q_OBJECT +public: + StartRoom( QWidget *parent=0, const char *name=0 ); + +private slots: + void loadLevel(int level); + +private: + QLabel *picture; + QSpinBox *roomRange; + +}; + +#endif // STARTROOM_H + diff --git a/ksnake/version.h b/ksnake/version.h new file mode 100644 index 00000000..0fc13a82 --- /dev/null +++ b/ksnake/version.h @@ -0,0 +1 @@ +#define KSNAKE_VERSION "0.4.0" diff --git a/ksnake/view.cpp b/ksnake/view.cpp new file mode 100644 index 00000000..3db7d34f --- /dev/null +++ b/ksnake/view.cpp @@ -0,0 +1,59 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include "view.h" + +#include "progress.h" +#include "rattler.h" + +int BRICKSIZE = 16; +int MAPWIDTH = BRICKSIZE * 35; +int MAPHEIGHT = MAPWIDTH; + +#define BAR_HEIGHT 12 +View::View( QWidget *parent, const char *name ) + : QWidget( parent, name ) +{ + progress = new Progress(this); + rattler = new Rattler( this); + setMinimumSize(145,145+BAR_HEIGHT); +} + +void View::resizeEvent( QResizeEvent * ) +{ + // These hard coded number really need to be documented + BRICKSIZE= (int)16* ((width() < height() - BAR_HEIGHT) ? width() : height() - BAR_HEIGHT)/ 560; + MAPWIDTH=BRICKSIZE * BoardWidth; + MAPHEIGHT=MAPWIDTH; + + progress->setGeometry(5, 0, width()-5, BAR_HEIGHT); + rattler->setGeometry(0, BAR_HEIGHT, width(), height()-BAR_HEIGHT); +} + +QSize View::sizeHint() const +{ + return QSize(490,502); +} + +#include "view.moc" diff --git a/ksnake/view.h b/ksnake/view.h new file mode 100644 index 00000000..3dff2dbb --- /dev/null +++ b/ksnake/view.h @@ -0,0 +1,48 @@ +/** + * Copyright Michel Filippi + * Robert Williams + * Andrew Chant + * André Luiz dos Santos + * Benjamin Meyer + * + * This file is part of the ksnake package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef VIEW_H +#define VIEW_H + +#include + +class Progress; +class Rattler; + +class View : public QWidget +{ + Q_OBJECT +public: + View ( QWidget *parent=0, const char *name=0 ); + + Progress *progress; + Rattler *rattler; + +protected: + void resizeEvent( QResizeEvent * ); + virtual QSize sizeHint() const; +}; + +#endif // VIEW_H + diff --git a/ksokoban/AUTHORS b/ksokoban/AUTHORS new file mode 100644 index 00000000..3b450a4c --- /dev/null +++ b/ksokoban/AUTHORS @@ -0,0 +1,36 @@ +------------------------------------------------------------------------ +ksokoban is written by: + +Anders Widell + +------------------------------------------------------------------------ +The levels were taken from: + +xsokoban 3.3c for X-windows + www: http://xsokoban.lcs.mit.edu/xsokoban.html + author: Andrew Myers + +MacSokoban 3.0.3 for Macintosh + www: http://www.lysator.liu.se/~ingemar/games.html + author: Ingemar Ragnemalm + +Sokoban 2.4 for Macintosh + www: http://members.aol.com/SokobanMac/ + author: Scott Lindhurst + +------------------------------------------------------------------------ +The levels originally come from: + +Original the 50 original sokoban levels + made by Thinking rabbit Inc. in Japan +Extra some more levels from xsokoban +Still more by J. Franklin Mentzer +MacTommy inventions by a guy called Tommy in Pennsylvania +Yoshio's autogenerated by Yoshio Murase + see http://www.ne.jp/asahi/ai/yoshio/sokoban/main.htm +For the kids by Ross (W.R.) Brown +Simple Sokoban simplified original levels + by Phil Shapiro +Dimitri & Yorick by Jacques Duthen + +------------------------------------------------------------------------ diff --git a/ksokoban/Bookmark.cpp b/ksokoban/Bookmark.cpp new file mode 100644 index 00000000..796d54b6 --- /dev/null +++ b/ksokoban/Bookmark.cpp @@ -0,0 +1,108 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "Bookmark.h" +#include "History.h" +#include "LevelMap.h" + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +void +Bookmark::fileName(QString &p) { + p = KGlobal::dirs()->saveLocation("appdata"); + + QString n; + n.setNum(number_); + p += "/bookmark" + n; +} + +Bookmark::Bookmark(int _num) : + number_(_num), collection_(-1), level_(-1), moves_(0), data_("") { + + QString p; + fileName(p); + + FILE *file = fopen(p.latin1(), "r"); + if (file == NULL) return; + + char buf[4096]; + buf[0] = '\0'; + fgets (buf, 4096, file); + if (sscanf(buf, "%d %d %d", &collection_, &level_, &moves_) != 3) { + collection_ = level_ = -1; + data_ = ""; + fclose(file); + return; + } + + data_ = ""; + int len; + while (!feof(file)) { + len = fread(buf, 1, 4095, file); + if (ferror(file)) break; + buf[len] = '\0'; + data_ += buf; + } + fclose(file); + + data_ = data_.stripWhiteSpace(); +} + + + +void +Bookmark::set(int _collection, int _level, int _moves, History *_h) { + assert(_collection >= 0); + if (_collection < 0) return; + + collection_ = _collection; + level_ = _level; + moves_ = _moves; + + data_ = ""; + _h->save(data_); + + QString p; + fileName(p); + FILE *file = fopen(QFile::encodeName(p), "w"); + if (file == NULL) return; + fprintf(file, "%d %d %d\n", collection_, level_, moves_); + fprintf(file, "%s\n", data_.latin1()); + fclose(file); +} + +bool +Bookmark::goTo(LevelMap *_map, History *_h) { + return _h->load(_map, data_.latin1()) != 0; +} + + diff --git a/ksokoban/Bookmark.h b/ksokoban/Bookmark.h new file mode 100644 index 00000000..30c5faff --- /dev/null +++ b/ksokoban/Bookmark.h @@ -0,0 +1,51 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef BOOKMARK_H +#define BOOKMARK_H + +class History; +class LevelMap; + +#include + +class Bookmark { +public: + Bookmark(int _num); + + int collection() const { return collection_; } + int level() const { return level_; } + int moves() const { return moves_; } + //int pushes() { return pushes_; } + + void set(int _collection, int _level, int _moves, History *_h); + bool goTo(LevelMap *_map, History *_h); + +private: + void fileName(QString &p); + + int number_; + int collection_; + int level_; + int moves_; + //int pushes_; + QString data_; +}; + +#endif /* BOOKMARK_H */ diff --git a/ksokoban/History.cpp b/ksokoban/History.cpp new file mode 100644 index 00000000..4c73827d --- /dev/null +++ b/ksokoban/History.cpp @@ -0,0 +1,131 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "History.h" +#include "Move.h" +#include "MoveSequence.h" +#include "LevelMap.h" + +History::History() { + past_.setAutoDelete(true); + future_.setAutoDelete(true); +} + + +void +History::add(Move *_m) { + future_.clear(); + past_.append(_m); +} + +void +History::clear() { + past_.clear(); + future_.clear(); +} + +void +History::save(QString &_str) { + Move *m = past_.first(); + + while (m != 0) { + m->save(_str); + m = past_.next(); + } + _str += '-'; + + m = future_.first(); + while (m != 0) { + m->save(_str); + m = future_.next(); + } +} + +const char * +History::load(LevelMap *map, const char *_str) { + Move *m; + int x = map->xpos(); + int y = map->ypos(); + + clear(); + while (*_str != '\0' && *_str != '-') { + m = new Move(x, y); + _str = m->load(_str); + if (_str == 0) return 0; + x = m->finalX(); + y = m->finalY(); + past_.append(m); + if (!m->redo(map)) { + //printf("redo failed: %s\n", _str); + //abort(); + return 0; + } + } + if (*_str != '-') return 0; + + _str++; + while (*_str != '\0') { + m = new Move(x, y); + _str = m->load(_str); + if (_str == 0) return 0; + x = m->finalX(); + y = m->finalY(); + future_.append(m); + } + + return _str; +} + +bool +History::redo(LevelMap *map) { + if (future_.isEmpty()) return false; + + Move *m=future_.take(0); + past_.append(m); + return m->redo(map); +} + +MoveSequence * +History::deferRedo(LevelMap *map) { + if (future_.isEmpty()) return 0; + + Move *m=future_.take(0); + past_.append(m); + return new MoveSequence(m, map); +} + +bool +History::undo(LevelMap *map) { + if (past_.isEmpty()) return false; + + Move *m = past_.take(past_.count ()-1); + future_.insert(0, m); + return m->undo(map); +} + +MoveSequence * +History::deferUndo(LevelMap *map) { + if (past_.isEmpty()) return 0; + + Move *m = past_.take(past_.count()-1); + future_.insert(0, m); + return new MoveSequence(m, map, true); +} diff --git a/ksokoban/History.h b/ksokoban/History.h new file mode 100644 index 00000000..c3db5194 --- /dev/null +++ b/ksokoban/History.h @@ -0,0 +1,64 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef HISTORY_H +#define HISTORY_H + +#include +#include + +#include "Move.h" +class MoveSequence; + +/** + * Maintains movement history + * + * @short Maintains game movement history + * @author Anders Widell + * @version 0.1 + * @see PlayField + */ + +class History { +private: + QPtrList past_; + QPtrList future_; + +protected: + +public: + History(); + /** + * Add a move to the history. Deletes all currently undone moves. + */ + void add(Move *_m); + /** + * Clear the history and delete all Move objects stored in it. + */ + void clear(); + + void save(QString &_str); + const char *load(LevelMap *map, const char *_str); + bool redo(LevelMap *map); + MoveSequence *deferRedo(LevelMap *map); + bool undo(LevelMap *map); + MoveSequence *deferUndo(LevelMap *map); +}; + +#endif /* HISTORY_H */ diff --git a/ksokoban/HtmlPrinter.cpp b/ksokoban/HtmlPrinter.cpp new file mode 100644 index 00000000..26e7207f --- /dev/null +++ b/ksokoban/HtmlPrinter.cpp @@ -0,0 +1,100 @@ +#include +#include +#include "HtmlPrinter.h" + +void +HtmlPrinter::wall (bool up, bool down, bool left, bool right) +{ + switch ( (up!=0) | + ((down!=0) << 1) | + ((left!=0) << 2) | + ((right!=0) << 3)) { + case 0: + case 1: + case 2: + case 3: + image ("vertiwall"); + break; + case 4: + case 5: + case 6: + case 7: + image ("eastwall"); + break; + case 8: + case 9: + case 10: + case 11: + image ("westwall"); + break; + case 12: + case 13: + case 14: + case 15: + image ("horizwall"); + break; + + default: + abort (); + } +} + +void +HtmlPrinter::image (const char *name) { + printf ("
\n", name); +} + +void +HtmlPrinter::empty () { + printf ("\n"); +} + +void +HtmlPrinter::printSquare (LevelMap *lm, int x, int y) { + if (lm->xpos () == x && lm->ypos () == y) { + image (lm->goal (x, y) ? "saveman" : "man"); + return; + } + if (lm->empty (x, y)) { + if (lm->floor (x, y)) { + image (lm->goal (x, y) ? "goal" : "floor"); + } else { + empty (); + } + return; + } + if (lm->wall (x, y)) { + wall (lm->wallUp (x, y), + lm->wallDown (x, y), + lm->wallLeft (x, y), + lm->wallRight (x, y)); + return; + } + if (lm->object (x, y)) { + image (lm->goal (x, y) ? "treasure" : "object"); + return; + } +} + +void +HtmlPrinter::printHtml (LevelMap *lm) { + printf ("\ +\n\ +\n\ +ksokoban level\n\ +\n\ +\n\ +"); + printf ("\n"); + for (int y=0; yheight(); y++) { + printf ("\n"); + for (int x=0; xwidth(); x++) { + printSquare (lm, x, y); + } + } + printf ("\ +
\n\ +\n\ +\n\ +"); +} diff --git a/ksokoban/HtmlPrinter.h b/ksokoban/HtmlPrinter.h new file mode 100644 index 00000000..1f277607 --- /dev/null +++ b/ksokoban/HtmlPrinter.h @@ -0,0 +1,18 @@ +#ifndef HTMLPRINTER_H +#define HTMLPRINTER_H + +#include "LevelMap.h" + +class HtmlPrinter { +public: + static void printHtml (LevelMap *lm); + +protected: + static void wall (bool up, bool down, bool left, bool right); + static void image (const char *name); + static void empty (); + static void printSquare (LevelMap *lm, int x, int y); +}; + + +#endif /* HTMLPRINTER_H */ diff --git a/ksokoban/ImageData.cpp b/ksokoban/ImageData.cpp new file mode 100644 index 00000000..13040c47 --- /dev/null +++ b/ksokoban/ImageData.cpp @@ -0,0 +1,213 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "ImageData.h" + +#include +#include +#include +#include +#include + +ImageData::ImageData() : indexSize_(0), size_(0), halfSize_(0) { + random.setSeed(0); +} + +ImageData::~ImageData() { +} + +void +ImageData::expandIndex(int size) { + size++; + assert(size < 2500); + + upperLargeIndex_.resize(size); + lowerLargeIndex_.resize(size); + leftSmallIndex_.resize(size); + rightSmallIndex_.resize(size); + + for (int i=indexSize_; i= 0); + if (indexSize_ <= index) expandIndex(index); + return largeStone_xpm_[(unsigned char)upperLargeIndex_[index]]; +} + +const QPixmap & +ImageData::lowerLarge(int index) { + assert(index >= 0); + if (indexSize_ <= index) expandIndex(index); + return largeStone_xpm_[(unsigned char)lowerLargeIndex_[index]]; +} + +const QPixmap & +ImageData::leftSmall(int index) { + assert(index >= 0); + if (indexSize_ <= index) expandIndex(index); + return smallStone_xpm_[(unsigned char)leftSmallIndex_[index]]; +} + +const QPixmap & +ImageData::rightSmall(int index) { + assert(index >= 0); + if (indexSize_ <= index) expandIndex(index); + return smallStone_xpm_[(unsigned char)rightSmallIndex_[index]]; +} + +int +ImageData::resize(int size) { + assert(size > 0); + size &= ~1u; + if (size == size_) return size; + + size_ = size; + halfSize_ = size/2; + + for (int i=0; i g && r > b) { + // only modify redish pixels + + QColor col(r, g, b); + QColor lcol = col.light(130); + + img.setPixel(x, y, lcol.rgb()); + } + } + } +} + +void +ImageData::wall(QPainter &p, int x, int y, int index, bool left, bool right) { + if (left) p.drawPixmap(x, y, upperLarge(index-1), halfSize_); + else p.drawPixmap(x, y, leftSmall(index)); + + if (right) p.drawPixmap(x+halfSize_, y, upperLarge(index), 0, 0, halfSize_); + else p.drawPixmap(x+halfSize_, y, rightSmall(index)); + + p.drawPixmap(x, y+halfSize_, lowerLarge(index)); +} + +void +ImageData::floor(QPainter &p, int x, int y) { + p.eraseRect(x, y, size_, size_); +} + +void +ImageData::goal(QPainter &p, int x, int y) { + p.drawPixmap(x, y, otherPixmaps_[2]); +} + +void +ImageData::man(QPainter &p, int x, int y) { + p.drawPixmap(x, y, otherPixmaps_[3]); +} + +void +ImageData::object(QPainter &p, int x, int y) { + p.drawPixmap(x, y, otherPixmaps_[0]); +} + +void +ImageData::saveman(QPainter &p, int x, int y) { + p.drawPixmap(x, y, otherPixmaps_[4]); +} + +void +ImageData::treasure(QPainter &p, int x, int y) { + p.drawPixmap(x, y, otherPixmaps_[1]); +} + +void +ImageData::brightObject(QPainter &p, int x, int y) { + p.drawPixmap(x, y, brightObject_); +} + +void +ImageData::brightTreasure(QPainter &p, int x, int y) { + p.drawPixmap(x, y, brightTreasure_); +} diff --git a/ksokoban/ImageData.h b/ksokoban/ImageData.h new file mode 100644 index 00000000..4e57bc7d --- /dev/null +++ b/ksokoban/ImageData.h @@ -0,0 +1,87 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef IMAGEDATA_H +#define IMAGEDATA_H + +#include +#include +#include + +#include + +class QPainter; + +#define SMALL_STONES 4 +#define LARGE_STONES 6 +#define OTHER_IMAGES 5 +#define NO_OF_IMAGES (SMALL_STONES + LARGE_STONES + OTHER_IMAGES) + +class +ImageData { +public: + virtual ~ImageData(); + + int resize(int size); + int size() { return size_; } + + void wall(QPainter &p, int x, int y, int index, bool left, bool right); + void floor(QPainter &p, int x, int y); + void goal(QPainter &p, int x, int y); + void man(QPainter &p, int x, int y); + void object(QPainter &p, int x, int y); + void saveman(QPainter &p, int x, int y); + void treasure(QPainter &p, int x, int y); + void brightObject(QPainter &p, int x, int y); + void brightTreasure(QPainter &p, int x, int y); + + const QPixmap &background() { return background_; } + const QImage& objectImg() const { return objectImg_; } + +protected: + ImageData(); + + void expandIndex(int size); + void image2pixmap(QImage img, QPixmap& xpm, bool diffuse=true); + void brighten(QImage& img); + + const QPixmap &upperLarge(int index); + const QPixmap &lowerLarge(int index); + const QPixmap &leftSmall(int index); + const QPixmap &rightSmall(int index); + + QImage images_[NO_OF_IMAGES]; + + QPixmap smallStone_xpm_[SMALL_STONES]; + QPixmap largeStone_xpm_[LARGE_STONES]; + QPixmap otherPixmaps_[OTHER_IMAGES]; + QPixmap background_, brightObject_, brightTreasure_; + QImage objectImg_; + + int indexSize_; + QByteArray upperLargeIndex_; + QByteArray lowerLargeIndex_; + QByteArray leftSmallIndex_; + QByteArray rightSmallIndex_; + + int size_, halfSize_; + KRandomSequence random; +}; + +#endif /* IMAGEDATA_H */ diff --git a/ksokoban/InternalCollections.cpp b/ksokoban/InternalCollections.cpp new file mode 100644 index 00000000..cdc3dbb3 --- /dev/null +++ b/ksokoban/InternalCollections.cpp @@ -0,0 +1,148 @@ +#include +#include +#include +#include + +#ifdef USE_LIBZ +#include +#endif + +#include "InternalCollections.h" + +#ifndef LEVELS_INCLUDED +#define LEVELS_INCLUDED 1 +#include "levels/data.c" +#endif + +#define BUFSIZE (128*1024) + +// static const int collection_save_id[] = { +// 0, 1, 3, 5, 9, 6, 7, 8, 2, 4 +// }; + +static const int collection_save_id[] = { + 10, 11, 12, 13, 14 +}; + +int +InternalCollections::configCollection2Real (int collection) { + for (int i=0; i < (int) (sizeof (collection_save_id) / sizeof (int)); i++) { + if (collection_save_id[i] == collection) return i; + } + return 0; +} + +int +InternalCollections::realCollection2Config(int collection) { + assert(collection < (int) (sizeof (collection_save_id) / sizeof (int))); + return collection_save_id[collection]; +} + +QString +InternalCollections::collectionName(int _level) { + switch (_level) { + case 0: + return i18n("Sasquatch"); + break; + + case 1: + return i18n("Mas Sasquatch"); + break; + + case 2: + return i18n("Sasquatch III"); + break; + + case 3: + return i18n("Microban (easy)"); + break; + + case 4: + return i18n("Sasquatch IV"); + break; + } + + assert(false); + return QString(); +} + + +InternalCollections::InternalCollections() { + int datasize, levelnum=0; + +#ifdef USE_LIBZ + data_ = (char *) malloc(BUFSIZE); + if (data_ == NULL) abort(); + + datasize = BUFSIZE; + uncompress ((unsigned char *) data_, (long unsigned int *) &datasize, level_data_, sizeof (level_data_)); + data_ = (char *) realloc(data_, datasize); + if (data_ == NULL) abort (); +#else + datasize = sizeof (level_data_); + data_ = (char *) malloc(datasize); + if (data_ == NULL) abort(); + memcpy(data_, level_data_, datasize); +#endif + + int start=0, end=0, name=0; + enum {NAME, DATA} state=NAME; + while (end < datasize) { + switch (state) { + case NAME: + if (data_[end] == '\n') { + data_[end] = '\0'; + state = DATA; + } + end++; + start = end; + break; + + case DATA: + if (isalpha(data_[end])) { +// collections_.add(new LevelCollection(data_+start, end-start, data_+name, collection_save_id[levelnum])); + add(new LevelCollection(data_+start, end-start, collectionName(levelnum), collection_save_id[levelnum])); + //printf("Level found: '%s'\n", data_+name); + levelnum++; + name = end; + state = NAME; + } + end++; + break; + + default: + assert(0); + } + } + if (state == DATA) { +// collections_.add(new LevelCollection(data_+start, end-start, data_+name, collection_save_id[levelnum])); + add(new LevelCollection(data_+start, end-start, collectionName(levelnum), collection_save_id[levelnum])); + //printf("***Level found: '%s'\n", data_+name); + } + //printf("numlevels: %d/%d\n", levelnum+1, collections_.size()); +} + +InternalCollections::~InternalCollections() { + for (unsigned i=0; i + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef INTERNALCOLLECTIONS_H +#define INTERNALCOLLECTIONS_H + +#include +#include +#include + +#include "LevelCollection.h" + +class InternalCollections { +public: + InternalCollections(); + ~InternalCollections(); + + static int toInternalId(int _id) { + if (_id < 10 || _id > 14) return 1000; + return _id - 10; + } + + int collections(); + LevelCollection *operator[](int n); + +private: + void add(LevelCollection* c); + + static int configCollection2Real(int collection); + static int realCollection2Config(int collection); + static QString collectionName(int _level); + + QPtrVector collections_; + char *data_; + +}; + +#endif /* INTERNALCOLLECTIONS_H */ diff --git a/ksokoban/LevelCollection.cpp b/ksokoban/LevelCollection.cpp new file mode 100644 index 00000000..7f5db852 --- /dev/null +++ b/ksokoban/LevelCollection.cpp @@ -0,0 +1,424 @@ +#include "LevelCollection.h" + +#include "Map.h" + +#include +#include + +#include +#include +#include +#include +#include + +static inline unsigned long +forward(unsigned long a, unsigned long b, unsigned long c, unsigned long d) +{ + unsigned long x=(a^b)&0xfffffffful; + return (((x<>((32ul-c)&31ul)))*d)&0xfffffffful; +} + +static inline unsigned long +backward(unsigned long a, unsigned long b, unsigned long c, unsigned long d) +{ + unsigned long x=(a*b)&0xfffffffful; + return (((x<>((32ul-c)&31ul)))^d)&0xfffffffful; +} + + +void +LevelCollection::indexTextCollection() { + enum states { + BEFORE_NONE, BEFORE_VALID, BEFORE_INVALID, + DURING_NONE, DURING_VALID, DURING_INVALID + } state = BEFORE_NONE; + + int levelstart=0, levelend=0; + for (unsigned pos=0; pos<(data_.size()-1); pos++) { + switch (state) { + case BEFORE_NONE: + switch (data_[pos]) { + case '#': case '.': case '$': case '+': case '*': case '@': + state = BEFORE_VALID; + break; + + case ' ': case '\t': case '\r': + break; + + case '\n': + levelstart = pos + 1; + break; + + default: + state = BEFORE_INVALID; + break; + } + break; + + case BEFORE_VALID: + switch (data_[pos]) { + case '#': case '.': case '$': case '+': case '*': case '@': + case ' ': case '\t': case '\r': + break; + + case '\n': + addLevel(&data_[levelstart]); + levelend = levelstart; + state = DURING_NONE; + break; + + default: + state = BEFORE_INVALID; + break; + } + break; + + case BEFORE_INVALID: + switch (data_[pos]) { + case '\n': + levelstart = pos + 1; + state = BEFORE_NONE; + break; + } + break; + + case DURING_NONE: + switch (data_[pos]) { + case '#': case '.': case '$': case '+': case '*': case '@': + state = DURING_VALID; + break; + + case ' ': case '\t': case '\r': + break; + + case '\n': + data_[levelend] = '\0'; + levelstart = pos + 1; + state = BEFORE_NONE; + break; + + default: + state = DURING_INVALID; + break; + } + break; + + case DURING_VALID: + switch (data_[pos]) { + case '#': case '.': case '$': case '+': case '*': case '@': + case ' ': case '\t': case '\r': + break; + + case '\n': + levelend = pos; + state = DURING_NONE; + break; + + default: + state = DURING_INVALID; + break; + } + break; + + case DURING_INVALID: + switch (data_[pos]) { + case '\n': + data_[levelend] = '\0'; + levelstart = pos + 1; + state = BEFORE_NONE; + break; + } + break; + + default: + assert(0); + } + } + + if (state==DURING_NONE || state==DURING_INVALID) { + data_[levelend] = '\0'; + } +} + +void +LevelCollection::loadPrefs() { + if (id_ >= 0) { + KConfig *cfg=(KApplication::kApplication())->config(); + cfg->setGroup("settings"); + + QString key; + key.sprintf("level%d", id_); + level_ = cfg->readNumEntry(key, 0); + + key.sprintf("status%d", id_); + unsigned long x = cfg->readUnsignedLongNumEntry(key, 0); + + x = backward(x, 0xc1136a15ul, 0x12ul, 0x80ff0b94ul); + x = backward(x, 0xd38fd2ddul, 0x01ul, 0xd4d657b4ul); + x = backward(x, 0x59004eeful, 0x1eul, 0xf6c75e2cul); + x = backward(x, 0x366c3e25ul, 0x0aul, 0x61ebc208ul); + x = backward(x, 0x20a784c9ul, 0x15ul, 0x207d488bul); + x = backward(x, 0xc02864abul, 0x09ul, 0x709e62a3ul); + x = backward(x, 0xe2a60f19ul, 0x0eul, 0x8bb02c07ul); + x = backward(x, 0x3b0e11f3ul, 0x13ul, 0x608aef3ful); + + completedLevels_ = x>>16 & 0x3ff; + if (!cfg->hasKey(key)) completedLevels_ = 0; + if (((x>>26) & 0x3ful) != (unsigned long) id_) completedLevels_ = 0; + if ((x & 0xfffful) != (unsigned long) getuid()) completedLevels_ = 0; + if (completedLevels_ > noOfLevels_) completedLevels_ = 0; + + if (level_ > completedLevels_) level_ = completedLevels_; + if (level_ >= noOfLevels_) level_ = noOfLevels_-1; + if (level_ < 0) level_ = 0; + } else { + level_ = 0; + completedLevels_ = noOfLevels_; + } +} + +void +LevelCollection::addLevel(const char* _level) { + unsigned s = index_.size(); + index_.resize(s + 1); + index_.insert(s, _level); +} + +void +LevelCollection::addData(const char* _data, unsigned _len) { + unsigned pos = data_.size(); + data_.resize(pos + _len); + memcpy(data_.data() + pos, _data, _len); +} + +void +LevelCollection::addSeparator() { + unsigned pos = data_.size(); + data_.resize(pos + 1); + data_[pos] = '\0'; +} + +LevelCollection::LevelCollection(const char *_def, int _len, + const QString &_name, int _id) : + level_(0), completedLevels_(0), noOfLevels_(0), + name_(_name), id_(_id) { + + addData(_def, _len); + addSeparator(); + + indexTextCollection(); + + noOfLevels_ = index_.size(); + + loadPrefs(); +} + +LevelCollection::LevelCollection(const QString &_path, const QString &_name, + int _id) : + level_(0), completedLevels_(0), noOfLevels_(0), + name_(_name), path_(_path), id_(_id) { + + char buf[1024]; + int len; + + QFile file(path_); + if (file.open(IO_Raw | IO_ReadOnly)) { + while ((len = file.readBlock(buf, 1024)) > 0) { + addData((const char *) buf, len); + } + file.close(); + addSeparator(); + } + + indexTextCollection(); + + noOfLevels_ = index_.size(); + + loadPrefs(); + +} + +LevelCollection::~LevelCollection() { + if (id_ >= 0) { + KConfig *cfg=(KApplication::kApplication())->config(); + cfg->setGroup ("settings"); + + QString key; + key.sprintf("level%d", id_); + cfg->writeEntry(key, level_, true, false, false); + } +} + + +void +LevelCollection::levelCompleted() { + if (completedLevels_ < (level_+1)) completedLevels_ = level_+1; + + if (id_ >= 0) { + unsigned long x=(((unsigned long) getuid()) & 0xfffful); + x |= ((unsigned long) id_)<<26; + x |= ((unsigned long) completedLevels_)<<16; + + x = forward(x, 0x608aef3ful, 0x0dul, 0xfb00ef3bul); + x = forward(x, 0x8bb02c07ul, 0x12ul, 0x2a37dd29ul); + x = forward(x, 0x709e62a3ul, 0x17ul, 0x23607603ul); + x = forward(x, 0x207d488bul, 0x0bul, 0xc31fd579ul); + x = forward(x, 0x61ebc208ul, 0x16ul, 0xbcffadadul); + x = forward(x, 0xf6c75e2cul, 0x02ul, 0xa2baa00ful); + x = forward(x, 0xd4d657b4ul, 0x1ful, 0x7e129575ul); + x = forward(x, 0x80ff0b94ul, 0x0eul, 0x92fc153dul); + + QString key; + key.sprintf("status%d", id_); + + KConfig *cfg=(KApplication::kApplication())->config(); + cfg->setGroup("settings"); + cfg->writeEntry(key, x, true, false, false); + cfg->sync(); + } +} + + +void +LevelCollection::level(int _level) { + assert(_level >= 0 && _level < noOfLevels_); + + level_ = _level; + if (level_ > completedLevels_) level_ = completedLevels_; + if (level_ >= noOfLevels_) level_ = noOfLevels_ - 1; + if (level_ < 0) level_ = 0; +} + +static int +minX(const char *def) { + int min_x = 10000; + + int x=0; + for (int pos=0; def[pos]; pos++) { + switch(def[pos]) { + case '\n': + x = 0; + break; + + case ' ': + x++; + break; + + case '\t': + x = (x+8) & ~7; + break; + + case '\r': + break; + + default: + if (x < min_x) min_x = x; + break; + } + } + + return min_x == 10000 ? -1 : min_x; +} + + +bool +LevelCollection::loadLevel(Map *_map) { + _map->clearMap(); + + const char *def = index_[level_]; + bool goodMap = true; + int x=0, y=0, goalsLeft=0; + + int min_x = minX(def); + if (min_x < 0) { + min_x = 0; + goodMap = false; + } + + + _map->xpos_ = -1; + _map->ypos_ = -1; + + for (int pos=0; def[pos]; pos++) { + switch(def[pos]) { + case '\n': + y++; + x = 0; + break; + + case ' ': + x++; + break; + + case '\t': + x = (x+8) & ~7; + break; + + case '@': + if (x-min_x > MAX_X || y > MAX_Y) goodMap = false; + else { + _map->xpos_ = x-min_x; + _map->ypos_ = y; + } + x++; + break; + + case '$': + if (x-min_x > MAX_X || y > MAX_Y) goodMap = false; + else _map->map(x-min_x, y, OBJECT); + x++; + break; + + case '.': + if (x-min_x > MAX_X || y > MAX_Y) goodMap = false; + else { + _map->map(x-min_x, y, GOAL); + goalsLeft++; + } + x++; + break; + + case '#': + if (x-min_x > MAX_X || y > MAX_Y) goodMap = false; + else _map->map(x-min_x, y, WALL); + x++; + break; + + case '+': + if (x-min_x > MAX_X || y > MAX_Y) goodMap = false; + else { + _map->xpos_ = x-min_x; + _map->ypos_ = y; + _map->map(x-min_x, y, GOAL); + goalsLeft++; + } + x++; + break; + + case '*': + if (x-min_x > MAX_X || y > MAX_Y) goodMap = false; + else _map->map(x-min_x, y, OBJECT|GOAL); + x++; + break; + + case '\r': + break; + + default: + goodMap = false; + break; + } + } + + if (_map->objectsLeft() != goalsLeft) goodMap = false; + if (_map->completed()) goodMap = false; + + if (_map->badCoords(_map->xpos_, _map->ypos_)) goodMap = false; + else { + if (!_map->empty(_map->xpos_, _map->ypos_)) goodMap = false; + else if (!_map->fillFloor(_map->xpos_, _map->ypos_)) goodMap = false; + } + + return goodMap; +} + diff --git a/ksokoban/LevelCollection.h b/ksokoban/LevelCollection.h new file mode 100644 index 00000000..f01d1316 --- /dev/null +++ b/ksokoban/LevelCollection.h @@ -0,0 +1,66 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998,1999 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LEVELCOLLECTION_H +#define LEVELCOLLECTION_H + +#include +#include +#include + +class Map; + +class LevelCollection { +public: + LevelCollection(const char *_def, int _len, const QString &_name, int _id=-1); + LevelCollection(const QString &_path, const QString &_name, int _id=-1); + ~LevelCollection(); + + const QString &name() const { return name_; } + int id() const { return id_; } + int level() const { return level_; } + void level(int _level); + void levelCompleted(); + int completedLevels() const { return completedLevels_; } + int noOfLevels() const { return noOfLevels_; } + bool loadLevel(Map *_map); + +protected: + void indexTextCollection(); + void loadPrefs(); + + +private: + void addLevel(const char* _level); + void addData(const char* _data, unsigned _len); + void addSeparator(); + + QPtrVector index_; + QByteArray data_; + //int dataLen_; + + int level_; + int completedLevels_; + int noOfLevels_; + QString name_; + QString path_; + int id_; +}; + +#endif /* LEVELCOLLECTION_H */ diff --git a/ksokoban/LevelMap.cpp b/ksokoban/LevelMap.cpp new file mode 100644 index 00000000..954dc588 --- /dev/null +++ b/ksokoban/LevelMap.cpp @@ -0,0 +1,205 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef USE_LIBZ +#include +#endif + +#include "LevelMap.h" +#include "LevelCollection.h" + +#define BUFSIZE (128*1024) + +const QString & +LevelMap::collectionName() { + return collection_->name(); +} + +LevelMap::LevelMap () : collection_(0), totalMoves_(0), totalPushes_(0), + goodLevel_(false) { +} + +LevelMap::~LevelMap () { +} + +void +LevelMap::changeCollection (LevelCollection *_collection) +{ + collection_ = _collection; + goodLevel_ = collection_->loadLevel(this); + totalMoves_ = totalPushes_ = 0; +} + +int +LevelMap::level () const { + if (collection_ == 0) return 0; + return collection_->level(); +} + +void +LevelMap::level (int _level) { + assert(collection_ != 0); + + collection_->level(_level); + goodLevel_ = collection_->loadLevel(this); + + totalMoves_ = totalPushes_ = 0; +} + +int +LevelMap::noOfLevels () const { + assert(collection_ != 0); + return collection_->noOfLevels(); +} + +int +LevelMap::completedLevels () const{ + assert(collection_ != 0); + return collection_->completedLevels(); +} + +int +LevelMap::distance (int x1, int y1, int x2, int y2) { + int d; + + if (x2 > x1) d = x2-x1; + else d = x1-x2; + + if (y2 > y1) d += y2-y1; + else d += y1-y2; + + return d; +} + +bool +LevelMap::step (int _x, int _y) { + int oldX=xpos_, oldY=ypos_; + + bool success = Map::step (_x, _y); + + totalMoves_ += distance (oldX, oldY, xpos_, ypos_); + + return success; +} + +bool +LevelMap::push (int _x, int _y) { + int oldX=xpos_, oldY=ypos_; + + bool success = Map::push (_x, _y); + + int d = distance (oldX, oldY, xpos_, ypos_); + totalMoves_ += d; + totalPushes_ += d; + + if (completed ()) collection_->levelCompleted(); + + return success; +} + +bool +LevelMap::unstep (int _x, int _y) { + int oldX=xpos_, oldY=ypos_; + + bool success = Map::unstep (_x, _y); + + totalMoves_ -= distance (oldX, oldY, xpos_, ypos_); + + return success; +} + +bool +LevelMap::unpush (int _x, int _y) { + int oldX=xpos_, oldY=ypos_; + + bool success = Map::unpush (_x, _y); + + int d = distance (oldX, oldY, xpos_, ypos_); + totalMoves_ -= d; + totalPushes_ -= d; + + return success; +} + +#if 0 +void +LevelMap::random (void) { + printf ("start!\n"); + + minX_ = 0; + minY_ = 0; + maxX_ = MAX_X; + maxY_ = MAX_Y; + totalMoves_ = totalPushes_ = 0; + clearMap (); + + xpos_ = 13; + ypos_ = 9; + + KRandomSequence random(0); + + for (int i=0; i<200; i++) { + map (xpos_, ypos_, FLOOR); + + switch (random.getLong(4)) { + case 0: + if (ypos_ > 1) ypos_--; else i--; + break; + + case 1: + if (ypos_ < MAX_Y-1) ypos_++; else i--; + break; + + case 2: + if (xpos_ > 1) xpos_--; else i--; + break; + + case 3: + if (xpos_ < MAX_X-1) xpos_++; else i--; + break; + } + } + + for (int y=1; y + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LEVELMAP_H +#define LEVELMAP_H + +#include +#include + +#include "Map.h" +class LevelCollection; + +//#define EXTERNAL_LEVEL 100 + +class LevelMap : public Map { +public: + LevelMap(); + ~LevelMap(); + + LevelCollection *collection() const { return collection_; } + const QString &collectionName(); + void changeCollection(LevelCollection *_collection); + int totalMoves() const { return totalMoves_; } + int totalPushes() const { return totalPushes_; } + void level(int _level); + int level() const; + int noOfLevels() const; + int completedLevels() const; + bool goodLevel() const { return goodLevel_; } + + bool step(int _x, int _y); + bool push(int _x, int _y); + bool unstep(int _x, int _y); + bool unpush(int _x, int _y); + + //void random(); + +protected: + LevelCollection *collection_; + + +private: + int totalMoves_; + int totalPushes_; + bool goodLevel_; + + static int distance(int x1, int y1, int x2, int y2); +}; + +#endif /* LEVELMAP_H */ diff --git a/ksokoban/MainWindow.cpp b/ksokoban/MainWindow.cpp new file mode 100644 index 00000000..63654f1c --- /dev/null +++ b/ksokoban/MainWindow.cpp @@ -0,0 +1,364 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MainWindow.h" +#include "PlayField.h" +#include "LevelCollection.h" + +#include "MainWindow.moc" + +void +MainWindow::createCollectionMenu() { + collection_ = new QPopupMenu(0,"collection menu"); + collection_->setCheckable(true); + //connect(collection_, SIGNAL(activated(int)), playField_, SLOT(changeCollection(int))); + connect(collection_, SIGNAL(activated(int)), this, SLOT(changeCollection(int))); + + for (int i=0; iinsertItem(internalCollections_[i]->name(), i); + } + checkedCollection_ = 0; + + KConfig *cfg=(KApplication::kApplication())->config(); + cfg->setGroup("settings"); + int id = cfg->readNumEntry("collection", 10); + + currentCollection_ = 0; + for (int i=0; iid() == id) currentCollection_ = i; + } + + changeCollection(currentCollection_); +} + + +MainWindow::MainWindow() : KMainWindow(0), externalCollection_(0) { + int i; + QPixmap pixmap; + + setEraseColor(QColor(0,0,0)); + + KConfig *cfg=(KApplication::kApplication())->config(); + cfg->setGroup("Geometry"); + int width = cfg->readNumEntry("width", 750); + int height = cfg->readNumEntry("height", 562); + resize(width, height); + + playField_ = new PlayField(this, "playfield"); + setCentralWidget(playField_); + playField_->show(); + + menu_ = new KMenuBar(this, "menubar" ); + + game_ = new QPopupMenu(0,"game menu"); + pixmap = SmallIcon("fileopen"); + game_->insertItem(QIconSet(pixmap), i18n("&Load Levels..."), this, SLOT(loadLevels())); + pixmap = SmallIcon("forward"); + game_->insertItem(QIconSet(pixmap), i18n("&Next Level"), playField_, SLOT(nextLevel()), Key_N); + pixmap = SmallIcon("back"); + game_->insertItem(QIconSet(pixmap), i18n("&Previous Level"), playField_, SLOT(previousLevel()), Key_P); + pixmap = SmallIcon("reload"); + game_->insertItem(QIconSet(pixmap), i18n("Re&start Level"), playField_, SLOT(restartLevel()), Key_Escape); + + createCollectionMenu(); + game_->insertItem(i18n("&Level Collection"), collection_); + + pixmap = SmallIcon("undo"); + game_->insertItem(QIconSet(pixmap), i18n("&Undo"), playField_, SLOT(undo()),QKeySequence( (KStdAccel::undo()).toString())); + pixmap = SmallIcon("redo"); + game_->insertItem(QIconSet(pixmap), i18n("&Redo"), playField_, SLOT(redo()), QKeySequence( (KStdAccel::redo()).toString())); + game_->insertSeparator(); + pixmap = SmallIcon("exit"); + game_->insertItem(QIconSet(pixmap), i18n("&Quit"), KApplication::kApplication(), SLOT(closeAllWindows()), QKeySequence( (KStdAccel::quit()).toString())); + menu_->insertItem(i18n("&Game"), game_); + + animation_ = new QPopupMenu(0,"animation menu"); + animation_->setCheckable(true); + connect(animation_, SIGNAL(activated(int)), this, SLOT(updateAnimMenu(int))); + connect(animation_, SIGNAL(activated(int)), playField_, SLOT(changeAnim(int))); + animation_->insertItem(i18n("&Slow"), 3); + animation_->insertItem(i18n("&Medium"), 2); + animation_->insertItem(i18n("&Fast"), 1); + animation_->insertItem(i18n("&Off"), 0); + checkedAnim_ = playField_->animDelay(); + updateAnimMenu(checkedAnim_); + menu_->insertItem(i18n("&Animation"), animation_); + + pixmap = SmallIcon("bookmark_add"); + bookmarkMenu_ = new QPopupMenu(0,"bookmarks menu"); + setBM_ = new QPopupMenu(0, "set bookmark menu"); + setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 1); + setBM_->setAccel(CTRL+Key_1, 1); + setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 2); + setBM_->setAccel(CTRL+Key_2, 2); + setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 3); + setBM_->setAccel(CTRL+Key_3, 3); + setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 4); + setBM_->setAccel(CTRL+Key_4, 4); + setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 5); + setBM_->setAccel(CTRL+Key_5, 5); + setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 6); + setBM_->setAccel(CTRL+Key_6, 6); + setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 7); + setBM_->setAccel(CTRL+Key_7, 7); + setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 8); + setBM_->setAccel(CTRL+Key_8, 8); + setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 9); + setBM_->setAccel(CTRL+Key_9, 9); + setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 10); + setBM_->setAccel(CTRL+Key_0, 10); + connect(setBM_, SIGNAL(activated(int)), this, SLOT(setBookmark(int))); + bookmarkMenu_->insertItem(i18n("&Set Bookmark"), setBM_); + + pixmap = SmallIcon("bookmark"); + goToBM_ = new QPopupMenu(0, "go to bookmark menu"); + goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 1); + goToBM_->setAccel(Key_1, 1); + goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 2); + goToBM_->setAccel(Key_2, 2); + goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 3); + goToBM_->setAccel(Key_3, 3); + goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 4); + goToBM_->setAccel(Key_4, 4); + goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 5); + goToBM_->setAccel(Key_5, 5); + goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 6); + goToBM_->setAccel(Key_6, 6); + goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 7); + goToBM_->setAccel(Key_7, 7); + goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 8); + goToBM_->setAccel(Key_8, 8); + goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 9); + goToBM_->setAccel(Key_9, 9); + goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 10); + goToBM_->setAccel(Key_0, 10); + connect(goToBM_, SIGNAL(activated(int)), this, SLOT(goToBookmark(int))); + bookmarkMenu_->insertItem(i18n("&Go to Bookmark"), goToBM_); + menu_->insertItem(i18n("&Bookmarks"), bookmarkMenu_); + + for (i=1; i<=10; i++) { + bookmarks_[i-1] = new Bookmark(i); + updateBookmark(i); + } + + help_ = helpMenu(QString::null, false); + menu_->insertSeparator(); + menu_->insertItem(i18n("&Help"), help_); + + menu_->show(); + + setAcceptDrops(true); +} + +MainWindow::~MainWindow() +{ + KConfig *cfg=(KApplication::kApplication())->config(); + + cfg->setGroup("Geometry"); + cfg->writeEntry("width", width()); + cfg->writeEntry("height", height()); + + cfg->setGroup("settings"); + cfg->writeEntry("collection", internalCollections_[checkedCollection_]->id()); + + for (int i=1; i<=10; i++) { + delete bookmarks_[i-1]; + } + + + delete externalCollection_; + + // The following line segfaults when linked against qt 1.44 + //delete help_; + delete goToBM_; + delete setBM_; + delete bookmarkMenu_; + delete animation_; + delete collection_; + delete game_; + //delete menu_; + + //delete playField_; +} + + + +void +MainWindow::focusInEvent(QFocusEvent *) { + playField_->setFocus(); +} + +void +MainWindow::updateAnimMenu(int id) { + animation_->setItemChecked(checkedAnim_, false); + checkedAnim_ = id; + animation_->setItemChecked(checkedAnim_, true); +} + +void +MainWindow::updateBookmark(int num) { + int col = internalCollections_.toInternalId(bookmarks_[num-1]->collection()); + int lev = bookmarks_[num-1]->level(); + int mov = bookmarks_[num-1]->moves(); + + if (col < 0 || lev < 0) return; + + QString name; + if (col >= 0 && col < internalCollections_.collections()) + name = internalCollections_[col]->name(); + else + name = i18n("(invalid)"); + QString l; + l.setNum(lev+1); + name += " #" + l; + l.setNum(mov); + name += " (" + l + ")"; + + setBM_->changeItem(name, num); + goToBM_->changeItem(name, num); +} + +void +MainWindow::setBookmark(int id) { + assert(id >= 1 && id <= 10); + playField_->setBookmark(bookmarks_[id-1]); + updateBookmark(id); +} + +void +MainWindow::goToBookmark(int id) { + assert(id >= 1 && id <= 10); + + Bookmark *bm = bookmarks_[id-1]; + int collection = internalCollections_.toInternalId(bm->collection()); + int level = bm->level(); + + if (collection < 0 || collection >= internalCollections_.collections()) return; + LevelCollection* colPtr = internalCollections_[collection]; + if (colPtr == 0) return; + if (level < 0 || level >= colPtr->noOfLevels()) return; + if (level > colPtr->completedLevels()) return; + + playField_->setUpdatesEnabled(false); + changeCollection(collection); + playField_->setUpdatesEnabled(true); + playField_->goToBookmark(bookmarks_[id-1]); +} + +void +MainWindow::changeCollection(int id) +{ + collection_->setItemChecked(checkedCollection_, false); + checkedCollection_ = id; + collection_->setItemChecked(checkedCollection_, true); + + delete externalCollection_; + externalCollection_ = 0; + playField_->changeCollection(internalCollections_[id]); +} + +void +MainWindow::loadLevels() { + KConfig *cfg=(KApplication::kApplication())->config(); + cfg->setGroup("settings"); + QString lastFile = cfg->readPathEntry("lastLevelFile"); + + KURL result = KFileDialog::getOpenURL(lastFile, "*", this, i18n("Load Levels From File")); + if (result.isEmpty()) return; + + openURL(result); +} + +void +MainWindow::openURL(KURL _url) { + KConfig *cfg=(KApplication::kApplication())->config(); + +// int namepos = _url.path().findRev('/') + 1; // NOTE: findRev can return -1 +// QString levelName = _url.path().mid(namepos); + QString levelName = _url.fileName(); + + QString levelFile; + if (_url.isLocalFile()) { + levelFile = _url.path(); + } else { +// levelFile = locateLocal("appdata", "levels/" + levelName); + if(!KIO::NetAccess::download( _url, levelFile ) ) + return; + } + + LevelCollection *tmpCollection = new LevelCollection(levelFile, levelName); + KIO::NetAccess::removeTempFile(levelFile ); + + if (tmpCollection->noOfLevels() < 1) { + KMessageBox::sorry(this, i18n("No levels found in file")); + delete tmpCollection; + return; + } + if (_url.isLocalFile()) { + cfg->setGroup("settings"); + cfg->writePathEntry("lastLevelFile", _url.path()); + } + + delete externalCollection_; + externalCollection_ = tmpCollection; + + collection_->setItemChecked(checkedCollection_, false); + playField_->changeCollection(externalCollection_); + + +} + +void +MainWindow::dragEnterEvent(QDragEnterEvent* event) { + event->accept(KURLDrag::canDecode(event)); +} + +void +MainWindow::dropEvent(QDropEvent* event) { + KURL::List urls; + if (KURLDrag::decode(event, urls)) { +// kdDebug() << "MainWindow:Handling QUriDrag..." << endl; + if (urls.count() > 0) { + const KURL &url = urls.first(); + openURL(url); + } + } +} diff --git a/ksokoban/MainWindow.h b/ksokoban/MainWindow.h new file mode 100644 index 00000000..202afd13 --- /dev/null +++ b/ksokoban/MainWindow.h @@ -0,0 +1,80 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include "Bookmark.h" +#include "InternalCollections.h" + +class KMenuBar; +class PlayField; +class QPopupMenu; +class QFocusEvent; +class QDragEnterEvent; +class QDropEvent; +class LevelCollection; + +class MainWindow : public KMainWindow { + Q_OBJECT +public: + MainWindow(); + ~MainWindow(); + + void openURL(KURL _url); + +public slots: + void changeCollection(int id); + void updateAnimMenu(int id); + void setBookmark(int id); + void goToBookmark(int id); + + void loadLevels(); + +protected: + void focusInEvent(QFocusEvent*); + void createCollectionMenu(); + virtual void dragEnterEvent(QDragEnterEvent*); + virtual void dropEvent(QDropEvent*); + +private: + InternalCollections internalCollections_; + LevelCollection *externalCollection_; + KMenuBar *menu_; + PlayField *playField_; + Bookmark *bookmarks_[10]; + int currentCollection_; + + QPopupMenu *game_; + QPopupMenu *collection_; + QPopupMenu *animation_; + QPopupMenu *bookmarkMenu_; + QPopupMenu *setBM_; + QPopupMenu *goToBM_; + QPopupMenu *help_; + int checkedCollection_; + int checkedAnim_; + + void updateBookmark(int num); + +}; + +#endif /* MAINWINDOW_H */ diff --git a/ksokoban/Makefile.am b/ksokoban/Makefile.am new file mode 100644 index 00000000..300d997e --- /dev/null +++ b/ksokoban/Makefile.am @@ -0,0 +1,23 @@ +APPSDIR = $(kde_appsdir)/Games/TacticStrategy + +SUBDIRS=levels data images + +bin_PROGRAMS = ksokoban +ksokoban_SOURCES = Bookmark.cpp History.cpp HtmlPrinter.cpp ImageData.cpp InternalCollections.cpp LevelCollection.cpp LevelMap.cpp MainWindow.cpp Map.cpp MapDelta.cpp ModalLabel.cpp Move.cpp MoveSequence.cpp PathFinder.cpp PlayField.cpp StaticImage.cpp main.cpp +ksokoban_LDFLAGS = $(all_libraries) $(KDE_RPATH) +ksokoban_LDADD = $(LIB_KIO) + +noinst_HEADERS = Bookmark.h History.h HtmlPrinter.h ImageData.h InternalCollections.h LevelCollection.h LevelMap.h MainWindow.h Map.h MapDelta.h ModalLabel.h Move.h MoveSequence.h PathFinder.h PlayField.h Queue.h StaticImage.h + +METASOURCES= MainWindow.moc ModalLabel.moc PlayField.moc + +INCLUDES = $(all_includes) + +EXTRA_DIST=AUTHORS NEWS README TODO + +# we need theese deps for the automatic generation of other deps +StaticImage.o: images/data.c +InternalCollections.o: levels/data.c + +messages: + $(XGETTEXT) *.cpp -o $(podir)/ksokoban.pot diff --git a/ksokoban/Map.cpp b/ksokoban/Map.cpp new file mode 100644 index 00000000..7dbb1cfa --- /dev/null +++ b/ksokoban/Map.cpp @@ -0,0 +1,204 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include + +#include "Map.h" + +Map::Map() : xpos_(-1), ypos_(-1), width_(0), height_(0), objectsLeft_(-1) { +} + + +void +Map::map (int x, int y, int val) { + assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y); + if ((map (x, y) & (OBJECT | GOAL)) == OBJECT) objectsLeft_--; + if ((val & (OBJECT | GOAL)) == OBJECT) objectsLeft_++; + currentMap_[y+1][x+1] = val; + + if (val != 0) { + if (width_ <= x) width_ = x+1; + if (height_ <= y) height_ = y+1; + } +} + +void +Map::setMap (int x, int y, int bits) { + assert ((map (x, y) & bits) == 0); + if (goal (x, y) && ((bits & OBJECT) == OBJECT)) objectsLeft_--; + assert (objectsLeft_ >= 0); + currentMap_[y+1][x+1] |= bits; + + if (bits != 0) { + if (width_ <= x) width_ = x+1; + if (height_ <= y) height_ = y+1; + } +} + +void +Map::clearMap (int x, int y, int bits) { + assert ((map (x, y) & bits) == bits); + if (goal (x, y) && ((bits & OBJECT) == OBJECT)) objectsLeft_++; + currentMap_[y+1][x+1] &= ~bits; +} + +void +Map::clearMap () { + memset (currentMap_, 0, (MAX_Y+3)*(MAX_X+3)*sizeof (char)); + objectsLeft_ = 0; + width_ = height_ = 0; +} + +bool +Map::fillFloor (int x, int y) { + if (badCoords (x, y)) return false; + if ((currentMap_[y+1][x+1] & (WALL|FLOOR)) != 0) return true; + + currentMap_[y+1][x+1] |= FLOOR; + bool a = fillFloor (x, y-1); + bool b = fillFloor (x, y+1); + bool c = fillFloor (x-1, y); + bool d = fillFloor (x+1, y); + + return a && b && c && d; +} + +bool +Map::step (int _x, int _y) { + assert (!badCoords (xpos_, ypos_)); + assert (empty (xpos_, ypos_)); + + int xd=0, yd=0; + if (_x < xpos_) xd = -1; + if (_x > xpos_) xd = 1; + if (_y < ypos_) yd = -1; + if (_y > ypos_) yd = 1; + if (badDelta (xd, yd) || badCoords (_x, _y)) return false; + + int x=xpos_, y=ypos_; + do { + x += xd; + y += yd; + if (!empty (x, y)) return false; + } while (!(x==_x && y==_y)); + + xpos_ = _x; + ypos_ = _y; + + return true; +} + +bool +Map::push (int _x, int _y) { + assert (!badCoords (xpos_, ypos_)); + assert (empty (xpos_, ypos_)); + + int xd=0, yd=0; + if (_x < xpos_) xd = -1; + if (_x > xpos_) xd = 1; + if (_y < ypos_) yd = -1; + if (_y > ypos_) yd = 1; + if (badDelta (xd, yd) || badCoords (_x+xd, _y+yd)) return false; + + int x=xpos_+xd, y=ypos_+yd; + if (!object (x, y)) return false; + if (!empty (_x+xd, _y+yd)) return false; + + while (!(x==_x && y==_y)) { + x += xd; + y += yd; + if (!empty (x, y)) return false; + } + + clearMap (xpos_+xd, ypos_+yd, OBJECT); + setMap (_x+xd, _y+yd, OBJECT); + + xpos_ = _x; + ypos_ = _y; + + return true; +} + +bool +Map::unstep (int _x, int _y) { + return Map::step (_x, _y); +} + +bool +Map::unpush (int _x, int _y) { + assert (!badCoords (xpos_, ypos_)); + assert (empty (xpos_, ypos_)); + + int xd=0, yd=0; + if (_x < xpos_) xd = -1; + if (_x > xpos_) xd = 1; + if (_y < ypos_) yd = -1; + if (_y > ypos_) yd = 1; + if (badDelta (xd, yd) || badCoords (_x+xd, _y+yd)) return false; + + int x=xpos_, y=ypos_; + if (!object (x-xd, y-yd)) return false; + + do { + x += xd; + y += yd; + if (!empty (x, y)) return false; + } while (!(x==_x && y==_y)); + + clearMap (xpos_-xd, ypos_-yd, OBJECT); + setMap (_x-xd, _y-yd, OBJECT); + + xpos_ = _x; + ypos_ = _y; + + return true; +} + +void +Map::printMap(void) { + for (int y=0; y", map(x,y)&FLOOR); + break; + } + } + printf ("\n"); + } +} + diff --git a/ksokoban/Map.h b/ksokoban/Map.h new file mode 100644 index 00000000..32cd9ddc --- /dev/null +++ b/ksokoban/Map.h @@ -0,0 +1,122 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MAP_H +#define MAP_H + +#include + +#define MAX_X 49 +#define MAX_Y 49 + +#define WALL 1 +#define GOAL 2 +#define OBJECT 4 +#define FLOOR 8 + +class Map { + friend class MapDelta; + friend class LevelCollection; +public: + Map(); + + bool completed () const { return objectsLeft_ <= 0; } + + bool step (int _x, int _y); + bool push (int _x, int _y); + bool unstep (int _x, int _y); + bool unpush (int _x, int _y); + + + static bool badCoords (int _x, int _y) { + return _x<0 || _y<0 || _x>MAX_X || _y>MAX_Y; + } + + static bool badDelta (int _xd, int _yd) { + return (_xd!=0 && _yd!=0) || (_xd==0 && _yd==0); + } + + int xpos () const { return xpos_; } + int ypos () const { return ypos_; } + + bool empty (int x, int y) { + assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y); + return (currentMap_[y+1][x+1] & (WALL|OBJECT)) == 0; + } + bool wall (int x, int y) { + assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y); + return (currentMap_[y+1][x+1] & WALL) != 0; + } + bool goal (int x, int y) { + assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y); + return (currentMap_[y+1][x+1] & GOAL) != 0; + } + bool object (int x, int y) { + assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y); + return (currentMap_[y+1][x+1] & OBJECT) != 0; + } + bool floor (int x, int y) { + assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y); + return (currentMap_[y+1][x+1] & FLOOR) != 0; + } + + bool wallUp (int x, int y) { + assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y); + return (currentMap_[y ][x+1] & WALL) != 0; + } + bool wallDown (int x, int y) { + assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y); + return (currentMap_[y+2][x+1] & WALL) != 0; + } + bool wallLeft (int x, int y) { + assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y); + return (currentMap_[y+1][x ] & WALL) != 0; + } + bool wallRight (int x, int y) { + assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y); + return (currentMap_[y+1][x+2] & WALL) != 0; + } + + void printMap (); + + int width() const { return width_; } + int height() const { return height_; } + +protected: + int map (int x, int y) { + assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y); + return currentMap_[y+1][x+1]; + } + void map (int x, int y, int val); + + void setMap (int x, int y, int bits); + void clearMap (int x, int y, int bits); + void clearMap (); + bool fillFloor (int x, int y); + int objectsLeft () const { return objectsLeft_; } + + int xpos_, ypos_; + +private: + char currentMap_[MAX_Y+3][MAX_X+3]; + int width_, height_; + int objectsLeft_; +}; + +#endif /* MAP_H */ diff --git a/ksokoban/MapDelta.cpp b/ksokoban/MapDelta.cpp new file mode 100644 index 00000000..bbf16ff4 --- /dev/null +++ b/ksokoban/MapDelta.cpp @@ -0,0 +1,63 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "MapDelta.h" + + +MapDelta::MapDelta (Map *m) { + source_ = m; + ended_ = true; + start (); +} + +void +MapDelta::start () { + assert (ended_); + ((Map &) *this) = *source_; + +#if 0 + memcpy (map_, source_->currentMap_, (MAX_Y+3)*(MAX_X+3)*sizeof (int)); + for (int y=1; ycurrentMap_[y][x]; + } + } + xpos_ = source_->xpos_; + ypos_ = source_->ypos_; +#endif + + ended_ = false; +} + +void +MapDelta::end () { + assert (!ended_); + for (int y=0; y<=MAX_Y; y++) { + for (int x=0; x<=MAX_X; x++) { + map (x, y, map (x, y) != source_->map (x, y)); + } + } + if (xpos_ != source_->xpos_ || ypos_ != source_->ypos_) { + map (xpos_, ypos_, 1); + map (source_->xpos_, source_->ypos_, 1); + } + ended_ = true; +} diff --git a/ksokoban/MapDelta.h b/ksokoban/MapDelta.h new file mode 100644 index 00000000..c4e6763d --- /dev/null +++ b/ksokoban/MapDelta.h @@ -0,0 +1,44 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MAPDELTA_H +#define MAPDELTA_H + +#include + +#include "Map.h" + +class MapDelta : private Map { +public: + MapDelta (Map *m); + + void start (); + void end (); + + bool hasChanged (int x, int y) { + assert (ended_); + return map (x, y) == 1; + } + +private: + Map *source_; + bool ended_; +}; + +#endif /* MAPDELTA_H */ diff --git a/ksokoban/ModalLabel.cpp b/ksokoban/ModalLabel.cpp new file mode 100644 index 00000000..1ed55dcf --- /dev/null +++ b/ksokoban/ModalLabel.cpp @@ -0,0 +1,115 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "ModalLabel.h" + +#include +#include +#include +#include +#include +#include + +#include "ModalLabel.moc" + +ModalLabel::ModalLabel(const QString &text, QWidget *parent, + const char *name, WFlags f) + : QLabel(text, parent, name, f) { + QFont font(KGlobalSettings::generalFont().family(), 24, QFont::Bold); + QFontMetrics fontMet(font); + + QString currentLine; + QRect bounds; + int lineLen, width=0, height=0; + + for (int linePos=0; linePos < (int) text.length(); linePos += lineLen+1) { + + lineLen = text.find('\n', linePos); + if (lineLen < 0) lineLen = text.length() - linePos; + else lineLen -= linePos; + + currentLine = text.mid(linePos, lineLen); + bounds = fontMet.boundingRect(currentLine); + + if (bounds.width() > width) width = bounds.width(); + height += bounds.height(); + } + + width += 32; + height += 32; + + if (width < 300) width = 300; + if (height < 75) height = 75; + + setAlignment (AlignCenter); + setFrameStyle (QFrame::Panel | QFrame::Raised); + setLineWidth (4); + setFont (font); + move (parent->width ()/2 - width/2, parent->height ()/2 - height/2); + resize (width, height); + show (); + + QWidgetList *list = QApplication::allWidgets(); + QWidgetListIt it( *list ); + while (it.current()) { + it.current()->installEventFilter (this); + ++it; + } + delete list; + + completed_ = false; + startTimer (1000); +} + +void +ModalLabel::timerEvent (QTimerEvent *) { + completed_ = true; +} + +bool +ModalLabel::eventFilter (QObject *, QEvent *e) { + switch (e->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + case QEvent::KeyPress: + case QEvent::KeyRelease: + case QEvent::Accel: + //case QEvent::DragEnter: + case QEvent::DragMove: + case QEvent::DragLeave: + case QEvent::Drop: + //case QEvent::DragResponse: + + //kdDebug << "Ate event" << endl; + return true; + break; + default: + return false; + } +} + +void +ModalLabel::message (const QString &text, QWidget *parent) { + KApplication *app = KApplication::kApplication (); + ModalLabel cl (text, parent); + + while (!cl.completed_) app->processOneEvent (); +} diff --git a/ksokoban/ModalLabel.h b/ksokoban/ModalLabel.h new file mode 100644 index 00000000..4b35a82a --- /dev/null +++ b/ksokoban/ModalLabel.h @@ -0,0 +1,39 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MODALLABEL_H +#define MODALLABEL_H + +#include + +class ModalLabel : public QLabel { + Q_OBJECT +public: + static void message (const QString &text, QWidget *parent); + + void timerEvent (QTimerEvent *); + bool eventFilter (QObject *, QEvent *); + bool completed_; + +protected: + ModalLabel (const QString &text, QWidget *parent, const char *name=0, WFlags f=0); + +}; + +#endif /* MODALLABEL_H */ diff --git a/ksokoban/Move.cpp b/ksokoban/Move.cpp new file mode 100644 index 00000000..22773774 --- /dev/null +++ b/ksokoban/Move.cpp @@ -0,0 +1,213 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include + +#include "Move.h" +#include "LevelMap.h" + +Move::Move (int _startX, int _startY) { + assert (_startX>=0 && _startX<=MAX_X && _startY>=0 && _startY<=MAX_Y); + + moves_ = new unsigned short[400]; + moves_[0] = _startX | (_startY<<8); + moveIndex_ = 1; + finished_ = false; + +#ifndef NDEBUG + lastX_ = _startX; + lastY_ = _startY; +#endif +} + +Move::~Move () { + delete [] moves_; +} + +void +Move::finish () { + assert (!finished_); + assert (moveIndex_ > 1); + + unsigned short *newMoves = new unsigned short[moveIndex_]; + memcpy (newMoves, moves_, moveIndex_*sizeof (unsigned short)); + delete [] moves_; + moves_ = newMoves; + + finished_ = true; +} + +void +Move::save (QString &s) { + static const char move1[] = "lrud"; + static const char push1[] = "LRUD"; + static const char move2[] = "wens"; + static const char push2[] = "WENS"; + + assert (finished_); + int x=startX (); + int y=startY (); + int pos=1; + + int x2, y2, dist=0; + int dir=-1; + bool push=false; + while (pos= 0) s += push ? push1[dir] : move1[dir]; + + x2 = moves_[pos]&0x7f; + y2 = (moves_[pos]>>8)&0x7f; + push = (moves_[pos++]&0x80)==0x80; + + if (x2x) { + dir = 1; + dist = x2-x; + } else if (y2y) { + dir = 3; + dist = y2-y; + } else { + assert (0); + } + assert (dist > 0); + + if (dist > 1) { + if (dist>=10) { + s += '0' + (dist/10); + dist %= 10; + } + s += '0' + dist; + } + + x = x2; + y = y2; + } + + if (dir >= 0) s += push ? push2[dir] : move2[dir]; +} + +const char * +Move::load (const char *s) { + assert (!finished_); + int x=finalX (); + int y=finalY (); + + int dist; + bool last=false; + char c; + while ((c = *s++) != '\0') { + dist = 1; + if (c >= '0' && c <= '9') { + dist = c - '0'; + c = *s++; + if (c >= '0' && c <= '9') { + dist = 10*dist + c - '0'; + c = *s++; + } + } + + switch (tolower (c)) { + case 'w': + last = true; + case 'l': + x -= dist; + break; + case 'e': + last = true; + case 'r': + x += dist; + break; + case 'n': + last = true; + case 'u': + y -= dist; + break; + case 's': + last = true; + case 'd': + y += dist; + break; + + default: + //printf ("2><>%s\n", s); + //abort (); + return 0; + } + + if (x<=0 || x>=MAX_X || y<=0 || y>=MAX_Y) { + //printf ("x: %d, y:%d ><>%s\n", x, y, s); + //abort (); + + return 0; + } + + if (isupper (c)) push (x, y); + else step (x, y); + + if (last) break; + } + finish (); + + return s; +} + +bool +Move::redo (LevelMap *map) { + assert (finished_); + + for (int pos=1; pos>8)&0x7f; + bool push = (moves_[pos]&0x80)==0x80; + bool ret; + + if (push) ret = map->push (x, y); + else ret = map->step (x, y); + + if (!ret) return false; + } + + return true; +} + +bool +Move::undo (LevelMap *map) { + assert (finished_); + + for (int pos=moveIndex_-2; pos>=0; --pos) { + int x = moves_[pos]&0x7f; + int y = (moves_[pos]>>8)&0x7f; + bool push = (moves_[pos+1]&0x80)==0x80; + bool ret; + + if (push) ret = map->unpush (x, y); + else ret = map->unstep (x, y); + + if (!ret) return false; + } + + return true; +} diff --git a/ksokoban/Move.h b/ksokoban/Move.h new file mode 100644 index 00000000..ad6b3eed --- /dev/null +++ b/ksokoban/Move.h @@ -0,0 +1,115 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MOVE_H +#define MOVE_H + +#include +#include + +#include "Map.h" +class LevelMap; + +/** + * Holds information about a move + * + * The move can consist of several atomic steps and pushes. An atomic + * step/push is a step/push along a straight line. The reason why these are + * grouped together in a Move object is that they belong to the same logical + * move in the player's point of view. An undo/redo will undo/redo all the + * atomic moves in one step. + * + * @short Maintains game movement move + * @author Anders Widell + * @version 0.1 + * @see History + */ + +class Move { + friend class MoveSequence; +private: + unsigned short *moves_; + int moveIndex_; + bool finished_; + +#ifndef NDEBUG + int lastX_, lastY_; +#endif + + +public: + Move (int _startX, int _startY); + ~Move (); + + /** + * Add an atomic move. + * NOTE: either (x != (previous x)) or (y != (previous y)) + * must be true (but not both). + * + * @see LevelMap#move + * + * @param x x position of destination + * @param y y position of destination + */ + void step (int _x, int _y) { +#ifndef NDEBUG + assert (!finished_); + assert (_x>=0 && _x<=MAX_X && _y>=0 && _y<=MAX_Y); + assert (moveIndex_ < 400); + assert ((_x!=lastX_ && _y==lastY_) || (_x==lastX_ && _y!=lastY_)); + lastX_ = _x; + lastY_ = _y; +#endif + + moves_[moveIndex_++] = _x | (_y<<8); + } + + /** + * Same as move above, but used when an object is pushed. + * + * @see LevelMap#push + */ + void push (int _x, int _y) { +#ifndef NDEBUG + assert (!finished_); + assert (_x>=0 && _x<=MAX_X && _y>=0 && _y<=MAX_Y); + assert (moveIndex_ < 400); + assert ((_x!=lastX_ && _y==lastY_) || (_x==lastX_ && _y!=lastY_)); + lastX_ = _x; + lastY_ = _y; +#endif + + moves_[moveIndex_++] = _x | (_y<<8) | 0x80; + } + + void finish (); + + int startX () const { return moves_[0]&0x7f; } + int startY () const { return (moves_[0]>>8)&0x7f; } + int finalX () const { return moves_[moveIndex_-1]&0x7f; } + int finalY () const { return (moves_[moveIndex_-1]>>8)&0x7f; } + + + void save (QString &_str); + const char *load (const char *_str); + bool redo (LevelMap *map); + bool undo (LevelMap *map); +}; + +#endif /* MOVE_H */ diff --git a/ksokoban/MoveSequence.cpp b/ksokoban/MoveSequence.cpp new file mode 100644 index 00000000..b5a5c824 --- /dev/null +++ b/ksokoban/MoveSequence.cpp @@ -0,0 +1,81 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "MoveSequence.h" +#include "LevelMap.h" +#include "Move.h" + +MoveSequence::MoveSequence (Move *_move, LevelMap *_map, bool _undo) { + assert (_move->finished_); + + move_ = _move; + map_ = _map; + undo_ = _undo; + + if (undo_) { + pos_ = move_->moveIndex_-2; + + xDest_ = x_ = move_->moves_[move_->moveIndex_-1]&0x7f; + yDest_ = y_ = (move_->moves_[move_->moveIndex_-1]>>8)&0x7f; + } else { + pos_ = 1; + + xDest_ = x_ = move_->moves_[0]&0x7f; + yDest_ = y_ = (move_->moves_[0]>>8)&0x7f; + } + + newStep (); +} + +bool +MoveSequence::newStep () { + if (pos_>=move_->moveIndex_ || pos_<0) return false; + + xDest_ = move_->moves_[pos_]&0x7f; + yDest_ = (move_->moves_[pos_]>>8)&0x7f; + if (undo_) push_ = (move_->moves_[pos_+1]&0x80)==0x80; + else push_ = (move_->moves_[pos_]&0x80)==0x80; + + xd_ = yd_ = 0; + if (xDest_ < x_) xd_ = -1; + if (xDest_ > x_) xd_ = 1; + if (yDest_ < y_) yd_ = -1; + if (yDest_ > y_) yd_ = 1; + + if (undo_) pos_--; + else pos_++; + + return true; +} + +bool +MoveSequence::next () { + if (x_ == xDest_ && y_ == yDest_ && !newStep ()) return false; + + x_ += xd_; + y_ += yd_; + + if (undo_) { + if (push_) return map_->unpush (x_, y_); + else return map_->unstep (x_, y_); + } else { + if (push_) return map_->push (x_, y_); + else return map_->step (x_, y_); + } +} diff --git a/ksokoban/MoveSequence.h b/ksokoban/MoveSequence.h new file mode 100644 index 00000000..14cf5ac7 --- /dev/null +++ b/ksokoban/MoveSequence.h @@ -0,0 +1,45 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MOVESEQUENCE_H +#define MOVESEQUENCE_H + +#include + +class Move; +class LevelMap; + +class MoveSequence { +public: + MoveSequence (Move *_move, LevelMap *_map, bool _undo=false); + + bool newStep (); + bool next (); + +private: + LevelMap *map_; + Move *move_; + int pos_; + int x_, xDest_, y_, yDest_, xd_, yd_; + bool push_; + bool undo_; + +}; + +#endif /* MOVESEQUENCE_H */ diff --git a/ksokoban/NEWS b/ksokoban/NEWS new file mode 100644 index 00000000..bf717f78 --- /dev/null +++ b/ksokoban/NEWS @@ -0,0 +1,73 @@ +------------------------------------------------------------------------------- +version 0.3.0: Date 1999-07-13 + +* Graphics: + - Switched to one single image resolution + - Converted images to PNG and JPEG formats + - The window can now be resized freely - graphics is scaled to fit window + - Graphics now looks MUCH better on 256 colour displays + - Improved wall graphics (several stones with different textures) + - Removed status bar - now draws info directly in widget (not perfect yet) + +* New experimental feature: + - It is now possible to load external level collections from text files + +* Some changes related to KDE 2.0 / Qt 2.0 + - replaced many char * with QString + - uses KStandardDirs instead of localkdedir + +* Some minor bugfixes/cleanups + +------------------------------------------------------------------------------- +version 0.2.2: Date 1998-11-06 + +* Bugfix: "Animation off" now works again + +* Possibly fixed bug: Settings should now always be saved properly + I haven't been able to reproduce this bug, but it has been reported + that the settings would sometimes not be saved to the right section + in the config file. Thanks to Tore Skaug for + reporting this. + +* Now uses the zlib check in acinclude + +------------------------------------------------------------------------------- +version 0.2.1: Date 1998-10-25 + +* Bugfix: Fixed failed assertion that showed up if you changed to small + graphics and restarted the game. + Thanks to Bernd Weber for reporting + this. + +* No longer requires zlib. It is still used if found, though. + +------------------------------------------------------------------------------- +version 0.2.0: Date 1998-10-15 + +New features: + +* Bookmarks +* Animation speed menu +* Status bar +* Internationalisation + +------------------------------------------------------------------------------- +version 0.1.2: ksokoban imported to CVS. Date 1998-08-30 + +------------------------------------------------------------------------------- +version 0.1.1: bugfix release. Date 1998-08-25 + +* BUGFIX: ksokoban now ignores mouseclicks while a move is in progress. + Previously such a click would cause a memory leak and a corrupted + undo/redo history (or a failed assertion if debugging was turned on). + Thanks to Natali Giuliano for reporting this. + +* ksokoban should now work with older zlib & Qt libraries. + I have tested it with zlib 1.0.3 and Qt 1.33 + +* Detects the old gcc 2.7 and turns off optimisations if it is found. + It might work with the gcc 2.7 now. No promises, though. + I can only test it with gcc 2.8 and egcs/pgcc. + +------------------------------------------------------------------------------- +version 0.1.0: initial release. Date 1998-08-22 diff --git a/ksokoban/PathFinder.cpp b/ksokoban/PathFinder.cpp new file mode 100644 index 00000000..a7d8bd4d --- /dev/null +++ b/ksokoban/PathFinder.cpp @@ -0,0 +1,176 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +//#include +#include "PathFinder.h" +#include "LevelMap.h" +#include "Queue.h" +#include "Move.h" + +void +PathFinder::BFS (int _x, int _y) { + Queue xq; + Queue yq; + Queue dq; + int x, y, d; + + xq.enqueue (_x); + yq.enqueue (_y); + dq.enqueue (1); + + while (!xq.empty ()) { + x = xq.dequeue (); + y = yq.dequeue (); + d = dq.dequeue (); + + if (x<0 || x>MAX_X || y<0 || y>MAX_Y || dist[y][x]) continue; + dist[y][x] = d; + + xq.enqueue (x); + xq.enqueue (x); + xq.enqueue (x-1); + xq.enqueue (x+1); + + yq.enqueue (y-1); + yq.enqueue (y+1); + yq.enqueue (y); + yq.enqueue (y); + + dq.enqueue (d+1); + dq.enqueue (d+1); + dq.enqueue (d+1); + dq.enqueue (d+1); + } +} + +Move * +PathFinder::search (Map *_map, int _x, int _y) { + int xpos=_map->xpos (); + int ypos=_map->ypos (); + if (xpos == _x && ypos == _y) return 0; + + for (int y=0; y<=MAX_Y; y++) { + for (int x=0; x<=MAX_X; x++) { + if (_map->empty (x, y)) dist[y][x] = 0; + else dist[y][x] = PATH_WALL; + } + } + + BFS (_x, _y); + +#if 0 + for (int y=0; y<=MAX_Y; y++) { + for (int x=0; x<=MAX_X; x++) { + //if (x==_x && y==_y) {printf ("++ "); continue;} + //if (x==xpos && y==ypos) {printf ("@@ "); continue;} + if (dist[y][x] == PATH_WALL) {printf ("## "); continue;} + printf ("%02d ", dist[y][x]); + } + printf ("\n"); + } +#endif + + int d; + Move *move=new Move (xpos, ypos); + int oldX, oldY; + for (;;) { + oldX = xpos; + oldY = ypos; + + if (xpos == _x && ypos == _y) { + move->finish (); + //printf ("move->finish ()\n"); + return move; + } + + d = dist[ypos][xpos]; + + while (ypos-1 >= 0 && dist[ypos-1][xpos] < d) { + ypos--; + d = dist[ypos][xpos]; + } + if (oldY != ypos) { + move->step (xpos, ypos); + //printf ("step (%d, %d)\n", xpos, ypos); + continue; + } + + while (ypos+1 <= MAX_Y && dist[ypos+1][xpos] < d) { + ypos++; + d = dist[ypos][xpos]; + } + if (oldY != ypos) { + move->step (xpos, ypos); + //printf ("step (%d, %d)\n", xpos, ypos); + continue; + } + + while (xpos-1 >= 0 && dist[ypos][xpos-1] < d) { + xpos--; + d = dist[ypos][xpos]; + } + if (oldX != xpos) { + move->step (xpos, ypos); + //printf ("step (%d, %d)\n", xpos, ypos); + continue; + } + + while (xpos+1 <= MAX_X && dist[ypos][xpos+1] < d) { + xpos++; + d = dist[ypos][xpos]; + } + if (oldX != xpos) { + move->step (xpos, ypos); + //printf ("step (%d, %d)\n", xpos, ypos); + continue; + } + + delete move; + return 0; + } +} + +Move* +PathFinder::drag(int /* x1 */, int /* y1 */, int /* x2 */, int /* y2 */) { + return 0; +} + +bool +PathFinder::canDrag(int /* x */, int /* y */) const { + return false; +} + +bool +PathFinder::canWalkTo(int /* x */, int /* y */) const { + return false; +} + +bool +PathFinder::canDragTo(int /* x */, int /* y */) const { + return false; +} + +void +PathFinder::updatePossibleMoves() { +} + +void +PathFinder::updatePossibleDestinations(int /* x */, int /* y */) { +} + diff --git a/ksokoban/PathFinder.h b/ksokoban/PathFinder.h new file mode 100644 index 00000000..63187810 --- /dev/null +++ b/ksokoban/PathFinder.h @@ -0,0 +1,48 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PATHFINDER_H +#define PATHFINDER_H + +#include "Map.h" +class Move; + +#define PATH_WALL 32767 + +class PathFinder { +public: + Move *search (Map *_map, int _x, int _y); + Move* drag(int x1, int y1, int x2, int y2); + bool canDrag(int x, int y) const; + bool canWalkTo(int x, int y) const; + bool canDragTo(int x, int y) const; + void updatePossibleMoves(); + void updatePossibleDestinations(int x, int y); + +protected: + //static const int PATH_WALL=32767; + + int dist[MAX_Y+1][MAX_X+1]; + + void BFS (int _x, int _y); + + +}; + +#endif /* PATHFINDER_H */ diff --git a/ksokoban/PlayField.cpp b/ksokoban/PlayField.cpp new file mode 100644 index 00000000..4d98d309 --- /dev/null +++ b/ksokoban/PlayField.cpp @@ -0,0 +1,1044 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "PlayField.h" +#include "ModalLabel.h" +#include "LevelMap.h" +#include "Move.h" +#include "History.h" +#include "PathFinder.h" +#include "MapDelta.h" +#include "MoveSequence.h" +#include "StaticImage.h" +#include "HtmlPrinter.h" +#include "Bookmark.h" +#include "LevelCollection.h" + +#include "PlayField.moc" + +PlayField::PlayField(QWidget *parent, const char *name, WFlags f) + : QWidget(parent, name, f|WResizeNoErase), imageData_(0), lastLevel_(-1), + moveSequence_(0), moveInProgress_(false), dragInProgress_(false), + xOffs_(0), yOffs_(0), + wheelDelta_(0), + levelText_(i18n("Level:")), stepsText_(i18n("Steps:")), + pushesText_(i18n("Pushes:")), + statusFont_(KGlobalSettings::generalFont().family(), 18, QFont::Bold), statusMetrics_(statusFont_) { + + setFocusPolicy(QWidget::StrongFocus); + setFocus(); + setBackgroundMode(Qt::NoBackground); + setMouseTracking(true); + + highlightX_ = highlightY_ = 0; + + KConfig *cfg = (KApplication::kApplication())->config(); + cfg->setGroup("settings"); + + imageData_ = new StaticImage; + + animDelay_ = cfg->readNumEntry("animDelay", 2); + if (animDelay_ < 0 || animDelay_ > 3) animDelay_ = 2; + + history_ = new History; + + background_.setPixmap(imageData_->background()); + floor_ = QColor(0x66,0x66,0x66); + + levelMap_ = new LevelMap; + mapDelta_ = new MapDelta(levelMap_); + mapDelta_->end(); + + levelChange(); +} + +PlayField::~PlayField() { + KConfig *cfg = (KApplication::kApplication())->config(); + cfg->setGroup("settings"); + cfg->writeEntry("animDelay", animDelay_, true, false, false); + + delete mapDelta_; + delete history_; + delete levelMap_; + delete imageData_; +} + +void +PlayField::changeCursor(const QCursor* c) { + if (cursor_ == c) return; + + cursor_ = c; + if (c == 0) unsetCursor(); + else setCursor(*c); +} + +int +PlayField::level() const { + if (levelMap_ == 0) return 0; + return levelMap_->level(); +} + +const QString & +PlayField::collectionName() { + static QString error = "????"; + if (levelMap_ == 0) return error; + return levelMap_->collectionName(); +} + +int +PlayField::totalMoves() const { + if (levelMap_ == 0) return 0; + return levelMap_->totalMoves(); +} + +int +PlayField::totalPushes() const{ + if (levelMap_ == 0) return 0; + return levelMap_->totalPushes(); +} + +void +PlayField::levelChange() { + stopMoving(); + stopDrag(); + history_->clear(); + setSize(width(), height()); + + updateLevelXpm(); + updateStepsXpm(); + updatePushesXpm(); + highlight(); +} + +void +PlayField::paintSquare(int x, int y, QPainter &paint) { + if (levelMap_->xpos() == x && levelMap_->ypos() == y) { + if (levelMap_->goal(x, y)) + imageData_->saveman(paint, x2pixel(x), y2pixel(y)); + else + imageData_->man(paint, x2pixel(x), y2pixel(y)); + return; + } + if (levelMap_->empty(x, y)) { + if (levelMap_->floor(x, y)) { + if (levelMap_->goal(x, y)) + imageData_->goal(paint, x2pixel(x), y2pixel(y)); + else + paint.fillRect(x2pixel(x), y2pixel(y), size_, size_, floor_); + } else { + paint.fillRect(x2pixel(x), y2pixel(y), size_, size_, background_); + } + return; + } + if (levelMap_->wall(x, y)) { + imageData_->wall(paint, x2pixel(x), y2pixel(y), x+y*(MAX_X+1), + levelMap_->wallLeft(x, y), + levelMap_->wallRight(x, y)); + return; + } + + + if (levelMap_->object(x, y)) { + if (highlightX_ == x && highlightY_ == y) { + if (levelMap_->goal(x, y)) + imageData_->brightTreasure(paint, x2pixel(x), y2pixel(y)); + else + imageData_->brightObject(paint, x2pixel(x), y2pixel(y)); + } else { + if (levelMap_->goal(x, y)) + imageData_->treasure(paint, x2pixel(x), y2pixel(y)); + else + imageData_->object(paint, x2pixel(x), y2pixel(y)); + } + return; + } +} + +void +PlayField::paintDelta() { + QPainter paint(this); + + // the following line is a workaround for a bug in Qt 2.0.1 + // (and possibly earlier versions) + paint.setBrushOrigin(0, 0); + + for (int y=0; yheight(); y++) { + for (int x=0; xwidth(); x++) { + if (mapDelta_->hasChanged(x, y)) paintSquare(x, y, paint); + } + } +} + + + +void +PlayField::paintEvent(QPaintEvent *e) { + QPainter paint(this); + + // the following line is a workaround for a bug in Qt 2.0.1 + // (and possibly earlier versions) + paint.setBrushOrigin(0, 0); + + paint.setClipRegion(e->region()); + paint.setClipping(true); + + paintPainter(paint, e->rect()); +} + +void +PlayField::paintPainterClip(QPainter &paint, int x, int y, int w, int h) { + QRect rect(x, y, w, h); + + paint.setClipRect(rect); + paint.setClipping(true); + paintPainter(paint, rect); +} + +void +PlayField::paintPainter(QPainter &paint, const QRect &rect) { + if (size_ <= 0) return; + int minx = pixel2x(rect.x()); + int miny = pixel2y(rect.y()); + int maxx = pixel2x(rect.x()+rect.width()-1); + int maxy = pixel2y(rect.y()+rect.height()-1); + + if (minx < 0) minx = 0; + if (miny < 0) miny = 0; + if (maxx >= levelMap_->width()) maxx = levelMap_->width()-1; + if (maxy >= levelMap_->height()) maxy = levelMap_->height()-1; + + { + int x1, x2, y1, y2; + y1 = y2pixel(miny); + if (y1 > rect.y()) paint.fillRect(rect.x(), rect.y(), rect.width(), y1-rect.y(), background_); + + int bot=rect.y()+rect.height(); + if (bot > height()-collRect_.height()) bot = height()-collRect_.height(); + + y2 = y2pixel(maxy+1); + if (y2 < bot) paint.fillRect(rect.x(), y2, rect.width(), bot-y2, background_); + + x1 = x2pixel(minx); + if (x1 > rect.x()) paint.fillRect(rect.x(), y1, x1-rect.x(), y2-y1, background_); + + x2 = x2pixel(maxx+1); + if (x2 < rect.x()+rect.width()) paint.fillRect(x2, y1, rect.x()+rect.width()-x2, y2-y1, background_); + + // paint.eraseRect + } + + for (int y=miny; y<=maxy; y++) { + for (int x=minx; x<=maxx; x++) { + paintSquare(x, y, paint); + } + } + + if (collRect_.intersects(rect)) paint.drawPixmap(collRect_.x(), collRect_.y(), collXpm_); + if (ltxtRect_.intersects(rect)) paint.drawPixmap(ltxtRect_.x(), ltxtRect_.y(), ltxtXpm_); + if (lnumRect_.intersects(rect)) paint.drawPixmap(lnumRect_.x(), lnumRect_.y(), lnumXpm_); + if (stxtRect_.intersects(rect)) paint.drawPixmap(stxtRect_.x(), stxtRect_.y(), stxtXpm_); + if (snumRect_.intersects(rect)) paint.drawPixmap(snumRect_.x(), snumRect_.y(), snumXpm_); + if (ptxtRect_.intersects(rect)) paint.drawPixmap(ptxtRect_.x(), ptxtRect_.y(), ptxtXpm_); + if (pnumRect_.intersects(rect)) paint.drawPixmap(pnumRect_.x(), pnumRect_.y(), pnumXpm_); +} + +void +PlayField::resizeEvent(QResizeEvent *e) { + setSize(e->size().width(), e->size().height()); +} + +void +PlayField::mouseMoveEvent(QMouseEvent *e) { + lastMouseXPos_ = e->x(); + lastMouseYPos_ = e->y(); + + if (!dragInProgress_) return highlight(); + + int old_x = dragX_, old_y = dragY_; + + dragX_ = lastMouseXPos_ - mousePosX_; + dragY_ = lastMouseYPos_ - mousePosY_; + + { + int x = pixel2x(dragX_ + size_/2); + int y = pixel2y(dragY_ + size_/2); + if (x >= 0 && x < levelMap_->width() && + y >= 0 && y < levelMap_->height() && + pathFinder_.canDragTo(x, y)) { + x = x2pixel(x); + y = y2pixel(y); + + if (dragX_ >= x - size_/4 && + dragX_ < x + size_/4 && + dragY_ >= y - size_/4 && + dragY_ < y + size_/4) { + dragX_ = x; + dragY_ = y; + } + } + } + + if (dragX_ == old_x && dragY_ == old_y) return; + + QRect rect(dragX_, dragY_, size_, size_); + + dragXpm_.resize(size_, size_); + + QPainter paint; + paint.begin(&dragXpm_); + paint.setBackgroundColor(backgroundColor()); + paint.setBrushOrigin(- dragX_, - dragY_); + paint.translate((double) (- dragX_), (double) (- dragY_)); + paintPainter(paint, rect); + paint.end(); + + dragImage_ = dragXpm_; + for (int yy=0; yyobjectImg().pixel(xx, yy); + int r1 = qRed(rgb1); + int g1 = qGreen(rgb1); + int b1 = qBlue(rgb1); + if (r1 != g1 || r1 != b1 || r1 == 255) { + QRgb rgb2 = dragImage_.pixel(xx, yy); + int r2 = qRed(rgb2); + int g2 = qGreen(rgb2); + int b2 = qBlue(rgb2); + r2 = (int) (0.75 * r1 + 0.25 * r2 + 0.5); + g2 = (int) (0.75 * g1 + 0.25 * g2 + 0.5); + b2 = (int) (0.75 * b1 + 0.25 * b2 + 0.5); + dragImage_.setPixel(xx, yy, qRgb(r2, g2, b2)); + } + } + } + + paint.begin(this); + + // the following line is a workaround for a bug in Qt 2.0.1 + // (and possibly earlier versions) + paint.setBrushOrigin(0, 0); + + dragXpm_.convertFromImage(dragImage_, + OrderedDither|OrderedAlphaDither| + ColorOnly|AvoidDither); + paint.drawPixmap(dragX_, dragY_, dragXpm_); + + { + int dx = dragX_ - old_x; + int dy = dragY_ - old_y; + int y2 = old_y; + if (dy > 0) { + paintPainterClip(paint, old_x, old_y, size_, dy); + // NOTE: clipping is now activated in the QPainter paint + y2 += dy; + } else if (dy < 0) { + paintPainterClip(paint, old_x, old_y+size_+dy, size_, -dy); + // NOTE: clipping is now activated in the QPainter paint + dy = -dy; + } + if (dx > 0) { + paintPainterClip(paint, old_x, y2, dx, size_-dy); + // NOTE: clipping is now activated in the QPainter paint + } else if (dx < 0) { + paintPainterClip(paint, old_x+size_+dx, y2, -dx, size_-dy); + // NOTE: clipping is now activated in the QPainter paint + } + } + paint.end(); +} + +void +PlayField::highlight() { + // FIXME: the line below should not be needed + if (size_ == 0) return; + + int x=pixel2x(lastMouseXPos_); + int y=pixel2y(lastMouseYPos_); + + if (x < 0 || y < 0 || x >= levelMap_->width() || y >= levelMap_->height()) + return; + + if (x == highlightX_ && y == highlightY_) return; + + if (pathFinder_.canDrag(x, y)) { + QPainter paint(this); + + if (highlightX_ >= 0) { + int x = highlightX_, y = highlightY_; + highlightX_ = -1; + paintSquare(x, y, paint); + } else + changeCursor(&sizeAllCursor); + + if (levelMap_->goal(x, y)) + imageData_->brightTreasure(paint, x2pixel(x), y2pixel(y)); + else + imageData_->brightObject(paint, x2pixel(x), y2pixel(y)); + highlightX_ = x; + highlightY_ = y; + } else { + if (pathFinder_.canWalkTo(x, y)) changeCursor(&crossCursor); + else changeCursor(0); + if (highlightX_ >= 0) { + QPainter paint(this); + + int x = highlightX_, y = highlightY_; + highlightX_ = -1; + + paintSquare(x, y, paint); + } + } +} + +void +PlayField::stopMoving() { + killTimers(); + delete moveSequence_; + moveSequence_ = 0; + moveInProgress_ = false; + updateStepsXpm(); + updatePushesXpm(); + + QPainter paint(this); + paint.drawPixmap(snumRect_.x(), snumRect_.y(), snumXpm_); + paint.drawPixmap(pnumRect_.x(), pnumRect_.y(), pnumXpm_); + + pathFinder_.updatePossibleMoves(); +} + + +void +PlayField::startMoving(Move *m) { + startMoving(new MoveSequence(m, levelMap_)); +} + +void +PlayField::startMoving(MoveSequence *ms) { + static const int delay[4] = {0, 15, 35, 60}; + + assert(moveSequence_ == 0 && !moveInProgress_); + moveSequence_ = ms; + moveInProgress_ = true; + if (animDelay_) startTimer(delay[animDelay_]); + timerEvent(0); +} + +void +PlayField::timerEvent(QTimerEvent *) { + assert(moveInProgress_); + if (moveSequence_ == 0) { + killTimers(); + moveInProgress_ = false; + return; + } + + bool more=false; + + mapDelta_->start(); + if (animDelay_) more = moveSequence_->next(); + else { + while (moveSequence_->next()) if (levelMap_->completed()) break; + more = true; // FIXME: clean this up + stopMoving(); + } + mapDelta_->end(); + + if (more) { + paintDelta(); + if (levelMap_->completed()) { + stopMoving(); + ModalLabel::message(i18n("Level completed"), this); + nextLevel(); + return; + } + } else stopMoving(); +} + +void +PlayField::step(int _x, int _y) { + if (!canMoveNow()) return; + + int oldX=levelMap_->xpos(); + int oldY=levelMap_->ypos(); + int x=oldX, y=oldY; + + int dx=0, dy=0; + if (_x>oldX) dx=1; + if (_xoldY) dy=1; + if (_ystep(x+dx, y+dy)) { + x += dx; + y += dy; + } + + if (x!=oldX || y!=oldY) { + Move *m = new Move(oldX, oldY); + m->step(x, y); + m->finish(); + history_->add(m); + m->undo(levelMap_); + + startMoving(m); + + } +} + +void +PlayField::push(int _x, int _y) { + if (!canMoveNow()) return; + + int oldX=levelMap_->xpos(); + int oldY=levelMap_->ypos(); + int x=oldX, y=oldY; + + int dx=0, dy=0; + if (_x>oldX) dx=1; + if (_xoldY) dy=1; + if (_ystep(x+dx, y+dy)) { + x += dx; + y += dy; + } + int objX=x, objY=y; + while (!(x==_x && y==_y) && levelMap_->push(x+dx, y+dy)) { + x += dx; + y += dy; + } + + if (x!=oldX || y!=oldY) { + Move *m = new Move(oldX, oldY); + + if (objX!=oldX || objY!=oldY) m->step(objX, objY); + + if (objX!=x || objY!=y) { + m->push(x, y); + + objX += dx; + objY += dy; + } + m->finish(); + history_->add(m); + + m->undo(levelMap_); + + startMoving(m); + } +} + +void +PlayField::keyPressEvent(QKeyEvent * e) { + int x=levelMap_->xpos(); + int y=levelMap_->ypos(); + + switch (e->key()) { + case Key_Up: + if (e->state() & ControlButton) step(x, 0); + else if (e->state() & ShiftButton) push(x, 0); + else push(x, y-1); + break; + case Key_Down: + if (e->state() & ControlButton) step(x, MAX_Y); + else if (e->state() & ShiftButton) push(x, MAX_Y); + else push(x, y+1); + break; + case Key_Left: + if (e->state() & ControlButton) step(0, y); + else if (e->state() & ShiftButton) push(0, y); + else push(x-1, y); + break; + case Key_Right: + if (e->state() & ControlButton) step(MAX_X, y); + else if (e->state() & ShiftButton) push(MAX_X, y); + else push(x+1, y); + break; + + case Key_Q: + KApplication::kApplication()->closeAllWindows(); + break; + + case Key_Backspace: + case Key_Delete: + if (e->state() & ControlButton) redo(); + else undo(); + break; + +#if 0 + case Key_X: + levelMap_->random(); + levelChange(); + repaint(false); + break; + + case Key_R: + level(levelMap_->level()); + return; + break; + case Key_N: + nextLevel(); + return; + break; + case Key_P: + previousLevel(); + return; + break; + case Key_U: + undo(); + return; + break; + case Key_I: + history_->redo(levelMap_); + repaint(false); + return; + break; + + case Key_S: + { + QString buf; + history_->save(buf); + printf("%s\n", (char *) buf); + } + return; + break; + + case Key_L: + stopMoving(); + history_->clear(); + level(levelMap_->level()); + { + char buf[4096]="r1*D1*D1*r1*@r1*D1*"; + //scanf("%s", buf); + history_->load(levelMap_, buf); + } + updateStepsXpm(); + updatePushesXpm(); + repaint(false); + return; + break; +#endif + + + case Key_Print: + HtmlPrinter::printHtml(levelMap_); + break; + + default: + e->ignore(); + return; + break; + } +} + +void +PlayField::stopDrag() { + if (!dragInProgress_) return; + + changeCursor(0); + + QPainter paint(this); + + // the following line is a workaround for a bug in Qt 2.0.1 + // (and possibly earlier versions) + paint.setBrushOrigin(0, 0); + + int x = highlightX_, y = highlightY_; + paintSquare(x, y, paint); + + paintPainterClip(paint, dragX_, dragY_, size_, size_); + // NOTE: clipping is now activated in the QPainter paint + dragInProgress_ = false; + +} + +void +PlayField::dragObject(int xpixel, int ypixel) { + int x=pixel2x(xpixel - mousePosX_ + size_/2); + int y=pixel2y(ypixel - mousePosY_ + size_/2); + + if (x == highlightX_ && y == highlightY_) return; + + printf("drag %d,%d to %d,%d\n", highlightX_, highlightY_, x, y); + pathFinder_.drag(highlightX_, highlightY_, x, y); + stopDrag(); +} + + +void +PlayField::mousePressEvent(QMouseEvent *e) { + if (!canMoveNow()) return; + + if (dragInProgress_) { + if (e->button() == LeftButton) dragObject(e->x(), e->y()); + else stopDrag(); + return; + } + + int x=pixel2x(e->x()); + int y=pixel2y(e->y()); + + if (x < 0 || y < 0 || x >= levelMap_->width() || y >= levelMap_->height()) + return; + + if (e->button() == LeftButton && pathFinder_.canDrag(x, y)) { + QPainter paint(this); + changeCursor(&sizeAllCursor); + + if (levelMap_->goal(x, y)) + imageData_->brightTreasure(paint, x2pixel(x), y2pixel(y)); + else + imageData_->brightObject(paint, x2pixel(x), y2pixel(y)); + highlightX_ = x; + highlightY_ = y; + pathFinder_.updatePossibleDestinations(x, y); + + dragX_ = x2pixel(x); + dragY_ = y2pixel(y); + mousePosX_ = e->x() - dragX_; + mousePosY_ = e->y() - dragY_; + dragInProgress_ = true; + } + + Move *m; + switch (e->button()) { + case LeftButton: + m = pathFinder_.search(levelMap_, x, y); + if (m != 0) { + history_->add(m); + + startMoving(m); + } + break; + case MidButton: + undo(); + return; + break; + case RightButton: + push(x, y); + break; + + default: + return; + } +} + +void +PlayField::wheelEvent(QWheelEvent *e) { + wheelDelta_ += e->delta(); + + if (wheelDelta_ >= 120) { + wheelDelta_ %= 120; + redo(); + } else if (wheelDelta_ <= -120) { + wheelDelta_ = -(-wheelDelta_ % 120); + undo(); + } +} + +void +PlayField::mouseReleaseEvent(QMouseEvent *e) { + if (dragInProgress_) dragObject(e->x(), e->y()); +} + + +void +PlayField::focusInEvent(QFocusEvent *) { + //printf("PlayField::focusInEvent\n"); +} + +void +PlayField::focusOutEvent(QFocusEvent *) { + //printf("PlayField::focusOutEvent\n"); +} + +void +PlayField::leaveEvent(QEvent *) { + stopDrag(); +} + +void +PlayField::setSize(int w, int h) { + int sbarHeight = statusMetrics_.height(); + int sbarNumWidth = statusMetrics_.boundingRect("88888").width()+8; + int sbarLevelWidth = statusMetrics_.boundingRect(levelText_).width()+8; + int sbarStepsWidth = statusMetrics_.boundingRect(stepsText_).width()+8; + int sbarPushesWidth = statusMetrics_.boundingRect(pushesText_).width()+8; + + pnumRect_.setRect(w-sbarNumWidth, h-sbarHeight, sbarNumWidth, sbarHeight); + ptxtRect_.setRect(pnumRect_.x()-sbarPushesWidth, h-sbarHeight, sbarPushesWidth, sbarHeight); + snumRect_.setRect(ptxtRect_.x()-sbarNumWidth, h-sbarHeight, sbarNumWidth, sbarHeight); + stxtRect_.setRect(snumRect_.x()-sbarStepsWidth, h-sbarHeight, sbarStepsWidth, sbarHeight); + lnumRect_.setRect(stxtRect_.x()-sbarNumWidth, h-sbarHeight, sbarNumWidth, sbarHeight); + ltxtRect_.setRect(lnumRect_.x()-sbarLevelWidth, h-sbarHeight, sbarLevelWidth, sbarHeight); + collRect_.setRect(0, h-sbarHeight, ltxtRect_.x(), sbarHeight); + + collXpm_.resize(collRect_.size()); + ltxtXpm_.resize(ltxtRect_.size()); + lnumXpm_.resize(lnumRect_.size()); + stxtXpm_.resize(stxtRect_.size()); + snumXpm_.resize(snumRect_.size()); + ptxtXpm_.resize(ptxtRect_.size()); + pnumXpm_.resize(pnumRect_.size()); + + h -= sbarHeight; + + int cols = levelMap_->width(); + int rows = levelMap_->height(); + + // FIXME: the line below should not be needed + if (cols == 0 || rows == 0) return; + + int xsize = w / cols; + int ysize = h / rows; + + if (xsize < 8) xsize = 8; + if (ysize < 8) ysize = 8; + + size_ = imageData_->resize(xsize > ysize ? ysize : xsize); + + xOffs_ = (w - cols*size_) / 2; + yOffs_ = (h - rows*size_) / 2; + + + updateCollectionXpm(); + updateTextXpm(); + updateLevelXpm(); + updateStepsXpm(); + updatePushesXpm(); +} + +void +PlayField::nextLevel() { + if (levelMap_->level()+1 >= levelMap_->noOfLevels()) { + ModalLabel::message(i18n("\ +This is the last level in\n\ +the current collection."), this); + return; + } + if (levelMap_->level() >= levelMap_->completedLevels()) { + ModalLabel::message(i18n("\ +You have not completed\n\ +this level yet."), this); + return; + } + + level(levelMap_->level()+1); + levelChange(); + repaint(false); +} + +void +PlayField::previousLevel() { + if (levelMap_->level() <= 0) { + ModalLabel::message(i18n("\ +This is the first level in\n\ +the current collection."), this); + return; + } + level(levelMap_->level()-1); + levelChange(); + repaint(false); +} + +void +PlayField::undo() { + if (!canMoveNow()) return; + + startMoving(history_->deferUndo(levelMap_)); +} + +void +PlayField::redo() { + if (!canMoveNow()) return; + + startMoving(history_->deferRedo(levelMap_)); +} + +void +PlayField::restartLevel() { + stopMoving(); + history_->clear(); + level(levelMap_->level()); + updateStepsXpm(); + updatePushesXpm(); + repaint(false); +} + +void +PlayField::changeCollection(LevelCollection *collection) { + if (levelMap_->collection() == collection) return; + levelMap_->changeCollection(collection); + levelChange(); + //erase(collRect_); + repaint(false); +} + +void +PlayField::updateCollectionXpm() { + if (collXpm_.isNull()) return; + + QPainter paint(&collXpm_); + paint.setBrushOrigin(- collRect_.x(), - collRect_.y()); + paint.fillRect(0, 0, collRect_.width(), collRect_.height(), background_); + + paint.setFont(statusFont_); + paint.setPen(QColor(0,255,0)); + paint.drawText(0, 0, collRect_.width(), collRect_.height(), + AlignLeft, collectionName()); +} + +void +PlayField::updateTextXpm() { + if (ltxtXpm_.isNull()) return; + + QPainter paint; + + paint.begin(<xtXpm_); + paint.setBrushOrigin(- ltxtRect_.x(), - ltxtRect_.y()); + paint.fillRect(0, 0, ltxtRect_.width(), ltxtRect_.height(), background_); + paint.setFont(statusFont_); + paint.setPen(QColor(128,128,128)); + paint.drawText(0, 0, ltxtRect_.width(), ltxtRect_.height(), AlignLeft, levelText_); + paint.end(); + + paint.begin(&stxtXpm_); + paint.setBrushOrigin(- stxtRect_.x(), - stxtRect_.y()); + paint.fillRect(0, 0, stxtRect_.width(), stxtRect_.height(), background_); + paint.setFont(statusFont_); + paint.setPen(QColor(128,128,128)); + paint.drawText(0, 0, stxtRect_.width(), stxtRect_.height(), AlignLeft, stepsText_); + paint.end(); + + paint.begin(&ptxtXpm_); + paint.setBrushOrigin(- ptxtRect_.x(), - ptxtRect_.y()); + paint.fillRect(0, 0, ptxtRect_.width(), ptxtRect_.height(), background_); + paint.setFont(statusFont_); + paint.setPen(QColor(128,128,128)); + paint.drawText(0, 0, ptxtRect_.width(), ptxtRect_.height(), AlignLeft, pushesText_); + paint.end(); +} + +void +PlayField::updateLevelXpm() { + if (lnumXpm_.isNull()) return; + + QPainter paint(&lnumXpm_); + paint.setBrushOrigin(- lnumRect_.x(), - lnumRect_.y()); + paint.fillRect(0, 0, lnumRect_.width(), lnumRect_.height(), background_); + + QString str; + paint.setFont(statusFont_); + paint.setPen(QColor(255,0,0)); + paint.drawText(0, 0, lnumRect_.width(), lnumRect_.height(), + AlignLeft, str.sprintf("%05d", level()+1)); +} + +void +PlayField::updateStepsXpm() { + if (snumXpm_.isNull()) return; + + QPainter paint(&snumXpm_); + paint.setBrushOrigin(- snumRect_.x(), - snumRect_.y()); + paint.fillRect(0, 0, snumRect_.width(), snumRect_.height(), background_); + + QString str; + paint.setFont(statusFont_); + paint.setPen(QColor(255,0,0)); + paint.drawText(0, 0, snumRect_.width(), snumRect_.height(), + AlignLeft, str.sprintf("%05d", totalMoves())); +} + +void +PlayField::updatePushesXpm() { + if (pnumXpm_.isNull()) return; + + QPainter paint(&pnumXpm_); + paint.setBrushOrigin(- pnumRect_.x(), - pnumRect_.y()); + paint.fillRect(0, 0, pnumRect_.width(), pnumRect_.height(), background_); + + QString str; + paint.setFont(statusFont_); + paint.setPen(QColor(255,0,0)); + paint.drawText(0, 0, pnumRect_.width(), pnumRect_.height(), + AlignLeft, str.sprintf("%05d", totalPushes())); +} + + +void +PlayField::changeAnim(int num) +{ + assert(num >= 0 && num <= 3); + + animDelay_ = num; +} + +// FIXME: clean up bookmark stuff + +// static const int bookmark_id[] = { +// 0, 1, 8, 2, 9, 3, 5, 6, 7, 4 +// }; + +void +PlayField::setBookmark(Bookmark *bm) { + if (!levelMap_->goodLevel()) return; + + if (collection()->id() < 0) { + KMessageBox::sorry(this, i18n("Sorry, bookmarks for external levels\n" + "is not implemented yet.")); + return; + } + + bm->set(collection()->id(), levelMap_->level(), levelMap_->totalMoves(), history_); +} + +void +PlayField::goToBookmark(Bookmark *bm) { + level(bm->level()); + levelChange(); + if (!bm->goTo(levelMap_, history_)) fprintf(stderr, "Warning: bad bookmark\n"); + //updateLevelXpm(); + updateStepsXpm(); + updatePushesXpm(); + repaint(false); +} + +bool +PlayField::canMoveNow() { + if (moveInProgress_) return false; + if (!levelMap_->goodLevel()) { + ModalLabel::message(i18n("This level is broken"), this); + return false; + } + return true; +} diff --git a/ksokoban/PlayField.h b/ksokoban/PlayField.h new file mode 100644 index 00000000..57524ea8 --- /dev/null +++ b/ksokoban/PlayField.h @@ -0,0 +1,149 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PLAYFIELD_H +#define PLAYFIELD_H + +#include +#include +#include +#include +#include +#include +#include + +#include "ImageData.h" +#include "LevelMap.h" +class MapDelta; +class MoveSequence; +class Move; +#include "PathFinder.h" + +class History; +class Bookmark; +class LevelCollection; +class QPainter; +class QCursor; + +class PlayField : public QWidget { + Q_OBJECT +public: + PlayField(QWidget *parent, const char *name=0, WFlags f=0); + ~PlayField (); + + bool canMoveNow(); + int animDelay() { return animDelay_; } + + void setSize(int w, int h); + void level(int _l) { levelMap_->level(_l); } + LevelCollection *collection() const { return levelMap_->collection(); } + void setBookmark(Bookmark *bm); + void goToBookmark(Bookmark *bm); + + int level() const; + const QString &collectionName(); + int totalMoves() const; + int totalPushes() const; + + void updateCollectionXpm(); + void updateTextXpm(); + void updateLevelXpm(); + void updateStepsXpm(); + void updatePushesXpm(); + +public slots: + void nextLevel(); + void previousLevel(); + void undo(); + void redo(); + void restartLevel(); + void changeCollection(LevelCollection *collection); + void changeAnim(int num); + +protected: + ImageData *imageData_; + LevelMap *levelMap_; + History *history_; + int lastLevel_; + MoveSequence *moveSequence_; + MapDelta *mapDelta_; + bool moveInProgress_; + bool dragInProgress_; + PathFinder pathFinder_; + int animDelay_; + const QCursor* cursor_; + + void levelChange (); + void paintSquare (int x, int y, QPainter &paint); + void paintDelta (); + void paintEvent (QPaintEvent *e); + void paintPainterClip(QPainter& paint, int x, int y, int w, int h); + void paintPainter(QPainter& paint, const QRect& rect); + void resizeEvent (QResizeEvent *e); + void mouseMoveEvent(QMouseEvent* e); + void keyPressEvent (QKeyEvent *); + void focusInEvent (QFocusEvent *); + void focusOutEvent (QFocusEvent *); + void mousePressEvent (QMouseEvent *); + void mouseReleaseEvent(QMouseEvent*); + void leaveEvent(QEvent*); + void wheelEvent (QWheelEvent *); + void step (int _x, int _y); + void push (int _x, int _y); + virtual void timerEvent (QTimerEvent *); + void stopDrag(); + void dragObject(int xpixel, int ypixel); + void highlight(); + void changeCursor(const QCursor* c); + void eatKeyPressEvents(); + +private: + int size_, xOffs_, yOffs_; + int highlightX_, highlightY_; + int dragX_, dragY_; + int lastMouseXPos_, lastMouseYPos_; + int mousePosX_, mousePosY_; + int wheelDelta_; + + int x2pixel (int x) const { return size_*x+xOffs_; } + int y2pixel (int y) const { return size_*y+yOffs_; } + + int pixel2x (int x) const { return (x-xOffs_)/size_; } + int pixel2y (int y) const { return (y-yOffs_)/size_; } + + void startMoving (Move *m); + void startMoving (MoveSequence *ms); + void stopMoving (); + + QRect pnumRect_, ptxtRect_, snumRect_, stxtRect_, lnumRect_, ltxtRect_; + QRect collRect_; + + const QString levelText_, stepsText_, pushesText_; + QPixmap pnumXpm_, ptxtXpm_, snumXpm_, stxtXpm_, lnumXpm_, ltxtXpm_; + QPixmap collXpm_; + QPixmap dragXpm_; + QImage dragImage_; + QFont statusFont_; + QFontMetrics statusMetrics_; + QBrush background_; + QBrush floor_; + +}; + +#endif /* PLAYFIELD_H */ diff --git a/ksokoban/Queue.h b/ksokoban/Queue.h new file mode 100644 index 00000000..cab9db90 --- /dev/null +++ b/ksokoban/Queue.h @@ -0,0 +1,56 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef QUEUE_H +#define QUEUE_H + +#include + +template +class Queue { +private: + Type *queue_; + long head_, tail_; + +public: + void clear() { head_ = tail_ = 0; } + bool empty() { return head_ == tail_; } + bool full() { return ((tail_ + 1) & ((1l< + +ksokoban is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License. See the file +COPYING for details. + +See http://hem.passagen.se/awl/ksokoban/ for later versions of ksokoban. +See the file AUTHORS for details about where the levels come from. + +------------------------------------------------------------------------ + +PLAYING +======= + +The objective of the game is to push all the red gems (these should +actually have been crates, but gems looked nicer) to the goal squares, +which are marked with green glassy things on the floor. + +Use the cursor keys to move about. If you move onto a gem and there is +noting blocking it on the opposite side, then you will push the gem. + +Use the CONTROL key together with the cursor keys to move as far as +possible in a direction without pushing any gems. With the SHIFT key +you will move as far as possible in a direction, possibly pushing a +gem if it is in the way. + +Use the left mouse button to move to any place you can reach without +pushing any gems. The middle mouse moves in a straight line, possibly +pushing a gem if it is in the way. + +The U key or the right mouse button undoes the last move. diff --git a/ksokoban/StaticImage.cpp b/ksokoban/StaticImage.cpp new file mode 100644 index 00000000..49a2aafa --- /dev/null +++ b/ksokoban/StaticImage.cpp @@ -0,0 +1,86 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include +#include "StaticImage.h" + +#include "images/data.c" + +const unsigned char *const +imageData[NO_OF_IMAGES] = { + halfstone_1_data_, + halfstone_2_data_, + halfstone_3_data_, + halfstone_4_data_, + + stone_1_data_, + stone_2_data_, + stone_3_data_, + stone_4_data_, + stone_5_data_, + stone_6_data_, + + object_data_, + treasure_data_, + goal_data_, + man_data_, + saveman_data_, +}; + +const unsigned +imageSize[NO_OF_IMAGES] = { + sizeof halfstone_1_data_, + sizeof halfstone_2_data_, + sizeof halfstone_3_data_, + sizeof halfstone_4_data_, + + sizeof stone_1_data_, + sizeof stone_2_data_, + sizeof stone_3_data_, + sizeof stone_4_data_, + sizeof stone_5_data_, + sizeof stone_6_data_, + + sizeof object_data_, + sizeof treasure_data_, + sizeof goal_data_, + sizeof man_data_, + sizeof saveman_data_, +}; + +StaticImage::StaticImage () { + bool valid = background_.loadFromData((const uchar *) starfield_data_, + (uint) sizeof (starfield_data_)); + + if (!valid) { + background_.resize(128, 128); + background_.fill(Qt::black); + } + + for (int i=0; i + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef STATICIMAGE_H +#define STATICIMAGE_H + +#include "ImageData.h" + +class +StaticImage : public ImageData { +public: + StaticImage (); + virtual ~StaticImage (); + +}; + +#endif /* STATICIMAGE_H */ diff --git a/ksokoban/TODO b/ksokoban/TODO new file mode 100644 index 00000000..4eed7abd --- /dev/null +++ b/ksokoban/TODO @@ -0,0 +1,5 @@ +* Update the documentation +* Go to specific level +* Level editor +* Drag & drop movement +* Scores diff --git a/ksokoban/data/Makefile.am b/ksokoban/data/Makefile.am new file mode 100644 index 00000000..67f36885 --- /dev/null +++ b/ksokoban/data/Makefile.am @@ -0,0 +1,5 @@ + +KDE_ICON = ksokoban + +xdg_apps_DATA = ksokoban.desktop + diff --git a/ksokoban/data/hi128-app-ksokoban.png b/ksokoban/data/hi128-app-ksokoban.png new file mode 100644 index 00000000..25ff5810 Binary files /dev/null and b/ksokoban/data/hi128-app-ksokoban.png differ diff --git a/ksokoban/data/hi16-app-ksokoban.png b/ksokoban/data/hi16-app-ksokoban.png new file mode 100644 index 00000000..a6f1b41b Binary files /dev/null and b/ksokoban/data/hi16-app-ksokoban.png differ diff --git a/ksokoban/data/hi22-app-ksokoban.png b/ksokoban/data/hi22-app-ksokoban.png new file mode 100644 index 00000000..3239ddbf Binary files /dev/null and b/ksokoban/data/hi22-app-ksokoban.png differ diff --git a/ksokoban/data/hi32-app-ksokoban.png b/ksokoban/data/hi32-app-ksokoban.png new file mode 100644 index 00000000..f9050856 Binary files /dev/null and b/ksokoban/data/hi32-app-ksokoban.png differ diff --git a/ksokoban/data/hi48-app-ksokoban.png b/ksokoban/data/hi48-app-ksokoban.png new file mode 100644 index 00000000..9de2975d Binary files /dev/null and b/ksokoban/data/hi48-app-ksokoban.png differ diff --git a/ksokoban/data/hi64-app-ksokoban.png b/ksokoban/data/hi64-app-ksokoban.png new file mode 100644 index 00000000..1ddce158 Binary files /dev/null and b/ksokoban/data/hi64-app-ksokoban.png differ diff --git a/ksokoban/data/ksokoban.desktop b/ksokoban/data/ksokoban.desktop new file mode 100644 index 00000000..eed35f4c --- /dev/null +++ b/ksokoban/data/ksokoban.desktop @@ -0,0 +1,70 @@ +[Desktop Entry] +Name=KSokoban +Name[af]=Ksokoban +Name[be]=Сакабан +Name[bn]=কে-সোকোবান +Name[hi]=के-शोकोबॉन +Name[mk]=КСокобан +Name[nb]=Sokoban +Name[ne]=केडीई सोकोबान +Name[pl]=Sokoban +Name[sv]=Ksokoban +Name[ta]=Kசோகோபான௠+Name[tg]=KСокобан +Name[zh_TW]=KSokoban 倉庫番 +Name[zu]=I-KSokoban +GenericName=Sokoban Game +GenericName[be]=Ð“ÑƒÐ»ÑŒÐ½Ñ Ð¡Ð°ÐºÐ°Ð±Ð°Ð½ +GenericName[bg]=ЛогичеÑка игра +GenericName[bn]=সোকোবান খেলা +GenericName[bs]=Igra Sokoban +GenericName[ca]=Joc Sokoban +GenericName[cs]=Hra Sokoban +GenericName[cy]=Gêm Sokoban +GenericName[da]=Sokoban spil +GenericName[de]=Sokoban Spiel +GenericName[el]=Παιχνίδι Sokoban +GenericName[eo]=Sokobana ludo +GenericName[es]=Juego Sokoban +GenericName[et]=Sokoban +GenericName[eu]=Sokoban jokoa +GenericName[fa]=بازی Sokoban +GenericName[fi]=Sokoban +GenericName[fr]=Jeu Sokoban +GenericName[ga]=Cluiche Sokoban +GenericName[he]=משחק Sokoban +GenericName[hr]=Sokoban +GenericName[hu]=Sokoban +GenericName[is]=Sokoban leikur +GenericName[it]=Gioco del Sokoban +GenericName[ja]=倉庫番ゲーム +GenericName[km]=ល្បែង​សូកូបាន +GenericName[lt]=Sokoban žaidimas +GenericName[lv]=Sokoban spÄ“le +GenericName[mk]=Игра Сокобан +GenericName[nb]=Sokoban-spill +GenericName[nds]=Sokoban-Speel +GenericName[ne]=सोकोबान खेल +GenericName[nl]=Sokobanspel +GenericName[nn]=Sokoban-spel +GenericName[pl]=Sokoban +GenericName[pt]=Jogo de Sokoban +GenericName[pt_BR]=Jogo de Sokoban +GenericName[ru]=Сокобан +GenericName[se]=Sokoban-speallu +GenericName[sk]=Hra Sokoban +GenericName[sl]=Igra Sokobana +GenericName[sr]=Игра Sokoban-а +GenericName[sr@Latn]=Igra Sokoban-a +GenericName[sv]=Sokoban-spel +GenericName[ta]=சொகோபான௠விளையாடà¯à®Ÿà¯ +GenericName[uk]=Гра Sokoban +GenericName[zh_TW]=倉庫番éŠæˆ² +DocPath=ksokoban/index.html +Exec=ksokoban %i %m -caption "%c" +Icon=ksokoban +Terminal=false +Type=Application +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;StrategyGame; diff --git a/ksokoban/images/Makefile.am b/ksokoban/images/Makefile.am new file mode 100644 index 00000000..f2af8b9f --- /dev/null +++ b/ksokoban/images/Makefile.am @@ -0,0 +1,88 @@ + +noinst_DATA = data.c +bin2c_SOURCES = bin2c.c +bin2c_LDFLAGS = $(all_libraries) +bin2c_LDADD = $(LIBZ) + +noinst_PROGRAMS = bin2c + +test: + x-povray +W200 +H200 +I$@.pov +O$@.png +p +d + rm -f $@.png + +POVFILES=floor_common.inc goal.pov halfstone_1.pov halfstone_2.pov halfstone_3.pov halfstone_4.pov man.pov man_common.inc object.pov saveman.pov stone_1.pov stone_2.pov stone_3.pov stone_4.pov stone_5.pov stone_6.pov stone_common.inc treasure.pov + +IMAGES=goal.png halfstone_1.png halfstone_2.png halfstone_3.png halfstone_4.png man.png object.png saveman.png stone_1.png stone_2.png stone_3.png stone_4.png stone_5.png stone_6.png treasure.png starfield.png + +EXTRA_DIST = $(POVFILES) $(IMAGES) +CLEANFILES = data.c + +RESOLUTION=+W96 +H96 +STONE_RESOLUTION=+W96 +H48 +HALFSTONE_RESOLUTION=+W48 +H48 + +# no antialias +#ANTIALIAS= + +# normal antialias +#ANTIALIAS=+A + +# slow antialias +ANTIALIAS=+A0 +R9 + +POVRAY=povray $(ANTIALIAS) + +data.c: $(IMAGES) bin2c + list=""; for i in $(IMAGES); do list="$$list $(srcdir)/$$i"; done; \ + ./bin2c "" $$list + +############################################################################ +# Povray rules to generate images +# +#halfstone_1.png: halfstone_1.pov stone_common.inc +# $(POVRAY) $(HALFSTONE_RESOLUTION) +I$< +O$@ +# +#halfstone_2.png: halfstone_2.pov stone_common.inc +# $(POVRAY) $(HALFSTONE_RESOLUTION) +I$< +O$@ +# +#halfstone_3.png: halfstone_3.pov stone_common.inc +# $(POVRAY) $(HALFSTONE_RESOLUTION) +I$< +O$@ +# +#halfstone_4.png: halfstone_4.pov stone_common.inc +# $(POVRAY) $(HALFSTONE_RESOLUTION) +I$< +O$@ +# +# +#stone_1.png: stone_1.pov stone_common.inc +# $(POVRAY) $(STONE_RESOLUTION) +I$< +O$@ +# +#stone_2.png: stone_2.pov stone_common.inc +# $(POVRAY) $(STONE_RESOLUTION) +I$< +O$@ +# +#stone_3.png: stone_3.pov stone_common.inc +# $(POVRAY) $(STONE_RESOLUTION) +I$< +O$@ +# +#stone_4.png: stone_4.pov stone_common.inc +# $(POVRAY) $(STONE_RESOLUTION) +I$< +O$@ +# +#stone_5.png: stone_5.pov stone_common.inc +# $(POVRAY) $(STONE_RESOLUTION) +I$< +O$@ +# +#stone_6.png: stone_6.pov stone_common.inc +# $(POVRAY) $(STONE_RESOLUTION) +I$< +O$@ +# +# +#treasure.png: treasure.pov goal.pov floor_common.inc +# $(POVRAY) $(RESOLUTION) +I$< +O$@ +# +#object.png: object.pov floor_common.inc +# $(POVRAY) $(RESOLUTION) +I$< +O$@ +# +#man.png: man.pov man_common.inc floor_common.inc +# $(POVRAY) $(RESOLUTION) +I$< +O$@ +# +#saveman.png: saveman.pov man_common.inc goal.pov floor_common.inc +# $(POVRAY) $(RESOLUTION) +I$< +O$@ +# +#goal.png: goal.pov floor_common.inc +# $(POVRAY) $(RESOLUTION) +I$< +O$@ + diff --git a/ksokoban/images/bin2c.c b/ksokoban/images/bin2c.c new file mode 100644 index 00000000..0dad91d1 --- /dev/null +++ b/ksokoban/images/bin2c.c @@ -0,0 +1,256 @@ +/* + * bin2c - compresses data files & converts the result to C source code + * Copyright (C) 1998-2000 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * This command uses the zlib library to compress each file given on + * the command line, and outputs the compressed data as C source code + * to the file 'data.c' in the current directory + * + */ + +#include "../../config.h" + +#include +#include +#include + +#ifdef USE_LIBZ +#include +#else +typedef unsigned char Bytef; +typedef unsigned long uLongf; +#endif + +#define BUFSIZE 16384 /* Increase buffer size by this amount */ + +static Bytef *source=NULL; /* Buffer containing uncompressed data */ +static Bytef *dest=NULL; /* Buffer containing compressed data */ +static uLongf sourceBufSize=0; /* Buffer size */ +#ifdef USE_LIBZ +static uLongf destBufSize=0; /* Buffer size */ +#endif + +static uLongf sourceLen; /* Length of uncompressed data */ +static uLongf destLen; /* Length of compressed data */ + +static FILE *infile=NULL; /* The input file containing binary data */ +static FILE *outfile=NULL; /* The output file 'data.c' */ + +static const char *programName=""; + +/* + * Print error message and free allocated resources + * + */ + +static int +error (msg1, msg2, msg3) + char *msg1; + char *msg2; + char *msg3; +{ + fprintf (stderr, "%s: %s%s%s\n", programName, msg1, msg2, msg3); + + if (infile != NULL) fclose (infile); + if (outfile != NULL) fclose (outfile); + remove ("data.c"); + free (dest); + free (source); + + return 1; +} + +/* + * Replacement for strrchr in case it isn't present in libc + * + */ + +static char * +my_strrchr (s, c) + char *s; + int c; +{ + char *ptr = NULL; + + while (*s) { + if (*s == c) ptr = s; + s++; + } + + return ptr; +} + +#ifdef USE_LIBZ +/* + * NOTE: my_compress2 is taken directly from zlib 1.1.3 + * + * This is for compatibility with early versions of zlib that + * don't have the compress2 function. + * + */ + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int my_compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} +#endif + +int +main (argc, argv) + int argc; + char **argv; +{ + int i; + const char *suffix; +#ifdef USE_LIBZ + int result; +#endif + unsigned j; + char *ptr; + int position; + + programName = argv[0]; + + outfile = fopen ("data.c", "w"); + if (outfile == NULL) { + fprintf (stderr, "%s: can't open 'data.c' for writing\n", argv[0]); + return 1; + } + + suffix = argv[1]; + /* Process each file given on command line */ + for (i=2; i sourceBufSize) { + sourceBufSize += BUFSIZE; + source = realloc (source, sourceBufSize); + if (source == NULL) return error ("memory exhausted", "", ""); + } + sourceLen += fread (source+sourceLen, 1, BUFSIZE, infile); + if (ferror (infile)) return error ("error reading '", argv[i], "'"); + } + fclose (infile); + +#ifdef USE_LIBZ + + /* (Re)allocate dest buffer */ + destLen = sourceBufSize + (sourceBufSize+9)/10 + 12; + if (destBufSize < destLen) { + destBufSize = destLen; + dest = realloc (dest, destBufSize); + if (dest == NULL) return error ("memory exhausted", "", ""); + } + + /* Compress dest buffer */ + destLen = destBufSize; + result = my_compress2 (dest, &destLen, source, sourceLen, 9); + if (result != Z_OK) return error ("error compressing '", argv[i], "'"); + +#else + + destLen = sourceLen; + dest = source; + +#endif + + /* Output dest buffer as C source code to outfile */ + ptr = my_strrchr (argv[i], '.'); + if (ptr != NULL) *ptr = '\0'; + /* use only the file 2name and throw away the path name */ + position = strlen(argv[i]) - 1; + while (position && argv[i][position] != '/') position--; + if (argv[i][position] == '/') position++; + + fprintf (outfile, "static const unsigned char %s_data_%s[] = {\n", argv[i] + position, suffix); + + for (j=0; j + +#declare FloorColour = <114/255, 114/255, 114/255>; + +camera { + orthographic + location <0, 0, -50> + look_at <0, 0, 0> + right <1, 0, 0> + up <0, 1, 0> +} + +light_source { <-5000, 10000, -10000> color rgb <1, 1, 1> } +light_source { <-5000, 10000, -10000> color rgb <1, 1, 1> } + +plane { -z, -2 + pigment { color rgb FloorColour } +} diff --git a/ksokoban/images/goal.png b/ksokoban/images/goal.png new file mode 100644 index 00000000..fcac3f41 Binary files /dev/null and b/ksokoban/images/goal.png differ diff --git a/ksokoban/images/goal.pov b/ksokoban/images/goal.pov new file mode 100644 index 00000000..ba2fa4a1 --- /dev/null +++ b/ksokoban/images/goal.pov @@ -0,0 +1,34 @@ +// Pov-ray image source for ksokoban +// copyright (c) 1998-1999 Anders Widell + +#include "floor_common.inc" + +torus { + 0.35, 0.05 + rotate <90, 0, 0> + translate <0,0,2> + pigment { color rgb FloorColour } +} + +cylinder {< 0.35,0,2>, < 10,0,2>, 0.05 pigment { color rgb FloorColour}} +cylinder {<-0.35,0,2>, <-10,0,2>, 0.05 pigment { color rgb FloorColour}} +cylinder {<0, 0.35,2>, <0, 10,2>, 0.05 pigment { color rgb FloorColour}} +cylinder {<0,-0.35,2>, <0,-10,2>, 0.05 pigment { color rgb FloorColour}} + +sphere { + 2*z, 0.3 + + finish { + ambient 0.1 + diffuse 0.3 + reflection .25 + specular 1 + roughness 0.02 + } + interior { + ior 2.4 + } + pigment { color rgbf <0.1, 1, 0.2, 0.8>} + + scale <1,1,0.1> +} diff --git a/ksokoban/images/halfstone_1.png b/ksokoban/images/halfstone_1.png new file mode 100644 index 00000000..267b3e45 Binary files /dev/null and b/ksokoban/images/halfstone_1.png differ diff --git a/ksokoban/images/halfstone_1.pov b/ksokoban/images/halfstone_1.pov new file mode 100644 index 00000000..e015b666 --- /dev/null +++ b/ksokoban/images/halfstone_1.pov @@ -0,0 +1,17 @@ +// Pov-ray image source for ksokoban +// copyright (c) 1998-1999 Anders Widell + +#include "stone_common.inc" + +camera { + orthographic + location <0, 0, -50> + look_at <0, 0, 0> + right <1/2, 0, 0> + up <0, 1/2, 0> +} + +object { + HalfStone + texture { StoneTexture translate <0,1,1> } +} diff --git a/ksokoban/images/halfstone_2.png b/ksokoban/images/halfstone_2.png new file mode 100644 index 00000000..96144c60 Binary files /dev/null and b/ksokoban/images/halfstone_2.png differ diff --git a/ksokoban/images/halfstone_2.pov b/ksokoban/images/halfstone_2.pov new file mode 100644 index 00000000..afc9fda1 --- /dev/null +++ b/ksokoban/images/halfstone_2.pov @@ -0,0 +1,17 @@ +// Pov-ray image source for ksokoban +// copyright (c) 1998-1999 Anders Widell + +#include "stone_common.inc" + +camera { + orthographic + location <0, 0, -50> + look_at <0, 0, 0> + right <1/2, 0, 0> + up <0, 1/2, 0> +} + +object { + HalfStone + texture { StoneTexture translate <0,2,0> } +} diff --git a/ksokoban/images/halfstone_3.png b/ksokoban/images/halfstone_3.png new file mode 100644 index 00000000..30994f96 Binary files /dev/null and b/ksokoban/images/halfstone_3.png differ diff --git a/ksokoban/images/halfstone_3.pov b/ksokoban/images/halfstone_3.pov new file mode 100644 index 00000000..d6a53bba --- /dev/null +++ b/ksokoban/images/halfstone_3.pov @@ -0,0 +1,17 @@ +// Pov-ray image source for ksokoban +// copyright (c) 1998-1999 Anders Widell + +#include "stone_common.inc" + +camera { + orthographic + location <0, 0, -50> + look_at <0, 0, 0> + right <1/2, 0, 0> + up <0, 1/2, 0> +} + +object { + HalfStone + texture { StoneTexture translate <0,3,0> } +} diff --git a/ksokoban/images/halfstone_4.png b/ksokoban/images/halfstone_4.png new file mode 100644 index 00000000..d8137006 Binary files /dev/null and b/ksokoban/images/halfstone_4.png differ diff --git a/ksokoban/images/halfstone_4.pov b/ksokoban/images/halfstone_4.pov new file mode 100644 index 00000000..4bb286e6 --- /dev/null +++ b/ksokoban/images/halfstone_4.pov @@ -0,0 +1,17 @@ +// Pov-ray image source for ksokoban +// copyright (c) 1998-1999 Anders Widell + +#include "stone_common.inc" + +camera { + orthographic + location <0, 0, -50> + look_at <0, 0, 0> + right <1/2, 0, 0> + up <0, 1/2, 0> +} + +object { + HalfStone + texture { StoneTexture translate <0,4,0> } +} diff --git a/ksokoban/images/man.png b/ksokoban/images/man.png new file mode 100644 index 00000000..491e7ebf Binary files /dev/null and b/ksokoban/images/man.png differ diff --git a/ksokoban/images/man.pov b/ksokoban/images/man.pov new file mode 100644 index 00000000..f5d71061 --- /dev/null +++ b/ksokoban/images/man.pov @@ -0,0 +1,5 @@ +// Pov-ray image source for ksokoban +// copyright (c) 1998-1999 Anders Widell + +#include "floor_common.inc" +#include "man_common.inc" diff --git a/ksokoban/images/man_common.inc b/ksokoban/images/man_common.inc new file mode 100644 index 00000000..87fa7423 --- /dev/null +++ b/ksokoban/images/man_common.inc @@ -0,0 +1,59 @@ +// Pov-ray image source for ksokoban +// copyright (c) 1998-1999 Anders Widell + +#include "colors.inc" + +blob { + threshold .5 + + sphere { <0, .4, 0>, .2, 1 pigment {Flesh} } // head + + + sphere { <-.04, .42, -.1>, .025, -1 pigment {Flesh} } // left eye hole + sphere { < .04, .42, -.1>, .025, -1 pigment {Flesh} } // right eye hole + + //sphere { <-.04, .42, -.09>, .025, 1 pigment {Flesh} } // left eye + //sphere { < .04, .42, -.09>, .025, 1 pigment {Flesh} } // right eye + + + + cylinder { <0, .415, -.1>, <0, .4, -.11>, .02, 1 pigment {Flesh} } // nose + + + cylinder { <0, .4, 0>, <0, .2, 0>, .075, 1 pigment {Flesh} } // neck + sphere { <0, .4, 0>, .05, -1 pigment {Flesh} } // head- + sphere { <0, .2, 0>, .05, -1 pigment {Flesh} } // shoulder- + + cylinder { <-.2,.2,0>, <.2,.2,0>, .115, .9 pigment {White} } // shoulder + cylinder { <-.1,.1,0>, <.1,.1,0>, .115, .9 pigment {White} } // stomach + cylinder { <-.1, 0,0>, <.1, 0,0>, .115, .9 pigment {White} } // stomach + cylinder { <-.1,-.05,0>, <.1,-.05,0>, .115, 1 pigment {White} } // stomach + cylinder { <-.1,-.05,0>, <.1,-.05,0>, .115, -1 pigment {White} } // stomach + cylinder { <-.1,-.1,0>, <.1,-.1,0>, .115, .9 pigment {Blue} } // waist + + +// Left arm + + cylinder { <-.2,.2,0>, <-.3, 0,0>, .1, 1 pigment {White } } + sphere { <-.2,.2,0>, .1, -1 pigment {White } } // shoulder- + cylinder { <-.3,0,0>, <-.2,-.1,-.1>, .075, 1 pigment {Flesh } } + sphere { <-.3,0,0>, .075, -1 pigment {Flesh } } // arm- + + +// Right arm + + cylinder { < .2,.2,0>, < .3, 0,0>, .1, 1 pigment {White}} + sphere { < .2,.2,0>, .1, -1 pigment {White}} // shoulder- + cylinder { < .3,0,0>, < .2,-.1,-.1>, .075, 1 pigment {Flesh}} + sphere { < .3,0,0>, .075, -1 pigment {Flesh}} // arm- + + // left leg + cylinder { <-.1,-.2,0>, <-.125,-.5,-.025>, .15, 1 pigment {Blue } } + + // right leg + cylinder { < .1,-.2,0>, < .125,-.5,-.025>, .15, 1 pigment {Blue } } + + + scale 0.95 + translate 0.01*y +} diff --git a/ksokoban/images/object.png b/ksokoban/images/object.png new file mode 100644 index 00000000..f6a842d0 Binary files /dev/null and b/ksokoban/images/object.png differ diff --git a/ksokoban/images/object.pov b/ksokoban/images/object.pov new file mode 100644 index 00000000..813af77f --- /dev/null +++ b/ksokoban/images/object.pov @@ -0,0 +1,36 @@ +// Pov-ray image source for ksokoban +// copyright (c) 1998-1999 Anders Widell + +#include "floor_common.inc" + +object { + intersection { + plane {-z, 0.3 rotate < 30, 0, 0>} + plane {-z, 0.3 rotate < 30, 60, 0>} + plane {-z, 0.3 rotate < 30, 120, 0>} + plane {-z, 0.3 rotate < 30, 180, 0>} + plane {-z, 0.3 rotate < 30, 240, 0>} + plane {-z, 0.3 rotate < 30, 300, 0>} + + plane {-z, 0.3 rotate <-50, 0, 0>} + plane {-z, 0.3 rotate <-50, 60, 0>} + plane {-z, 0.3 rotate <-50, 120, 0>} + plane {-z, 0.3 rotate <-50, 180, 0>} + plane {-z, 0.3 rotate <-50, 240, 0>} + plane {-z, 0.3 rotate <-50, 300, 0>} + } + + finish { + ambient 0.1 + diffuse 0.3 + reflection .25 + specular 1 + roughness 0.02 + } + interior { + ior 2.4 + } + pigment { color rgbf <1, 0.1, 0.2, 0.8>} + + translate <0, -0.1, 0> +} diff --git a/ksokoban/images/saveman.png b/ksokoban/images/saveman.png new file mode 100644 index 00000000..c859047c Binary files /dev/null and b/ksokoban/images/saveman.png differ diff --git a/ksokoban/images/saveman.pov b/ksokoban/images/saveman.pov new file mode 100644 index 00000000..df6ad750 --- /dev/null +++ b/ksokoban/images/saveman.pov @@ -0,0 +1,5 @@ +// Pov-ray image source for ksokoban +// copyright (c) 1998-1999 Anders Widell + +#include "goal.pov" +#include "man_common.inc" diff --git a/ksokoban/images/starfield.png b/ksokoban/images/starfield.png new file mode 100644 index 00000000..e09204be Binary files /dev/null and b/ksokoban/images/starfield.png differ diff --git a/ksokoban/images/stone_1.png b/ksokoban/images/stone_1.png new file mode 100644 index 00000000..221975ca Binary files /dev/null and b/ksokoban/images/stone_1.png differ diff --git a/ksokoban/images/stone_1.pov b/ksokoban/images/stone_1.pov new file mode 100644 index 00000000..357abafb --- /dev/null +++ b/ksokoban/images/stone_1.pov @@ -0,0 +1,17 @@ +// Pov-ray image source for ksokoban +// copyright (c) 1998-1999 Anders Widell + +#include "stone_common.inc" + +camera { + orthographic + location <0, 0, -50> + look_at <0, 0, 0> + right <1, 0, 0> + up <0, 1/2, 0> +} + +object { + Stone + texture { StoneTexture translate <1,0,0> } +} diff --git a/ksokoban/images/stone_2.png b/ksokoban/images/stone_2.png new file mode 100644 index 00000000..71ea2962 Binary files /dev/null and b/ksokoban/images/stone_2.png differ diff --git a/ksokoban/images/stone_2.pov b/ksokoban/images/stone_2.pov new file mode 100644 index 00000000..791e0f9e --- /dev/null +++ b/ksokoban/images/stone_2.pov @@ -0,0 +1,17 @@ +// Pov-ray image source for ksokoban +// copyright (c) 1998-1999 Anders Widell + +#include "stone_common.inc" + +camera { + orthographic + location <0, 0, -50> + look_at <0, 0, 0> + right <1, 0, 0> + up <0, 1/2, 0> +} + +object { + Stone + texture { StoneTexture translate <2,0,0> } +} diff --git a/ksokoban/images/stone_3.png b/ksokoban/images/stone_3.png new file mode 100644 index 00000000..a85c5eec Binary files /dev/null and b/ksokoban/images/stone_3.png differ diff --git a/ksokoban/images/stone_3.pov b/ksokoban/images/stone_3.pov new file mode 100644 index 00000000..8d983b5b --- /dev/null +++ b/ksokoban/images/stone_3.pov @@ -0,0 +1,17 @@ +// Pov-ray image source for ksokoban +// copyright (c) 1998-1999 Anders Widell + +#include "stone_common.inc" + +camera { + orthographic + location <0, 0, -50> + look_at <0, 0, 0> + right <1, 0, 0> + up <0, 1/2, 0> +} + +object { + Stone + texture { StoneTexture translate <3,0,0> } +} diff --git a/ksokoban/images/stone_4.png b/ksokoban/images/stone_4.png new file mode 100644 index 00000000..5f17e094 Binary files /dev/null and b/ksokoban/images/stone_4.png differ diff --git a/ksokoban/images/stone_4.pov b/ksokoban/images/stone_4.pov new file mode 100644 index 00000000..5d3a5636 --- /dev/null +++ b/ksokoban/images/stone_4.pov @@ -0,0 +1,17 @@ +// Pov-ray image source for ksokoban +// copyright (c) 1998-1999 Anders Widell + +#include "stone_common.inc" + +camera { + orthographic + location <0, 0, -50> + look_at <0, 0, 0> + right <1, 0, 0> + up <0, 1/2, 0> +} + +object { + Stone + texture { StoneTexture translate <4,0,0> } +} diff --git a/ksokoban/images/stone_5.png b/ksokoban/images/stone_5.png new file mode 100644 index 00000000..7a29b25d Binary files /dev/null and b/ksokoban/images/stone_5.png differ diff --git a/ksokoban/images/stone_5.pov b/ksokoban/images/stone_5.pov new file mode 100644 index 00000000..612dfffc --- /dev/null +++ b/ksokoban/images/stone_5.pov @@ -0,0 +1,17 @@ +// Pov-ray image source for ksokoban +// copyright (c) 1998-1999 Anders Widell + +#include "stone_common.inc" + +camera { + orthographic + location <0, 0, -50> + look_at <0, 0, 0> + right <1, 0, 0> + up <0, 1/2, 0> +} + +object { + Stone + texture { StoneTexture translate <5,0,0> } +} diff --git a/ksokoban/images/stone_6.png b/ksokoban/images/stone_6.png new file mode 100644 index 00000000..22eefb05 Binary files /dev/null and b/ksokoban/images/stone_6.png differ diff --git a/ksokoban/images/stone_6.pov b/ksokoban/images/stone_6.pov new file mode 100644 index 00000000..463c5131 --- /dev/null +++ b/ksokoban/images/stone_6.pov @@ -0,0 +1,17 @@ +// Pov-ray image source for ksokoban +// copyright (c) 1998-1999 Anders Widell + +#include "stone_common.inc" + +camera { + orthographic + location <0, 0, -50> + look_at <0, 0, 0> + right <1, 0, 0> + up <0, 1/2, 0> +} + +object { + Stone + texture { StoneTexture translate <6,0,0> } +} diff --git a/ksokoban/images/stone_common.inc b/ksokoban/images/stone_common.inc new file mode 100644 index 00000000..3c23a86d --- /dev/null +++ b/ksokoban/images/stone_common.inc @@ -0,0 +1,32 @@ +// Pov-ray image source for ksokoban +// copyright (c) 1998-1999 Anders Widell + +#include "colors.inc" +#include "stones.inc" + +#declare StoneTexture = T_Stone8 + +background { color rgb <0, 0, 0> } + +light_source { + <-1000000/3, 1000000/3, -1000000> color rgb <0.625, 0.625, 0.625> +} +light_source { + <-1000000/3, 1000000/3, -1000000> color rgb <0.625, 0.625, 0.625> +} + +#declare Stone = object { + superellipsoid { + <1/3, 1/3> + + scale <1/2, 1/4, 1/4> + } +} + +#declare HalfStone = object { + superellipsoid { + <1/2, 1/3> + + scale <1/4, 1/4, 1/4> + } +} diff --git a/ksokoban/images/treasure.png b/ksokoban/images/treasure.png new file mode 100644 index 00000000..2560f1ef Binary files /dev/null and b/ksokoban/images/treasure.png differ diff --git a/ksokoban/images/treasure.pov b/ksokoban/images/treasure.pov new file mode 100644 index 00000000..2f4ccfe7 --- /dev/null +++ b/ksokoban/images/treasure.pov @@ -0,0 +1,36 @@ +// Pov-ray image source for ksokoban +// copyright (c) 1998-1999 Anders Widell + +#include "goal.pov" + +object { + intersection { + plane {-z, 0.3 rotate < 30, 0, 0>} + plane {-z, 0.3 rotate < 30, 60, 0>} + plane {-z, 0.3 rotate < 30, 120, 0>} + plane {-z, 0.3 rotate < 30, 180, 0>} + plane {-z, 0.3 rotate < 30, 240, 0>} + plane {-z, 0.3 rotate < 30, 300, 0>} + + plane {-z, 0.3 rotate <-50, 0, 0>} + plane {-z, 0.3 rotate <-50, 60, 0>} + plane {-z, 0.3 rotate <-50, 120, 0>} + plane {-z, 0.3 rotate <-50, 180, 0>} + plane {-z, 0.3 rotate <-50, 240, 0>} + plane {-z, 0.3 rotate <-50, 300, 0>} + } + + finish { + ambient 0.1 + diffuse 0.3 + reflection .25 + specular 1 + roughness 0.02 + } + interior { + ior 2.4 + } + pigment { color rgbf <1, 0.1, 0.2, 0.8>} + + translate <0, -0.1, 0> +} diff --git a/ksokoban/levels/Makefile.am b/ksokoban/levels/Makefile.am new file mode 100644 index 00000000..d0ed02a2 --- /dev/null +++ b/ksokoban/levels/Makefile.am @@ -0,0 +1,11 @@ + +noinst_DATA = data.c + +data.c: ../images/bin2c level.data + ../images/bin2c "" $(srcdir)/level.data + +../images/bin2c: $(srcdir)/../images/bin2c.c + (cd ../images && $(MAKE) bin2c) + +CLEANFILES=data.c +EXTRA_DIST=level.data diff --git a/ksokoban/levels/level.data b/ksokoban/levels/level.data new file mode 100644 index 00000000..029e5c72 Binary files /dev/null and b/ksokoban/levels/level.data differ diff --git a/ksokoban/main.cpp b/ksokoban/main.cpp new file mode 100644 index 00000000..9c997613 --- /dev/null +++ b/ksokoban/main.cpp @@ -0,0 +1,86 @@ +/* + * ksokoban - a Sokoban game for KDE + * Copyright (C) 1998-2000 Anders Widell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +// #include +#include +#include +#include +#include +#include + +#include "MainWindow.h" + + +static const char description[] = I18N_NOOP("The japanese warehouse keeper game"); + +static const char version[] = "0.4.2"; + + +static KCmdLineOptions options[] = +{ + { "+[file]", I18N_NOOP("Level collection file to load"), 0 }, + KCmdLineLastOption // End of options. +}; + + +int +main (int argc, char **argv) +{ + KAboutData aboutData("ksokoban", I18N_NOOP("KSokoban"), + version, description, KAboutData::License_GPL, + "(c) 1998-2001 Anders Widell", 0, + "http://hem.passagen.se/awl/ksokoban/"); + aboutData.addAuthor("Anders Widell", 0, + "awl@passagen.se", + "http://hem.passagen.se/awl/"); + aboutData.addCredit("David W. Skinner", + I18N_NOOP("For contributing the Sokoban levels included in this game"), + "sasquatch@bentonrea.com", + "http://users.bentonrea.com/~sasquatch/"); + KCmdLineArgs::init(argc, argv, &aboutData); + KCmdLineArgs::addCmdLineOptions(options); +// KUniqueApplication::addCmdLineOptions(); + +// if (!KUniqueApplication::start()) +// return 0; + + QApplication::setColorSpec(QApplication::ManyColor); + +// KUniqueApplication app; + KApplication app; +// KImageIO::registerFormats(); + + MainWindow *widget = new MainWindow(); + app.setMainWidget(widget); + widget->show(); + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + if (args->count() > 0) { + widget->openURL(args->url(0)); + } + args->clear(); + + QObject::connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit())); + + int rc = app.exec(); + +// delete widget; + + return rc; +} diff --git a/kspaceduel/AUTHORS b/kspaceduel/AUTHORS new file mode 100644 index 00000000..bcb050bf --- /dev/null +++ b/kspaceduel/AUTHORS @@ -0,0 +1,2 @@ +Author of KSpaceduel: +Andreas Zehender diff --git a/kspaceduel/ChangeLog b/kspaceduel/ChangeLog new file mode 100644 index 00000000..95bada83 --- /dev/null +++ b/kspaceduel/ChangeLog @@ -0,0 +1,11 @@ +version 1.0 + * [Andreas Zehender] Portet to Qt-2.0 + * [Andreas Zehender] Changed option dialogs + +version 0.2.2 + * [Andreas Zehender] fixed error in ExplosionSprite::setSequence + +version 0.2.1 + * [Andreas Zehender] Implemented movements, bullets, mines + * [Andreas Zehender] First released version + diff --git a/kspaceduel/Makefile.am b/kspaceduel/Makefile.am new file mode 100644 index 00000000..00bdbe76 --- /dev/null +++ b/kspaceduel/Makefile.am @@ -0,0 +1,36 @@ +INCLUDES= -I$(top_srcdir)/libkdegames $(all_includes) + +SUBDIRS = sprites pics + +# you can add here more. This one gets installed +bin_PROGRAMS = kspaceduel + +# Which sources should be compiled for kspaceduel. +kspaceduel_SOURCES = general.ui mathroutines.cpp topwidget.cpp \ + playerinfo.cpp sprites.cpp mainview.cpp dialogs.cpp ai.cpp \ + main.cpp options.kcfgc + +kspaceduel_METASOURCES = AUTO +EXTRA_DIST = kspaceduel.desktop kspaceduel.xpm mini-kspaceduel.xpm + +kspaceduel_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kspaceduel_LDADD = $(LIB_KDEGAMES) +kspaceduel_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +# this option you can leave out. Just, if you use "make dist", you need it +noinst_HEADERS = mathroutines.h topwidget.h playerinfo.h \ + sprites.h dialogs.h mainview.h defines.h ai.h structs.h \ + version.h + +DISTCLEANFILES = $(kspaceduel_METASOURCES) + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kspaceduel.pot + +KDE_ICON = kspaceduel + +xdg_apps_DATA = kspaceduel.desktop +kde_kcfg_DATA = kspaceduel.kcfg + +rcdir=$(kde_datadir)/kspaceduel +rc_DATA = kspaceduelui.rc diff --git a/kspaceduel/README b/kspaceduel/README new file mode 100644 index 00000000..6b5565ff --- /dev/null +++ b/kspaceduel/README @@ -0,0 +1,10 @@ +KSpaceduel +---------- + +KSpaceduel is an arcade two-player space game for KDE. +Two ships fly around the sun and have to shoot the other ship. + +Look at the html manual for further information. + +Andreas Zehender +31 Oct 1998 diff --git a/kspaceduel/TODO b/kspaceduel/TODO new file mode 100644 index 00000000..4e93d5ba --- /dev/null +++ b/kspaceduel/TODO @@ -0,0 +1,5 @@ +todo: +----- + +sound!!!!! + diff --git a/kspaceduel/ai.cpp b/kspaceduel/ai.cpp new file mode 100644 index 00000000..5a7bec20 --- /dev/null +++ b/kspaceduel/ai.cpp @@ -0,0 +1,680 @@ +#include + +#include "ai.h" +#include "mathroutines.h" +#include "options.h" + +int Ai::calcFrameIncrement[Options::EnumAiDifficulty::COUNT] = {15,8,6,2}; +int Ai::calcPositionNumber[Options::EnumAiDifficulty::COUNT] = {10,15,20,60}; +int Ai::calcShotDirections[Options::EnumAiDifficulty::COUNT] = {4,7,10,12}; +int Ai::calcCollisions[Options::EnumAiDifficulty::COUNT] = {30,15,10,10}; +int Ai::calcNextShot[Options::EnumAiDifficulty::COUNT] = {300,200,90,60}; + +Ai::Ai(int pn,ShipSprite* s[2],QPtrList* b[2], + QPtrList* m[2],SConfig *c) +{ + int i; + + playerNumber=pn; + opponentNumber=(pn+1)%2; + cfg=c; + + for(i=0;i<2;i++) + { + ship[i]=s[i]; + bullets[i]=b[i]; + mines[i]=m[i]; + shipsNextPositions[i]=new QMemArray + ((int)(calcPositionNumber[Options::aiDifficulty(playerNumber)]/cfg->gamespeed)); + aiMines[i]=new QMemArray(cfg->maxMines); + mineNumber[i]=0; + } + myShots.setAutoDelete(true); + objectsHitByShip.setAutoDelete(true); + minesHitByShot.setAutoDelete(true); +} + +void Ai::newRound() +{ + accelerateFramesNumber=0; + rotateFramesNumber=0; + shoot=false; + score=1e10; + + rotation=RNONE; + acc=false; + bullet=false; + mine=false; + + borderTime=-1; + sunTime=-1; + + calculateCollisions=(int)(calcCollisions[Options::aiDifficulty(playerNumber)] + /cfg->gamespeed); + waitShot=(int) rint( random.getDouble() * + calcNextShot[Options::aiDifficulty(playerNumber)] + /cfg->gamespeed); + + myShots.clear(); + objectsHitByShip.clear(); + minesHitByShot.clear(); +} + +void Ai::think() +{ + setSpriteFieldSize(); + + myShots.clear(); + borderTime=-1; + sunTime=-1; + score--; + if(waitShot>0) + waitShot--; + + calculateNextPositions(); + if(Options::aiDifficulty(playerNumber)!=Options::EnumAiDifficulty::Trainee) + testForHits(); + if(waitShot<=0) + { + tryShots(); + shotScores(); + } + chooseAction(); + + + if(rotateFramesNumber<=0) + { + rotation=RNONE; + if(accelerateFramesNumber<=0) + { + acc=false; + if(shoot) + { + bullet=true; + shoot=false; + } + else + bullet=false; + score=1e10; + } + else + { + acc=true; + accelerateFramesNumber--; + } + } + else + rotateFramesNumber--; + +} + + +AiSprite Ai::nextPosition(AiSprite sp,double mult) +{ + double abs_2,nx,ny,sq,eg; + if(!sp.sun) + { + abs_2=sp.x*sp.x+sp.y*sp.y; + if(abs_2<1) + abs_2=1; + sq=sqrt(abs_2); + nx=sp.x/sq; + ny=sp.y/sq; + eg=cfg->gravity*mult; + sp.dx-=eg*nx/abs_2; + sp.dy-=eg*ny/abs_2; + + sp.x+=sp.dx*mult; + sp.y+=sp.dy*mult; + + if(sp.x*sp.x+sp.y*sp.y<1600) + sp.sun=true; + else + { + //simple bounds actions + if(sp.x>sfwidth_2) + { + sp.x-=sfwidth; + sp.border=true; + } + else if(sp.x<-sfwidth_2) + { + sp.x+=sfwidth; + sp.border=true; + } + if(sp.y>sfheight_2) + { + sp.y-=sfheight; + sp.border=true; + } + else if(sp.y<-sfheight_2) + { + sp.y+=sfheight; + sp.border=true; + } + } + } + + return sp; +} + +void Ai::nextPositions(AiSprite sp,QMemArray *a,int frames) +{ + int i,num; + double fmult=cfg->gamespeed*frames; + + (*a)[0]=nextPosition(sp,cfg->gamespeed); + num=a->size(); + for(i=1;igamespeed); + + if(shipsNextPositions[0]->size() != j) + for(i=0;i<2;i++) + shipsNextPositions[i]->resize(j); + + for(i=0;i<2;i++) + nextPositions(ship[i]->toAiSprite(),shipsNextPositions[i], + calcFrameIncrement[Options::aiDifficulty(playerNumber)]); + + if(cfg->maxMines > aiMines[0]->size()) + for(i=0;i<2;i++) + aiMines[i]->resize(cfg->maxMines); + + for(i=0;i<2;i++) + { + j=0; + ms=mines[i]->first(); + while(ms) + { + (*(aiMines[i]))[j]=ms->toAiSprite(); + ms=mines[i]->next(); + j++; + } + mineNumber[i]=j; + } +} + +void Ai::tryShots() +{ + AiSprite shot,me; + double rot,nr,nx,ny; + int i,f,frameIncrement,frameNum; + Hit hit; + Shot *goodShot; + + me=ship[playerNumber]->toAiSprite(); + rot=ship[playerNumber]->getRotation(); + + //Each 'frameIncrement' frames a shot is tried + frameIncrement=(int)((2*M_PI/calcShotDirections[Options::aiDifficulty(playerNumber)]) + /cfg->rotationSpeed); + if(frameIncrement==0) + frameIncrement=1; + //Number of frames needed to rotate 180 degrees + frameNum=(int)(M_PI/(frameIncrement*cfg->rotationSpeed)); + + //if too much bullets are on the playfield, no shot is tried + if(bullets[playerNumber]->count() < + (cfg->maxBullets+ship[playerNumber]->getBulletPowerups())) + { + for(f=0;f<=frameNum;f++) + { + if(f!=0) + for(i=0;igamespeed); + else + me=nextPosition(me,cfg->gamespeed); + + if(!ship[playerNumber]->reloadsBullet(f*frameIncrement*cfg->gamespeed)) + { + for(i=0;i<2;i++) + { + if((f==0)&&(i==1)) + continue; + if(i==0) + nr=rot+frameIncrement*f*cfg->rotationSpeed; + else + nr=rot-frameIncrement*f*cfg->rotationSpeed; + + nx=cos(nr); + ny=sin(nr); + shot.x=me.x+nx*SHOTDIST; + shot.y=me.y+ny*SHOTDIST; + shot.dx=me.dx+nx*cfg->shotSpeed; + shot.dy=me.dy+ny*cfg->shotSpeed; + shot.sun=false; + shot.border=false; + + hit=firstObject(shot,f*frameIncrement, + calcFrameIncrement[Options::aiDifficulty(playerNumber)]); + if((hit.object!=HNOTHING) && + !((hit.object==HSHIP)&&(hit.playerNumber==playerNumber))) + { + goodShot=new Shot; + goodShot->hit=hit; + goodShot->rotation=(i==0?RLEFT:RRIGHT); + goodShot->rotationFrames=f*frameIncrement; + goodShot->score=1e10; + myShots.append(goodShot); + } + } + } + } + } +} + +Hit Ai::firstObject(AiSprite shot,int time,int frames) +{ + int optime,i,num,rtime,basetime,t,m; + double dist,distx,disty,shiplastdist=0; + bool shipdistgreater=true,hitfound=false; + Hit hit={HNOTHING,0,0,0,1e10}; + + basetime=time/frames; + if((time%frames)>0) + basetime++; + rtime=basetime*frames-time; + optime=shipsNextPositions[0]->size(); + + num=optime-basetime; + + if(num>0) + { + for(t=0;(tgamespeed*rtime); + else + shot=nextPosition(shot,cfg->gamespeed*frames); + + //distance to other objects + for(i=0;i<2;i++) + { + distx=(*(shipsNextPositions[i]))[basetime].x-shot.x; + disty=(*(shipsNextPositions[i]))[basetime].y-shot.y; + dist=distx*distx+disty*disty; + //own ship + if(i==playerNumber) + { + if(dist0) + { + calculateCollisions--; + h=objectsHitByShip.first(); + while(h) + { + if(h->hitTime>0) + { + h->hitTime--; + h=objectsHitByShip.next(); + } + else + { + objectsHitByShip.remove(); + h=objectsHitByShip.current(); + } + } + h=minesHitByShot.first(); + while(h) + { + if(h->hitTime>0) + { + h->hitTime--; + h=minesHitByShot.next(); + } + else + { + minesHitByShot.remove(); + h=minesHitByShot.current(); + } + } + } + else + { + objectsHitByShip.clear(); + minesHitByShot.clear(); + for(i=0;i<2;i++) + { + for(bullet=bullets[i]->first();bullet;bullet=bullets[i]->next()) + { + shot=bullet->toAiSprite(); + hit=firstObject(shot,0,calcFrameIncrement[Options::aiDifficulty(playerNumber)]); + if(hit.object==HMINE) + { + h=new Hit(hit); + minesHitByShot.append(h); + } + if((hit.object==HSHIP)&&(hit.playerNumber==playerNumber)) + { + h=new Hit(hit); + h->object=HSHOT; + objectsHitByShip.append(h); + } + } + } + + hit.object=HNOTHING; + hit.distance=400; + + for(i=0;(isize()) && + !(*shipsNextPositions[playerNumber])[i].sun;i++) + { + if((borderTime<0) && (*shipsNextPositions[playerNumber])[i].border) + borderTime=i*calcFrameIncrement[Options::aiDifficulty(playerNumber)]; + + dx=(*shipsNextPositions[playerNumber])[i].x; + dy=(*shipsNextPositions[playerNumber])[i].y; + distance=dx*dx+dy*dy; + if((distance<3025)&&(sunTime<0)) + sunTime=i*calcFrameIncrement[Options::aiDifficulty(playerNumber)]; + + if(!hitfound) + for(p=0;p<2;p++) + for(m=0;mdistance) + { + hit.object=HMINE; + hit.playerNumber=p; + hit.objectNumber=m; + hit.hitTime=i*calcFrameIncrement[Options::aiDifficulty(playerNumber)]; + hit.distance=distance; + if(distance<100) + hitfound=true; + } + } + } + if(hit.object!=HNOTHING) + { + h=new Hit(hit); + objectsHitByShip.append(h); + } + calculateCollisions=(int)(calcCollisions[Options::aiDifficulty(playerNumber)]/cfg->gamespeed); + } +} + +void Ai::shotScores() +{ + Shot *s; + Hit *h,*mh; + bool found,foundmh; + double dist,dx,dy,fuel; + + + dx=(*shipsNextPositions[playerNumber])[0].x-(*shipsNextPositions[opponentNumber])[0].x; + dy=(*shipsNextPositions[playerNumber])[0].y-(*shipsNextPositions[opponentNumber])[0].y; + dist=dx*dx+dy*dy; + + for(s=myShots.first();s;s=myShots.next()) + { + fuel=(100-(ship[playerNumber]->getEnergy()-cfg->shotEnergyNeed)); + s->score=fuel*fuel/10 + s->hit.distance+s->hit.hitTime; + if(dist > (75*75)) + s->score+=waitShot*8; + else + s->score+=waitShot*4; + + if(s->hit.object==HMINE) + { + found=false; + for(h=objectsHitByShip.first();h && !found;h=objectsHitByShip.next()) + { + if((h->object==HMINE)&&(h->playerNumber==s->hit.playerNumber) + &&(h->objectNumber==s->hit.objectNumber)) + //ship will hit a mine that will be hitten by the shot + { + found=true; + //ship hits earlier then shot + if(h->hitTimehit.hitTime) + s->score+=1000; + else + { + foundmh=false; + for(mh=minesHitByShot.first();mh && !foundmh;mh=minesHitByShot.next()) + { + if((mh->playerNumber==s->hit.playerNumber) + &&(mh->objectNumber==s->hit.objectNumber)) + //another shot will hit the mine + { + if(mh->hitTimehit.hitTime) + s->score+=500; + else + s->score-=300; + } + } + } + } + } + if(!found) + s->score+=1000; + } + } +} + +void Ai::chooseAction() +{ + double bestScore=1e10; + Shot *bestShot=NULL,*s; + AiSprite actualpos; + double posangle,movephi,phiright,phileft,torotate=0,velangle; + int framesleft,framesright; + bool rotateAndAccelerate=false; + Hit *nextHit=0; + int shotHitTime; + + + shotHitTime=1000000; + nextHit=0; +/* for(h=objectsHitByShip.first();h;h=objectsHitByShip.next()) + if(h->object==HSHOT) + if(h->hitTimehitTime; + }*/ + + if((borderTime>0) || (sunTime>0) || (nextHit)) + { + actualpos=ship[playerNumber]->toAiSprite(); + posangle=rectToAngle(actualpos.x,actualpos.y); + + movephi=rectToAngle((*shipsNextPositions[playerNumber])[0].x, + (*shipsNextPositions[playerNumber])[0].y) - posangle; + + phileft=movephi+cfg->rotationSpeed; + phiright=movephi-cfg->rotationSpeed; + + if((borderTime>0)&& !((sunTime>0)&&(sunTimegamespeed; + if(score>bestScore) + { + velangle=rectToAngle(actualpos.dx,actualpos.dy); + if(fabs(difference(posangle+3*M_PI/4,velangle)) + < fabs(difference(posangle-3*M_PI/4,velangle))) + torotate=posangle-3*M_PI/4-ship[playerNumber]->getRotation(); + else + torotate=posangle+3*M_PI/4-ship[playerNumber]->getRotation(); + rotateAndAccelerate=true; + score=bestScore; + accelerateFramesNumber=(int)(8/cfg->gamespeed); + } + } + else if(sunTime>0) + { + bestScore=sunTime/(cfg->gamespeed*10) + +(actualpos.x*actualpos.x+actualpos.y*actualpos.y)/5000; + if(score>bestScore) + { + velangle=rectToAngle(actualpos.dx,actualpos.dy); + if(fabs(difference(posangle+2*M_PI/5,velangle)) + < fabs(difference(posangle-2*M_PI/5,velangle))) + torotate=posangle+2*M_PI/5-ship[playerNumber]->getRotation(); + else + torotate=posangle-2*M_PI/5-ship[playerNumber]->getRotation(); + rotateAndAccelerate=true; + score=bestScore; + accelerateFramesNumber=(int)(8/cfg->gamespeed); + } + } + else + { +/* bestScore=abs(nextHit->hitTime-90)*4/cfg->gamespeed + nextHit->distance*2 + + (100-(ship[playerNumber]->getEnergy()-cfg->shotEnergyNeed))*4; + if((score>bestScore)&&(bestScore<400)) + { + velangle=rectToAngle(actualpos.dx,actualpos.dy); + if(fabs(difference(posangle+2*M_PI/5,velangle)) + < fabs(difference(posangle-2*M_PI/5,velangle))) + torotate=posangle+2*M_PI/5-ship[playerNumber]->getRotation(); + else + torotate=posangle-2*M_PI/5-ship[playerNumber]->getRotation(); + rotateAndAccelerate=true; + score=bestScore; + accelerateFramesNumber=(int)(4/cfg->gamespeed); + }*/ + } + + if(rotateAndAccelerate) + { + if(phileft<0) + framesleft=1000; + else + { + while(torotate<0) + torotate+=2*M_PI; + while(torotate>=2*M_PI) + torotate-=2*M_PI; + framesleft=(int)(torotate/phileft+0.5); + } + + if(phiright>0) + framesright=1000; + else + { + while(torotate>0) + torotate-=2*M_PI; + while(torotate<=-2*M_PI) + torotate+=2*M_PI; + framesright=(int)(torotate/phiright+0.5); + } + + if(framesrightscorescore; + bestShot=s; + } + if(bestShot) + { + if((bestScorerotation; + rotateFramesNumber=bestShot->rotationFrames; + accelerateFramesNumber=0; + shoot=true; + score=bestScore; + calculateCollisions = 0; + waitShot=(int) rint( random.getDouble() * + calcNextShot[Options::aiDifficulty(playerNumber)] + /cfg->gamespeed); + } + } + } +} + +void Ai::setSpriteFieldSize() +{ + sfwidth=(double)(ship[playerNumber]->spriteFieldWidth()); + sfheight=(double)(ship[playerNumber]->spriteFieldHeight()); + sfwidth_2=sfwidth/2.0; + sfheight_2=sfheight/2.0; +} + diff --git a/kspaceduel/ai.h b/kspaceduel/ai.h new file mode 100644 index 00000000..0369ea02 --- /dev/null +++ b/kspaceduel/ai.h @@ -0,0 +1,102 @@ +#ifndef __KSD_AI_H +#define __KSD_AI_H + +#include + +#include +#include + +#include "sprites.h" +#include "dialogs.h" +#include "options.h" + +enum Rotation {RLEFT,RNONE,RRIGHT}; + +struct MineHit +{ + int mineNumber,hitTime; + double distance; +}; + +enum HitObject {HSHIP,HMINE,HSHOT,HNOTHING}; + +struct Hit +{ + HitObject object; + int playerNumber,objectNumber,hitTime; + double distance; //distance^2 +}; + +struct Shot +{ + Hit hit; + Rotation rotation; + int rotationFrames; + double score; +}; + + +class Ai +{ +public: + Ai(int pn,ShipSprite* s[2],QPtrList *b[2], + QPtrList *m[2],SConfig *c); + void newRound(); + void think(); + bool rotateLeft(){return rotation==RLEFT;} + bool rotateRight(){return rotation==RRIGHT;} + bool accelerate(){return acc;} + bool shootBullet(){return bullet;} + bool layMine(){return mine;} +private: + AiSprite nextPosition(AiSprite sp,double mult); + void nextPositions(AiSprite sp,QMemArray *a,int frames); + Hit firstObject(AiSprite shot,int shotframes,int frames); + void shotScores(); + void calculateNextPositions(); + void setSpriteFieldSize(); + void testForHits(); + void tryShots(); + void chooseAction(); + + SConfig *cfg; + + KRandomSequence random; + + //actions + Rotation rotation; + bool acc; + bool bullet,mine; + //what to do when + int rotateFramesNumber,accelerateFramesNumber; + bool shoot; + double score; + //sprites + int playerNumber,opponentNumber; + ShipSprite *ship[2]; + QPtrList *bullets[2]; + QPtrList *mines[2]; + QMemArray *shipsNextPositions[2]; + QMemArray *aiMines[2]; + int mineNumber[2]; + //possible Hits + QPtrList myShots; + QPtrList objectsHitByShip; + QPtrList minesHitByShot; + int borderTime; + int sunTime; + //SpriteField width and height + double sfwidth,sfheight,sfwidth_2,sfheight_2; + //Difficulty + static int calcFrameIncrement[Options::EnumAiDifficulty::COUNT]; + static int calcPositionNumber[Options::EnumAiDifficulty::COUNT]; + static int calcShotDirections[Options::EnumAiDifficulty::COUNT]; + static int calcCollisions[Options::EnumAiDifficulty::COUNT]; + static int calcNextShot[Options::EnumAiDifficulty::COUNT]; + static double calcShotRandom[Options::EnumAiDifficulty::COUNT]; + + int calculateCollisions; + int waitShot; +}; + +#endif diff --git a/kspaceduel/configure.in.in b/kspaceduel/configure.in.in new file mode 100644 index 00000000..cd18ea8d --- /dev/null +++ b/kspaceduel/configure.in.in @@ -0,0 +1 @@ +dnl KDE_CHECK_QWSPRITEFIELD(DO_NOT_COMPILE="$DO_NOT_COMPILE kspaceduel") diff --git a/kspaceduel/defines.h b/kspaceduel/defines.h new file mode 100644 index 00000000..81162d77 --- /dev/null +++ b/kspaceduel/defines.h @@ -0,0 +1,66 @@ +#include + + +#define IDS_PAUSE 1 +#define IDS_MAIN 2 + +#define PlayerKeyLeft 0 +#define PlayerKeyRight 1 +#define PlayerKeyAcc 2 +#define PlayerKeyShot 3 +#define PlayerKeyMine 4 +#define PlayerKeyNum 5 + +#define ROTNUM 64 + +#define MV_BACKGROUND "sprites/backgr.png" + +#define MV_SHIP1_PPM "sprites/ship1/ship%02d.ppm" +#define MV_SHIP1_PBM "sprites/ship1/ship%02d.pbm" +#define MV_SHIP2_PPM "sprites/ship2/ship%02d.ppm" +#define MV_SHIP2_PBM "sprites/ship2/ship%02d.pbm" + +#define MV_BULLET1_PPM "sprites/ship1/bullet.ppm" +#define MV_BULLET1_PBM "sprites/ship1/bullet.pbm" +#define MV_BULLET2_PPM "sprites/ship2/bullet.ppm" +#define MV_BULLET2_PBM "sprites/ship2/bullet.pbm" + +#define MV_MINE1_PPM "sprites/ship1/mine%d.ppm" +#define MV_MINE1_PBM "sprites/ship1/mine%d.pbm" +#define MV_MINE2_PPM "sprites/ship2/mine%d.ppm" +#define MV_MINE2_PBM "sprites/ship2/mine%d.pbm" + +#define MV_SUN_PPM "sprites/sun/sun.ppm" +#define MV_SUN_PBM "sprites/sun/sun.pbm" + +#define MV_EXPLOSION_PPM "sprites/explosion/explos%02d.ppm" +#define MV_EXPLOSION_PBM "sprites/explosion/explos%02d.pbm" +#define MV_MINEEX_PPM "sprites/explosion/mineex%02d.ppm" +#define MV_MINEEX_PBM "sprites/explosion/mineex%02d.pbm" + +#define MV_POWERBULLET_PPM "sprites/powerups/pbullet.ppm" +#define MV_POWERBULLET_PBM "sprites/powerups/pbullet.pbm" +#define MV_POWERMINE_PPM "sprites/powerups/pmine.ppm" +#define MV_POWERMINE_PBM "sprites/powerups/pmine.pbm" +#define MV_POWERSHIELD_PPM "sprites/powerups/pshield.ppm" +#define MV_POWERSHIELD_PBM "sprites/powerups/pshield.pbm" +#define MV_POWERENERGY_PPM "sprites/powerups/penergy.ppm" +#define MV_POWERENERGY_PBM "sprites/powerups/penergy.pbm" + +#define DEF_WIDTH 640 +#define DEF_HEIGHT 480 + +#define EXPLOSION_TIME 7 + +#define S_BASE 1500 +#define S_SUN S_BASE+0 +#define S_SHIP S_BASE+1 +#define S_BULLET S_BASE+2 +#define S_MINE S_BASE+3 +#define S_EXPLOSION S_BASE+4 +#define S_POWERUP S_BASE+5 + +#define SHOTDIST 14 +#define EPSILON 0.1 + +#define GAME_START_SHORTCUT Qt::Key_Space diff --git a/kspaceduel/dialogs.cpp b/kspaceduel/dialogs.cpp new file mode 100644 index 00000000..9e5b4b7a --- /dev/null +++ b/kspaceduel/dialogs.cpp @@ -0,0 +1,434 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include "options.h" +#include "dialogs.h" +#include "general.h" + +bool operator!=(const SConfig &s1, const SConfig &s2) +{ + return +(s1.gamespeed != s2.gamespeed) || +(s1.gravity != s2.gravity) || +(s1.acc != s2.acc) || +(s1.energyNeed != s2.energyNeed) || +(s1.sunEnergy != s2.sunEnergy) || +(s1.rotationSpeed != s2.rotationSpeed) || +(s1.mineActivateTime != s2.mineActivateTime) || +(s1.mineFuel != s2.mineFuel) || +(s1.shotSpeed != s2.shotSpeed) || +(s1.shotEnergyNeed != s2.shotEnergyNeed) || +(s1.mineEnergyNeed != s2.mineEnergyNeed) || +(s1.rotationEnergyNeed != s2.rotationEnergyNeed) || +(s1.startPosX != s2.startPosX) || +(s1.startPosY != s2.startPosY) || +(s1.startVelX != s2.startVelX) || +(s1.startVelY != s2.startVelY) || +(s1.bulletLifeTime != s2.bulletLifeTime) || +(s1.mineReloadTime != s2.mineReloadTime) || +(s1.bulletReloadTime != s2.bulletReloadTime) || +(s1.bulletDamage != s2.bulletDamage) || +(s1.shipDamage != s2.shipDamage) || +(s1.mineDamage != s2.mineDamage) || +(s1.maxBullets != s2.maxBullets) || +(s1.maxMines != s2.maxMines) || +(s1.powerupLifeTime != s2.powerupLifeTime) || +(s1.powerupRefreshTime != s2.powerupRefreshTime) || +(s1.powerupEnergyAmount != s2.powerupEnergyAmount) || +(s1.powerupShieldAmount != s2.powerupShieldAmount); +} + + +char ConfigSetup::LabelName[EditNum][25]= +{ + I18N_NOOP("Game speed:"), I18N_NOOP("Shot speed:"), + I18N_NOOP("Energy need:"), I18N_NOOP("Max number:"), + I18N_NOOP("Damage:"), I18N_NOOP("Life time:"), + I18N_NOOP("Reload time:"), I18N_NOOP("Mine fuel:"), + I18N_NOOP("Energy need:"), I18N_NOOP("Activate time:"), + I18N_NOOP("Damage:"), I18N_NOOP("Max number:"), + I18N_NOOP("Reload time:"), I18N_NOOP("Acceleration:"), + I18N_NOOP("Energy need:"), I18N_NOOP("Rotation speed:"), + I18N_NOOP("Energy need:"), I18N_NOOP("Crash damage:"), + I18N_NOOP("Sun energy:"), I18N_NOOP("Gravity:"), + I18N_NOOP("Position X:"), I18N_NOOP("Position Y:"), + I18N_NOOP("Velocity X:"), I18N_NOOP("Velocity Y:"), + I18N_NOOP("Appearance time:"),I18N_NOOP("Life time:"), + I18N_NOOP("Energy amount:"), I18N_NOOP("Shield amount:") +}; + +enum ConfigSetup::Type ConfigSetup::VarType[EditNum]= +{VarFloat, + VarFloat,VarFloat,VarInt,VarInt,VarFloat,VarFloat, + VarFloat,VarFloat,VarFloat,VarInt,VarInt,VarFloat, + VarFloat,VarFloat,VarFloat, + VarFloat,VarInt, + VarFloat,VarFloat, + VarFloat,VarFloat,VarFloat,VarFloat, + VarFloat,VarFloat,VarFloat,VarInt}; + +double ConfigSetup::EditVal[EditNum][3]= +{{0.2,4.0,1}, + {0.1,10,3},{0,99,10},{0,10,5},{1,100,20},{50,2000,100},{0,400,10}, + {5,200,65},{0,99,5},{5,100,15},{1,100,30},{0,10,3},{0,400,10}, + {0,1.0,0.2},{0,10,1.0},{0.1,4,1}, + {0,10,0.2},{0,100,50}, + {1000,30000,9000},{0,10000,2200}, + {-250,250,-130},{-180,180,-100},{-10,10,3},{-10,10,-1.7}, + {200,4000,800},{200,4000,400},{0,99,50},{0,99,30}}; + +int ConfigSetup::EditDiv[EditNum]= +{100, + 10,1,1,1,1,10, + 1,1,10,1,1,10, + 100,10,10, + 10,1, + 1,1, + 10,10,100,100, + 1,1,1,1}; + +int ConfigSetup::Parent[EditNum]= +{TabGeneral, + TabBullet,TabBullet,TabBullet,TabBullet,TabBullet,TabBullet, + TabMine,TabMine,TabMine,TabMine,TabMine,TabMine, + TabShip,TabShip,TabShip,TabShip,TabShip, + TabSun,TabSun, + TabStart,TabStart,TabStart,TabStart, + TabPowerups,TabPowerups,TabPowerups,TabPowerups}; + +int ConfigSetup::Position[EditNum]= +{0, + 0,1,2,3,4,5, + 0,1,2,3,4,5, + 0,1,2,3,4, + 0,1, + 0,1,2,3, + 0,1,2,3}; + +const int LCDLen=6; + +ConfigSetup::ConfigSetup(SConfig *custom,QWidget *parent,const char *name) + :QWidget( parent, name ) +{ + QLabel *label[EditNum]; + QGridLayout *stacklayout[TabNum]; + QWidget *configWidgets[TabNum]; + //QGroupBox *box; + + int i; + + resize(500,400); + //setCaption(i18n("Game Setup")); + //setHelp( "OptionsConfigurations" ); + + + //box=new QGroupBox(i18n("Config"),this); + //setMainWidget( box ); + QVBoxLayout *boxlayout = new QVBoxLayout( this, 6 ); + + tabs=new QTabWidget(this); + for(i=0;isetFrameStyle(QFrame::NoFrame); + } + + configCombo=new QComboBox(false,this); + connect(configCombo,SIGNAL(activated(int)),SLOT(configSelected(int))); + for(i=0;iinsertItem(i18n(predefinedConfigName[i])); + configCombo->insertItem(i18n("Custom")); + + boxlayout->addSpacing( 2 * 6 ); + boxlayout->addWidget(configCombo); + boxlayout->addWidget(tabs); + + for(i=0;iaddWidget(label[i],Position[i],0); + stacklayout[Parent[i]]->addWidget(slider[i],Position[i],1); + stacklayout[Parent[i]]->addWidget(value[i],Position[i],2); + } + + for(i=0;iactivate(); + + tabs->addTab(configWidgets[0],i18n("General")); + tabs->addTab(configWidgets[1],i18n("Bullet")); + tabs->addTab(configWidgets[2],i18n("Name","Mine")); + tabs->addTab(configWidgets[3],i18n("Ship")); + tabs->addTab(configWidgets[4],i18n("Sun")); + tabs->addTab(configWidgets[5],i18n("Start")); + tabs->addTab(configWidgets[6],i18n("Powerups")); + + customConfig=custom; + + updateWidgets(); +} + +void ConfigSetup::updateWidgets() +{ + config=*customConfig; + selectedConfig = -1; + configCombo->setCurrentItem(Options::lastConfig()); + configSelected(Options::lastConfig()); +} + +void ConfigSetup::valueChanged(int ednum,int value) +{ + if(selectedConfig==predefinedConfigNum) + { + switch(ednum) + { + case EditMaxBullets:config.maxBullets=value;break; + case EditBulletDamage:config.bulletDamage=value;break; + case EditMaxMines:config.maxMines=value;break; + case EditMineDamage:config.mineDamage=value;break; + case EditShipDamage:config.shipDamage=value;break; + case EditPowerupShieldAmount:config.powerupShieldAmount=value;break; + } + emit changed(); + } +} + +void ConfigSetup::valueChanged(int ednum,double value) +{ + if(selectedConfig==predefinedConfigNum) + { + switch(ednum) + { + case EditSunEnergy:config.sunEnergy=value;break; + case EditGravity:config.gravity=value;break; + case EditShotSpeed:config.shotSpeed=value;break; + case EditShotEnergyNeed:config.shotEnergyNeed=value;break; + case EditBulletLifeTime:config.bulletLifeTime=value;break; + case EditBulletReloadTime:config.bulletReloadTime=value;break; + case EditMineFuel:config.mineFuel=value;break; + case EditMineEnergyNeed:config.mineEnergyNeed=value;break; + case EditMineActivateTime:config.mineActivateTime=value;break; + case EditMineReloadTime:config.mineReloadTime=value;break; + case EditGamespeed:config.gamespeed=value;break; + case EditPosX:config.startPosX=value;break; + case EditPosY:config.startPosY=value;break; + case EditVelX:config.startVelX=value;break; + case EditVelY:config.startVelY=value;break; + case EditAcc:config.acc=value;break; + case EditEnergyNeed:config.energyNeed=value;break; + case EditRotationSpeed:config.rotationSpeed=value;break; + case EditRotationEnergyNeed:config.rotationEnergyNeed=value;break; + case EditPowerupRefreshTime:config.powerupRefreshTime=value;break; + case EditPowerupLifeTime:config.powerupLifeTime=value;break; + case EditPowerupEnergyAmount:config.powerupEnergyAmount=value;break; + } + emit changed(); + } +} + +void ConfigSetup::updateSettings() +{ + *customConfig=config; + + Options::setLastConfig(selectedConfig); + Options::writeConfig(); +} + +bool ConfigSetup::hasChanged() +{ + if (configCombo->currentItem() != Options::lastConfig()) + return true; + + if (configCombo->currentItem() != predefinedConfigNum) + return false; + + return ((*customConfig) != config); +} + +void ConfigSetup::updateWidgetsDefault() +{ + configCombo->setCurrentItem(0); + configSelected(0); +} + +bool ConfigSetup::isDefault() +{ + return configCombo->currentItem() == 0; +} + +void ConfigSetup::configSelected(int num) +{ + int i; + if(!((selectedConfig==predefinedConfigNum)&&(num==predefinedConfigNum))) + { + selectedConfig = num; + for(i=0;isetEnabled(num==predefinedConfigNum); + //enableButton( Default, num==predefinedConfigNum ); + if(numdisplay(str); + slider[ednum]->setValue(val); +} + +void ConfigSetup::setValue(int ednum,double val) +{ + QString str; + int hval=(int)(val*EditDiv[ednum]+0.5); + int n,h; + + if(EditDiv[ednum]==1) + str.sprintf("%*i",LCDLen,hval); + else + { + h=1; + for(n=0;hdisplay(str); + slider[ednum]->setValue(hval); +} + +void ConfigSetup::setValue(int ednum,unsigned val) +{ + QString str; + str.sprintf("%*i",LCDLen,(int)val); + value[ednum]->display(str); + slider[ednum]->setValue((int)val); +} + +void ConfigSetup::sliderChanged(int val) +{ + int i,n,h; + QString str; + + for(i=0;(idisplay(str); + if(VarType[i]==VarFloat) + valueChanged(i,(double)val/EditDiv[i]); + else + valueChanged(i,val); + } + +} + +SettingsDialog::SettingsDialog(SConfig *customConfig, QWidget *parent, const char *name) + : KConfigDialog( parent, name, Options::self()) +{ + General *general = new General(); + addPage(general, i18n("General"), "package_settings", i18n("General Settings")); + + cs = new ConfigSetup(customConfig); + addPage(cs, i18n("Game"), "kspaceduel", i18n("Game Settings")); + connect(cs, SIGNAL(changed()), this, SLOT(updateButtons())); + +// resize(600,400); +} + +SettingsDialog::~SettingsDialog() +{ +} + +void SettingsDialog::updateWidgetsDefault() +{ + cs->updateWidgetsDefault(); +} + +void SettingsDialog::updateWidgets() +{ + cs->updateWidgets(); +} + +void SettingsDialog::updateSettings() +{ + cs->updateSettings(); + emit settingsUpdated(); +} + +bool SettingsDialog::hasChanged() +{ + return cs->hasChanged(); +} + +bool SettingsDialog::isDefault() +{ + return cs->isDefault(); +} + +#include "dialogs.moc" diff --git a/kspaceduel/dialogs.h b/kspaceduel/dialogs.h new file mode 100644 index 00000000..1a1ef6d2 --- /dev/null +++ b/kspaceduel/dialogs.h @@ -0,0 +1,102 @@ +#ifndef __SP_DIALOG_H +#define __SP_DIALOG_H + +class QPushButton; +class QLabel; +class QComboBox; +class QTabWidget; +class QSlider; +class QLCDNumber; +class QCheckBox; + +#include + +#include "defines.h" +#include "structs.h" + + +class ConfigSetup:public QWidget +{ + Q_OBJECT +public: + ConfigSetup(SConfig *custom,QWidget *parent=0,const char* name=0); + + bool hasChanged(); + bool isDefault(); + void updateSettings(); + void updateWidgets(); + void updateWidgetsDefault(); + +signals: + void changed(); + +protected slots: + void configSelected(int num); + void sliderChanged(int val); +protected: + void valueChanged(int ednum,int val); + void valueChanged(int ednum,double val); + void displayConfig(SConfig cfg); + void setValue(int ednum,int val); + void setValue(int ednum,double val); + void setValue(int ednum,unsigned val); +private: + enum {EditGamespeed=0, + EditShotSpeed,EditShotEnergyNeed,EditMaxBullets,EditBulletDamage, + EditBulletLifeTime,EditBulletReloadTime, + EditMineFuel,EditMineEnergyNeed,EditMineActivateTime, + EditMineDamage,EditMaxMines,EditMineReloadTime, + EditAcc,EditEnergyNeed,EditRotationSpeed, + EditRotationEnergyNeed,EditShipDamage, + EditSunEnergy,EditGravity, + EditPosX,EditPosY,EditVelX,EditVelY, + EditPowerupRefreshTime,EditPowerupLifeTime, + EditPowerupEnergyAmount,EditPowerupShieldAmount, + EditNum}; + enum {TabGeneral=0,TabBullet,TabMine, + TabShip,TabSun,TabStart,TabPowerups,TabNum}; + enum Type {VarInt,VarFloat}; + + static char LabelName[EditNum][25]; + static int Parent[EditNum]; + static int Position[EditNum]; + static const char *TabName[TabNum]; + static double EditVal[EditNum][3]; + static int EditDiv[EditNum]; + static Type VarType[EditNum]; + + QTabWidget *tabs; + + QSlider *slider[EditNum]; + QLCDNumber *value[EditNum]; + + QComboBox *configCombo; + SConfig *customConfig,config; + + int selectedConfig; +}; + +class SettingsDialog : public KConfigDialog +{ + Q_OBJECT +public: + SettingsDialog(SConfig *customConfig, QWidget *parent=0, const char *name=0); + ~SettingsDialog(); + +signals: + void settingsUpdated(); + +private slots: + void updateWidgets(); + void updateWidgetsDefault(); + void updateSettings(); + +private: + bool hasChanged(); + bool isDefault(); + +private: + ConfigSetup* cs; +}; + +#endif diff --git a/kspaceduel/general.ui b/kspaceduel/general.ui new file mode 100644 index 00000000..83f3108f --- /dev/null +++ b/kspaceduel/general.ui @@ -0,0 +1,337 @@ + +General + + + General + + + + 0 + 0 + 512 + 488 + + + + + unnamed + + + + groupBox3 + + + GroupBoxPanel + + + Sunken + + + Hit Points + + + + unnamed + + + + lCDNumber2 + + + 2 + + + 99 + + + + + lCDNumber1 + + + 2 + + + 99 + + + + + textLabel3 + + + Red player: + + + + + textLabel4 + + + Blue player: + + + + + kcfg_StartHitPoints1 + + + 1 + + + Horizontal + + + + + kcfg_StartHitPoints0 + + + 1 + + + Horizontal + + + + + + + groupBox4 + + + Graphics + + + + unnamed + + + + textLabel5 + + + Refresh time: + + + + + kcfg_RefreshTime + + + 10 + + + 100 + + + 33 + + + Horizontal + + + + + lCDNumber3 + + + 3 + + + 33 + + + + + + + spacer4 + + + Vertical + + + Expanding + + + + 20 + 91 + + + + + + groupBox1 + + + Red Player + + + + unnamed + + + + kcfg_Player0IsAi + + + Player is AI + + + + + textLabel1 + + + false + + + Difficulty: + + + + + + Trainee + + + + + Normal + + + + + Hard + + + + + Insane + + + + kcfg_AiDifficulty0 + + + false + + + 1 + + + + + + + groupBox2 + + + Blue Player + + + + unnamed + + + + kcfg_Player1IsAi + + + Player is AI + + + + + textLabel2 + + + false + + + Difficulty: + + + + + + Trainee + + + + + Normal + + + + + Hard + + + + + Insane + + + + kcfg_AiDifficulty1 + + + false + + + 1 + + + + + + + + + kcfg_StartHitPoints0 + valueChanged(int) + lCDNumber1 + display(int) + + + kcfg_StartHitPoints1 + valueChanged(int) + lCDNumber2 + display(int) + + + kcfg_RefreshTime + valueChanged(int) + lCDNumber3 + display(int) + + + kcfg_Player0IsAi + toggled(bool) + textLabel1 + setEnabled(bool) + + + kcfg_Player0IsAi + toggled(bool) + kcfg_AiDifficulty0 + setEnabled(bool) + + + kcfg_Player1IsAi + toggled(bool) + textLabel2 + setEnabled(bool) + + + kcfg_Player1IsAi + toggled(bool) + kcfg_AiDifficulty1 + setEnabled(bool) + + + + diff --git a/kspaceduel/hi128-app-kspaceduel.png b/kspaceduel/hi128-app-kspaceduel.png new file mode 100644 index 00000000..623b8b0a Binary files /dev/null and b/kspaceduel/hi128-app-kspaceduel.png differ diff --git a/kspaceduel/hi16-app-kspaceduel.png b/kspaceduel/hi16-app-kspaceduel.png new file mode 100644 index 00000000..a5b4b333 Binary files /dev/null and b/kspaceduel/hi16-app-kspaceduel.png differ diff --git a/kspaceduel/hi22-app-kspaceduel.png b/kspaceduel/hi22-app-kspaceduel.png new file mode 100644 index 00000000..66037f3f Binary files /dev/null and b/kspaceduel/hi22-app-kspaceduel.png differ diff --git a/kspaceduel/hi32-app-kspaceduel.png b/kspaceduel/hi32-app-kspaceduel.png new file mode 100644 index 00000000..129dcb02 Binary files /dev/null and b/kspaceduel/hi32-app-kspaceduel.png differ diff --git a/kspaceduel/hi48-app-kspaceduel.png b/kspaceduel/hi48-app-kspaceduel.png new file mode 100644 index 00000000..c452befa Binary files /dev/null and b/kspaceduel/hi48-app-kspaceduel.png differ diff --git a/kspaceduel/hi64-app-kspaceduel.png b/kspaceduel/hi64-app-kspaceduel.png new file mode 100644 index 00000000..16d07cde Binary files /dev/null and b/kspaceduel/hi64-app-kspaceduel.png differ diff --git a/kspaceduel/kspaceduel.desktop b/kspaceduel/kspaceduel.desktop new file mode 100644 index 00000000..87da3326 --- /dev/null +++ b/kspaceduel/kspaceduel.desktop @@ -0,0 +1,76 @@ +[Desktop Entry] +Name=KSpaceDuel +Name[af]=K-ruimte-uitdaging +Name[ar]=لعبة مبارزة الÙضاء (KSpaceDuel) +Name[be]=КаÑÐ¼Ñ–Ñ‡Ð½Ð°Ñ Ð´ÑƒÑль +Name[bn]=কে-সà§à¦ªà§‡à¦¸à¦¡à§à§Ÿà§‡à¦² +Name[hi]=के-सà¥à¤ªà¥‡à¤¸à¤¡à¥à¤¯à¥‚à¤à¤² +Name[hr]=KSvemirski dvoboj +Name[hu]=Å°rpárbaj +Name[is]=Geimeinvígi +Name[nb]=Rom-duell +Name[ne]=केडीई सà¥à¤ªà¥‡à¤¸ डà¥à¤¯à¥à¤² +Name[nn]=Romduell +Name[ro]=Duel spaÅ£ial +Name[sv]=Kspaceduel +Name[ta]=Kஸà¯à®ªà¯‡à®¸à¯à®Ÿà¯à®¯à®²à¯ +Name[tg]=KМуҳорибаи Кайҳонӣ +Name[tr]=Uzay Düellosu +Name[zh_TW]=KSpaceDuel 決戰星空 +Name[zu]=I-KSpaceDuel +MimeType= +Exec=kspaceduel %i %m -caption "%c" +GenericName=Space Arcade Game +GenericName[be]=КаÑÐ¼Ñ–Ñ‡Ð½Ð°Ñ Ð°Ñ€ÐºÐ°Ð´Ð½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ +GenericName[bg]=КоÑмичеÑка игра +GenericName[bn]=মহাশূনà§à¦¯ নিয়ে à¦à¦•à¦Ÿà¦¿ আরà§à¦•à§‡à¦¡ (Arcade) খেলা +GenericName[bs]=Svemirska arkadna igra +GenericName[ca]=Joc arcade d'espai +GenericName[cs]=Vesmírná arkádová hra +GenericName[cy]=Gêm Arcêd Ofod +GenericName[da]=Rum arkadespil +GenericName[de]=Weltraum Arkade-Spiel +GenericName[el]=Διαστημικό Arcade παιχνίδι +GenericName[eo]=Kosma arkadoludo +GenericName[es]=Juego recreativo espacial +GenericName[et]=Põnevusmäng kosmoses +GenericName[eu]=Espazioko arcade jokoa +GenericName[fa]=بازی گذرگاه Ùضایی +GenericName[fi]=Arcade-peli +GenericName[fr]=Jeu d'arcade dans l'espace +GenericName[he]=משחק ×רקייד חללי +GenericName[hr]=Arkadna igra u Svemiru +GenericName[hu]=LövöldözÅ‘s +GenericName[is]=Skotgeimleikur +GenericName[it]=Gioco arcade nello spazio +GenericName[ja]=宇宙アーケードゲーム +GenericName[km]=ល្បែង​ទូ​លំហ +GenericName[ko]=우주 ì•„ì¼€ì´ë“œ 게임 +GenericName[lv]=KosmiskÄ dueļa spÄ“le +GenericName[mk]=Ð’ÑеленÑка аркадна игра +GenericName[nb]=Arkade-romspill +GenericName[nds]=Speelhall-Weltruumspeel +GenericName[ne]=सà¥à¤ªà¥‡à¤¸ आरà¥à¤•à¥‡à¤¡ खेल +GenericName[nl]=Ruimte-arcadespel +GenericName[nn]=Arkade-romspel +GenericName[pl]=Kosmiczna gra zrÄ™cznoÅ›ciowa +GenericName[pt]=Jogo de Arcada Espacial +GenericName[pt_BR]=Jogo de Arcade espacial +GenericName[ru]=КоÑмичеÑÐºÐ°Ñ Ð´ÑƒÑль +GenericName[sk]=Vesmírna dobrodružná hra +GenericName[sl]=Vesoljska arkadna igra +GenericName[sr]=СвемирÑка аркадна игра +GenericName[sr@Latn]=Svemirska arkadna igra +GenericName[sv]=Rymdarkadspel +GenericName[ta]=விணà¯à®µà¯†à®³à®¿ ஆரà¯à®•à¯‡à®Ÿà¯ விளையாடà¯à®Ÿà¯ +GenericName[uk]=КоÑмічна аркадна гра - дуель +GenericName[wa]=Djeu d' Ã¥rcÃ¥de di l' espÃ¥ce +GenericName[zh_TW]=大型éŠæˆ²æ©ŸéŠæˆ² +Icon=kspaceduel +Path= +DocPath=kspaceduel/index.html +Type=Application +Terminal=false +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;ArcadeGame; diff --git a/kspaceduel/kspaceduel.kcfg b/kspaceduel/kspaceduel.kcfg new file mode 100644 index 00000000..56b9fddd --- /dev/null +++ b/kspaceduel/kspaceduel.kcfg @@ -0,0 +1,41 @@ + + + + + + 42.9 + + + + 99 + 1 + 99 + + + + false + true + + + + + + + + + + Normal + + + 0 + + + 33 + 10 + 100 + + + diff --git a/kspaceduel/kspaceduelui.rc b/kspaceduel/kspaceduelui.rc new file mode 100644 index 00000000..4aab1ba0 --- /dev/null +++ b/kspaceduel/kspaceduelui.rc @@ -0,0 +1,16 @@ + + + + + &Game + + + + +Main Toolbar + + + + + + diff --git a/kspaceduel/main.cpp b/kspaceduel/main.cpp new file mode 100644 index 00000000..ab1952b1 --- /dev/null +++ b/kspaceduel/main.cpp @@ -0,0 +1,29 @@ +#include +#include +#include + +#include "version.h" +#include "topwidget.h" + +static const char description[] = I18N_NOOP("KDE Space Game"); + +int main(int argc,char **argv) +{ + KAboutData aboutData( "kspaceduel", I18N_NOOP("KSpaceDuel"), + KSPACEDUEL_VERSION, description, KAboutData::License_GPL, + "(c) 1998-2000, Andreas Zehender"); + aboutData.addAuthor("Andreas Zehender",0, "az@azweb.de"); + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication myapplication; + MyTopLevelWidget* top = new MyTopLevelWidget( ); + myapplication.setMainWidget(top); + top->show(); + top->start(); + return myapplication.exec(); +} + +#ifdef kspaceduel_only_for_xgettext +i18n( "Default" ), i18n( "Bullet" ), i18n( "Chaos" ), i18n( "Lack of energy" ) +#endif + diff --git a/kspaceduel/mainview.cpp b/kspaceduel/mainview.cpp new file mode 100644 index 00000000..0d385a49 --- /dev/null +++ b/kspaceduel/mainview.cpp @@ -0,0 +1,1052 @@ +#include "mainview.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ai.h" +#include "options.h" + +KToggleAction *MyMainView::pauseAction = 0; + +MyMainView::MyMainView(QWidget *parent) + :QWidget(parent), + field(DEF_WIDTH,DEF_HEIGHT), + view(&field,this) +{ + int i,p; + setMinimumSize(600,400); + random.setSeed(0); + QPixmap backgr(locate("appdata", MV_BACKGROUND)); + field.setBackgroundPixmap(backgr); + + view.setResizePolicy(QScrollView::AutoOne); + view.setHScrollBarMode(QScrollView::AlwaysOff); + view.setVScrollBarMode(QScrollView::AlwaysOff); + + for(p=0;p<2;p++) + { + for(i=0;ifindResourceDir("appdata", (QString)MV_BACKGROUND); + + QCanvasPixmapArray *sunsequence + = loadOldPixmapSequence( tmp + MV_SUN_PPM, tmp + MV_SUN_PBM ); + sun=new SunSprite(sunsequence, &field); + sun->move(width()/2-1,height()/2-1); + sun->show(); + + explosionsequence = loadOldPixmapSequence( tmp + MV_EXPLOSION_PPM, + tmp + MV_EXPLOSION_PBM, 31 ); + mineexplosionsequence = loadOldPixmapSequence(tmp + MV_MINEEX_PPM, + tmp + MV_MINEEX_PBM, 18 ); + + shipsequence[0] = loadOldPixmapSequence( tmp + MV_SHIP1_PPM, + tmp + MV_SHIP1_PBM, ROTNUM ); + shipsequence[1] = loadOldPixmapSequence( tmp + MV_SHIP2_PPM, + tmp + MV_SHIP2_PBM, ROTNUM); + ship[0]=new ShipSprite(shipsequence[0],&field,0); + ship[1]=new ShipSprite(shipsequence[1],&field,1); + + bulletsequence[0] = loadOldPixmapSequence( tmp + MV_BULLET1_PPM, + tmp + MV_BULLET1_PBM ); + bulletsequence[1] = loadOldPixmapSequence( tmp + MV_BULLET2_PPM, + tmp + MV_BULLET2_PBM ); + minesequence[0] = loadOldPixmapSequence( tmp + MV_MINE1_PPM, + tmp + MV_MINE1_PBM, 2); + minesequence[1] = loadOldPixmapSequence( tmp + MV_MINE2_PPM, + tmp + MV_MINE2_PBM, 2); + powerupsequence[PowerupSprite::PowerupMine] + = loadOldPixmapSequence( tmp + MV_POWERMINE_PPM, tmp + MV_POWERMINE_PBM ); + powerupsequence[PowerupSprite::PowerupBullet] + = loadOldPixmapSequence( tmp + MV_POWERBULLET_PPM, tmp + MV_POWERBULLET_PBM ); + powerupsequence[PowerupSprite::PowerupShield] + = loadOldPixmapSequence( tmp + MV_POWERSHIELD_PPM, tmp + MV_POWERSHIELD_PBM ); + powerupsequence[PowerupSprite::PowerupEnergy] + = loadOldPixmapSequence( tmp + MV_POWERENERGY_PPM, tmp + MV_POWERENERGY_PBM ); + + for(i=0;i<2;i++) + { + // ship[i]->setBoundsAction(QwRealMobileSprite::Wrap); + ship[i]->hide(); + bullets[i]=new QPtrList; + bullets[i]->setAutoDelete(true); + mines[i]=new QPtrList; + mines[i]->setAutoDelete(true); + + } + + explosions.setAutoDelete(true); + powerups.setAutoDelete(true); + + waitForStart=false; + textSprite=0; + readConfig(); +} + +MyMainView::~MyMainView() +{ + killTimers(); + writeConfig(); +} + +void MyMainView::setActionCollection(KActionCollection *a) +{ + actionCollection = a; +} + +void MyMainView::readConfig() +{ + KConfig *cfg = kapp->config(); + int i; + + cfg->setGroup("Game"); + customConfig.gamespeed=cfg->readDoubleNumEntry("gamespeed", + predefinedConfig[0].gamespeed); + + customConfig.gravity= + cfg->readDoubleNumEntry("gravity",predefinedConfig[0].gravity); + customConfig.acc= + cfg->readDoubleNumEntry("acceleration",predefinedConfig[0].acc); + customConfig.bulletDamage= + cfg->readUnsignedNumEntry("bulletDamage",predefinedConfig[0].bulletDamage); + customConfig.bulletLifeTime= + cfg->readDoubleNumEntry("bulletLifeTime",predefinedConfig[0].bulletLifeTime); + customConfig.bulletReloadTime= + cfg->readDoubleNumEntry("bulletReloadTime",predefinedConfig[0].bulletReloadTime); + customConfig.mineDamage= + cfg->readUnsignedNumEntry("mineDamage",predefinedConfig[0].mineDamage); + customConfig.shipDamage= + cfg->readUnsignedNumEntry("shipDamage",predefinedConfig[0].shipDamage); + customConfig.maxBullets= + cfg->readUnsignedNumEntry("maxBullets",predefinedConfig[0].maxBullets); + customConfig.maxMines= + cfg->readUnsignedNumEntry("maxMines",predefinedConfig[0].maxMines); + customConfig.mineReloadTime= + cfg->readDoubleNumEntry("mineReloadTime",predefinedConfig[0].mineReloadTime); + customConfig.rotationSpeed= + cfg->readDoubleNumEntry("rotationSpeed",predefinedConfig[0].rotationSpeed); + customConfig.shotSpeed= + cfg->readDoubleNumEntry("shotSpeed",predefinedConfig[0].shotSpeed); + customConfig.energyNeed= + cfg->readDoubleNumEntry("accEnergyNeed",predefinedConfig[0].energyNeed); + customConfig.rotationEnergyNeed= + cfg->readDoubleNumEntry("rotationEnergyNeed",predefinedConfig[0].rotationEnergyNeed); + customConfig.sunEnergy= + cfg->readDoubleNumEntry("sunEnergy",predefinedConfig[0].sunEnergy); + customConfig.mineActivateTime= + cfg->readDoubleNumEntry("mineActivateTime",predefinedConfig[0].mineActivateTime); + customConfig.mineFuel= + cfg->readDoubleNumEntry("mineFuel",predefinedConfig[0].mineFuel); + customConfig.shotEnergyNeed= + cfg->readDoubleNumEntry("shotEnergyNeed",predefinedConfig[0].shotEnergyNeed); + customConfig.mineEnergyNeed= + cfg->readDoubleNumEntry("mineEnergyNeed",predefinedConfig[0].mineEnergyNeed); + customConfig.startPosX= + cfg->readDoubleNumEntry("startPosX",predefinedConfig[0].startPosX); + customConfig.startPosY= + cfg->readDoubleNumEntry("startPosY",predefinedConfig[0].startPosY); + customConfig.startVelX= + cfg->readDoubleNumEntry("startVelX",predefinedConfig[0].startVelX); + customConfig.startVelY= + cfg->readDoubleNumEntry("startVelY",predefinedConfig[0].startVelY); + customConfig.powerupLifeTime= + cfg->readDoubleNumEntry("powerupLifeTime",predefinedConfig[0].powerupLifeTime); + customConfig.powerupRefreshTime= + cfg->readDoubleNumEntry("powerupRefreshTime",predefinedConfig[0].powerupRefreshTime); + customConfig.powerupShieldAmount= + cfg->readUnsignedNumEntry("powerupShieldAmount", + predefinedConfig[0].powerupShieldAmount); + customConfig.powerupEnergyAmount= + cfg->readDoubleNumEntry("powerupEnergyAmount", + predefinedConfig[0].powerupEnergyAmount); + + if(Options::lastConfig() < predefinedConfigNum) + config=modifyConfig(predefinedConfig[Options::lastConfig()]); + else + config=modifyConfig(customConfig); + + for(i=0;i<2;i++) + ai[i]=new Ai(i,ship,bullets,mines,&config); +} + +void MyMainView::writeConfig() +{ + KConfig *cfg; + cfg=KApplication::kApplication()->config(); + cfg->setGroup("Game"); + + cfg->writeEntry("gravity",customConfig.gravity); + cfg->writeEntry("acceleration",customConfig.acc); + cfg->writeEntry("bulletDamage",customConfig.bulletDamage); + cfg->writeEntry("bulletLifeTime",customConfig.bulletLifeTime); + cfg->writeEntry("bulletReloadTime",customConfig.bulletReloadTime); + cfg->writeEntry("mineDamage",customConfig.mineDamage); + cfg->writeEntry("shipDamage",customConfig.shipDamage); + cfg->writeEntry("maxBullets",customConfig.maxBullets); + cfg->writeEntry("maxMines",customConfig.maxMines); + cfg->writeEntry("rotationSpeed",customConfig.rotationSpeed); + cfg->writeEntry("shotSpeed",customConfig.shotSpeed); + cfg->writeEntry("accEnergyNeed",customConfig.energyNeed); + cfg->writeEntry("rotationEnergyNeed",customConfig.rotationEnergyNeed); + cfg->writeEntry("sunEnergy",customConfig.sunEnergy); + cfg->writeEntry("mineActivateTime",customConfig.mineActivateTime); + cfg->writeEntry("mineReloadTime",customConfig.mineReloadTime); + cfg->writeEntry("mineFuel",customConfig.mineFuel); + cfg->writeEntry("shotEnergyNeed",customConfig.shotEnergyNeed); + cfg->writeEntry("mineEnergyNeed",customConfig.mineEnergyNeed); + + cfg->writeEntry("startPosX",customConfig.startPosX); + cfg->writeEntry("startPosY",customConfig.startPosY); + cfg->writeEntry("startVelX",customConfig.startVelX); + cfg->writeEntry("startVelY",customConfig.startVelY); + + cfg->writeEntry("powerupLifeTime",customConfig.powerupLifeTime); + cfg->writeEntry("powerupRefreshTime",customConfig.powerupRefreshTime); + cfg->writeEntry("powerupShieldAmount",customConfig.powerupShieldAmount); + cfg->writeEntry("powerupEnergyAmount",customConfig.powerupEnergyAmount); +} + +SConfig MyMainView::modifyConfig(SConfig conf) +{ + SConfig newConfig=conf; + newConfig.gamespeed*=Options::refreshTime()/33.0; + newConfig.acc*=newConfig.gamespeed; + newConfig.rotationSpeed*=newConfig.gamespeed*M_PI/ROTNUM*4; + newConfig.energyNeed*=newConfig.gamespeed; + newConfig.rotationEnergyNeed*=newConfig.gamespeed; + newConfig.mineActivateTime*=newConfig.gamespeed; + + return newConfig; +} + +void MyMainView::keyPressEvent(QKeyEvent *ev) +{ + if((gameEnd<=0.0)&&(gameEnd>-2.0)) + { + /* + if(key==options.functionKey[FunctionKeyStart]) + newRound(); + */ + } + else if(waitForStart) + { + /* + if((key==options.functionKey[FunctionKeyStart]) + && (!functionKeyPressed[FunctionKeyStart])) + { + functionKeyPressed[FunctionKeyStart]=true; + resume(); + } + */ + } + else + { + KKey key(ev); + bool accept=true; + + if(actionCollection->action("P1KeyLeft")->shortcut().contains(key)) + playerKeyPressed[0][PlayerKeyLeft]=true; + else if(actionCollection->action("P2KeyLeft")->shortcut().contains(key)) + playerKeyPressed[1][PlayerKeyLeft]=true; + + else if(actionCollection->action("P1KeyRight")->shortcut().contains(key)) + playerKeyPressed[0][PlayerKeyRight]=true; + else if(actionCollection->action("P2KeyRight")->shortcut().contains(key)) + playerKeyPressed[1][PlayerKeyRight]=true; + + else if(actionCollection->action("P1KeyAcc")->shortcut().contains(key)) + playerKeyPressed[0][PlayerKeyAcc]=true; + else if(actionCollection->action("P2KeyAcc")->shortcut().contains(key)) + playerKeyPressed[1][PlayerKeyAcc]=true; + + else if(actionCollection->action("P1Shot")->shortcut().contains(key)) + playerKeyPressed[0][PlayerKeyShot]=true; + else if(actionCollection->action("P2Shot")->shortcut().contains(key)) + playerKeyPressed[1][PlayerKeyShot]=true; + + else if(actionCollection->action("P1Mine")->shortcut().contains(key)) + playerKeyPressed[0][PlayerKeyMine]=true; + else if(actionCollection->action("P2Mine")->shortcut().contains(key)) + playerKeyPressed[1][PlayerKeyMine]=true; + else + accept = false; + /* + if((key==options.functionKey[FunctionKeyStart]) + && (!functionKeyPressed[FunctionKeyStart])) + { + functionKeyPressed[FunctionKeyStart]=true; + pause(); + } + */ + if(!accept) + ev->ignore(); + } +} + +void MyMainView::keyReleaseEvent(QKeyEvent *ev) +{ + KKey key(ev); + bool accept=true; + + if(actionCollection->action("P1KeyLeft")->shortcut().contains(key)) + playerKeyPressed[0][PlayerKeyLeft]=false; + else if(actionCollection->action("P2KeyLeft")->shortcut().contains(key)) + playerKeyPressed[1][PlayerKeyLeft]=false; + + else if(actionCollection->action("P1KeyRight")->shortcut().contains(key)) + playerKeyPressed[0][PlayerKeyRight]=false; + else if(actionCollection->action("P2KeyRight")->shortcut().contains(key)) + playerKeyPressed[1][PlayerKeyRight]=false; + + else if(actionCollection->action("P1KeyAcc")->shortcut().contains(key)) + playerKeyPressed[0][PlayerKeyAcc]=false; + else if(actionCollection->action("P2KeyAcc")->shortcut().contains(key)) + playerKeyPressed[1][PlayerKeyAcc]=false; + + else if(actionCollection->action("P1Shot")->shortcut().contains(key)) + playerKeyPressed[0][PlayerKeyShot]=false; + else if(actionCollection->action("P2Shot")->shortcut().contains(key)) + playerKeyPressed[1][PlayerKeyShot]=false; + + else if(actionCollection->action("P1Mine")->shortcut().contains(key)) + playerKeyPressed[0][PlayerKeyMine]=false; + else if(actionCollection->action("P2Mine")->shortcut().contains(key)) + playerKeyPressed[1][PlayerKeyMine]=false; + else + accept = false; + + if(!accept) + ev->ignore(); +} + +void MyMainView::pause() +{ + if( !waitForStart ) + { + pauseAction->setChecked( true ); + + waitForStart=true; + killTimers(); + emit setStatusText(i18n(" paused "), IDS_PAUSE); + } +} + +void MyMainView::resume() +{ + waitForStart=false; + timerID=startTimer(Options::refreshTime()); + emit(setStatusText("",IDS_PAUSE)); + emit(setStatusText("",IDS_MAIN)); +} + +void MyMainView::start( ) +{ + if( ( gameEnd <= 0.0 ) && ( gameEnd > -2.0 ) ) + { + newRound( ); + } + else if( waitForStart ) + { + waitForStart = false; + timerID=startTimer(Options::refreshTime()); + emit(setStatusText("",IDS_PAUSE)); + emit(setStatusText("",IDS_MAIN)); + pauseAction->setEnabled( true ); + pauseAction->setChecked( false ); + } +} + +void MyMainView::stop() +{ + pauseAction->setEnabled( false ); + pauseAction->setChecked( false ); + + killTimers(); + waitForStart = true; +} + +void MyMainView::togglePause( ) +{ + if( waitForStart ) + resume( ); + else + pause( ); +} + +void MyMainView::resizeEvent(QResizeEvent *event) +{ + double mx,my; + MineSprite *mine; + BulletSprite *bullet; + PowerupSprite *powerup; + int i,current; + + mx=(event->size().width()-event->oldSize().width())/2.0; + my=(event->size().height()-event->oldSize().height())/2.0; + QWidget::resizeEvent(event); + view.resize(width(),height()); + field.resize(width(),height()); + + // printf("%d %d\n",field.width(),field.height()); + sun->move(width()/2-1,height()/2-1); + + for(i=0;i<2;i++) + { + // ship[i]->adoptSpritefieldBounds(); + ship[i]->moveBy(mx,my); + current=mines[i]->at(); + for(mine=mines[i]->first();mine;mine=mines[i]->next()) + { + // mine->adoptSpritefieldBounds(); + mine->moveBy(mx,my); + } + if(current>=0) + mines[i]->at(current); + + current=bullets[i]->at(); + for(bullet=bullets[i]->first();bullet;bullet=bullets[i]->next()) + { + // bullet->adoptSpritefieldBounds(); + bullet->moveBy(mx,my); + } + if(current>=0) + bullets[i]->at(current); + + } + if(textSprite) + textSprite->moveBy((int)mx,(int)my); + current=powerups.at(); + for(powerup=powerups.first();powerup;powerup=powerups.next()) + powerup->moveBy(mx,my); + if(current>=0) + powerups.at(current); +} + +void MyMainView::newRound() +{ + double mx,my; + int i; + + timeToNextPowerup=random.getDouble() * config.powerupRefreshTime; + powerups.clear(); + + killTimers(); + mx=width()/2.0; + my=height()/2.0; + ship[0]->move(mx+config.startPosX,my+config.startPosY); + ship[0]->setRotation(0.0); + ship[0]->setFrame(0); + + ship[1]->move(mx-config.startPosX,my-config.startPosY); + ship[1]->setRotation(M_PI); + ship[1]->setFrame(ROTNUM/2); + + ship[0]->setVelocity(config.startVelX,config.startVelY); + ship[1]->setVelocity(-config.startVelX,-config.startVelY); + for(i=0;i<2;i++) + { + ship[i]->show(); + ship[i]->setEnergy(99.9); + ship[i]->setHitPoints(Options::startHitPoints(i)); + ship[i]->stop(false); + ship[i]->setExplosion(-1); + emit(energy(i,(int)ship[i]->getEnergy())); + emit(hitPoints(i,ship[i]->getHitPoints())); + bulletShot[i]=false; + bullets[i]->clear(); + mines[i]->clear(); + ship[i]->mine(0.0); + ship[i]->bullet(0.0); + ship[i]->setBulletPowerups(0); + ship[i]->setMinePowerups(0); + + ai[i]->newRound(); + } + explosions.clear(); + gameEnd=-10.0; + for(i=0;ihide(); + delete textSprite; + textSprite=0; + } + field.update(); + + QString str = i18n("Press %1 to start") + .arg(KShortcut(GAME_START_SHORTCUT).toString()); + emit(setStatusText(str,IDS_MAIN)); + emit( setStatusText( "", IDS_PAUSE ) ); + stop( ); +} + +void MyMainView::newGame() +{ + int i; + for(i=0;i<2;i++) + { + ship[i]->setWins(0); + emit(wins(i,0)); + } + newRound(); +} + +void MyMainView::timerEvent(QTimerEvent *event) +{ + unsigned w; + int i; + bool stopped = false; + + if(event->timerId()==timerID) + { + killTimers(); + if(gameEnd>0.0) + { + gameEnd-=1.0; + if(gameEnd<=0.0) + { + stopped = true; + if(textSprite) + { + textSprite->hide(); + delete textSprite; + textSprite=0; + } + + textSprite=new QCanvasText(&field); + textSprite->move(width()/2,height()/2-90); + textSprite->setTextFlags(AlignCenter); + textSprite->setColor(qRgb(255,160,0)); + textSprite->setFont(QFont(KGlobalSettings::generalFont().family(),14)); + textSprite->show( ); + if(ship[0]->getHitPoints()==0) + { + if(ship[1]->getHitPoints()==0) + textSprite->setText(i18n("draw round")); + else + { + textSprite->setText(i18n("blue player won the round")); + w=ship[1]->getWins()+1; + ship[1]->setWins(w); + emit(wins(1,w)); + } + } + else + { + textSprite->setText(i18n("red player won the round")); + w=ship[0]->getWins()+1; + ship[0]->setWins(w); + emit(wins(0,w)); + } + QString str = i18n("Press %1 for new round") + .arg(KShortcut(GAME_START_SHORTCUT).toString()); + emit(setStatusText(str,IDS_MAIN)); + stop( ); + } + } + + if( !stopped ) + { + for(i=0;i<2;i++) + if(Options::playerIsAi(i)&&(ship[i]->getHitPoints()>0)) + ai[i]->think(); + + moveMines(); + moveBullets(); + moveExplosions(); + moveShips(); + calculatePowerups(); + collisions(); + timerID=startTimer(Options::refreshTime()); + } + field.update(); + } +} + +void MyMainView::moveShips() +{ + int i,nf,olde; + double nx,ny,en,nr; + BulletSprite *bullet; + MineSprite *mine; + + + for(i=0;i<2;i++) + { + bool playerIsAi = Options::playerIsAi(i); + olde=(int)ship[i]->getEnergy(); + if(ship[i]->getHitPoints()==0) + { + ship[i]->forward(config.gamespeed); + ship[i]->calculateGravityAndEnergy(config.gravity,config.sunEnergy, + config.gamespeed); + } + else + { + ship[i]->calculateGravityAndEnergy(config.gravity,config.sunEnergy, + config.gamespeed); + + + if(!playerIsAi&&playerKeyPressed[i][PlayerKeyRight] + || playerIsAi&&ai[i]->rotateRight()) + ship[i]->rotateRight(config.rotationEnergyNeed, + config.rotationSpeed); + + if(!playerIsAi&&playerKeyPressed[i][PlayerKeyLeft] + || playerIsAi&&ai[i]->rotateLeft()) + ship[i]->rotateLeft(config.rotationEnergyNeed, + config.rotationSpeed); + + en=ship[i]->getEnergy(); + nr=ship[i]->getRotation(); + + + nf=ship[i]->frame(); + nx=cos(nr); + ny=sin(nr); + if((!playerIsAi&&playerKeyPressed[i][PlayerKeyAcc] + || playerIsAi&&ai[i]->accelerate()) + &&(en>config.energyNeed)) + { + en-=config.energyNeed; + ship[i]->setVelocity(ship[i]->xVelocity()+nx*config.acc, + ship[i]->yVelocity()-ny*config.acc); + } + if(en>99.9) + en=99.9; + + ship[i]->forward(config.gamespeed); + //Bullets and Mines + if(!playerIsAi&&playerKeyPressed[i][PlayerKeyShot] + ||playerIsAi&&ai[i]->shootBullet()) + { + if((en>config.shotEnergyNeed) && (!ship[i]->reloadsBullet())) + { + if(bullets[i]->count() < + (config.maxBullets+ship[i]->getBulletPowerups())) + { + ship[i]->bullet(config.bulletReloadTime); + en-=config.shotEnergyNeed; + bullet=new BulletSprite(bulletsequence[i],&field,i, + config.bulletLifeTime); + bullet->move(ship[i]->x()+nx*SHOTDIST, + ship[i]->y()-ny*SHOTDIST); + bullet->setVelocity(ship[i]->xVelocity()+nx*config.shotSpeed, + ship[i]->yVelocity()-ny*config.shotSpeed); + // bullet->setBoundsAction(QwRealMobileSprite::Wrap); + bullet->show(); + + bullets[i]->append(bullet); + } + } + } + if(!Options::playerIsAi(i)&&playerKeyPressed[i][PlayerKeyMine] + || Options::playerIsAi(i)&&ai[i]->layMine()) + { + if((en>config.mineEnergyNeed) && (!ship[i]->reloadsMine())) + { + if(mines[i]->count() < + (config.maxMines+ship[i]->getMinePowerups())) + { + ship[i]->mine(config.mineReloadTime); + en-=config.mineEnergyNeed; + mine=new MineSprite(minesequence[i],&field,i, + config.mineActivateTime,config.mineFuel); + mine->move(ship[i]->x(),ship[i]->y()); + mine->setVelocity(0,0); + //mine->setBoundsAction(QwRealMobileSprite::Wrap); + mine->show(); + mines[i]->append(mine); + } + } + } + ship[i]->setEnergy(en); + if(olde!=(int)en) + emit(energy(i,(int)en)); + } + } +} + +void MyMainView::moveMines() +{ + MineSprite* mine; + int p; + + for(p=0;p<2;p++) + { + mine=mines[p]->first(); + while(mine) + { + mine->calculateGravity(config.gravity,config.gamespeed); + mine->forward(config.gamespeed); + if(mine->over()) + { + mine->hide(); + mines[p]->remove(); + mine=mines[p]->current(); + } + else + mine=mines[p]->next(); + } + } +} + +void MyMainView::moveBullets() +{ + int i; + BulletSprite *sp; + + for(i=0;i<2;i++) + { + sp=bullets[i]->first(); + while(sp) + { + sp->calculateGravity(config.gravity,config.gamespeed); + sp->forward(config.gamespeed); + if(sp->timeOut()) + { + sp->hide(); + bullets[i]->removeRef(sp); + sp=bullets[i]->current(); + } + else + sp=bullets[i]->next(); + } + } +} + +void MyMainView::moveExplosions() +{ + ExplosionSprite *ex; + ex=explosions.first(); + while(ex) + { + ex->forward(config.gamespeed); + if(ex->isOver()) + { + explosions.removeRef(ex); + ex=explosions.current(); + } + else + ex=explosions.next(); + } +} + +void MyMainView::calculatePowerups() +{ + PowerupSprite *sp; + int type,x,y; + + sp=powerups.first(); + while(sp) + { + sp->setLifetime(sp->getLifetime()-config.gamespeed); + if(sp->getLifetime()<0) + { + powerups.removeRef(sp); + sp=powerups.current(); + } + else + sp=powerups.next(); + } + timeToNextPowerup-=config.gamespeed; + if(timeToNextPowerup<0) + { + timeToNextPowerup= random.getDouble() * config.powerupRefreshTime; + type= random.getLong(PowerupSprite::PowerupNum); + sp=new PowerupSprite(powerupsequence[type],&field,type, + config.powerupLifeTime); + do + { + x = random.getLong(width()-40)+20; + y = random.getLong(height()-40)+20; + } + while(((x-width()/2)*(x-width()/2)+(y-height()/2)*(y-height()/2))<(50*50)); + sp->move(x,y); + powerups.append(sp); + sp->show(); + } +} + +void MyMainView::collisions() +{ + int pl,hp,op,oldhp[2],ohp; + QCanvasItemList unexact; + QCanvasItem *sprite; + BulletSprite *bullet; + MineSprite *mine; + ExplosionSprite *expl; + ShipSprite *s; + PowerupSprite *power; + QCanvasItemList hitlist; + double ndx[2],ndy[2]; + double en; + QCanvasItemList::Iterator it; + + for(pl=0;pl<2;pl++) + { + if(!ship[pl]->isStopped()) + { + unexact.clear(); + unexact=ship[pl]->collisions(false); + oldhp[pl]=hp=ship[pl]->getHitPoints(); + hitlist.clear(); + for(it=unexact.begin(); it != unexact.end(); ++it) + { + sprite = (*it); + if((sprite->rtti()!=S_EXPLOSION) + && !((sprite->rtti()!=S_SUN)&&(ship[pl]->getHitPoints()==0))) + if(ship[pl]->collidesWith(sprite)) + if(!hitlist.contains(sprite)) + hitlist.append(sprite); + } + + for(it=hitlist.begin(); it != hitlist.end(); ++it) + { + sprite = (*it); + switch(sprite->rtti()) + { + case S_SUN: + hp=0; + ship[pl]->stop(); + break; + case S_BULLET: + bullet=(BulletSprite *)sprite; + bullet->hide(); + bullets[bullet->getPlayerNumber()]->removeRef(bullet); + hp-=config.bulletDamage; + break; + case S_SHIP: + s=(ShipSprite*)sprite; + ohp=s->getHitPoints(); + if(ohp>0) + { + s->setHitPoints(ohp-hp-config.shipDamage); + emit(hitPoints(s->getPlayerNumber(),s->getHitPoints())); + ndx[0]=((1-EPSILON)*ship[0]->xVelocity()+(1+EPSILON)*ship[1]->xVelocity())/2.0; + ndy[0]=((1-EPSILON)*ship[0]->yVelocity()+(1+EPSILON)*ship[1]->yVelocity())/2.0; + ndx[1]=((1-EPSILON)*ship[1]->xVelocity()+(1+EPSILON)*ship[0]->xVelocity())/2.0; + ndy[1]=((1-EPSILON)*ship[1]->yVelocity()+(1+EPSILON)*ship[0]->yVelocity())/2.0; + ship[0]->setVelocity(ndx[0],ndy[0]); + ship[1]->setVelocity(ndx[1],ndy[1]); + hp-=ohp+config.shipDamage; + } + break; + case S_MINE: + mine=(MineSprite *)sprite; + if(mine->isActive()&& !mine->explodes()) + { + mine->explode(mineexplosionsequence); + ndx[0]=(ship[pl]->xVelocity()+0.3*mine->xVelocity())/1.3; + ndy[0]=(ship[pl]->yVelocity()+0.3*mine->yVelocity())/1.3; + ship[pl]->setVelocity(ndx[0],ndy[0]); + mine->setVelocity(ndx[0],ndy[0]); + hp-=config.mineDamage; + } + break; + case S_POWERUP: + power=(PowerupSprite *)sprite; + switch(power->getType()) + { + case PowerupSprite::PowerupShield: + hp+=config.powerupShieldAmount; + break; + case PowerupSprite::PowerupEnergy: + en=ship[pl]->getEnergy()+config.powerupEnergyAmount; + if(en>99) + en=99; + ship[pl]->setEnergy(en); + break; + case PowerupSprite::PowerupMine: + ship[pl]->setMinePowerups( + ship[pl]->getMinePowerups()+1); + break; + case PowerupSprite::PowerupBullet: + ship[pl]->setBulletPowerups( + ship[pl]->getMinePowerups()+1); + break; + } + power->hide(); + powerups.removeRef(power); + break; + } + } + if(hp>99) + hp=99; + ship[pl]->setHitPoints(hp); + } + + for(mine=mines[pl]->first();mine;mine=mines[pl]->next()) + { + if(!mine->explodes()) + { + unexact.clear(); + unexact=mine->collisions(false); + hitlist.clear(); + for( it=unexact.begin(); it != unexact.end(); ++it ) + { + sprite = (*it); + if(sprite->rtti()==S_BULLET) + if(mine->collidesWith(sprite)) + if(!hitlist.contains(sprite)) + hitlist.append(sprite); + } + if(hitlist.count()>0) + { + mine->explode(mineexplosionsequence); + for(it=hitlist.begin(); it != hitlist.end(); ++it) + { + bullet=(BulletSprite*)(*it); + bullets[bullet->getPlayerNumber()]->removeRef(bullet); + } + } + } + } + } + + hitlist.clear(); + unexact.clear(); + unexact=sun->collisions(false); + for( it = unexact.begin(); it != unexact.end(); ++it) + { + sprite=(*it); + switch(sprite->rtti()) + { + case S_BULLET: + if(sun->collidesWith(sprite)) + if(!hitlist.contains(sprite)) + hitlist.append(sprite); + break; + case S_MINE: + if(!((MobileSprite*)sprite)->isStopped()) + if(sun->collidesWith(sprite)) + if(!hitlist.contains(sprite)) + hitlist.append(sprite); + break; + } + } + + for(it=hitlist.begin(); it != hitlist.end(); ++it) + { + sprite=(*it); + switch(sprite->rtti()) + { + case S_BULLET: + bullet=(BulletSprite *)sprite; + bullet->hide(); + bullets[bullet->getPlayerNumber()]->removeRef(bullet); + break; + case S_MINE: + mine=(MineSprite*)sprite; + mine->stop(); + if(!mine->explodes()) + mine->explode(mineexplosionsequence); + break; + } + } + + + for(pl=0;pl<2;pl++) + { + hp=ship[pl]->getHitPoints(); + if(hp!=oldhp[pl]) + emit(hitPoints(pl,hp)); + if((hp==0)&&(ship[pl]->getExplosion()<0)) + { + op=(pl+1)%2; + ship[pl]->setExplosion((int)(EXPLOSION_TIME/config.gamespeed)); + expl=new ExplosionSprite(explosionsequence,&field,ship[pl]); + expl->show(); + explosions.append(expl); + gameEnd=Options::timeAfterKill()/config.gamespeed; + } + } +} + +void MyMainView::gameSetup() +{ + if(!waitForStart) + pause(); + + if (KConfigDialog::showDialog("settings")) + return; + + SettingsDialog *settings=new SettingsDialog(&customConfig,this,"settings"); + connect(settings, SIGNAL(settingsUpdated()),this,SLOT(closeSettings())); + settings->show(); +} + +void MyMainView::closeSettings(){ + if(Options::lastConfig() pixmaplist; + QPtrList pointlist; + QString dataname, maskname; + QPixmap *pix; + QBitmap *bitmap; + int hotx=0, hoty=0; + QPoint *point; + + for( image=0; image < framecount; image++ ) + { + // #### Why is this a QString?? + dataname.sprintf( datapattern.ascii(), image ); + maskname.sprintf( maskpattern.ascii(), image ); + + QFile file(dataname); + if( file.open( IO_ReadOnly ) ) + { + char line[128]; + file.readLine( line, 128 ); // Skip "P6"/"P3" line + file.readLine( line, 128 ); + + while ( line[0] == '#' ) + { + // Comment line - see if it has additional parameters + if ( 0 == strncmp( line,"# HOTSPOT ", 10 ) ) + sscanf( line+10, "%d %d", &hotx, &hoty); + + file.readLine( line, 128 ); + } + point = new QPoint( hotx, hoty ); + pointlist.append( point ); + } + + pix = new QPixmap( dataname ); + bitmap = new QBitmap( maskname ); + pix->setMask( *bitmap ); + + pixmaplist.append( pix ); + } + + QCanvasPixmapArray* newarray = new QCanvasPixmapArray( pixmaplist, pointlist ); + return newarray; +} + +#include "mainview.moc" diff --git a/kspaceduel/mainview.h b/kspaceduel/mainview.h new file mode 100644 index 00000000..98dd8e15 --- /dev/null +++ b/kspaceduel/mainview.h @@ -0,0 +1,104 @@ +#ifndef __MY_MAIN_VIEW_H +#define __MY_MAIN_VIEW_H + +#include +#include + +class KToggleAction; +class KActionCollection; +#include + +#include "sprites.h" +#include "dialogs.h" +class Ai; + +#ifdef sun +#undef sun +#endif + +class MyMainView:public QWidget +{ + Q_OBJECT +public: + MyMainView(QWidget *parent=0); + ~MyMainView(); + + static KToggleAction *pauseAction; + void setActionCollection(KActionCollection *a); + +public slots: + void newRound(); + void newGame(); + void togglePause( ); + void pause(); + void resume(); + void start(); + void stop(); + void gameSetup(); + void closeSettings(); + void readConfig(); + void writeConfig(); +signals: + void hitPoints(int pn,int hp); + void energy(int pn,int en); + void wins(int pn,int w); + void setStatusText(const QString & str,int id); + +protected: + virtual void resizeEvent(QResizeEvent *event); + virtual void timerEvent(QTimerEvent *event); + virtual void keyPressEvent(QKeyEvent *event); + virtual void keyReleaseEvent(QKeyEvent *event); + SConfig modifyConfig(SConfig conf); + QCanvasPixmapArray* loadOldPixmapSequence(const QString& datapattern, + const QString& maskpattern, int framecount=1); + void moveShips(); + void moveBullets(); + void moveMines(); + void moveExplosions(); + void calculatePowerups(); + void collisions(); +private: + KActionCollection *actionCollection; + + QCanvas field; + QCanvasView view; + + SConfig customConfig,config; + + int timerID; + bool playerKeyPressed[2][PlayerKeyNum]; + bool bulletShot[2]; + bool minePut[2]; + bool waitForStart; + double gameEnd; + double timeToNextPowerup; + +// sprites + QPtrList shipImages; + QPtrList points; + QImage bulletImage; + QCanvasPixmapArray *bulletsequence[2]; + QCanvasPixmapArray *shipsequence[2]; + QCanvasPixmapArray *explosionsequence; + QCanvasPixmapArray *minesequence[2]; + QCanvasPixmapArray *mineexplosionsequence; + QCanvasPixmapArray *powerupsequence[PowerupSprite::PowerupNum]; + + + ShipSprite *ship[2]; + SunSprite *sun; + QCanvasText *textSprite; + QPtrList *bullets[2]; + QPtrList *mines[2]; + QPtrList explosions; + QPtrList powerups; + + KRandomSequence random; + +//Ai + Ai *ai[2]; + +}; + +#endif diff --git a/kspaceduel/mathroutines.cpp b/kspaceduel/mathroutines.cpp new file mode 100644 index 00000000..4597a3ad --- /dev/null +++ b/kspaceduel/mathroutines.cpp @@ -0,0 +1,41 @@ +#include "mathroutines.h" +#include "math.h" + +double rectToAngle(double x,double y) +{ + double phi=0; + if(fabs(x)<1e-6) + { + if(y>0) + phi=M_PI_2; + else + phi=-M_PI_2; + } + else + { + phi=atan(y/x); + if(x<0) + phi+=M_PI; + } + if(phi>M_PI) + phi-=2*M_PI; + return phi; +} + +double average(double phi1,double phi2) +{ + return phi2+difference(phi1,phi2)/2.0; +} + +double difference(double phi1,double phi2) +{ + double dif; + + dif=phi1-phi2; + while(dif>M_PI) + dif-=2*M_PI; + while(dif<-M_PI) + dif+=2*M_PI; + + return dif; +} diff --git a/kspaceduel/mathroutines.h b/kspaceduel/mathroutines.h new file mode 100644 index 00000000..5363496d --- /dev/null +++ b/kspaceduel/mathroutines.h @@ -0,0 +1,8 @@ +#ifndef _KSD_MR_H +#define _KSD_MR_H + +double rectToAngle(double x,double y); +double average(double phi1,double phi2); +double difference(double phi1,double phi2); + +#endif diff --git a/kspaceduel/options.kcfgc b/kspaceduel/options.kcfgc new file mode 100644 index 00000000..d6356cc7 --- /dev/null +++ b/kspaceduel/options.kcfgc @@ -0,0 +1,5 @@ +# Code generation options for kconfig_compiler +File=kspaceduel.kcfg +ClassName=Options +Singleton=true +Mutators=LastConfig diff --git a/kspaceduel/pics/Makefile.am b/kspaceduel/pics/Makefile.am new file mode 100644 index 00000000..cc7f32c7 --- /dev/null +++ b/kspaceduel/pics/Makefile.am @@ -0,0 +1,2 @@ +kspacedueldir = $(kde_datadir)/kspaceduel/icons +kspaceduel_ICON = AUTO \ No newline at end of file diff --git a/kspaceduel/pics/cr16-action-spnewgame.png b/kspaceduel/pics/cr16-action-spnewgame.png new file mode 100644 index 00000000..8eb54762 Binary files /dev/null and b/kspaceduel/pics/cr16-action-spnewgame.png differ diff --git a/kspaceduel/pics/cr16-action-spnewround.png b/kspaceduel/pics/cr16-action-spnewround.png new file mode 100644 index 00000000..a6e89ac9 Binary files /dev/null and b/kspaceduel/pics/cr16-action-spnewround.png differ diff --git a/kspaceduel/pics/cr16-action-sppausegame.png b/kspaceduel/pics/cr16-action-sppausegame.png new file mode 100644 index 00000000..2459e2e0 Binary files /dev/null and b/kspaceduel/pics/cr16-action-sppausegame.png differ diff --git a/kspaceduel/pics/cr22-action-spnewgame.png b/kspaceduel/pics/cr22-action-spnewgame.png new file mode 100644 index 00000000..bfe69fa1 Binary files /dev/null and b/kspaceduel/pics/cr22-action-spnewgame.png differ diff --git a/kspaceduel/pics/cr22-action-spnewround.png b/kspaceduel/pics/cr22-action-spnewround.png new file mode 100644 index 00000000..60122fb6 Binary files /dev/null and b/kspaceduel/pics/cr22-action-spnewround.png differ diff --git a/kspaceduel/pics/cr22-action-sppausegame.png b/kspaceduel/pics/cr22-action-sppausegame.png new file mode 100644 index 00000000..6f3225f1 Binary files /dev/null and b/kspaceduel/pics/cr22-action-sppausegame.png differ diff --git a/kspaceduel/pics/cr32-action-spnewgame.png b/kspaceduel/pics/cr32-action-spnewgame.png new file mode 100644 index 00000000..ad797080 Binary files /dev/null and b/kspaceduel/pics/cr32-action-spnewgame.png differ diff --git a/kspaceduel/pics/cr32-action-spnewround.png b/kspaceduel/pics/cr32-action-spnewround.png new file mode 100644 index 00000000..5488f6d1 Binary files /dev/null and b/kspaceduel/pics/cr32-action-spnewround.png differ diff --git a/kspaceduel/pics/cr32-action-sppausegame.png b/kspaceduel/pics/cr32-action-sppausegame.png new file mode 100644 index 00000000..483ce4d2 Binary files /dev/null and b/kspaceduel/pics/cr32-action-sppausegame.png differ diff --git a/kspaceduel/pics/lo16-action-spnewgame.png b/kspaceduel/pics/lo16-action-spnewgame.png new file mode 100644 index 00000000..8eb54762 Binary files /dev/null and b/kspaceduel/pics/lo16-action-spnewgame.png differ diff --git a/kspaceduel/pics/lo16-action-spnewround.png b/kspaceduel/pics/lo16-action-spnewround.png new file mode 100644 index 00000000..a6e89ac9 Binary files /dev/null and b/kspaceduel/pics/lo16-action-spnewround.png differ diff --git a/kspaceduel/pics/lo16-action-sppausegame.png b/kspaceduel/pics/lo16-action-sppausegame.png new file mode 100644 index 00000000..2459e2e0 Binary files /dev/null and b/kspaceduel/pics/lo16-action-sppausegame.png differ diff --git a/kspaceduel/playerinfo.cpp b/kspaceduel/playerinfo.cpp new file mode 100644 index 00000000..1cd2cd31 --- /dev/null +++ b/kspaceduel/playerinfo.cpp @@ -0,0 +1,80 @@ +#include "playerinfo.h" +#include +#include +#include +#include + + +PlayerInfo::PlayerInfo(int pnr,QWidget *parent,const char *name) + :QFrame(parent,name), + lplayer(this),lenergy(this),lwins(this), + hitpoints(2,this),energy(2,this),wins(2,this) +{ + setFixedWidth(45); + setFrameStyle(Panel|Raised); + QString str; + int i; + + lplayer.setFrameStyle(Panel|Sunken); + lplayer.setMargin(0); + QToolTip::add(&lplayer,i18n("Hit points")); + lenergy.setFrameStyle(Panel|Sunken); + lenergy.setMargin(0); + QToolTip::add(&lenergy,i18n("Energy")); + lwins.setFrameStyle(Panel|Sunken); + lwins.setMargin(0); + QToolTip::add(&lwins,i18n("Wins")); + + lplayer.setGeometry(5,5,35,35); + lplayer.setIndent(0); + lenergy.setGeometry(5,80,35,35); + lenergy.setIndent(0); + lwins.setGeometry(5,155,35,35); + lwins.setIndent(0); + + for(i=0;i<4;i++) + { + str = QString::fromLatin1("sprites/playerinfo/ship%1%2.pnm") + .arg(pnr+1) + .arg(i); + pix[i]=new QPixmap(locate("appdata", str)); + } + + lplayer.setPixmap(*pix[0]); + currentPixmap=0; + lenergy.setPixmap(QPixmap(locate("appdata", "sprites/playerinfo/energy.pnm"))); + lwins.setPixmap(QPixmap(locate("appdata", "sprites/playerinfo/win.pnm"))); + + hitpoints.setGeometry(9,45,26,26); + energy.setGeometry(9,120,26,26); + wins.setGeometry(9,195,26,26); + hitpoints.setFrameStyle(NoFrame); + QToolTip::add(&hitpoints,i18n("Hit points")); + energy.setFrameStyle(NoFrame); + QToolTip::add(&energy,i18n("Energy")); + wins.setFrameStyle(NoFrame); + QToolTip::add(&wins,i18n("Wins")); +} + +void PlayerInfo::setHitpoints(int h) +{ + int p=3-h/25; + hitpoints.display(h); + if(p!=currentPixmap) + { + lplayer.setPixmap(*(pix[p])); + currentPixmap=p; + } +} + +void PlayerInfo::setEnergy(int e) +{ + energy.display(e); +} + +void PlayerInfo::setWins(int w) +{ + wins.display(w); +} + +#include "playerinfo.moc" diff --git a/kspaceduel/playerinfo.h b/kspaceduel/playerinfo.h new file mode 100644 index 00000000..e172c982 --- /dev/null +++ b/kspaceduel/playerinfo.h @@ -0,0 +1,26 @@ +#ifndef __PLAYER_INFO_H +#define __PLAYER_INFO_H + +#include +#include +class QPixmap; +#include +#include + +class PlayerInfo:public QFrame +{ + Q_OBJECT +public: + PlayerInfo(int pnr,QWidget *parent=0,const char *name=0); +public slots: + void setHitpoints(int h); + void setEnergy(int e); + void setWins(int w); +private: + QPixmap* pix[4]; + int currentPixmap; + QLabel lplayer,lenergy,lwins; + QLCDNumber hitpoints,energy,wins; +}; + +#endif diff --git a/kspaceduel/sprites.cpp b/kspaceduel/sprites.cpp new file mode 100644 index 00000000..08eab3c7 --- /dev/null +++ b/kspaceduel/sprites.cpp @@ -0,0 +1,340 @@ +#include "sprites.h" +#include "mathroutines.h" +#include + + +SunSprite::SunSprite(QCanvasPixmapArray *seq, QCanvas* canvas) + :QCanvasSprite(seq, canvas) +{ + // doesn't work with Qt 2.2.2 anymore + // setZ(0); +} + + +PowerupSprite::PowerupSprite(QCanvasPixmapArray* seq, QCanvas* canvas, int t, + double lifetime) + :QCanvasSprite(seq, canvas) +{ + time=lifetime; + type=t; +} + + +MobileSprite::MobileSprite(QCanvasPixmapArray* seq, QCanvas* canvas, int pn) + :QCanvasSprite(seq, canvas) +{ + stopped=false; + playerNumber=pn; +} + +void MobileSprite::forward(double mult, int fr) +{ + if(!stopped) + { + QCanvasSprite::moveBy(xVelocity()*mult,yVelocity()*mult); + checkBounds(); + setFrame(fr); + } + else + setFrame(fr); +} + +void MobileSprite::forward(double mult) +{ + if(!stopped) + { + QCanvasSprite::moveBy(xVelocity()*mult,yVelocity()*mult); + checkBounds(); + } +} + +void MobileSprite::checkBounds() +{ + double cx, cy; + int ch, cw; + + cx = x(); + cy = y(); + ch = canvas()->height(); + cw = canvas()->width(); + + if ( (int)(cx+0.5) < 0 ) + cx = cw - 1 - fmod( -cx, cw ); + else if ( (int)(cx+0.5) > ( cw-1 ) ) + cx = fmod( cx-cw-1, cw ); + if ( (int)(cy+0.5) < 0 ) + cy = ch-1 - fmod( -cy, ch ); + else if ( (int)(cy+0.5) > ( ch-1 ) ) + cy = fmod( cy-ch-1, ch ); + if ( (cx != x()) || (cy != y()) ) + { + // printf("%5.2f %5.2f %5.2f %5.2f\n", x(), y(), cx, cy); + move( cx, cy ); + } +} + +void MobileSprite::calculateGravity(double gravity,double mult) +{ + double abs_2,nx,ny,ex,ey,sq,eg; + + if(!stopped) + { + ex=x()-canvas()->width()/2.0; + ey=y()-canvas()->height()/2.0; + + abs_2=ex*ex+ey*ey; + sq=sqrt(abs_2); + + nx=ex/sq; + ny=ey/sq; + eg=gravity*mult; + setVelocity(xVelocity()-eg*nx/abs_2, + yVelocity()-eg*ny/abs_2); + } +} + +AiSprite MobileSprite::toAiSprite() +{ + // y direction: screen: bottom to top + // mathematical: top to bottom + AiSprite as; + as.x=x()-canvas()->width()/2.0; + as.y=-(y()-canvas()->height()/2.0); + as.dx=xVelocity(); + as.dy=-yVelocity(); + as.sun=false; + as.border=false; + return as; +} + +ShipSprite::ShipSprite(QCanvasPixmapArray* seq, QCanvas* canvas, int pn) + :MobileSprite(seq,canvas,pn) +{ + hitpoints=99; + energy=99.9; + explosion=-1; + setZ(-20); + rotation=0; + bulletPowerups=0; + minePowerups=0; +} + +void ShipSprite::setRotation(double r) +{ + int nf,of=frame(); + rotation=r; + if(rotation<0) + rotation-=((int)(rotation/(2*M_PI))-1)*2*M_PI; + if(rotation>=2*M_PI) + rotation-=(int)(rotation/(2*M_PI))*2*M_PI; + nf=(int)(rotation/(2*M_PI)*ROTNUM)%ROTNUM; + if(nf!=of) + setFrame(nf); +} + +void ShipSprite::forward(double mult) +{ + MobileSprite::forward(mult); + if(explosion>0) + { + explosion--; + if(explosion==0) + hide(); + } + if(reloadBulletTime>0) + reloadBulletTime-=mult; + if(reloadMineTime>0) + reloadMineTime-=mult; +} + +void ShipSprite::forward(double mult,int fr) +{ + MobileSprite::forward(mult,fr); + rotation=fr/ROTNUM*M_PI*2; + if(explosion>0) + { + explosion--; + if(explosion==0) + hide(); + } + if(reloadBulletTime>0) + reloadBulletTime-=mult; + if(reloadMineTime>0) + reloadMineTime-=mult; +} + +void ShipSprite::calculateGravityAndEnergy(double gravity,double sunEnergy, + double mult) +{ + double nx,ny,ex,ey,abs_2,phi,sq,eg; + + if(!stopped) + { + ex=x()-canvas()->width()/2.0; + ey=y()-canvas()->height()/2.0; + + abs_2=ex*ex+ey*ey; + sq=sqrt(abs_2); + + if ( explodes() && (sq<20) ) + stopped = true; + else + { + nx=ex/sq; + ny=ey/sq; + eg=gravity*mult; + setVelocity(xVelocity()-eg*nx/abs_2, + yVelocity()-eg*ny/abs_2); + if(hitpoints!=0) + { + if(energy<99.8) + { + phi=rectToAngle(nx,ny); + energy+=fabs(sunEnergy*mult/(abs_2)*cos(phi+rotation)); + } + } + } + } +} + +void ShipSprite::rotateRight(double rotationEnergyNeed,double rotationSpeed) +{ + if(energy>rotationEnergyNeed) + { + energy-=rotationEnergyNeed; + setRotation(rotation-rotationSpeed); + } +} + +void ShipSprite::rotateLeft(double rotationEnergyNeed,double rotationSpeed) +{ + if(energy>rotationEnergyNeed) + { + energy-=rotationEnergyNeed; + setRotation(rotation+rotationSpeed); + } +} + +BulletSprite::BulletSprite(QCanvasPixmapArray* seq,QCanvas* canvas, int pn,double lifetime) + :MobileSprite(seq,canvas,pn) +{ + setZ(-10); + time=lifetime; +} + +void BulletSprite::forward(double mult) +{ + MobileSprite::forward(mult); + time-=mult; +} + +void BulletSprite::forward(double mult,int fr) +{ + MobileSprite::forward(mult,fr); + time-=mult; +} + +MineSprite::MineSprite(QCanvasPixmapArray* seq, QCanvas* canvas, int pn,double atime,double f) + :MobileSprite(seq,canvas,pn) +{ + activateTime=atime; + setZ(-25); + fuel=f; + explosiontime=0; + timeToGo=0.0; + expl=false; + active=false; +} + +void MineSprite::explode(QCanvasPixmapArray *seq) +{ + setSequence(seq); + timeToGo=seq->count(); + expl=true; + setFrame(0); + explosiontime=0.0; + setZ(-8); + setFuel(0.0); +} + +void MineSprite::forward(double mult) +{ + if(active) + { + if(expl) + { + explosiontime+=mult; + if(explosiontime>(timeToGo-0.001)) + explosiontime=timeToGo-0.01; + setFrame((int)explosiontime); + } + } + else + { + activateTime-=(double)mult; + if(activateTime<0.0) + { + active=true; + setFrame(1); + } + } + if(fuel<0.001) + MobileSprite::forward(mult); +} + +void MineSprite::calculateGravity(double gravity,double mult) +{ + double abs_2,nx,ny,ex,ey,sq,eg; + + if(!stopped) + { + ex=x()-canvas()->width()/2.0; + ey=y()-canvas()->height()/2.0; + + abs_2=ex*ex+ey*ey; + sq=sqrt(abs_2); + nx=ex/sq; + ny=ey/sq; + eg=gravity*mult; + if(fuel<0.001) + setVelocity(xVelocity()-eg*nx/abs_2, + yVelocity()-eg*ny/abs_2); + else + fuel-=eg/abs_2; + } +} + +ExplosionSprite::ExplosionSprite(QCanvasPixmapArray* seq, QCanvas* canvas, MobileSprite *sp) + :QCanvasSprite(seq, canvas) +{ + over=false; + setZ(-5); + obj=sp; + timeToGo=seq->count(); + time=0; + + move(sp->x(),sp->y()); +} + +void ExplosionSprite::forward(double mult) +{ + int of=frame(); + move(obj->x(),obj->y()); + time+=mult; + + if(time>=timeToGo) + { + over=true; + hide(); + } + else + if((int)time!=of) + setFrame((int)time); +} + + +void ExplosionSprite::setSequence(QCanvasPixmapArray *seq) +{ + timeToGo=seq->count(); + QCanvasSprite::setSequence(seq); +} diff --git a/kspaceduel/sprites.h b/kspaceduel/sprites.h new file mode 100644 index 00000000..0d0b1fb3 --- /dev/null +++ b/kspaceduel/sprites.h @@ -0,0 +1,143 @@ +#ifndef __SPRITE_OBJECTS_H +#define __SPRITE_OBJECTS_H + +#include +#include "defines.h" + +#ifdef sun +#undef sun +#endif + +struct AiSprite +{ + double x,y,dx,dy; + bool sun, border; +}; + +class SunSprite:public QCanvasSprite +{ +public: + SunSprite(QCanvasPixmapArray* seq, QCanvas* canvas); + virtual int rtti() const {return S_SUN;} +}; + + +class PowerupSprite:public QCanvasSprite +{ +public: + enum {PowerupMine=0, PowerupBullet, PowerupShield, PowerupEnergy, + PowerupNum}; + PowerupSprite(QCanvasPixmapArray* seq, QCanvas* canvas, int t, double lifetime); + virtual int rtti() const {return S_POWERUP;} + + double getLifetime() {return time;} + void setLifetime(double t) {time=t;} + int getType() {return type;} +private: + double time; + int type; +}; + +class MobileSprite:public QCanvasSprite +{ +public: + MobileSprite(QCanvasPixmapArray* array, QCanvas* canvas, int pn); + + virtual void forward(double mult,int frame); + virtual void forward(double mult); + virtual void calculateGravity(double gravity,double mult); + int spriteFieldWidth(){return canvas()->width();} + int spriteFieldHeight(){return canvas()->height();} + AiSprite toAiSprite(); + + bool isStopped() {return stopped;} + void stop(bool s=true) {stopped=s;} + int getPlayerNumber() {return playerNumber;} +protected: + void checkBounds(); + bool stopped; + int playerNumber; +}; + + +class ShipSprite:public MobileSprite +{ +public: + ShipSprite(QCanvasPixmapArray* seq, QCanvas* canvas, int pn); + virtual int rtti() const {return S_SHIP;} + int getHitPoints() {return hitpoints;} + void setHitPoints(int hp) {hitpoints=(hp<0?0:hp);} + double getEnergy() {return energy;} + void setEnergy(double e) {energy=(e<0?0:e);} + void setWins(int w) {wins=w;} + int getWins() {return wins;} + void setExplosion(int f) {explosion=f;} + int getExplosion() {return explosion;} + void setRotation(double r); + double getRotation() {return rotation;} + void rotateRight(double rotationEnergyNeed,double rotationSpeed); + void rotateLeft(double rotationEnergyNeed,double rotationSpeed); + void bullet(double reloadTime) {reloadBulletTime=reloadTime;} + bool reloadsBullet(double t=0.0) {return reloadBulletTime>t;} + void mine(double reloadTime) {reloadMineTime=reloadTime;} + bool reloadsMine(double t=0.0) {return reloadMineTime>t;} + bool explodes() {return explosion>=0;} + void setMinePowerups(int m) {minePowerups=m;} + int getMinePowerups() {return minePowerups;} + void setBulletPowerups(int b) {bulletPowerups=b;} + int getBulletPowerups() {return bulletPowerups;} + virtual void forward(double mult); + virtual void forward(double mult,int fr); + virtual void calculateGravityAndEnergy(double gravity,double sunEnergy, + double mult); +private: + int hitpoints, wins, explosion; + double energy,rotation,reloadBulletTime,reloadMineTime; + int bulletPowerups,minePowerups; +}; + +class BulletSprite:public MobileSprite +{ +public: + BulletSprite(QCanvasPixmapArray* seq, QCanvas* canvas, int pn,double lifetime); + virtual int rtti() const {return S_BULLET;} + virtual void forward(double mult); + virtual void forward(double mult,int fr); + bool timeOut() {return time<=0;} +private: + double time; +}; + +class MineSprite:public MobileSprite +{ +public: + MineSprite(QCanvasPixmapArray* seq, QCanvas* canvas, int pn,double atime,double f); + virtual int rtti() const {return S_MINE;} + bool isActive() {return active;} + double getFuel() {return fuel;} + void setFuel(double f) {fuel=(f<0.0?0.0:f);} + virtual void forward(double mult); + void explode(QCanvasPixmapArray* s); + bool explodes() {return expl;} + bool over() {return (expl&&(explosiontime>(timeToGo-0.1)));} + virtual void calculateGravity(double gravity,double mult); +private: + bool expl,active; + double activateTime,fuel,timeToGo,explosiontime; +}; + +class ExplosionSprite:public QCanvasSprite +{ +public: + ExplosionSprite(QCanvasPixmapArray *seq, QCanvas* field, MobileSprite *sp); + virtual int rtti() const {return S_EXPLOSION;} + bool isOver() {return over;} + virtual void forward(double mult); + void setSequence(QCanvasPixmapArray *seq); +private: + double timeToGo,time; + bool over; + MobileSprite *obj; +}; + +#endif diff --git a/kspaceduel/sprites/Makefile.am b/kspaceduel/sprites/Makefile.am new file mode 100644 index 00000000..58bce015 --- /dev/null +++ b/kspaceduel/sprites/Makefile.am @@ -0,0 +1,6 @@ +SUBDIRS = ship1 ship2 playerinfo explosion sun powerups + +bgdir = $(kde_datadir)/kspaceduel/sprites +bg_DATA = backgr.png + +EXTRA_DIST = $(bg_DATA) diff --git a/kspaceduel/sprites/backgr.png b/kspaceduel/sprites/backgr.png new file mode 100644 index 00000000..3b30f396 Binary files /dev/null and b/kspaceduel/sprites/backgr.png differ diff --git a/kspaceduel/sprites/explosion/Makefile.am b/kspaceduel/sprites/explosion/Makefile.am new file mode 100644 index 00000000..680ffbb1 --- /dev/null +++ b/kspaceduel/sprites/explosion/Makefile.am @@ -0,0 +1,26 @@ + +expldir = $(kde_datadir)/kspaceduel/sprites/explosion + +expl_DATA = \ +explos00.pbm explos00.ppm explos01.pbm explos01.ppm explos02.pbm \ +explos02.ppm explos03.pbm explos03.ppm explos04.pbm explos04.ppm \ +explos05.pbm explos05.ppm explos06.pbm explos06.ppm explos07.pbm \ +explos07.ppm explos08.pbm explos08.ppm explos09.pbm explos09.ppm \ +explos10.pbm explos10.ppm explos11.pbm explos11.ppm explos12.pbm \ +explos12.ppm explos13.pbm explos13.ppm explos14.pbm explos14.ppm \ +explos15.pbm explos15.ppm explos16.pbm explos16.ppm explos17.pbm \ +explos17.ppm explos18.pbm explos18.ppm explos19.pbm explos19.ppm \ +explos20.pbm explos20.ppm explos21.pbm explos21.ppm explos22.pbm \ +explos22.ppm explos23.pbm explos23.ppm explos24.pbm explos24.ppm \ +explos25.pbm explos25.ppm explos26.pbm explos26.ppm explos27.pbm \ +explos27.ppm explos28.pbm explos28.ppm explos29.pbm explos29.ppm \ +explos30.pbm explos30.ppm mineex00.pbm mineex00.ppm mineex01.pbm \ +mineex01.ppm mineex02.pbm mineex02.ppm mineex03.pbm mineex03.ppm \ +mineex04.pbm mineex04.ppm mineex05.pbm mineex05.ppm mineex06.pbm \ +mineex06.ppm mineex07.pbm mineex07.ppm mineex08.pbm mineex08.ppm \ +mineex09.pbm mineex09.ppm mineex10.pbm mineex10.ppm mineex11.pbm \ +mineex11.ppm mineex12.pbm mineex12.ppm mineex13.pbm mineex13.ppm \ +mineex14.pbm mineex14.ppm mineex15.pbm mineex15.ppm mineex16.pbm \ +mineex16.ppm mineex17.pbm mineex17.ppm + +EXTRA_DIST = $(expl_DATA) diff --git a/kspaceduel/sprites/explosion/explos00.pbm b/kspaceduel/sprites/explosion/explos00.pbm new file mode 100644 index 00000000..9b57de08 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos00.pbm differ diff --git a/kspaceduel/sprites/explosion/explos00.ppm b/kspaceduel/sprites/explosion/explos00.ppm new file mode 100644 index 00000000..1a9d7f3e Binary files /dev/null and b/kspaceduel/sprites/explosion/explos00.ppm differ diff --git a/kspaceduel/sprites/explosion/explos01.pbm b/kspaceduel/sprites/explosion/explos01.pbm new file mode 100644 index 00000000..30c4f701 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos01.pbm differ diff --git a/kspaceduel/sprites/explosion/explos01.ppm b/kspaceduel/sprites/explosion/explos01.ppm new file mode 100644 index 00000000..5c5d90e9 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos01.ppm differ diff --git a/kspaceduel/sprites/explosion/explos02.pbm b/kspaceduel/sprites/explosion/explos02.pbm new file mode 100644 index 00000000..8624bbec Binary files /dev/null and b/kspaceduel/sprites/explosion/explos02.pbm differ diff --git a/kspaceduel/sprites/explosion/explos02.ppm b/kspaceduel/sprites/explosion/explos02.ppm new file mode 100644 index 00000000..bfd07769 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos02.ppm differ diff --git a/kspaceduel/sprites/explosion/explos03.pbm b/kspaceduel/sprites/explosion/explos03.pbm new file mode 100644 index 00000000..e9037df3 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos03.pbm differ diff --git a/kspaceduel/sprites/explosion/explos03.ppm b/kspaceduel/sprites/explosion/explos03.ppm new file mode 100644 index 00000000..ec9c071d Binary files /dev/null and b/kspaceduel/sprites/explosion/explos03.ppm differ diff --git a/kspaceduel/sprites/explosion/explos04.pbm b/kspaceduel/sprites/explosion/explos04.pbm new file mode 100644 index 00000000..906679f0 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos04.pbm differ diff --git a/kspaceduel/sprites/explosion/explos04.ppm b/kspaceduel/sprites/explosion/explos04.ppm new file mode 100644 index 00000000..f3b098bc Binary files /dev/null and b/kspaceduel/sprites/explosion/explos04.ppm differ diff --git a/kspaceduel/sprites/explosion/explos05.pbm b/kspaceduel/sprites/explosion/explos05.pbm new file mode 100644 index 00000000..086068b8 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos05.pbm differ diff --git a/kspaceduel/sprites/explosion/explos05.ppm b/kspaceduel/sprites/explosion/explos05.ppm new file mode 100644 index 00000000..8154989f Binary files /dev/null and b/kspaceduel/sprites/explosion/explos05.ppm differ diff --git a/kspaceduel/sprites/explosion/explos06.pbm b/kspaceduel/sprites/explosion/explos06.pbm new file mode 100644 index 00000000..ff19e016 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos06.pbm differ diff --git a/kspaceduel/sprites/explosion/explos06.ppm b/kspaceduel/sprites/explosion/explos06.ppm new file mode 100644 index 00000000..d86e535c Binary files /dev/null and b/kspaceduel/sprites/explosion/explos06.ppm differ diff --git a/kspaceduel/sprites/explosion/explos07.pbm b/kspaceduel/sprites/explosion/explos07.pbm new file mode 100644 index 00000000..cefd17af Binary files /dev/null and b/kspaceduel/sprites/explosion/explos07.pbm differ diff --git a/kspaceduel/sprites/explosion/explos07.ppm b/kspaceduel/sprites/explosion/explos07.ppm new file mode 100644 index 00000000..0f515a5f Binary files /dev/null and b/kspaceduel/sprites/explosion/explos07.ppm differ diff --git a/kspaceduel/sprites/explosion/explos08.pbm b/kspaceduel/sprites/explosion/explos08.pbm new file mode 100644 index 00000000..38fd3aa8 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos08.pbm differ diff --git a/kspaceduel/sprites/explosion/explos08.ppm b/kspaceduel/sprites/explosion/explos08.ppm new file mode 100644 index 00000000..3eaa4a62 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos08.ppm differ diff --git a/kspaceduel/sprites/explosion/explos09.pbm b/kspaceduel/sprites/explosion/explos09.pbm new file mode 100644 index 00000000..bc015285 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos09.pbm differ diff --git a/kspaceduel/sprites/explosion/explos09.ppm b/kspaceduel/sprites/explosion/explos09.ppm new file mode 100644 index 00000000..69f574b3 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos09.ppm differ diff --git a/kspaceduel/sprites/explosion/explos10.pbm b/kspaceduel/sprites/explosion/explos10.pbm new file mode 100644 index 00000000..e031712c Binary files /dev/null and b/kspaceduel/sprites/explosion/explos10.pbm differ diff --git a/kspaceduel/sprites/explosion/explos10.ppm b/kspaceduel/sprites/explosion/explos10.ppm new file mode 100644 index 00000000..65eaff0d Binary files /dev/null and b/kspaceduel/sprites/explosion/explos10.ppm differ diff --git a/kspaceduel/sprites/explosion/explos11.pbm b/kspaceduel/sprites/explosion/explos11.pbm new file mode 100644 index 00000000..e8021289 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos11.pbm differ diff --git a/kspaceduel/sprites/explosion/explos11.ppm b/kspaceduel/sprites/explosion/explos11.ppm new file mode 100644 index 00000000..d2dc949e Binary files /dev/null and b/kspaceduel/sprites/explosion/explos11.ppm differ diff --git a/kspaceduel/sprites/explosion/explos12.pbm b/kspaceduel/sprites/explosion/explos12.pbm new file mode 100644 index 00000000..0e073796 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos12.pbm differ diff --git a/kspaceduel/sprites/explosion/explos12.ppm b/kspaceduel/sprites/explosion/explos12.ppm new file mode 100644 index 00000000..acaa96e1 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos12.ppm differ diff --git a/kspaceduel/sprites/explosion/explos13.pbm b/kspaceduel/sprites/explosion/explos13.pbm new file mode 100644 index 00000000..05179167 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos13.pbm differ diff --git a/kspaceduel/sprites/explosion/explos13.ppm b/kspaceduel/sprites/explosion/explos13.ppm new file mode 100644 index 00000000..43c4e5de Binary files /dev/null and b/kspaceduel/sprites/explosion/explos13.ppm differ diff --git a/kspaceduel/sprites/explosion/explos14.pbm b/kspaceduel/sprites/explosion/explos14.pbm new file mode 100644 index 00000000..d09e77ac Binary files /dev/null and b/kspaceduel/sprites/explosion/explos14.pbm differ diff --git a/kspaceduel/sprites/explosion/explos14.ppm b/kspaceduel/sprites/explosion/explos14.ppm new file mode 100644 index 00000000..14ed717a Binary files /dev/null and b/kspaceduel/sprites/explosion/explos14.ppm differ diff --git a/kspaceduel/sprites/explosion/explos15.pbm b/kspaceduel/sprites/explosion/explos15.pbm new file mode 100644 index 00000000..6ef019bb Binary files /dev/null and b/kspaceduel/sprites/explosion/explos15.pbm differ diff --git a/kspaceduel/sprites/explosion/explos15.ppm b/kspaceduel/sprites/explosion/explos15.ppm new file mode 100644 index 00000000..3ae97336 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos15.ppm differ diff --git a/kspaceduel/sprites/explosion/explos16.pbm b/kspaceduel/sprites/explosion/explos16.pbm new file mode 100644 index 00000000..8a6f41b7 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos16.pbm differ diff --git a/kspaceduel/sprites/explosion/explos16.ppm b/kspaceduel/sprites/explosion/explos16.ppm new file mode 100644 index 00000000..9a62a486 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos16.ppm differ diff --git a/kspaceduel/sprites/explosion/explos17.pbm b/kspaceduel/sprites/explosion/explos17.pbm new file mode 100644 index 00000000..ff8f8373 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos17.pbm differ diff --git a/kspaceduel/sprites/explosion/explos17.ppm b/kspaceduel/sprites/explosion/explos17.ppm new file mode 100644 index 00000000..5fb061fa Binary files /dev/null and b/kspaceduel/sprites/explosion/explos17.ppm differ diff --git a/kspaceduel/sprites/explosion/explos18.pbm b/kspaceduel/sprites/explosion/explos18.pbm new file mode 100644 index 00000000..863eb7d0 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos18.pbm differ diff --git a/kspaceduel/sprites/explosion/explos18.ppm b/kspaceduel/sprites/explosion/explos18.ppm new file mode 100644 index 00000000..0d892a7a Binary files /dev/null and b/kspaceduel/sprites/explosion/explos18.ppm differ diff --git a/kspaceduel/sprites/explosion/explos19.pbm b/kspaceduel/sprites/explosion/explos19.pbm new file mode 100644 index 00000000..e9873fcf Binary files /dev/null and b/kspaceduel/sprites/explosion/explos19.pbm differ diff --git a/kspaceduel/sprites/explosion/explos19.ppm b/kspaceduel/sprites/explosion/explos19.ppm new file mode 100644 index 00000000..a2a24916 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos19.ppm differ diff --git a/kspaceduel/sprites/explosion/explos20.pbm b/kspaceduel/sprites/explosion/explos20.pbm new file mode 100644 index 00000000..23dbee36 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos20.pbm differ diff --git a/kspaceduel/sprites/explosion/explos20.ppm b/kspaceduel/sprites/explosion/explos20.ppm new file mode 100644 index 00000000..0de63929 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos20.ppm differ diff --git a/kspaceduel/sprites/explosion/explos21.pbm b/kspaceduel/sprites/explosion/explos21.pbm new file mode 100644 index 00000000..df4d3d39 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos21.pbm differ diff --git a/kspaceduel/sprites/explosion/explos21.ppm b/kspaceduel/sprites/explosion/explos21.ppm new file mode 100644 index 00000000..c1d74f3a Binary files /dev/null and b/kspaceduel/sprites/explosion/explos21.ppm differ diff --git a/kspaceduel/sprites/explosion/explos22.pbm b/kspaceduel/sprites/explosion/explos22.pbm new file mode 100644 index 00000000..d2c04460 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos22.pbm differ diff --git a/kspaceduel/sprites/explosion/explos22.ppm b/kspaceduel/sprites/explosion/explos22.ppm new file mode 100644 index 00000000..bffedc3d Binary files /dev/null and b/kspaceduel/sprites/explosion/explos22.ppm differ diff --git a/kspaceduel/sprites/explosion/explos23.pbm b/kspaceduel/sprites/explosion/explos23.pbm new file mode 100644 index 00000000..10942982 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos23.pbm differ diff --git a/kspaceduel/sprites/explosion/explos23.ppm b/kspaceduel/sprites/explosion/explos23.ppm new file mode 100644 index 00000000..f26992f1 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos23.ppm differ diff --git a/kspaceduel/sprites/explosion/explos24.pbm b/kspaceduel/sprites/explosion/explos24.pbm new file mode 100644 index 00000000..665e10b9 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos24.pbm differ diff --git a/kspaceduel/sprites/explosion/explos24.ppm b/kspaceduel/sprites/explosion/explos24.ppm new file mode 100644 index 00000000..17f0203e Binary files /dev/null and b/kspaceduel/sprites/explosion/explos24.ppm differ diff --git a/kspaceduel/sprites/explosion/explos25.pbm b/kspaceduel/sprites/explosion/explos25.pbm new file mode 100644 index 00000000..30fda10b Binary files /dev/null and b/kspaceduel/sprites/explosion/explos25.pbm differ diff --git a/kspaceduel/sprites/explosion/explos25.ppm b/kspaceduel/sprites/explosion/explos25.ppm new file mode 100644 index 00000000..877a9c28 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos25.ppm differ diff --git a/kspaceduel/sprites/explosion/explos26.pbm b/kspaceduel/sprites/explosion/explos26.pbm new file mode 100644 index 00000000..351a035a Binary files /dev/null and b/kspaceduel/sprites/explosion/explos26.pbm differ diff --git a/kspaceduel/sprites/explosion/explos26.ppm b/kspaceduel/sprites/explosion/explos26.ppm new file mode 100644 index 00000000..ee488917 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos26.ppm differ diff --git a/kspaceduel/sprites/explosion/explos27.pbm b/kspaceduel/sprites/explosion/explos27.pbm new file mode 100644 index 00000000..d98ea215 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos27.pbm differ diff --git a/kspaceduel/sprites/explosion/explos27.ppm b/kspaceduel/sprites/explosion/explos27.ppm new file mode 100644 index 00000000..96e26dd8 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos27.ppm differ diff --git a/kspaceduel/sprites/explosion/explos28.pbm b/kspaceduel/sprites/explosion/explos28.pbm new file mode 100644 index 00000000..dc872400 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos28.pbm differ diff --git a/kspaceduel/sprites/explosion/explos28.ppm b/kspaceduel/sprites/explosion/explos28.ppm new file mode 100644 index 00000000..692ff55d Binary files /dev/null and b/kspaceduel/sprites/explosion/explos28.ppm differ diff --git a/kspaceduel/sprites/explosion/explos29.pbm b/kspaceduel/sprites/explosion/explos29.pbm new file mode 100644 index 00000000..e67a072f Binary files /dev/null and b/kspaceduel/sprites/explosion/explos29.pbm differ diff --git a/kspaceduel/sprites/explosion/explos29.ppm b/kspaceduel/sprites/explosion/explos29.ppm new file mode 100644 index 00000000..5ba8adfe Binary files /dev/null and b/kspaceduel/sprites/explosion/explos29.ppm differ diff --git a/kspaceduel/sprites/explosion/explos30.pbm b/kspaceduel/sprites/explosion/explos30.pbm new file mode 100644 index 00000000..63cce7e5 Binary files /dev/null and b/kspaceduel/sprites/explosion/explos30.pbm differ diff --git a/kspaceduel/sprites/explosion/explos30.ppm b/kspaceduel/sprites/explosion/explos30.ppm new file mode 100644 index 00000000..44c38d5d Binary files /dev/null and b/kspaceduel/sprites/explosion/explos30.ppm differ diff --git a/kspaceduel/sprites/explosion/mineex00.pbm b/kspaceduel/sprites/explosion/mineex00.pbm new file mode 100644 index 00000000..0924b0cb --- /dev/null +++ b/kspaceduel/sprites/explosion/mineex00.pbm @@ -0,0 +1,13 @@ +P1 +8 11 +00001000 +00111110 +01111110 +11111111 +11111111 +11111111 +11111111 +11111111 +11111110 +01111110 +00111100 diff --git a/kspaceduel/sprites/explosion/mineex00.ppm b/kspaceduel/sprites/explosion/mineex00.ppm new file mode 100644 index 00000000..80f7c3aa Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex00.ppm differ diff --git a/kspaceduel/sprites/explosion/mineex01.pbm b/kspaceduel/sprites/explosion/mineex01.pbm new file mode 100644 index 00000000..d2e5487b Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex01.pbm differ diff --git a/kspaceduel/sprites/explosion/mineex01.ppm b/kspaceduel/sprites/explosion/mineex01.ppm new file mode 100644 index 00000000..514a1d77 Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex01.ppm differ diff --git a/kspaceduel/sprites/explosion/mineex02.pbm b/kspaceduel/sprites/explosion/mineex02.pbm new file mode 100644 index 00000000..d9b584f6 Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex02.pbm differ diff --git a/kspaceduel/sprites/explosion/mineex02.ppm b/kspaceduel/sprites/explosion/mineex02.ppm new file mode 100644 index 00000000..a4ddccd4 Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex02.ppm differ diff --git a/kspaceduel/sprites/explosion/mineex03.pbm b/kspaceduel/sprites/explosion/mineex03.pbm new file mode 100644 index 00000000..fce5deee Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex03.pbm differ diff --git a/kspaceduel/sprites/explosion/mineex03.ppm b/kspaceduel/sprites/explosion/mineex03.ppm new file mode 100644 index 00000000..baba3afc Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex03.ppm differ diff --git a/kspaceduel/sprites/explosion/mineex04.pbm b/kspaceduel/sprites/explosion/mineex04.pbm new file mode 100644 index 00000000..7ed25873 Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex04.pbm differ diff --git a/kspaceduel/sprites/explosion/mineex04.ppm b/kspaceduel/sprites/explosion/mineex04.ppm new file mode 100644 index 00000000..752ab4c8 Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex04.ppm differ diff --git a/kspaceduel/sprites/explosion/mineex05.pbm b/kspaceduel/sprites/explosion/mineex05.pbm new file mode 100644 index 00000000..e16f95cc Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex05.pbm differ diff --git a/kspaceduel/sprites/explosion/mineex05.ppm b/kspaceduel/sprites/explosion/mineex05.ppm new file mode 100644 index 00000000..c36b15cd Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex05.ppm differ diff --git a/kspaceduel/sprites/explosion/mineex06.pbm b/kspaceduel/sprites/explosion/mineex06.pbm new file mode 100644 index 00000000..54fbd102 Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex06.pbm differ diff --git a/kspaceduel/sprites/explosion/mineex06.ppm b/kspaceduel/sprites/explosion/mineex06.ppm new file mode 100644 index 00000000..abaaca8c Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex06.ppm differ diff --git a/kspaceduel/sprites/explosion/mineex07.pbm b/kspaceduel/sprites/explosion/mineex07.pbm new file mode 100644 index 00000000..cc62a4a8 Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex07.pbm differ diff --git a/kspaceduel/sprites/explosion/mineex07.ppm b/kspaceduel/sprites/explosion/mineex07.ppm new file mode 100644 index 00000000..564ecaea Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex07.ppm differ diff --git a/kspaceduel/sprites/explosion/mineex08.pbm b/kspaceduel/sprites/explosion/mineex08.pbm new file mode 100644 index 00000000..b3db5320 Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex08.pbm differ diff --git a/kspaceduel/sprites/explosion/mineex08.ppm b/kspaceduel/sprites/explosion/mineex08.ppm new file mode 100644 index 00000000..99f7bfe7 Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex08.ppm differ diff --git a/kspaceduel/sprites/explosion/mineex09.pbm b/kspaceduel/sprites/explosion/mineex09.pbm new file mode 100644 index 00000000..9d2d32f8 Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex09.pbm differ diff --git a/kspaceduel/sprites/explosion/mineex09.ppm b/kspaceduel/sprites/explosion/mineex09.ppm new file mode 100644 index 00000000..b23aec3a Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex09.ppm differ diff --git a/kspaceduel/sprites/explosion/mineex10.pbm b/kspaceduel/sprites/explosion/mineex10.pbm new file mode 100644 index 00000000..101e5845 Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex10.pbm differ diff --git a/kspaceduel/sprites/explosion/mineex10.ppm b/kspaceduel/sprites/explosion/mineex10.ppm new file mode 100644 index 00000000..e2f23352 Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex10.ppm differ diff --git a/kspaceduel/sprites/explosion/mineex11.pbm b/kspaceduel/sprites/explosion/mineex11.pbm new file mode 100644 index 00000000..40818303 Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex11.pbm differ diff --git a/kspaceduel/sprites/explosion/mineex11.ppm b/kspaceduel/sprites/explosion/mineex11.ppm new file mode 100644 index 00000000..ed38957c Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex11.ppm differ diff --git a/kspaceduel/sprites/explosion/mineex12.pbm b/kspaceduel/sprites/explosion/mineex12.pbm new file mode 100644 index 00000000..fc7efa9f Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex12.pbm differ diff --git a/kspaceduel/sprites/explosion/mineex12.ppm b/kspaceduel/sprites/explosion/mineex12.ppm new file mode 100644 index 00000000..1d2b4394 Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex12.ppm differ diff --git a/kspaceduel/sprites/explosion/mineex13.pbm b/kspaceduel/sprites/explosion/mineex13.pbm new file mode 100644 index 00000000..286f2d94 Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex13.pbm differ diff --git a/kspaceduel/sprites/explosion/mineex13.ppm b/kspaceduel/sprites/explosion/mineex13.ppm new file mode 100644 index 00000000..fc2b928e Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex13.ppm differ diff --git a/kspaceduel/sprites/explosion/mineex14.pbm b/kspaceduel/sprites/explosion/mineex14.pbm new file mode 100644 index 00000000..80f1d799 Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex14.pbm differ diff --git a/kspaceduel/sprites/explosion/mineex14.ppm b/kspaceduel/sprites/explosion/mineex14.ppm new file mode 100644 index 00000000..e674c572 Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex14.ppm differ diff --git a/kspaceduel/sprites/explosion/mineex15.pbm b/kspaceduel/sprites/explosion/mineex15.pbm new file mode 100644 index 00000000..c333dc4c Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex15.pbm differ diff --git a/kspaceduel/sprites/explosion/mineex15.ppm b/kspaceduel/sprites/explosion/mineex15.ppm new file mode 100644 index 00000000..c0de08db Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex15.ppm differ diff --git a/kspaceduel/sprites/explosion/mineex16.pbm b/kspaceduel/sprites/explosion/mineex16.pbm new file mode 100644 index 00000000..c333dc4c Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex16.pbm differ diff --git a/kspaceduel/sprites/explosion/mineex16.ppm b/kspaceduel/sprites/explosion/mineex16.ppm new file mode 100644 index 00000000..c0de08db Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex16.ppm differ diff --git a/kspaceduel/sprites/explosion/mineex17.pbm b/kspaceduel/sprites/explosion/mineex17.pbm new file mode 100644 index 00000000..cce4495a Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex17.pbm differ diff --git a/kspaceduel/sprites/explosion/mineex17.ppm b/kspaceduel/sprites/explosion/mineex17.ppm new file mode 100644 index 00000000..7a9e0cc2 Binary files /dev/null and b/kspaceduel/sprites/explosion/mineex17.ppm differ diff --git a/kspaceduel/sprites/playerinfo/Makefile.am b/kspaceduel/sprites/playerinfo/Makefile.am new file mode 100644 index 00000000..0e3dc6b8 --- /dev/null +++ b/kspaceduel/sprites/playerinfo/Makefile.am @@ -0,0 +1,6 @@ +playerdir = $(kde_datadir)/kspaceduel/sprites/playerinfo +player_DATA = \ +energy.pnm mine.pnm ship10.pnm ship11.pnm ship12.pnm ship13.pnm ship20.pnm \ +ship21.pnm ship22.pnm ship23.pnm win.pnm + +EXTRA_DIST = $(player_DATA) diff --git a/kspaceduel/sprites/playerinfo/energy.pnm b/kspaceduel/sprites/playerinfo/energy.pnm new file mode 100644 index 00000000..7468f3bd Binary files /dev/null and b/kspaceduel/sprites/playerinfo/energy.pnm differ diff --git a/kspaceduel/sprites/playerinfo/mine.pnm b/kspaceduel/sprites/playerinfo/mine.pnm new file mode 100644 index 00000000..44ff28ef Binary files /dev/null and b/kspaceduel/sprites/playerinfo/mine.pnm differ diff --git a/kspaceduel/sprites/playerinfo/ship10.pnm b/kspaceduel/sprites/playerinfo/ship10.pnm new file mode 100644 index 00000000..9463990e Binary files /dev/null and b/kspaceduel/sprites/playerinfo/ship10.pnm differ diff --git a/kspaceduel/sprites/playerinfo/ship11.pnm b/kspaceduel/sprites/playerinfo/ship11.pnm new file mode 100644 index 00000000..66ef5825 Binary files /dev/null and b/kspaceduel/sprites/playerinfo/ship11.pnm differ diff --git a/kspaceduel/sprites/playerinfo/ship12.pnm b/kspaceduel/sprites/playerinfo/ship12.pnm new file mode 100644 index 00000000..b5dafbd6 Binary files /dev/null and b/kspaceduel/sprites/playerinfo/ship12.pnm differ diff --git a/kspaceduel/sprites/playerinfo/ship13.pnm b/kspaceduel/sprites/playerinfo/ship13.pnm new file mode 100644 index 00000000..4daf565f Binary files /dev/null and b/kspaceduel/sprites/playerinfo/ship13.pnm differ diff --git a/kspaceduel/sprites/playerinfo/ship20.pnm b/kspaceduel/sprites/playerinfo/ship20.pnm new file mode 100644 index 00000000..aae88e86 Binary files /dev/null and b/kspaceduel/sprites/playerinfo/ship20.pnm differ diff --git a/kspaceduel/sprites/playerinfo/ship21.pnm b/kspaceduel/sprites/playerinfo/ship21.pnm new file mode 100644 index 00000000..600e6555 Binary files /dev/null and b/kspaceduel/sprites/playerinfo/ship21.pnm differ diff --git a/kspaceduel/sprites/playerinfo/ship22.pnm b/kspaceduel/sprites/playerinfo/ship22.pnm new file mode 100644 index 00000000..34a95574 Binary files /dev/null and b/kspaceduel/sprites/playerinfo/ship22.pnm differ diff --git a/kspaceduel/sprites/playerinfo/ship23.pnm b/kspaceduel/sprites/playerinfo/ship23.pnm new file mode 100644 index 00000000..8487b42a Binary files /dev/null and b/kspaceduel/sprites/playerinfo/ship23.pnm differ diff --git a/kspaceduel/sprites/playerinfo/win.pnm b/kspaceduel/sprites/playerinfo/win.pnm new file mode 100644 index 00000000..324ba53c Binary files /dev/null and b/kspaceduel/sprites/playerinfo/win.pnm differ diff --git a/kspaceduel/sprites/powerups/Makefile.am b/kspaceduel/sprites/powerups/Makefile.am new file mode 100644 index 00000000..5133d7d7 --- /dev/null +++ b/kspaceduel/sprites/powerups/Makefile.am @@ -0,0 +1,5 @@ +powerdir = $(kde_datadir)/kspaceduel/sprites/powerups +power_DATA = \ +pbullet.pbm pbullet.ppm penergy.pbm penergy.ppm pmine.pbm pmine.ppm \ +pshield.pbm pshield.ppm +EXTRA_DIST = $(power_DATA) diff --git a/kspaceduel/sprites/powerups/pbullet.pbm b/kspaceduel/sprites/powerups/pbullet.pbm new file mode 100644 index 00000000..f363dd1c --- /dev/null +++ b/kspaceduel/sprites/powerups/pbullet.pbm @@ -0,0 +1,3 @@ +P4 +14 14 +À?ðøÿøÿüÿüÿüÿüÿüÿüÿøø?ðà \ No newline at end of file diff --git a/kspaceduel/sprites/powerups/pbullet.ppm b/kspaceduel/sprites/powerups/pbullet.ppm new file mode 100644 index 00000000..b66820ec Binary files /dev/null and b/kspaceduel/sprites/powerups/pbullet.ppm differ diff --git a/kspaceduel/sprites/powerups/penergy.pbm b/kspaceduel/sprites/powerups/penergy.pbm new file mode 100644 index 00000000..f363dd1c --- /dev/null +++ b/kspaceduel/sprites/powerups/penergy.pbm @@ -0,0 +1,3 @@ +P4 +14 14 +À?ðøÿøÿüÿüÿüÿüÿüÿüÿøø?ðà \ No newline at end of file diff --git a/kspaceduel/sprites/powerups/penergy.ppm b/kspaceduel/sprites/powerups/penergy.ppm new file mode 100644 index 00000000..a2388c28 Binary files /dev/null and b/kspaceduel/sprites/powerups/penergy.ppm differ diff --git a/kspaceduel/sprites/powerups/pmine.pbm b/kspaceduel/sprites/powerups/pmine.pbm new file mode 100644 index 00000000..f363dd1c --- /dev/null +++ b/kspaceduel/sprites/powerups/pmine.pbm @@ -0,0 +1,3 @@ +P4 +14 14 +À?ðøÿøÿüÿüÿüÿüÿüÿüÿøø?ðà \ No newline at end of file diff --git a/kspaceduel/sprites/powerups/pmine.ppm b/kspaceduel/sprites/powerups/pmine.ppm new file mode 100644 index 00000000..8be25623 Binary files /dev/null and b/kspaceduel/sprites/powerups/pmine.ppm differ diff --git a/kspaceduel/sprites/powerups/pshield.pbm b/kspaceduel/sprites/powerups/pshield.pbm new file mode 100644 index 00000000..f363dd1c --- /dev/null +++ b/kspaceduel/sprites/powerups/pshield.pbm @@ -0,0 +1,3 @@ +P4 +14 14 +À?ðøÿøÿüÿüÿüÿüÿüÿüÿøø?ðà \ No newline at end of file diff --git a/kspaceduel/sprites/powerups/pshield.ppm b/kspaceduel/sprites/powerups/pshield.ppm new file mode 100644 index 00000000..9be24c93 Binary files /dev/null and b/kspaceduel/sprites/powerups/pshield.ppm differ diff --git a/kspaceduel/sprites/ship1/Makefile.am b/kspaceduel/sprites/ship1/Makefile.am new file mode 100644 index 00000000..aca11c80 --- /dev/null +++ b/kspaceduel/sprites/ship1/Makefile.am @@ -0,0 +1,25 @@ +ship1dir = $(kde_datadir)/kspaceduel/sprites/ship1 +ship1_DATA = \ +bullet.pbm bullet.ppm mine0.pbm mine0.ppm mine1.pbm mine1.ppm ship00.pbm \ +ship00.ppm ship01.pbm ship01.ppm ship02.pbm ship02.ppm ship03.pbm \ +ship03.ppm ship04.pbm ship04.ppm ship05.pbm ship05.ppm ship06.pbm \ +ship06.ppm ship07.pbm ship07.ppm ship08.pbm ship08.ppm ship09.pbm \ +ship09.ppm ship10.pbm ship10.ppm ship11.pbm ship11.ppm ship12.pbm \ +ship12.ppm ship13.pbm ship13.ppm ship14.pbm ship14.ppm ship15.pbm \ +ship15.ppm ship16.pbm ship16.ppm ship17.pbm ship17.ppm ship18.pbm \ +ship18.ppm ship19.pbm ship19.ppm ship20.pbm ship20.ppm ship21.pbm \ +ship21.ppm ship22.pbm ship22.ppm ship23.pbm ship23.ppm ship24.pbm \ +ship24.ppm ship25.pbm ship25.ppm ship26.pbm ship26.ppm ship27.pbm \ +ship27.ppm ship28.pbm ship28.ppm ship29.pbm ship29.ppm ship30.pbm \ +ship30.ppm ship31.pbm ship31.ppm ship32.pbm ship32.ppm ship33.pbm \ +ship33.ppm ship34.pbm ship34.ppm ship35.pbm ship35.ppm ship36.pbm \ +ship36.ppm ship37.pbm ship37.ppm ship38.pbm ship38.ppm ship39.pbm \ +ship39.ppm ship40.pbm ship40.ppm ship41.pbm ship41.ppm ship42.pbm \ +ship42.ppm ship43.pbm ship43.ppm ship44.pbm ship44.ppm ship45.pbm \ +ship45.ppm ship46.pbm ship46.ppm ship47.pbm ship47.ppm ship48.pbm \ +ship48.ppm ship49.pbm ship49.ppm ship50.pbm ship50.ppm ship51.pbm \ +ship51.ppm ship52.pbm ship52.ppm ship53.pbm ship53.ppm ship54.pbm \ +ship54.ppm ship55.pbm ship55.ppm ship56.pbm ship56.ppm ship57.pbm \ +ship57.ppm ship58.pbm ship58.ppm ship59.pbm ship59.ppm ship60.pbm \ +ship60.ppm ship61.pbm ship61.ppm ship62.pbm ship62.ppm ship63.pbm \ +ship63.ppm ship64.pbm ship64.ppm diff --git a/kspaceduel/sprites/ship1/bullet.pbm b/kspaceduel/sprites/ship1/bullet.pbm new file mode 100644 index 00000000..9f8fd80d --- /dev/null +++ b/kspaceduel/sprites/ship1/bullet.pbm @@ -0,0 +1,5 @@ +P1 +3 3 +111 +111 +111 diff --git a/kspaceduel/sprites/ship1/bullet.ppm b/kspaceduel/sprites/ship1/bullet.ppm new file mode 100644 index 00000000..56a1b85a --- /dev/null +++ b/kspaceduel/sprites/ship1/bullet.ppm @@ -0,0 +1,5 @@ +P6 +# HOTSPOT 1 1 +3 3 +255 +n""±""n""±""î""­""n""­""n"" \ No newline at end of file diff --git a/kspaceduel/sprites/ship1/mine0.pbm b/kspaceduel/sprites/ship1/mine0.pbm new file mode 100644 index 00000000..158c3baf Binary files /dev/null and b/kspaceduel/sprites/ship1/mine0.pbm differ diff --git a/kspaceduel/sprites/ship1/mine0.ppm b/kspaceduel/sprites/ship1/mine0.ppm new file mode 100644 index 00000000..94371392 Binary files /dev/null and b/kspaceduel/sprites/ship1/mine0.ppm differ diff --git a/kspaceduel/sprites/ship1/mine1.pbm b/kspaceduel/sprites/ship1/mine1.pbm new file mode 100644 index 00000000..0b39d79e Binary files /dev/null and b/kspaceduel/sprites/ship1/mine1.pbm differ diff --git a/kspaceduel/sprites/ship1/mine1.ppm b/kspaceduel/sprites/ship1/mine1.ppm new file mode 100644 index 00000000..1ae3374e Binary files /dev/null and b/kspaceduel/sprites/ship1/mine1.ppm differ diff --git a/kspaceduel/sprites/ship1/ship00.pbm b/kspaceduel/sprites/ship1/ship00.pbm new file mode 100644 index 00000000..6bf23756 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship00.pbm differ diff --git a/kspaceduel/sprites/ship1/ship00.ppm b/kspaceduel/sprites/ship1/ship00.ppm new file mode 100644 index 00000000..c55557a7 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship00.ppm differ diff --git a/kspaceduel/sprites/ship1/ship01.pbm b/kspaceduel/sprites/ship1/ship01.pbm new file mode 100644 index 00000000..d1f5d378 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship01.pbm differ diff --git a/kspaceduel/sprites/ship1/ship01.ppm b/kspaceduel/sprites/ship1/ship01.ppm new file mode 100644 index 00000000..e47c4504 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship01.ppm differ diff --git a/kspaceduel/sprites/ship1/ship02.pbm b/kspaceduel/sprites/ship1/ship02.pbm new file mode 100644 index 00000000..4f0dcafe Binary files /dev/null and b/kspaceduel/sprites/ship1/ship02.pbm differ diff --git a/kspaceduel/sprites/ship1/ship02.ppm b/kspaceduel/sprites/ship1/ship02.ppm new file mode 100644 index 00000000..8077b010 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship02.ppm differ diff --git a/kspaceduel/sprites/ship1/ship03.pbm b/kspaceduel/sprites/ship1/ship03.pbm new file mode 100644 index 00000000..8096630c Binary files /dev/null and b/kspaceduel/sprites/ship1/ship03.pbm differ diff --git a/kspaceduel/sprites/ship1/ship03.ppm b/kspaceduel/sprites/ship1/ship03.ppm new file mode 100644 index 00000000..706a8ece Binary files /dev/null and b/kspaceduel/sprites/ship1/ship03.ppm differ diff --git a/kspaceduel/sprites/ship1/ship04.pbm b/kspaceduel/sprites/ship1/ship04.pbm new file mode 100644 index 00000000..7d43a13e Binary files /dev/null and b/kspaceduel/sprites/ship1/ship04.pbm differ diff --git a/kspaceduel/sprites/ship1/ship04.ppm b/kspaceduel/sprites/ship1/ship04.ppm new file mode 100644 index 00000000..0d3fc821 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship04.ppm differ diff --git a/kspaceduel/sprites/ship1/ship05.pbm b/kspaceduel/sprites/ship1/ship05.pbm new file mode 100644 index 00000000..22d41bf2 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship05.pbm differ diff --git a/kspaceduel/sprites/ship1/ship05.ppm b/kspaceduel/sprites/ship1/ship05.ppm new file mode 100644 index 00000000..af2541cf Binary files /dev/null and b/kspaceduel/sprites/ship1/ship05.ppm differ diff --git a/kspaceduel/sprites/ship1/ship06.pbm b/kspaceduel/sprites/ship1/ship06.pbm new file mode 100644 index 00000000..d4c43aa1 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship06.pbm differ diff --git a/kspaceduel/sprites/ship1/ship06.ppm b/kspaceduel/sprites/ship1/ship06.ppm new file mode 100644 index 00000000..e41b0a43 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship06.ppm differ diff --git a/kspaceduel/sprites/ship1/ship07.pbm b/kspaceduel/sprites/ship1/ship07.pbm new file mode 100644 index 00000000..7f3e2683 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship07.pbm differ diff --git a/kspaceduel/sprites/ship1/ship07.ppm b/kspaceduel/sprites/ship1/ship07.ppm new file mode 100644 index 00000000..bf016f6c Binary files /dev/null and b/kspaceduel/sprites/ship1/ship07.ppm differ diff --git a/kspaceduel/sprites/ship1/ship08.pbm b/kspaceduel/sprites/ship1/ship08.pbm new file mode 100644 index 00000000..9cfd6bf1 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship08.pbm differ diff --git a/kspaceduel/sprites/ship1/ship08.ppm b/kspaceduel/sprites/ship1/ship08.ppm new file mode 100644 index 00000000..5939bda6 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship08.ppm differ diff --git a/kspaceduel/sprites/ship1/ship09.pbm b/kspaceduel/sprites/ship1/ship09.pbm new file mode 100644 index 00000000..6ec3a907 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship09.pbm differ diff --git a/kspaceduel/sprites/ship1/ship09.ppm b/kspaceduel/sprites/ship1/ship09.ppm new file mode 100644 index 00000000..9e94fa33 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship09.ppm differ diff --git a/kspaceduel/sprites/ship1/ship10.pbm b/kspaceduel/sprites/ship1/ship10.pbm new file mode 100644 index 00000000..4cd96f5d Binary files /dev/null and b/kspaceduel/sprites/ship1/ship10.pbm differ diff --git a/kspaceduel/sprites/ship1/ship10.ppm b/kspaceduel/sprites/ship1/ship10.ppm new file mode 100644 index 00000000..0cbf08f9 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship10.ppm differ diff --git a/kspaceduel/sprites/ship1/ship11.pbm b/kspaceduel/sprites/ship1/ship11.pbm new file mode 100644 index 00000000..37dd4050 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship11.pbm differ diff --git a/kspaceduel/sprites/ship1/ship11.ppm b/kspaceduel/sprites/ship1/ship11.ppm new file mode 100644 index 00000000..1b383d50 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship11.ppm differ diff --git a/kspaceduel/sprites/ship1/ship12.pbm b/kspaceduel/sprites/ship1/ship12.pbm new file mode 100644 index 00000000..c0ba451b Binary files /dev/null and b/kspaceduel/sprites/ship1/ship12.pbm differ diff --git a/kspaceduel/sprites/ship1/ship12.ppm b/kspaceduel/sprites/ship1/ship12.ppm new file mode 100644 index 00000000..79dd1087 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship12.ppm differ diff --git a/kspaceduel/sprites/ship1/ship13.pbm b/kspaceduel/sprites/ship1/ship13.pbm new file mode 100644 index 00000000..585dd0a0 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship13.pbm differ diff --git a/kspaceduel/sprites/ship1/ship13.ppm b/kspaceduel/sprites/ship1/ship13.ppm new file mode 100644 index 00000000..af962f8d Binary files /dev/null and b/kspaceduel/sprites/ship1/ship13.ppm differ diff --git a/kspaceduel/sprites/ship1/ship14.pbm b/kspaceduel/sprites/ship1/ship14.pbm new file mode 100644 index 00000000..aaf4bf9e Binary files /dev/null and b/kspaceduel/sprites/ship1/ship14.pbm differ diff --git a/kspaceduel/sprites/ship1/ship14.ppm b/kspaceduel/sprites/ship1/ship14.ppm new file mode 100644 index 00000000..886b6be9 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship14.ppm differ diff --git a/kspaceduel/sprites/ship1/ship15.pbm b/kspaceduel/sprites/ship1/ship15.pbm new file mode 100644 index 00000000..d61306e9 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship15.pbm differ diff --git a/kspaceduel/sprites/ship1/ship15.ppm b/kspaceduel/sprites/ship1/ship15.ppm new file mode 100644 index 00000000..e7f8f7e2 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship15.ppm differ diff --git a/kspaceduel/sprites/ship1/ship16.pbm b/kspaceduel/sprites/ship1/ship16.pbm new file mode 100644 index 00000000..47125d73 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship16.pbm differ diff --git a/kspaceduel/sprites/ship1/ship16.ppm b/kspaceduel/sprites/ship1/ship16.ppm new file mode 100644 index 00000000..228cf12c Binary files /dev/null and b/kspaceduel/sprites/ship1/ship16.ppm differ diff --git a/kspaceduel/sprites/ship1/ship17.pbm b/kspaceduel/sprites/ship1/ship17.pbm new file mode 100644 index 00000000..31828cbd Binary files /dev/null and b/kspaceduel/sprites/ship1/ship17.pbm differ diff --git a/kspaceduel/sprites/ship1/ship17.ppm b/kspaceduel/sprites/ship1/ship17.ppm new file mode 100644 index 00000000..4dff9f7b Binary files /dev/null and b/kspaceduel/sprites/ship1/ship17.ppm differ diff --git a/kspaceduel/sprites/ship1/ship18.pbm b/kspaceduel/sprites/ship1/ship18.pbm new file mode 100644 index 00000000..a104102d Binary files /dev/null and b/kspaceduel/sprites/ship1/ship18.pbm differ diff --git a/kspaceduel/sprites/ship1/ship18.ppm b/kspaceduel/sprites/ship1/ship18.ppm new file mode 100644 index 00000000..91828c52 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship18.ppm differ diff --git a/kspaceduel/sprites/ship1/ship19.pbm b/kspaceduel/sprites/ship1/ship19.pbm new file mode 100644 index 00000000..d4184744 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship19.pbm differ diff --git a/kspaceduel/sprites/ship1/ship19.ppm b/kspaceduel/sprites/ship1/ship19.ppm new file mode 100644 index 00000000..1e44c1fa Binary files /dev/null and b/kspaceduel/sprites/ship1/ship19.ppm differ diff --git a/kspaceduel/sprites/ship1/ship20.pbm b/kspaceduel/sprites/ship1/ship20.pbm new file mode 100644 index 00000000..c9e6592a Binary files /dev/null and b/kspaceduel/sprites/ship1/ship20.pbm differ diff --git a/kspaceduel/sprites/ship1/ship20.ppm b/kspaceduel/sprites/ship1/ship20.ppm new file mode 100644 index 00000000..b13956eb Binary files /dev/null and b/kspaceduel/sprites/ship1/ship20.ppm differ diff --git a/kspaceduel/sprites/ship1/ship21.pbm b/kspaceduel/sprites/ship1/ship21.pbm new file mode 100644 index 00000000..dc8a04fc Binary files /dev/null and b/kspaceduel/sprites/ship1/ship21.pbm differ diff --git a/kspaceduel/sprites/ship1/ship21.ppm b/kspaceduel/sprites/ship1/ship21.ppm new file mode 100644 index 00000000..d2a51f6d Binary files /dev/null and b/kspaceduel/sprites/ship1/ship21.ppm differ diff --git a/kspaceduel/sprites/ship1/ship22.pbm b/kspaceduel/sprites/ship1/ship22.pbm new file mode 100644 index 00000000..0675c265 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship22.pbm differ diff --git a/kspaceduel/sprites/ship1/ship22.ppm b/kspaceduel/sprites/ship1/ship22.ppm new file mode 100644 index 00000000..1471eb41 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship22.ppm differ diff --git a/kspaceduel/sprites/ship1/ship23.pbm b/kspaceduel/sprites/ship1/ship23.pbm new file mode 100644 index 00000000..0ff6d171 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship23.pbm differ diff --git a/kspaceduel/sprites/ship1/ship23.ppm b/kspaceduel/sprites/ship1/ship23.ppm new file mode 100644 index 00000000..008adf46 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship23.ppm differ diff --git a/kspaceduel/sprites/ship1/ship24.pbm b/kspaceduel/sprites/ship1/ship24.pbm new file mode 100644 index 00000000..613500ab Binary files /dev/null and b/kspaceduel/sprites/ship1/ship24.pbm differ diff --git a/kspaceduel/sprites/ship1/ship24.ppm b/kspaceduel/sprites/ship1/ship24.ppm new file mode 100644 index 00000000..e96306e9 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship24.ppm differ diff --git a/kspaceduel/sprites/ship1/ship25.pbm b/kspaceduel/sprites/ship1/ship25.pbm new file mode 100644 index 00000000..857db7c3 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship25.pbm differ diff --git a/kspaceduel/sprites/ship1/ship25.ppm b/kspaceduel/sprites/ship1/ship25.ppm new file mode 100644 index 00000000..4cbae30e Binary files /dev/null and b/kspaceduel/sprites/ship1/ship25.ppm differ diff --git a/kspaceduel/sprites/ship1/ship26.pbm b/kspaceduel/sprites/ship1/ship26.pbm new file mode 100644 index 00000000..adbc4f54 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship26.pbm differ diff --git a/kspaceduel/sprites/ship1/ship26.ppm b/kspaceduel/sprites/ship1/ship26.ppm new file mode 100644 index 00000000..3bc7bbb0 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship26.ppm differ diff --git a/kspaceduel/sprites/ship1/ship27.pbm b/kspaceduel/sprites/ship1/ship27.pbm new file mode 100644 index 00000000..d2a19254 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship27.pbm differ diff --git a/kspaceduel/sprites/ship1/ship27.ppm b/kspaceduel/sprites/ship1/ship27.ppm new file mode 100644 index 00000000..e92fae28 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship27.ppm differ diff --git a/kspaceduel/sprites/ship1/ship28.pbm b/kspaceduel/sprites/ship1/ship28.pbm new file mode 100644 index 00000000..f2274e62 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship28.pbm differ diff --git a/kspaceduel/sprites/ship1/ship28.ppm b/kspaceduel/sprites/ship1/ship28.ppm new file mode 100644 index 00000000..38137308 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship28.ppm differ diff --git a/kspaceduel/sprites/ship1/ship29.pbm b/kspaceduel/sprites/ship1/ship29.pbm new file mode 100644 index 00000000..2d48307f Binary files /dev/null and b/kspaceduel/sprites/ship1/ship29.pbm differ diff --git a/kspaceduel/sprites/ship1/ship29.ppm b/kspaceduel/sprites/ship1/ship29.ppm new file mode 100644 index 00000000..92dc7048 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship29.ppm differ diff --git a/kspaceduel/sprites/ship1/ship30.pbm b/kspaceduel/sprites/ship1/ship30.pbm new file mode 100644 index 00000000..85cf7861 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship30.pbm differ diff --git a/kspaceduel/sprites/ship1/ship30.ppm b/kspaceduel/sprites/ship1/ship30.ppm new file mode 100644 index 00000000..1e34a615 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship30.ppm differ diff --git a/kspaceduel/sprites/ship1/ship31.pbm b/kspaceduel/sprites/ship1/ship31.pbm new file mode 100644 index 00000000..0a6a2f98 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship31.pbm differ diff --git a/kspaceduel/sprites/ship1/ship31.ppm b/kspaceduel/sprites/ship1/ship31.ppm new file mode 100644 index 00000000..88de98ce Binary files /dev/null and b/kspaceduel/sprites/ship1/ship31.ppm differ diff --git a/kspaceduel/sprites/ship1/ship32.pbm b/kspaceduel/sprites/ship1/ship32.pbm new file mode 100644 index 00000000..da818895 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship32.pbm differ diff --git a/kspaceduel/sprites/ship1/ship32.ppm b/kspaceduel/sprites/ship1/ship32.ppm new file mode 100644 index 00000000..dd0ece74 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship32.ppm differ diff --git a/kspaceduel/sprites/ship1/ship33.pbm b/kspaceduel/sprites/ship1/ship33.pbm new file mode 100644 index 00000000..fe24c29b Binary files /dev/null and b/kspaceduel/sprites/ship1/ship33.pbm differ diff --git a/kspaceduel/sprites/ship1/ship33.ppm b/kspaceduel/sprites/ship1/ship33.ppm new file mode 100644 index 00000000..24c47275 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship33.ppm differ diff --git a/kspaceduel/sprites/ship1/ship34.pbm b/kspaceduel/sprites/ship1/ship34.pbm new file mode 100644 index 00000000..25e1f1d2 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship34.pbm differ diff --git a/kspaceduel/sprites/ship1/ship34.ppm b/kspaceduel/sprites/ship1/ship34.ppm new file mode 100644 index 00000000..4509b69b Binary files /dev/null and b/kspaceduel/sprites/ship1/ship34.ppm differ diff --git a/kspaceduel/sprites/ship1/ship35.pbm b/kspaceduel/sprites/ship1/ship35.pbm new file mode 100644 index 00000000..a832dafe Binary files /dev/null and b/kspaceduel/sprites/ship1/ship35.pbm differ diff --git a/kspaceduel/sprites/ship1/ship35.ppm b/kspaceduel/sprites/ship1/ship35.ppm new file mode 100644 index 00000000..084eafa0 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship35.ppm differ diff --git a/kspaceduel/sprites/ship1/ship36.pbm b/kspaceduel/sprites/ship1/ship36.pbm new file mode 100644 index 00000000..08a3295a Binary files /dev/null and b/kspaceduel/sprites/ship1/ship36.pbm differ diff --git a/kspaceduel/sprites/ship1/ship36.ppm b/kspaceduel/sprites/ship1/ship36.ppm new file mode 100644 index 00000000..b97b6391 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship36.ppm differ diff --git a/kspaceduel/sprites/ship1/ship37.pbm b/kspaceduel/sprites/ship1/ship37.pbm new file mode 100644 index 00000000..2727c02d Binary files /dev/null and b/kspaceduel/sprites/ship1/ship37.pbm differ diff --git a/kspaceduel/sprites/ship1/ship37.ppm b/kspaceduel/sprites/ship1/ship37.ppm new file mode 100644 index 00000000..4b1fa725 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship37.ppm differ diff --git a/kspaceduel/sprites/ship1/ship38.pbm b/kspaceduel/sprites/ship1/ship38.pbm new file mode 100644 index 00000000..87b51c80 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship38.pbm differ diff --git a/kspaceduel/sprites/ship1/ship38.ppm b/kspaceduel/sprites/ship1/ship38.ppm new file mode 100644 index 00000000..44bf768a Binary files /dev/null and b/kspaceduel/sprites/ship1/ship38.ppm differ diff --git a/kspaceduel/sprites/ship1/ship39.pbm b/kspaceduel/sprites/ship1/ship39.pbm new file mode 100644 index 00000000..99281fff Binary files /dev/null and b/kspaceduel/sprites/ship1/ship39.pbm differ diff --git a/kspaceduel/sprites/ship1/ship39.ppm b/kspaceduel/sprites/ship1/ship39.ppm new file mode 100644 index 00000000..ffafc076 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship39.ppm differ diff --git a/kspaceduel/sprites/ship1/ship40.pbm b/kspaceduel/sprites/ship1/ship40.pbm new file mode 100644 index 00000000..eb4203ed Binary files /dev/null and b/kspaceduel/sprites/ship1/ship40.pbm differ diff --git a/kspaceduel/sprites/ship1/ship40.ppm b/kspaceduel/sprites/ship1/ship40.ppm new file mode 100644 index 00000000..1def4c91 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship40.ppm differ diff --git a/kspaceduel/sprites/ship1/ship41.pbm b/kspaceduel/sprites/ship1/ship41.pbm new file mode 100644 index 00000000..61782784 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship41.pbm differ diff --git a/kspaceduel/sprites/ship1/ship41.ppm b/kspaceduel/sprites/ship1/ship41.ppm new file mode 100644 index 00000000..02d26e8c Binary files /dev/null and b/kspaceduel/sprites/ship1/ship41.ppm differ diff --git a/kspaceduel/sprites/ship1/ship42.pbm b/kspaceduel/sprites/ship1/ship42.pbm new file mode 100644 index 00000000..b98fabd1 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship42.pbm differ diff --git a/kspaceduel/sprites/ship1/ship42.ppm b/kspaceduel/sprites/ship1/ship42.ppm new file mode 100644 index 00000000..4c2f8b1a Binary files /dev/null and b/kspaceduel/sprites/ship1/ship42.ppm differ diff --git a/kspaceduel/sprites/ship1/ship43.pbm b/kspaceduel/sprites/ship1/ship43.pbm new file mode 100644 index 00000000..adc24118 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship43.pbm differ diff --git a/kspaceduel/sprites/ship1/ship43.ppm b/kspaceduel/sprites/ship1/ship43.ppm new file mode 100644 index 00000000..7a8da650 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship43.ppm differ diff --git a/kspaceduel/sprites/ship1/ship44.pbm b/kspaceduel/sprites/ship1/ship44.pbm new file mode 100644 index 00000000..0b8afcd9 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship44.pbm differ diff --git a/kspaceduel/sprites/ship1/ship44.ppm b/kspaceduel/sprites/ship1/ship44.ppm new file mode 100644 index 00000000..75c52fc3 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship44.ppm differ diff --git a/kspaceduel/sprites/ship1/ship45.pbm b/kspaceduel/sprites/ship1/ship45.pbm new file mode 100644 index 00000000..216d22a5 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship45.pbm differ diff --git a/kspaceduel/sprites/ship1/ship45.ppm b/kspaceduel/sprites/ship1/ship45.ppm new file mode 100644 index 00000000..f75d6322 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship45.ppm differ diff --git a/kspaceduel/sprites/ship1/ship46.pbm b/kspaceduel/sprites/ship1/ship46.pbm new file mode 100644 index 00000000..7a249684 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship46.pbm differ diff --git a/kspaceduel/sprites/ship1/ship46.ppm b/kspaceduel/sprites/ship1/ship46.ppm new file mode 100644 index 00000000..a23a8be9 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship46.ppm differ diff --git a/kspaceduel/sprites/ship1/ship47.pbm b/kspaceduel/sprites/ship1/ship47.pbm new file mode 100644 index 00000000..b5b60231 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship47.pbm differ diff --git a/kspaceduel/sprites/ship1/ship47.ppm b/kspaceduel/sprites/ship1/ship47.ppm new file mode 100644 index 00000000..3bda3af2 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship47.ppm differ diff --git a/kspaceduel/sprites/ship1/ship48.pbm b/kspaceduel/sprites/ship1/ship48.pbm new file mode 100644 index 00000000..b396376a Binary files /dev/null and b/kspaceduel/sprites/ship1/ship48.pbm differ diff --git a/kspaceduel/sprites/ship1/ship48.ppm b/kspaceduel/sprites/ship1/ship48.ppm new file mode 100644 index 00000000..e869d7dc Binary files /dev/null and b/kspaceduel/sprites/ship1/ship48.ppm differ diff --git a/kspaceduel/sprites/ship1/ship49.pbm b/kspaceduel/sprites/ship1/ship49.pbm new file mode 100644 index 00000000..4e40825c Binary files /dev/null and b/kspaceduel/sprites/ship1/ship49.pbm differ diff --git a/kspaceduel/sprites/ship1/ship49.ppm b/kspaceduel/sprites/ship1/ship49.ppm new file mode 100644 index 00000000..872c9be2 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship49.ppm differ diff --git a/kspaceduel/sprites/ship1/ship50.pbm b/kspaceduel/sprites/ship1/ship50.pbm new file mode 100644 index 00000000..72a4e31b Binary files /dev/null and b/kspaceduel/sprites/ship1/ship50.pbm differ diff --git a/kspaceduel/sprites/ship1/ship50.ppm b/kspaceduel/sprites/ship1/ship50.ppm new file mode 100644 index 00000000..6358387b Binary files /dev/null and b/kspaceduel/sprites/ship1/ship50.ppm differ diff --git a/kspaceduel/sprites/ship1/ship51.pbm b/kspaceduel/sprites/ship1/ship51.pbm new file mode 100644 index 00000000..6e1b27b3 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship51.pbm differ diff --git a/kspaceduel/sprites/ship1/ship51.ppm b/kspaceduel/sprites/ship1/ship51.ppm new file mode 100644 index 00000000..fb37a73e Binary files /dev/null and b/kspaceduel/sprites/ship1/ship51.ppm differ diff --git a/kspaceduel/sprites/ship1/ship52.pbm b/kspaceduel/sprites/ship1/ship52.pbm new file mode 100644 index 00000000..824eec39 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship52.pbm differ diff --git a/kspaceduel/sprites/ship1/ship52.ppm b/kspaceduel/sprites/ship1/ship52.ppm new file mode 100644 index 00000000..a044f6d6 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship52.ppm differ diff --git a/kspaceduel/sprites/ship1/ship53.pbm b/kspaceduel/sprites/ship1/ship53.pbm new file mode 100644 index 00000000..3fbc9832 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship53.pbm differ diff --git a/kspaceduel/sprites/ship1/ship53.ppm b/kspaceduel/sprites/ship1/ship53.ppm new file mode 100644 index 00000000..89f11b55 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship53.ppm differ diff --git a/kspaceduel/sprites/ship1/ship54.pbm b/kspaceduel/sprites/ship1/ship54.pbm new file mode 100644 index 00000000..97010f8d Binary files /dev/null and b/kspaceduel/sprites/ship1/ship54.pbm differ diff --git a/kspaceduel/sprites/ship1/ship54.ppm b/kspaceduel/sprites/ship1/ship54.ppm new file mode 100644 index 00000000..2e386eb8 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship54.ppm differ diff --git a/kspaceduel/sprites/ship1/ship55.pbm b/kspaceduel/sprites/ship1/ship55.pbm new file mode 100644 index 00000000..e2391283 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship55.pbm differ diff --git a/kspaceduel/sprites/ship1/ship55.ppm b/kspaceduel/sprites/ship1/ship55.ppm new file mode 100644 index 00000000..ee02a506 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship55.ppm differ diff --git a/kspaceduel/sprites/ship1/ship56.pbm b/kspaceduel/sprites/ship1/ship56.pbm new file mode 100644 index 00000000..c5953877 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship56.pbm differ diff --git a/kspaceduel/sprites/ship1/ship56.ppm b/kspaceduel/sprites/ship1/ship56.ppm new file mode 100644 index 00000000..38753fc8 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship56.ppm differ diff --git a/kspaceduel/sprites/ship1/ship57.pbm b/kspaceduel/sprites/ship1/ship57.pbm new file mode 100644 index 00000000..22bfacba Binary files /dev/null and b/kspaceduel/sprites/ship1/ship57.pbm differ diff --git a/kspaceduel/sprites/ship1/ship57.ppm b/kspaceduel/sprites/ship1/ship57.ppm new file mode 100644 index 00000000..1aaa1905 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship57.ppm differ diff --git a/kspaceduel/sprites/ship1/ship58.pbm b/kspaceduel/sprites/ship1/ship58.pbm new file mode 100644 index 00000000..4d9f0de3 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship58.pbm differ diff --git a/kspaceduel/sprites/ship1/ship58.ppm b/kspaceduel/sprites/ship1/ship58.ppm new file mode 100644 index 00000000..e1402352 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship58.ppm differ diff --git a/kspaceduel/sprites/ship1/ship59.pbm b/kspaceduel/sprites/ship1/ship59.pbm new file mode 100644 index 00000000..c5886207 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship59.pbm differ diff --git a/kspaceduel/sprites/ship1/ship59.ppm b/kspaceduel/sprites/ship1/ship59.ppm new file mode 100644 index 00000000..fb2a0d6b Binary files /dev/null and b/kspaceduel/sprites/ship1/ship59.ppm differ diff --git a/kspaceduel/sprites/ship1/ship60.pbm b/kspaceduel/sprites/ship1/ship60.pbm new file mode 100644 index 00000000..3edabddd Binary files /dev/null and b/kspaceduel/sprites/ship1/ship60.pbm differ diff --git a/kspaceduel/sprites/ship1/ship60.ppm b/kspaceduel/sprites/ship1/ship60.ppm new file mode 100644 index 00000000..e7655dfe Binary files /dev/null and b/kspaceduel/sprites/ship1/ship60.ppm differ diff --git a/kspaceduel/sprites/ship1/ship61.pbm b/kspaceduel/sprites/ship1/ship61.pbm new file mode 100644 index 00000000..78557a20 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship61.pbm differ diff --git a/kspaceduel/sprites/ship1/ship61.ppm b/kspaceduel/sprites/ship1/ship61.ppm new file mode 100644 index 00000000..026a618f Binary files /dev/null and b/kspaceduel/sprites/ship1/ship61.ppm differ diff --git a/kspaceduel/sprites/ship1/ship62.pbm b/kspaceduel/sprites/ship1/ship62.pbm new file mode 100644 index 00000000..bf3495cf Binary files /dev/null and b/kspaceduel/sprites/ship1/ship62.pbm differ diff --git a/kspaceduel/sprites/ship1/ship62.ppm b/kspaceduel/sprites/ship1/ship62.ppm new file mode 100644 index 00000000..48ca2897 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship62.ppm differ diff --git a/kspaceduel/sprites/ship1/ship63.pbm b/kspaceduel/sprites/ship1/ship63.pbm new file mode 100644 index 00000000..5ed04c90 Binary files /dev/null and b/kspaceduel/sprites/ship1/ship63.pbm differ diff --git a/kspaceduel/sprites/ship1/ship63.ppm b/kspaceduel/sprites/ship1/ship63.ppm new file mode 100644 index 00000000..b1d0841b Binary files /dev/null and b/kspaceduel/sprites/ship1/ship63.ppm differ diff --git a/kspaceduel/sprites/ship1/ship64.pbm b/kspaceduel/sprites/ship1/ship64.pbm new file mode 100644 index 00000000..e37c6fcf Binary files /dev/null and b/kspaceduel/sprites/ship1/ship64.pbm differ diff --git a/kspaceduel/sprites/ship1/ship64.ppm b/kspaceduel/sprites/ship1/ship64.ppm new file mode 100644 index 00000000..127a50be Binary files /dev/null and b/kspaceduel/sprites/ship1/ship64.ppm differ diff --git a/kspaceduel/sprites/ship2/Makefile.am b/kspaceduel/sprites/ship2/Makefile.am new file mode 100644 index 00000000..bd201dcb --- /dev/null +++ b/kspaceduel/sprites/ship2/Makefile.am @@ -0,0 +1,27 @@ +ship2dir = $(kde_datadir)/kspaceduel/sprites/ship2 +ship2_DATA = \ +bullet.pbm bullet.ppm mine0.pbm mine0.ppm mine1.pbm mine1.ppm ship00.pbm \ +ship00.ppm ship01.pbm ship01.ppm ship02.pbm ship02.ppm ship03.pbm \ +ship03.ppm ship04.pbm ship04.ppm ship05.pbm ship05.ppm ship06.pbm \ +ship06.ppm ship07.pbm ship07.ppm ship08.pbm ship08.ppm ship09.pbm \ +ship09.ppm ship10.pbm ship10.ppm ship11.pbm ship11.ppm ship12.pbm \ +ship12.ppm ship13.pbm ship13.ppm ship14.pbm ship14.ppm ship15.pbm \ +ship15.ppm ship16.pbm ship16.ppm ship17.pbm ship17.ppm ship18.pbm \ +ship18.ppm ship19.pbm ship19.ppm ship20.pbm ship20.ppm ship21.pbm \ +ship21.ppm ship22.pbm ship22.ppm ship23.pbm ship23.ppm ship24.pbm \ +ship24.ppm ship25.pbm ship25.ppm ship26.pbm ship26.ppm ship27.pbm \ +ship27.ppm ship28.pbm ship28.ppm ship29.pbm ship29.ppm ship30.pbm \ +ship30.ppm ship31.pbm ship31.ppm ship32.pbm ship32.ppm ship33.pbm \ +ship33.ppm ship34.pbm ship34.ppm ship35.pbm ship35.ppm ship36.pbm \ +ship36.ppm ship37.pbm ship37.ppm ship38.pbm ship38.ppm ship39.pbm \ +ship39.ppm ship40.pbm ship40.ppm ship41.pbm ship41.ppm ship42.pbm \ +ship42.ppm ship43.pbm ship43.ppm ship44.pbm ship44.ppm ship45.pbm \ +ship45.ppm ship46.pbm ship46.ppm ship47.pbm ship47.ppm ship48.pbm \ +ship48.ppm ship49.pbm ship49.ppm ship50.pbm ship50.ppm ship51.pbm \ +ship51.ppm ship52.pbm ship52.ppm ship53.pbm ship53.ppm ship54.pbm \ +ship54.ppm ship55.pbm ship55.ppm ship56.pbm ship56.ppm ship57.pbm \ +ship57.ppm ship58.pbm ship58.ppm ship59.pbm ship59.ppm ship60.pbm \ +ship60.ppm ship61.pbm ship61.ppm ship62.pbm ship62.ppm ship63.pbm \ +ship63.ppm ship64.pbm ship64.ppm + +EXTRA_DIST = $(ship2_DATA) diff --git a/kspaceduel/sprites/ship2/bullet.pbm b/kspaceduel/sprites/ship2/bullet.pbm new file mode 100644 index 00000000..9f8fd80d --- /dev/null +++ b/kspaceduel/sprites/ship2/bullet.pbm @@ -0,0 +1,5 @@ +P1 +3 3 +111 +111 +111 diff --git a/kspaceduel/sprites/ship2/bullet.ppm b/kspaceduel/sprites/ship2/bullet.ppm new file mode 100644 index 00000000..33d6d702 --- /dev/null +++ b/kspaceduel/sprites/ship2/bullet.ppm @@ -0,0 +1,5 @@ +P6 +# HOTSPOT 1 1 +3 3 +255 +::–Y_¸::–Y_¸~~ÿY_¸::–Y_¸::– \ No newline at end of file diff --git a/kspaceduel/sprites/ship2/mine0.pbm b/kspaceduel/sprites/ship2/mine0.pbm new file mode 100644 index 00000000..b9f2a2de Binary files /dev/null and b/kspaceduel/sprites/ship2/mine0.pbm differ diff --git a/kspaceduel/sprites/ship2/mine0.ppm b/kspaceduel/sprites/ship2/mine0.ppm new file mode 100644 index 00000000..85143cce Binary files /dev/null and b/kspaceduel/sprites/ship2/mine0.ppm differ diff --git a/kspaceduel/sprites/ship2/mine1.pbm b/kspaceduel/sprites/ship2/mine1.pbm new file mode 100644 index 00000000..21a31590 Binary files /dev/null and b/kspaceduel/sprites/ship2/mine1.pbm differ diff --git a/kspaceduel/sprites/ship2/mine1.ppm b/kspaceduel/sprites/ship2/mine1.ppm new file mode 100644 index 00000000..12e725d1 Binary files /dev/null and b/kspaceduel/sprites/ship2/mine1.ppm differ diff --git a/kspaceduel/sprites/ship2/ship00.pbm b/kspaceduel/sprites/ship2/ship00.pbm new file mode 100644 index 00000000..a2c22134 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship00.pbm differ diff --git a/kspaceduel/sprites/ship2/ship00.ppm b/kspaceduel/sprites/ship2/ship00.ppm new file mode 100644 index 00000000..31e9924c Binary files /dev/null and b/kspaceduel/sprites/ship2/ship00.ppm differ diff --git a/kspaceduel/sprites/ship2/ship01.pbm b/kspaceduel/sprites/ship2/ship01.pbm new file mode 100644 index 00000000..bc9521af Binary files /dev/null and b/kspaceduel/sprites/ship2/ship01.pbm differ diff --git a/kspaceduel/sprites/ship2/ship01.ppm b/kspaceduel/sprites/ship2/ship01.ppm new file mode 100644 index 00000000..8ecdfe06 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship01.ppm differ diff --git a/kspaceduel/sprites/ship2/ship02.pbm b/kspaceduel/sprites/ship2/ship02.pbm new file mode 100644 index 00000000..d89221b8 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship02.pbm differ diff --git a/kspaceduel/sprites/ship2/ship02.ppm b/kspaceduel/sprites/ship2/ship02.ppm new file mode 100644 index 00000000..5bb6af26 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship02.ppm differ diff --git a/kspaceduel/sprites/ship2/ship03.pbm b/kspaceduel/sprites/ship2/ship03.pbm new file mode 100644 index 00000000..6c758512 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship03.pbm differ diff --git a/kspaceduel/sprites/ship2/ship03.ppm b/kspaceduel/sprites/ship2/ship03.ppm new file mode 100644 index 00000000..cd34a9b7 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship03.ppm differ diff --git a/kspaceduel/sprites/ship2/ship04.pbm b/kspaceduel/sprites/ship2/ship04.pbm new file mode 100644 index 00000000..cb480126 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship04.pbm differ diff --git a/kspaceduel/sprites/ship2/ship04.ppm b/kspaceduel/sprites/ship2/ship04.ppm new file mode 100644 index 00000000..a1ec1250 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship04.ppm differ diff --git a/kspaceduel/sprites/ship2/ship05.pbm b/kspaceduel/sprites/ship2/ship05.pbm new file mode 100644 index 00000000..8c52f726 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship05.pbm differ diff --git a/kspaceduel/sprites/ship2/ship05.ppm b/kspaceduel/sprites/ship2/ship05.ppm new file mode 100644 index 00000000..facaf2c8 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship05.ppm differ diff --git a/kspaceduel/sprites/ship2/ship06.pbm b/kspaceduel/sprites/ship2/ship06.pbm new file mode 100644 index 00000000..4e275f75 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship06.pbm differ diff --git a/kspaceduel/sprites/ship2/ship06.ppm b/kspaceduel/sprites/ship2/ship06.ppm new file mode 100644 index 00000000..f773a627 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship06.ppm differ diff --git a/kspaceduel/sprites/ship2/ship07.pbm b/kspaceduel/sprites/ship2/ship07.pbm new file mode 100644 index 00000000..9ce84d59 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship07.pbm differ diff --git a/kspaceduel/sprites/ship2/ship07.ppm b/kspaceduel/sprites/ship2/ship07.ppm new file mode 100644 index 00000000..b7913b00 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship07.ppm differ diff --git a/kspaceduel/sprites/ship2/ship08.pbm b/kspaceduel/sprites/ship2/ship08.pbm new file mode 100644 index 00000000..ffbfb0fa Binary files /dev/null and b/kspaceduel/sprites/ship2/ship08.pbm differ diff --git a/kspaceduel/sprites/ship2/ship08.ppm b/kspaceduel/sprites/ship2/ship08.ppm new file mode 100644 index 00000000..79119eea Binary files /dev/null and b/kspaceduel/sprites/ship2/ship08.ppm differ diff --git a/kspaceduel/sprites/ship2/ship09.pbm b/kspaceduel/sprites/ship2/ship09.pbm new file mode 100644 index 00000000..4a46f232 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship09.pbm differ diff --git a/kspaceduel/sprites/ship2/ship09.ppm b/kspaceduel/sprites/ship2/ship09.ppm new file mode 100644 index 00000000..7e635876 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship09.ppm differ diff --git a/kspaceduel/sprites/ship2/ship10.pbm b/kspaceduel/sprites/ship2/ship10.pbm new file mode 100644 index 00000000..e93a6c72 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship10.pbm differ diff --git a/kspaceduel/sprites/ship2/ship10.ppm b/kspaceduel/sprites/ship2/ship10.ppm new file mode 100644 index 00000000..b3578f45 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship10.ppm differ diff --git a/kspaceduel/sprites/ship2/ship11.pbm b/kspaceduel/sprites/ship2/ship11.pbm new file mode 100644 index 00000000..13d2416b Binary files /dev/null and b/kspaceduel/sprites/ship2/ship11.pbm differ diff --git a/kspaceduel/sprites/ship2/ship11.ppm b/kspaceduel/sprites/ship2/ship11.ppm new file mode 100644 index 00000000..9df7a4c3 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship11.ppm differ diff --git a/kspaceduel/sprites/ship2/ship12.pbm b/kspaceduel/sprites/ship2/ship12.pbm new file mode 100644 index 00000000..4b344578 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship12.pbm differ diff --git a/kspaceduel/sprites/ship2/ship12.ppm b/kspaceduel/sprites/ship2/ship12.ppm new file mode 100644 index 00000000..7a6f532c Binary files /dev/null and b/kspaceduel/sprites/ship2/ship12.ppm differ diff --git a/kspaceduel/sprites/ship2/ship13.pbm b/kspaceduel/sprites/ship2/ship13.pbm new file mode 100644 index 00000000..29502533 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship13.pbm differ diff --git a/kspaceduel/sprites/ship2/ship13.ppm b/kspaceduel/sprites/ship2/ship13.ppm new file mode 100644 index 00000000..75ab41d6 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship13.ppm differ diff --git a/kspaceduel/sprites/ship2/ship14.pbm b/kspaceduel/sprites/ship2/ship14.pbm new file mode 100644 index 00000000..becd3f01 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship14.pbm differ diff --git a/kspaceduel/sprites/ship2/ship14.ppm b/kspaceduel/sprites/ship2/ship14.ppm new file mode 100644 index 00000000..753c615a Binary files /dev/null and b/kspaceduel/sprites/ship2/ship14.ppm differ diff --git a/kspaceduel/sprites/ship2/ship15.pbm b/kspaceduel/sprites/ship2/ship15.pbm new file mode 100644 index 00000000..e30e9994 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship15.pbm differ diff --git a/kspaceduel/sprites/ship2/ship15.ppm b/kspaceduel/sprites/ship2/ship15.ppm new file mode 100644 index 00000000..d12872ae Binary files /dev/null and b/kspaceduel/sprites/ship2/ship15.ppm differ diff --git a/kspaceduel/sprites/ship2/ship16.pbm b/kspaceduel/sprites/ship2/ship16.pbm new file mode 100644 index 00000000..7e42c115 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship16.pbm differ diff --git a/kspaceduel/sprites/ship2/ship16.ppm b/kspaceduel/sprites/ship2/ship16.ppm new file mode 100644 index 00000000..d0584030 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship16.ppm differ diff --git a/kspaceduel/sprites/ship2/ship17.pbm b/kspaceduel/sprites/ship2/ship17.pbm new file mode 100644 index 00000000..295c72da Binary files /dev/null and b/kspaceduel/sprites/ship2/ship17.pbm differ diff --git a/kspaceduel/sprites/ship2/ship17.ppm b/kspaceduel/sprites/ship2/ship17.ppm new file mode 100644 index 00000000..e2e164cb Binary files /dev/null and b/kspaceduel/sprites/ship2/ship17.ppm differ diff --git a/kspaceduel/sprites/ship2/ship18.pbm b/kspaceduel/sprites/ship2/ship18.pbm new file mode 100644 index 00000000..290b436f Binary files /dev/null and b/kspaceduel/sprites/ship2/ship18.pbm differ diff --git a/kspaceduel/sprites/ship2/ship18.ppm b/kspaceduel/sprites/ship2/ship18.ppm new file mode 100644 index 00000000..2e70ad30 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship18.ppm differ diff --git a/kspaceduel/sprites/ship2/ship19.pbm b/kspaceduel/sprites/ship2/ship19.pbm new file mode 100644 index 00000000..b6749b15 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship19.pbm differ diff --git a/kspaceduel/sprites/ship2/ship19.ppm b/kspaceduel/sprites/ship2/ship19.ppm new file mode 100644 index 00000000..473bf9a9 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship19.ppm differ diff --git a/kspaceduel/sprites/ship2/ship20.pbm b/kspaceduel/sprites/ship2/ship20.pbm new file mode 100644 index 00000000..dc74a396 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship20.pbm differ diff --git a/kspaceduel/sprites/ship2/ship20.ppm b/kspaceduel/sprites/ship2/ship20.ppm new file mode 100644 index 00000000..369de36e Binary files /dev/null and b/kspaceduel/sprites/ship2/ship20.ppm differ diff --git a/kspaceduel/sprites/ship2/ship21.pbm b/kspaceduel/sprites/ship2/ship21.pbm new file mode 100644 index 00000000..c23a98f6 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship21.pbm differ diff --git a/kspaceduel/sprites/ship2/ship21.ppm b/kspaceduel/sprites/ship2/ship21.ppm new file mode 100644 index 00000000..090ce9f0 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship21.ppm differ diff --git a/kspaceduel/sprites/ship2/ship22.pbm b/kspaceduel/sprites/ship2/ship22.pbm new file mode 100644 index 00000000..0fad8c90 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship22.pbm differ diff --git a/kspaceduel/sprites/ship2/ship22.ppm b/kspaceduel/sprites/ship2/ship22.ppm new file mode 100644 index 00000000..6991dd32 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship22.ppm differ diff --git a/kspaceduel/sprites/ship2/ship23.pbm b/kspaceduel/sprites/ship2/ship23.pbm new file mode 100644 index 00000000..5862f439 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship23.pbm differ diff --git a/kspaceduel/sprites/ship2/ship23.ppm b/kspaceduel/sprites/ship2/ship23.ppm new file mode 100644 index 00000000..250a3771 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship23.ppm differ diff --git a/kspaceduel/sprites/ship2/ship24.pbm b/kspaceduel/sprites/ship2/ship24.pbm new file mode 100644 index 00000000..857b2116 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship24.pbm differ diff --git a/kspaceduel/sprites/ship2/ship24.ppm b/kspaceduel/sprites/ship2/ship24.ppm new file mode 100644 index 00000000..55ac941b Binary files /dev/null and b/kspaceduel/sprites/ship2/ship24.ppm differ diff --git a/kspaceduel/sprites/ship2/ship25.pbm b/kspaceduel/sprites/ship2/ship25.pbm new file mode 100644 index 00000000..b8884975 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship25.pbm differ diff --git a/kspaceduel/sprites/ship2/ship25.ppm b/kspaceduel/sprites/ship2/ship25.ppm new file mode 100644 index 00000000..794fc038 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship25.ppm differ diff --git a/kspaceduel/sprites/ship2/ship26.pbm b/kspaceduel/sprites/ship2/ship26.pbm new file mode 100644 index 00000000..badd42a3 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship26.pbm differ diff --git a/kspaceduel/sprites/ship2/ship26.ppm b/kspaceduel/sprites/ship2/ship26.ppm new file mode 100644 index 00000000..cdc6ef50 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship26.ppm differ diff --git a/kspaceduel/sprites/ship2/ship27.pbm b/kspaceduel/sprites/ship2/ship27.pbm new file mode 100644 index 00000000..393b3f05 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship27.pbm differ diff --git a/kspaceduel/sprites/ship2/ship27.ppm b/kspaceduel/sprites/ship2/ship27.ppm new file mode 100644 index 00000000..4ec3cbd4 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship27.ppm differ diff --git a/kspaceduel/sprites/ship2/ship28.pbm b/kspaceduel/sprites/ship2/ship28.pbm new file mode 100644 index 00000000..31139aa8 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship28.pbm differ diff --git a/kspaceduel/sprites/ship2/ship28.ppm b/kspaceduel/sprites/ship2/ship28.ppm new file mode 100644 index 00000000..647923e1 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship28.ppm differ diff --git a/kspaceduel/sprites/ship2/ship29.pbm b/kspaceduel/sprites/ship2/ship29.pbm new file mode 100644 index 00000000..6c2a656c Binary files /dev/null and b/kspaceduel/sprites/ship2/ship29.pbm differ diff --git a/kspaceduel/sprites/ship2/ship29.ppm b/kspaceduel/sprites/ship2/ship29.ppm new file mode 100644 index 00000000..880526c1 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship29.ppm differ diff --git a/kspaceduel/sprites/ship2/ship30.pbm b/kspaceduel/sprites/ship2/ship30.pbm new file mode 100644 index 00000000..b456646b Binary files /dev/null and b/kspaceduel/sprites/ship2/ship30.pbm differ diff --git a/kspaceduel/sprites/ship2/ship30.ppm b/kspaceduel/sprites/ship2/ship30.ppm new file mode 100644 index 00000000..a367708c Binary files /dev/null and b/kspaceduel/sprites/ship2/ship30.ppm differ diff --git a/kspaceduel/sprites/ship2/ship31.pbm b/kspaceduel/sprites/ship2/ship31.pbm new file mode 100644 index 00000000..51279992 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship31.pbm differ diff --git a/kspaceduel/sprites/ship2/ship31.ppm b/kspaceduel/sprites/ship2/ship31.ppm new file mode 100644 index 00000000..f11ea682 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship31.ppm differ diff --git a/kspaceduel/sprites/ship2/ship32.pbm b/kspaceduel/sprites/ship2/ship32.pbm new file mode 100644 index 00000000..231f442f Binary files /dev/null and b/kspaceduel/sprites/ship2/ship32.pbm differ diff --git a/kspaceduel/sprites/ship2/ship32.ppm b/kspaceduel/sprites/ship2/ship32.ppm new file mode 100644 index 00000000..3710e5d8 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship32.ppm differ diff --git a/kspaceduel/sprites/ship2/ship33.pbm b/kspaceduel/sprites/ship2/ship33.pbm new file mode 100644 index 00000000..967d7531 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship33.pbm differ diff --git a/kspaceduel/sprites/ship2/ship33.ppm b/kspaceduel/sprites/ship2/ship33.ppm new file mode 100644 index 00000000..8d0fb3a3 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship33.ppm differ diff --git a/kspaceduel/sprites/ship2/ship34.pbm b/kspaceduel/sprites/ship2/ship34.pbm new file mode 100644 index 00000000..935fc508 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship34.pbm differ diff --git a/kspaceduel/sprites/ship2/ship34.ppm b/kspaceduel/sprites/ship2/ship34.ppm new file mode 100644 index 00000000..412ae1e3 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship34.ppm differ diff --git a/kspaceduel/sprites/ship2/ship35.pbm b/kspaceduel/sprites/ship2/ship35.pbm new file mode 100644 index 00000000..7fc405ee Binary files /dev/null and b/kspaceduel/sprites/ship2/ship35.pbm differ diff --git a/kspaceduel/sprites/ship2/ship35.ppm b/kspaceduel/sprites/ship2/ship35.ppm new file mode 100644 index 00000000..a7d53fd8 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship35.ppm differ diff --git a/kspaceduel/sprites/ship2/ship36.pbm b/kspaceduel/sprites/ship2/ship36.pbm new file mode 100644 index 00000000..9e081991 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship36.pbm differ diff --git a/kspaceduel/sprites/ship2/ship36.ppm b/kspaceduel/sprites/ship2/ship36.ppm new file mode 100644 index 00000000..f0bd791d Binary files /dev/null and b/kspaceduel/sprites/ship2/ship36.ppm differ diff --git a/kspaceduel/sprites/ship2/ship37.pbm b/kspaceduel/sprites/ship2/ship37.pbm new file mode 100644 index 00000000..85d5a716 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship37.pbm differ diff --git a/kspaceduel/sprites/ship2/ship37.ppm b/kspaceduel/sprites/ship2/ship37.ppm new file mode 100644 index 00000000..cf221325 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship37.ppm differ diff --git a/kspaceduel/sprites/ship2/ship38.pbm b/kspaceduel/sprites/ship2/ship38.pbm new file mode 100644 index 00000000..97544c97 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship38.pbm differ diff --git a/kspaceduel/sprites/ship2/ship38.ppm b/kspaceduel/sprites/ship2/ship38.ppm new file mode 100644 index 00000000..4b9c41a2 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship38.ppm differ diff --git a/kspaceduel/sprites/ship2/ship39.pbm b/kspaceduel/sprites/ship2/ship39.pbm new file mode 100644 index 00000000..72485ed4 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship39.pbm differ diff --git a/kspaceduel/sprites/ship2/ship39.ppm b/kspaceduel/sprites/ship2/ship39.ppm new file mode 100644 index 00000000..f920b9c5 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship39.ppm differ diff --git a/kspaceduel/sprites/ship2/ship40.pbm b/kspaceduel/sprites/ship2/ship40.pbm new file mode 100644 index 00000000..05ce8d9e Binary files /dev/null and b/kspaceduel/sprites/ship2/ship40.pbm differ diff --git a/kspaceduel/sprites/ship2/ship40.ppm b/kspaceduel/sprites/ship2/ship40.ppm new file mode 100644 index 00000000..982ca694 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship40.ppm differ diff --git a/kspaceduel/sprites/ship2/ship41.pbm b/kspaceduel/sprites/ship2/ship41.pbm new file mode 100644 index 00000000..55556661 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship41.pbm differ diff --git a/kspaceduel/sprites/ship2/ship41.ppm b/kspaceduel/sprites/ship2/ship41.ppm new file mode 100644 index 00000000..c75c12d7 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship41.ppm differ diff --git a/kspaceduel/sprites/ship2/ship42.pbm b/kspaceduel/sprites/ship2/ship42.pbm new file mode 100644 index 00000000..f0bef294 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship42.pbm differ diff --git a/kspaceduel/sprites/ship2/ship42.ppm b/kspaceduel/sprites/ship2/ship42.ppm new file mode 100644 index 00000000..1f402bf8 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship42.ppm differ diff --git a/kspaceduel/sprites/ship2/ship43.pbm b/kspaceduel/sprites/ship2/ship43.pbm new file mode 100644 index 00000000..16040fa5 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship43.pbm differ diff --git a/kspaceduel/sprites/ship2/ship43.ppm b/kspaceduel/sprites/ship2/ship43.ppm new file mode 100644 index 00000000..5151dde5 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship43.ppm differ diff --git a/kspaceduel/sprites/ship2/ship44.pbm b/kspaceduel/sprites/ship2/ship44.pbm new file mode 100644 index 00000000..8cea22b3 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship44.pbm differ diff --git a/kspaceduel/sprites/ship2/ship44.ppm b/kspaceduel/sprites/ship2/ship44.ppm new file mode 100644 index 00000000..12cac1d8 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship44.ppm differ diff --git a/kspaceduel/sprites/ship2/ship45.pbm b/kspaceduel/sprites/ship2/ship45.pbm new file mode 100644 index 00000000..833b42b5 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship45.pbm differ diff --git a/kspaceduel/sprites/ship2/ship45.ppm b/kspaceduel/sprites/ship2/ship45.ppm new file mode 100644 index 00000000..039ef873 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship45.ppm differ diff --git a/kspaceduel/sprites/ship2/ship46.pbm b/kspaceduel/sprites/ship2/ship46.pbm new file mode 100644 index 00000000..74887081 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship46.pbm differ diff --git a/kspaceduel/sprites/ship2/ship46.ppm b/kspaceduel/sprites/ship2/ship46.ppm new file mode 100644 index 00000000..9c274173 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship46.ppm differ diff --git a/kspaceduel/sprites/ship2/ship47.pbm b/kspaceduel/sprites/ship2/ship47.pbm new file mode 100644 index 00000000..be47bb91 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship47.pbm differ diff --git a/kspaceduel/sprites/ship2/ship47.ppm b/kspaceduel/sprites/ship2/ship47.ppm new file mode 100644 index 00000000..ab00ac53 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship47.ppm differ diff --git a/kspaceduel/sprites/ship2/ship48.pbm b/kspaceduel/sprites/ship2/ship48.pbm new file mode 100644 index 00000000..0b2d3e6e Binary files /dev/null and b/kspaceduel/sprites/ship2/ship48.pbm differ diff --git a/kspaceduel/sprites/ship2/ship48.ppm b/kspaceduel/sprites/ship2/ship48.ppm new file mode 100644 index 00000000..5ff7d24f Binary files /dev/null and b/kspaceduel/sprites/ship2/ship48.ppm differ diff --git a/kspaceduel/sprites/ship2/ship49.pbm b/kspaceduel/sprites/ship2/ship49.pbm new file mode 100644 index 00000000..83ac8363 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship49.pbm differ diff --git a/kspaceduel/sprites/ship2/ship49.ppm b/kspaceduel/sprites/ship2/ship49.ppm new file mode 100644 index 00000000..7ec6455d Binary files /dev/null and b/kspaceduel/sprites/ship2/ship49.ppm differ diff --git a/kspaceduel/sprites/ship2/ship50.pbm b/kspaceduel/sprites/ship2/ship50.pbm new file mode 100644 index 00000000..20e5b229 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship50.pbm differ diff --git a/kspaceduel/sprites/ship2/ship50.ppm b/kspaceduel/sprites/ship2/ship50.ppm new file mode 100644 index 00000000..a11cd0f0 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship50.ppm differ diff --git a/kspaceduel/sprites/ship2/ship51.pbm b/kspaceduel/sprites/ship2/ship51.pbm new file mode 100644 index 00000000..948062b5 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship51.pbm differ diff --git a/kspaceduel/sprites/ship2/ship51.ppm b/kspaceduel/sprites/ship2/ship51.ppm new file mode 100644 index 00000000..3974678c Binary files /dev/null and b/kspaceduel/sprites/ship2/ship51.ppm differ diff --git a/kspaceduel/sprites/ship2/ship52.pbm b/kspaceduel/sprites/ship2/ship52.pbm new file mode 100644 index 00000000..bbb498ec Binary files /dev/null and b/kspaceduel/sprites/ship2/ship52.pbm differ diff --git a/kspaceduel/sprites/ship2/ship52.ppm b/kspaceduel/sprites/ship2/ship52.ppm new file mode 100644 index 00000000..9b5075dc Binary files /dev/null and b/kspaceduel/sprites/ship2/ship52.ppm differ diff --git a/kspaceduel/sprites/ship2/ship53.pbm b/kspaceduel/sprites/ship2/ship53.pbm new file mode 100644 index 00000000..51fcf071 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship53.pbm differ diff --git a/kspaceduel/sprites/ship2/ship53.ppm b/kspaceduel/sprites/ship2/ship53.ppm new file mode 100644 index 00000000..27da47fa Binary files /dev/null and b/kspaceduel/sprites/ship2/ship53.ppm differ diff --git a/kspaceduel/sprites/ship2/ship54.pbm b/kspaceduel/sprites/ship2/ship54.pbm new file mode 100644 index 00000000..7ed85a20 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship54.pbm differ diff --git a/kspaceduel/sprites/ship2/ship54.ppm b/kspaceduel/sprites/ship2/ship54.ppm new file mode 100644 index 00000000..fb4373a1 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship54.ppm differ diff --git a/kspaceduel/sprites/ship2/ship55.pbm b/kspaceduel/sprites/ship2/ship55.pbm new file mode 100644 index 00000000..97223e26 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship55.pbm differ diff --git a/kspaceduel/sprites/ship2/ship55.ppm b/kspaceduel/sprites/ship2/ship55.ppm new file mode 100644 index 00000000..54939d95 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship55.ppm differ diff --git a/kspaceduel/sprites/ship2/ship56.pbm b/kspaceduel/sprites/ship2/ship56.pbm new file mode 100644 index 00000000..49cf8e8a Binary files /dev/null and b/kspaceduel/sprites/ship2/ship56.pbm differ diff --git a/kspaceduel/sprites/ship2/ship56.ppm b/kspaceduel/sprites/ship2/ship56.ppm new file mode 100644 index 00000000..b37e12f1 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship56.ppm differ diff --git a/kspaceduel/sprites/ship2/ship57.pbm b/kspaceduel/sprites/ship2/ship57.pbm new file mode 100644 index 00000000..6b5b3922 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship57.pbm differ diff --git a/kspaceduel/sprites/ship2/ship57.ppm b/kspaceduel/sprites/ship2/ship57.ppm new file mode 100644 index 00000000..0220769f Binary files /dev/null and b/kspaceduel/sprites/ship2/ship57.ppm differ diff --git a/kspaceduel/sprites/ship2/ship58.pbm b/kspaceduel/sprites/ship2/ship58.pbm new file mode 100644 index 00000000..a669df91 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship58.pbm differ diff --git a/kspaceduel/sprites/ship2/ship58.ppm b/kspaceduel/sprites/ship2/ship58.ppm new file mode 100644 index 00000000..b4e1fbe9 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship58.ppm differ diff --git a/kspaceduel/sprites/ship2/ship59.pbm b/kspaceduel/sprites/ship2/ship59.pbm new file mode 100644 index 00000000..005b4bdf Binary files /dev/null and b/kspaceduel/sprites/ship2/ship59.pbm differ diff --git a/kspaceduel/sprites/ship2/ship59.ppm b/kspaceduel/sprites/ship2/ship59.ppm new file mode 100644 index 00000000..ec1e44a7 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship59.ppm differ diff --git a/kspaceduel/sprites/ship2/ship60.pbm b/kspaceduel/sprites/ship2/ship60.pbm new file mode 100644 index 00000000..9829ce5e Binary files /dev/null and b/kspaceduel/sprites/ship2/ship60.pbm differ diff --git a/kspaceduel/sprites/ship2/ship60.ppm b/kspaceduel/sprites/ship2/ship60.ppm new file mode 100644 index 00000000..f66b15cc Binary files /dev/null and b/kspaceduel/sprites/ship2/ship60.ppm differ diff --git a/kspaceduel/sprites/ship2/ship61.pbm b/kspaceduel/sprites/ship2/ship61.pbm new file mode 100644 index 00000000..b055eefe Binary files /dev/null and b/kspaceduel/sprites/ship2/ship61.pbm differ diff --git a/kspaceduel/sprites/ship2/ship61.ppm b/kspaceduel/sprites/ship2/ship61.ppm new file mode 100644 index 00000000..c4fea47e Binary files /dev/null and b/kspaceduel/sprites/ship2/ship61.ppm differ diff --git a/kspaceduel/sprites/ship2/ship62.pbm b/kspaceduel/sprites/ship2/ship62.pbm new file mode 100644 index 00000000..6378eec9 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship62.pbm differ diff --git a/kspaceduel/sprites/ship2/ship62.ppm b/kspaceduel/sprites/ship2/ship62.ppm new file mode 100644 index 00000000..e88befbc Binary files /dev/null and b/kspaceduel/sprites/ship2/ship62.ppm differ diff --git a/kspaceduel/sprites/ship2/ship63.pbm b/kspaceduel/sprites/ship2/ship63.pbm new file mode 100644 index 00000000..4da971a2 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship63.pbm differ diff --git a/kspaceduel/sprites/ship2/ship63.ppm b/kspaceduel/sprites/ship2/ship63.ppm new file mode 100644 index 00000000..27bea663 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship63.ppm differ diff --git a/kspaceduel/sprites/ship2/ship64.pbm b/kspaceduel/sprites/ship2/ship64.pbm new file mode 100644 index 00000000..a144ef6b Binary files /dev/null and b/kspaceduel/sprites/ship2/ship64.pbm differ diff --git a/kspaceduel/sprites/ship2/ship64.ppm b/kspaceduel/sprites/ship2/ship64.ppm new file mode 100644 index 00000000..954fc1b2 Binary files /dev/null and b/kspaceduel/sprites/ship2/ship64.ppm differ diff --git a/kspaceduel/sprites/sun/Makefile.am b/kspaceduel/sprites/sun/Makefile.am new file mode 100644 index 00000000..0e50b0e2 --- /dev/null +++ b/kspaceduel/sprites/sun/Makefile.am @@ -0,0 +1,3 @@ +sundir = $(kde_datadir)/kspaceduel/sprites/sun +sun_DATA = sun.ppm sun.pbm +EXTRA_DIST = $(sun_DATA) diff --git a/kspaceduel/sprites/sun/sun.pbm b/kspaceduel/sprites/sun/sun.pbm new file mode 100644 index 00000000..fc4eda67 Binary files /dev/null and b/kspaceduel/sprites/sun/sun.pbm differ diff --git a/kspaceduel/sprites/sun/sun.ppm b/kspaceduel/sprites/sun/sun.ppm new file mode 100644 index 00000000..27494a9a Binary files /dev/null and b/kspaceduel/sprites/sun/sun.ppm differ diff --git a/kspaceduel/structs.h b/kspaceduel/structs.h new file mode 100644 index 00000000..e58b182a --- /dev/null +++ b/kspaceduel/structs.h @@ -0,0 +1,46 @@ +#ifndef __SP_STRUCTS_H +#define __SP_STRUCTS_H + +struct SConfig +{ + double gamespeed,gravity,acc,energyNeed,sunEnergy,rotationSpeed, + mineActivateTime,mineFuel,shotSpeed,shotEnergyNeed,mineEnergyNeed, + rotationEnergyNeed,startPosX,startPosY,startVelX,startVelY, + bulletLifeTime,mineReloadTime,bulletReloadTime; + unsigned bulletDamage,shipDamage,mineDamage,maxBullets,maxMines; + double powerupLifeTime, powerupRefreshTime; + double powerupEnergyAmount; + unsigned powerupShieldAmount; +}; + +bool operator!=(const SConfig &s1, const SConfig &s2); + +#define predefinedConfigNum 4 +const SConfig predefinedConfig[]={{1.0, 2200.0, 0.2, 1.0, 9000.0, 1.0, + 15.0, 65.0, 3.0, 10.0, 5.0, + 0.2, -130.0, -100.0, 3.0, -1.7, + 500.0,10.0,10.0, + 20, 50, 30, 5, 3, + 400.0, 800.0, 50, 30}, + {1.0, 2200.0, 0.2, 1.0, 9000.0, 1.0, + 15.0, 40.0, 5.0, 20.0, 10.0, + 0.2, -50.0, -150.0, 3.5, 0.9, + 500.0,10.0,10.0, + 20, 50, 30, 6, 2, + 400.0, 800.0, 50, 30}, + {1.3, 2200.0, 0.2, 1.0, 13000.0, 1.0, + 15.0, 50.0, 4.0, 10.0, 10.0, + 0.2, -50.0, -150.0, 3.2, -0.9, + 400.0,10.0,10.0, + 20, 50, 30, 7, 5, + 400.0, 800.0, 50, 30}, + {1.0, 2200.0, 0.2, 1.0, 9000.0, 1.0, + 15.0, 40.0, 5.0, 60.0, 50.0, + 0.4, -50.0, -170.0, 3.0, -0.5, + 500.0,10.0,10.0, + 20, 50, 30, 5, 3, + 400.0, 800.0, 50, 30}}; +const char predefinedConfigName[predefinedConfigNum][15]= +{"Default","Bullet","Chaos","Lack of Energy"}; + +#endif diff --git a/kspaceduel/topwidget.cpp b/kspaceduel/topwidget.cpp new file mode 100644 index 00000000..9f275848 --- /dev/null +++ b/kspaceduel/topwidget.cpp @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "topwidget.h" +#include "mainview.h" +#include "playerinfo.h" + +MyTopLevelWidget::MyTopLevelWidget() +{ + initGameWidgets(); + initStatusBar( ); + initActions( ); + setupGUI( ); +} + +void MyTopLevelWidget::initGameWidgets( ){ + QWidget *w = new QWidget(this); + + playerinfo[0]=new PlayerInfo(0,w); + playerinfo[1]=new PlayerInfo(1,w); + playfield=new MyMainView(w); + + QBoxLayout *toplayout=new QHBoxLayout(w); + toplayout->addWidget(playerinfo[0]); + toplayout->addWidget(playfield); + toplayout->addWidget(playerinfo[1]); + toplayout->activate(); + + playfield->setFocusPolicy(QWidget::StrongFocus); + playfield->setFocus(); + + QObject::connect(playfield,SIGNAL(energy(int,int)), + SLOT(energy(int,int))); + QObject::connect(playfield,SIGNAL(hitPoints(int,int)), + SLOT(hitPoints(int,int))); + QObject::connect(playfield,SIGNAL(wins(int,int)),SLOT(wins(int,int))); + QObject::connect(playfield,SIGNAL(setStatusText(const QString &,int)), + SLOT(setStatusText(const QString &,int))); + + setCentralWidget(w); +} + +void MyTopLevelWidget::energy(int pn,int en) +{ + playerinfo[pn]->setEnergy(en); +} + +void MyTopLevelWidget::hitPoints(int pn,int hp) +{ + playerinfo[pn]->setHitpoints(hp); +} + +void MyTopLevelWidget::wins(int pn,int w) +{ + playerinfo[pn]->setWins(w); +} + +void MyTopLevelWidget::initActions( ) +{ + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + KStdGameAction::gameNew(playfield, SLOT(newGame()), actionCollection()); + ( void )new KAction( i18n( "&New Round" ), "spnewround", + CTRL + Key_R, playfield, SLOT( newRound( ) ), + actionCollection( ), "new_round" ); + MyMainView::pauseAction = + KStdGameAction::pause(playfield, SLOT(togglePause()), actionCollection()); + MyMainView::pauseAction->setChecked( false ); + KAction* gameStart = new KAction( i18n( "Start" ), GAME_START_SHORTCUT, + playfield, SLOT( start( ) ), actionCollection( ), "game_start" ); + + KStdAction::preferences(playfield, SLOT(gameSetup()), actionCollection()); + + KAccel* acc = new KAccel(this); + gameStart->plugAccel(acc); + + // Default keys + actionCollection()->setAutoConnectShortcuts(false); + KAction* ac; + ac = new KAction(i18n("Player 1 Rotate Left"), Key_S, 0, 0, + actionCollection(), "P1KeyLeft"); + ac->setEnabled( false ); + ac = new KAction(i18n("Player 1 Rotate Right"), Key_F, 0, 0, + actionCollection(), "P1KeyRight"); + ac->setEnabled( false ); + ac = new KAction(i18n("Player 1 Accelerate"), Key_E, 0, 0, + actionCollection(), "P1KeyAcc"); + ac->setEnabled( false ); + ac = new KAction(i18n("Player 1 Shot"), Key_D, 0, 0, + actionCollection(), "P1Shot"); + ac->setEnabled( false ); + ac = new KAction(i18n("Player 1 Mine"), Key_A, 0, 0, + actionCollection(), "P1Mine"); + ac->setEnabled( false ); + + ac = new KAction(i18n("Player 2 Rotate Left"), Key_Left, 0, 0, + actionCollection(), "P2KeyLeft"); + ac->setEnabled( false ); + ac = new KAction(i18n("Player 2 Rotate Right"), Key_Right, 0, 0, + actionCollection(), "P2KeyRight"); + ac->setEnabled( false ); + ac = new KAction(i18n("Player 2 Accelerate"), Key_Up, 0, 0, + actionCollection(), "P2KeyAcc"); + ac->setEnabled( false ); + ac = new KAction(i18n("Player 2 Shot"), Key_Down, 0, 0, + actionCollection(), "P2Shot"); + ac->setEnabled( false ); + ac = new KAction(i18n("Player 2 Mine"), Key_Insert, 0, 0, + actionCollection(), "P2Mine"); + ac->setEnabled( false ); + + actionCollection()->setAutoConnectShortcuts(true); + playfield->setActionCollection(actionCollection()); +} + +void MyTopLevelWidget::initStatusBar( ) +{ + statusBar( )->insertItem(i18n(" paused "),IDS_PAUSE,1); + statusBar( )->insertItem(" ",IDS_MAIN,1); + statusBar( )->insertItem("",42,2); +} + +void MyTopLevelWidget::start() +{ + playfield->newGame(); + playfield->newRound(); +} + +void MyTopLevelWidget::setStatusText(const QString & str,int id) +{ + statusBar( )->changeItem(str,id); +} + +void MyTopLevelWidget::keySetup() +{ + playfield->pause(); + KKeyDialog::configure( actionCollection( ), this, true ); +} + +#include "topwidget.moc" diff --git a/kspaceduel/topwidget.h b/kspaceduel/topwidget.h new file mode 100644 index 00000000..39c0f5d3 --- /dev/null +++ b/kspaceduel/topwidget.h @@ -0,0 +1,33 @@ +#ifndef __MY_TOP_WIDGET_H +#define __MY_TOP_WIDGET_H + +#include + +class PlayerInfo; +class MyMainView; + +class MyTopLevelWidget:public KMainWindow +{ + Q_OBJECT +public: + MyTopLevelWidget(); + void start(); + +private slots: + void setStatusText(const QString & text,int id); + void keySetup(); + void energy(int pn,int en); + void hitPoints(int pn,int hp); + void wins(int pn,int w); + +protected: + void initActions( ); + void initStatusBar( ); + void initGameWidgets(); + +private: + PlayerInfo *playerinfo[2]; + MyMainView *playfield; +}; + +#endif diff --git a/kspaceduel/version.h b/kspaceduel/version.h new file mode 100644 index 00000000..531f6c0a --- /dev/null +++ b/kspaceduel/version.h @@ -0,0 +1 @@ +#define KSPACEDUEL_VERSION "1.1" diff --git a/ktron/AUTHORS b/ktron/AUTHORS new file mode 100644 index 00000000..2dc6d21a --- /dev/null +++ b/ktron/AUTHORS @@ -0,0 +1,2 @@ +Matthias Kiefer +Benjamin Meyer diff --git a/ktron/ChangeLog b/ktron/ChangeLog new file mode 100644 index 00000000..317f9ac4 --- /dev/null +++ b/ktron/ChangeLog @@ -0,0 +1,21 @@ +Changes 1.0: +- added possibility to load background images +- added possibility to give names to players + +Changes 0.5: +- new drawing style: 3d-line +- new improved computerplayer with possibility to change the skill + +Changes 0.4: +- worked over to compile with KDE 1.1.1 +- flickering fixed +- added some options + (block accelerator, disable changing color) +- some finetuning at drawing players and frame + +Changes 0.3.3: +- use of bitBlt(...) to get less flickering +- crashes are now shown by changing the color +- after a crash, the game is blocked for one second +- possiblity to change the size of the players +- possibility to change style of the players diff --git a/ktron/Makefile.am b/ktron/Makefile.am new file mode 100644 index 00000000..e7629bd2 --- /dev/null +++ b/ktron/Makefile.am @@ -0,0 +1,22 @@ +SUBDIRS=pics + +bin_PROGRAMS = ktron +INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes) + +ktron_SOURCES = ai.ui appearance.ui general.ui tron.cpp player.cpp ktron.cpp main.cpp settings.kcfgc +ktron_LDFLAGS = $(all_libraries) $(KDE_RPATH) +ktron_LDADD = $(LIB_KIO) $(LIB_KDEGAMES) +ktron_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +EXTRA_DIST = ktron.desktop + +xdg_apps_DATA = ktron.desktop +kde_kcfg_DATA = ktron.kcfg + +METASOURCES = AUTO + +rcdir = $(kde_datadir)/ktron +rc_DATA = ktronui.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/ktron.pot diff --git a/ktron/README b/ktron/README new file mode 100644 index 00000000..3f48d78d --- /dev/null +++ b/ktron/README @@ -0,0 +1,39 @@ +Hello! + +KTron is a simple tron-clone for the K Desktop Environment. +I think it's just nothing to say about the game: +avoid running into walls, your own tail, and that of your opponent. + +Read the Online-Documentation for more details. + + +Notice: +Parts of the code, especially parts of the algorithm for the computerplayer +are from xtron-1.1 by Rhett D. Jacobs + + +Copyright: + +KTron + +Copyright (C) 1998-2000 by Matthias Kiefer + +KTron is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 or any later version. + +KTron is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +Hope you have a lot of fun :-) + +--------------- +Matthias Kiefer +27 February 2000 diff --git a/ktron/TODO b/ktron/TODO new file mode 100644 index 00000000..6ff85f99 --- /dev/null +++ b/ktron/TODO @@ -0,0 +1,10 @@ +KTron in general does everything I wanted it to do. :-) + +Plans for the future are: +- possibility to create, load, save playfield with walls +- real cool would be to play KTron over the internet + +------------------ +Matthias Kiefer +5/99 + diff --git a/ktron/ai.ui b/ktron/ai.ui new file mode 100644 index 00000000..3abcef57 --- /dev/null +++ b/ktron/ai.ui @@ -0,0 +1,141 @@ + +Ai + + + Ai + + + + 0 + 0 + 302 + 168 + + + + + unnamed + + + 0 + + + 0 + + + + frame9 + + + StyledPanel + + + Raised + + + 0 + + + + unnamed + + + + groupBox3 + + + Computer Controls + + + + unnamed + + + + kcfg_Computerplayer1 + + + Player &1 + + + + + kcfg_Computerplayer2 + + + Player &2 + + + + + + + spacer2_2 + + + Horizontal + + + Expanding + + + + 101 + 20 + + + + + + textLabel15 + + + Intelligence: + + + + + + Beginner + + + + + Average + + + + + Expert + + + + kcfg_Skill + + + 1 + + + + + spacer2 + + + Vertical + + + Expanding + + + + 20 + 60 + + + + + + + + + diff --git a/ktron/appearance.ui b/ktron/appearance.ui new file mode 100644 index 00000000..af50183f --- /dev/null +++ b/ktron/appearance.ui @@ -0,0 +1,302 @@ + +Appearance + + + Appearance + + + + 0 + 0 + 284 + 337 + + + + + unnamed + + + 0 + + + 0 + + + + frame4 + + + StyledPanel + + + Raised + + + 0 + + + + unnamed + + + + textLabel16 + + + Line style: + + + + + + 3D Line + + + + + 3D Rectangles + + + + + Flat + + + + + Circles + + + + kcfg_Style + + + + + groupBox4 + + + Line Size + + + + unnamed + + + + textLabel13 + + + Large + + + AlignVCenter|AlignRight + + + + + textLabel11 + + + Small + + + AlignVCenter|AlignLeft + + + + + textLabel12 + + + Medium + + + AlignCenter + + + + + kcfg_RectSize + + + 4 + + + 16 + + + 3 + + + 3 + + + 10 + + + Horizontal + + + Right + + + 1 + + + + + + + kcfg_BackgroundImageChoice + + + Background + + + + unnamed + + + + BackgroundColorChoice + + + Color: + + + true + + + + + kcfg_BackgroundImage + + + false + + + + + BackgroundImageChoice + + + Image: + + + + + kcfg_Color_Background + + + true + + + + + + + 0 + 0 + 0 + + + + + + + + textLabel7 + + + Player 1 color: + + + + + kcfg_Color_Player1 + + + + + + + 255 + 0 + 0 + + + + + + textLabel8 + + + Player 2 color: + + + + + kcfg_Color_Player2 + + + + + + + 0 + 0 + 255 + + + + + + spacer2 + + + Vertical + + + Expanding + + + + 20 + 16 + + + + + + + + + + BackgroundColorChoice + toggled(bool) + kcfg_Color_Background + setEnabled(bool) + + + BackgroundImageChoice + toggled(bool) + kcfg_BackgroundImage + setEnabled(bool) + + + + kcfg_Style + kcfg_RectSize + kcfg_Color_Player1 + kcfg_Color_Player2 + + + + kurlrequester.h + klineedit.h + kpushbutton.h + kcolorbutton.h + + diff --git a/ktron/general.ui b/ktron/general.ui new file mode 100644 index 00000000..ca492b9e --- /dev/null +++ b/ktron/general.ui @@ -0,0 +1,219 @@ + +General + + + General + + + + 0 + 0 + 400 + 379 + + + + + unnamed + + + 0 + + + 0 + + + + frame4 + + + StyledPanel + + + Plain + + + 0 + + + + unnamed + + + + groupBox1 + + + Behavior + + + + unnamed + + + + kcfg_ChangeWinnerColor + + + &Show winner by changing color + + + + + kcfg_AcceleratorBlocked + + + &Disable acceleration + + + + + kcfg_OppositeDirCrashes + + + &Crash when moving in the opposite direction + + + + + + + groupBox2 + + + Player Names + + + + unnamed + + + + kcfg_NamePlayer1 + + + + + kcfg_NamePlayer2 + + + + + textLabel1 + + + Player 1: + + + + + textLabel2 + + + Player 2: + + + + + + + groupBox3 + + + Speed + + + + unnamed + + + + kcfg_Velocity + + + 1 + + + 9 + + + 1 + + + 5 + + + Horizontal + + + Right + + + 1 + + + + + layout1 + + + + unnamed + + + + textLabel4 + + + Slow + + + + + textLabel5 + + + Default + + + AlignCenter + + + + + textLabel6 + + + Fast + + + AlignVCenter|AlignRight + + + + + + + + + spacer2 + + + Vertical + + + Expanding + + + + 20 + 21 + + + + + + + + + diff --git a/ktron/ktron.cpp b/ktron/ktron.cpp new file mode 100644 index 00000000..67151ff0 --- /dev/null +++ b/ktron/ktron.cpp @@ -0,0 +1,195 @@ +/* **************************************************************************** + This file is part of the game 'KTron' + + Copyright (C) 1998-2000 by Matthias Kiefer + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + *****************************************************************************/ + +#include "ktron.h" + +#include +#include +#include +#include +#include +#include +#include + +// Settings +#include "settings.h" +#include "general.h" +#include "ai.h" +#include "appearance.h" + +#define ID_STATUS_BASE 40 +#define MESSAGE_TIME 2000 +#define WINNING_DIFF 5 + +/** + * Constuctor + */ +KTron::KTron(QWidget *parent, const char *name) : KMainWindow(parent, name) { + playerPoints[0]=playerPoints[1]=0; + + tron=new Tron(this, "Tron"); + connect(tron,SIGNAL(gameEnds(Player)),SLOT(changeStatus(Player))); + setCentralWidget(tron); + tron->setMinimumSize(200,180); + + // create statusbar + statusBar()->insertItem("abcdefghijklmnopqrst: 0 ",ID_STATUS_BASE+1); + statusBar()->insertItem("abcdefghijklmnopqrst: 0 ",ID_STATUS_BASE+2); + + // We match up keyboard events ourselves in Tron::keyPressEvent() + // We must disable the actions, otherwise we don't get the keyPressEvent's + KAction *act; + act = new KAction(i18n("Player 1 Up"), Key_R, 0, 0, actionCollection(), "Pl1Up"); + act->setEnabled(false); + act = new KAction(i18n("Player 1 Down"), Key_F, 0, 0, actionCollection(), "Pl1Down"); + act->setEnabled(false); + act = new KAction(i18n("Player 1 Right"), Key_G, 0, 0, actionCollection(), "Pl1Right"); + act->setEnabled(false); + act = new KAction(i18n("Player 1 Left"), Key_D, 0, 0, actionCollection(), "Pl1Left"); + act->setEnabled(false); + act = new KAction(i18n("Player 1 Accelerator"), Key_A, 0, 0, actionCollection(), "Pl1Ac"); + act->setEnabled(false); + + act = new KAction(i18n("Player 2 Up"), Key_Up, 0, 0, actionCollection(), "Pl2Up"); + act->setEnabled(false); + act = new KAction(i18n("Player 2 Down"), Key_Down, 0, 0, actionCollection(), "Pl2Down"); + act->setEnabled(false); + act = new KAction(i18n("Player 2 Right"), Key_Right, 0, 0, actionCollection(), "Pl2Right"); + act->setEnabled(false); + act = new KAction(i18n("Player 2 Left"), Key_Left, 0, 0, actionCollection(), "Pl2Left"); + act->setEnabled(false); + act = new KAction(i18n("Player 2 Accelerator"), Key_0, 0, 0, actionCollection(), "Pl2Ac"); + act->setEnabled(false); + + tron->setActionCollection(actionCollection()); + + KStdGameAction::pause(tron, SLOT(togglePause()), actionCollection()); + KStdGameAction::gameNew( tron, SLOT( newGame() ), actionCollection() ); + KStdGameAction::quit(this, SLOT( close() ), actionCollection()); + KStdAction::preferences(this, SLOT(showSettings()), actionCollection()); + + setupGUI( KMainWindow::Keys | StatusBar | Save | Create); + loadSettings(); +} + +void KTron::loadSettings() { + playerName[0]=Settings::namePlayer1(); + if ( playerName[0].isEmpty() ) + playerName[0] = i18n("Player 1"); + playerName[1]=Settings::namePlayer2(); + if ( playerName[1].isEmpty() ) + playerName[1] = i18n("Player 2"); + + updateStatusbar(); +} + +void KTron::updateStatusbar(){ + for(int i=0;i<2;i++){ + Player player; + player=(i==0?One:Two); + + QString name; + if(tron->isComputer(Both)) + name=i18n("Computer(%1)").arg(i+1); + else if(tron->isComputer(player)) + name=i18n("Computer"); + else + name=playerName[i]; + QString string = QString("%1: %2").arg(name).arg(playerPoints[i]); + statusBar()->changeItem(string,ID_STATUS_BASE+i+1); + } +} + +void KTron::changeStatus(Player player) { + // if player=Nobody, then new game + if(player==Nobody){ + playerPoints[0]=playerPoints[1]=0; + updateStatusbar(); + return; + } + + if(player==One) + playerPoints[0]++; + else if(player==Two) + playerPoints[1]++; + else if(player==Both){ + playerPoints[0]++; + playerPoints[1]++; + } + + updateStatusbar(); + + if(playerPoints[0]>=WINNING_DIFF && playerPoints[1] < playerPoints[0]-1) + showWinner(One); + else if(playerPoints[1]>=WINNING_DIFF && playerPoints[0] < playerPoints[1]-1) + showWinner(Two); +} + +void KTron::showWinner(Player winner){ + if(tron->isComputer(Both) || (winner != One && winner != Two)) + return; + + QString loserName = i18n("KTron"); + int loser = Two; + if(winner == Two) + loser = One; + if(!tron->isComputer(((Player)loser))) + loserName = playerName[loser]; + + QString winnerName = i18n("KTron"); + if(!tron->isComputer(winner)) + winnerName = playerName[winner]; + + QString message=i18n("%1 has won!").arg(winnerName); + statusBar()->message(message,MESSAGE_TIME); + + message = i18n("%1 has won versus %2 with %3 : %4 points!"); + message=message.arg(winnerName).arg(loserName); + message=message.arg(playerPoints[winner]).arg(playerPoints[loser]); + + KMessageBox::information(this, message, i18n("Winner")); + tron->newGame(); +} + +void KTron::paletteChange(const QPalette &/*oldPalette*/){ + update(); + tron->updatePixmap(); + tron->update(); +} + +/** + * Show Settings dialog. + */ +void KTron::showSettings(){ + if(KConfigDialog::showDialog("settings")) + return; + + KConfigDialog *dialog = new KConfigDialog(this, "settings", Settings::self()); + dialog->addPage(new General(0, "General"), i18n("General"), "package_settings"); + dialog->addPage(new Ai(0, "Ai"), i18n("A.I."), "personal"); + dialog->addPage(new Appearance(0, "Appearance"), i18n("Appearance"), "style"); + connect(dialog, SIGNAL(settingsChanged()), tron, SLOT(loadSettings())); + connect(dialog, SIGNAL(settingsChanged()), this, SLOT(loadSettings())); + dialog->show(); +} + +#include "ktron.moc" + diff --git a/ktron/ktron.desktop b/ktron/ktron.desktop new file mode 100644 index 00000000..20930d16 --- /dev/null +++ b/ktron/ktron.desktop @@ -0,0 +1,66 @@ +[Desktop Entry] +Type=Application +Exec=ktron -caption "%c" %i %m +Icon=ktron +DocPath=ktron/index.html +GenericName=Tron-like Game +GenericName[be]=Ð“ÑƒÐ»ÑŒÐ½Ñ Ð¢Ñ€Ð¾Ð½ +GenericName[bg]=ЛогичеÑка игра +GenericName[bn]=টà§à¦°à¦¨-জাতীয় খেলা +GenericName[br]=C'hoari doare Tron +GenericName[bs]=Igra nalik na Tron +GenericName[ca]=Joc a l'estil Tron +GenericName[cs]=Hra podobná Tronu +GenericName[cy]=Gêm sy'n debyg i Tron +GenericName[da]=Tron-lignende spil +GenericName[de]=Tron-ähnliches Spiel +GenericName[el]=Παιχνίδι παÏόμοιο με το Tron +GenericName[eo]="Tron"-simila ludo +GenericName[es]=Juego similar a Tron +GenericName[et]=Tron +GenericName[eu]=Tron-en antzeko jokoa +GenericName[fa]=بازی شبیهTron +GenericName[fi]=Tron-tyylinen peli +GenericName[fr]=Jeu dans le style de Tron +GenericName[he]=משחק חיקוי Tron +GenericName[hr]=Igra poput Trona +GenericName[hu]=Tron-szerű +GenericName[is]=Leikur sem líkist Tron +GenericName[it]=Gioco simile a Tron +GenericName[ja]=トロンã®ã‚ˆã†ãªã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​ដូច Tron +GenericName[lv]=Tron lÄ«dzÄ«ga spÄ“le +GenericName[mk]=Игра Ñлична на Tron +GenericName[nb]=Tron-lignende spill +GenericName[nds]=Tron-liek Speel +GenericName[ne]=टà¥à¤°à¥‹à¤¨ जसà¥à¤¤à¥ˆ खेल +GenericName[nl]=Tron-achtig spel +GenericName[nn]=Tron-liknande spel +GenericName[pl]=Gra typu Tron +GenericName[pt]=Jogo tipo Tron +GenericName[pt_BR]=Jogo parecido com Tron +GenericName[ru]=Трон +GenericName[se]=Tron-lágan speallu +GenericName[sk]=Hra typu Tron +GenericName[sl]=Igra podobna Tronu +GenericName[sr]=Игра налик на Tron +GenericName[sr@Latn]=Igra nalik na Tron +GenericName[sv]=Tron-liknande spel +GenericName[ta]=டà¯à®°à¯‹à®©à¯ போனà¯à®± விளையாடà¯à®Ÿà¯ +GenericName[uk]=Гра Ñхожа на гру "Трон" +GenericName[zh_TW]=é¡žä¼¼ Tron éŠæˆ² +Terminal=false +Name=KTron +Name[af]=Ktron +Name[be]=Трон +Name[bn]=কে-টà§à¦°à¦¨ +Name[hi]=के-टà¥à¤°à¤¾à¤¨ +Name[nb]=Tron +Name[ne]=केडीई टà¥à¤°à¥‹à¤¨ +Name[sv]=Ktron +Name[ta]=Kடà¯à®°à®¾à®©à¯ +Name[tg]=KТрон +Name[zu]=I-KTron +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;ArcadeGame; diff --git a/ktron/ktron.h b/ktron/ktron.h new file mode 100644 index 00000000..191f2b44 --- /dev/null +++ b/ktron/ktron.h @@ -0,0 +1,67 @@ +/* *************************************************************************** + This file is part of the game 'KTron' + + Copyright (C) 1998-2000 by Matthias Kiefer + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + ****************************************************************************/ + +#ifndef KTRON_H +#define KTRON_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include "tron.h" + +class KAccel; +class KSelectAction; +class Tron; + +/** + * @short The main window of KTron + */ +class KTron : public KMainWindow { + +Q_OBJECT + +public: + KTron(QWidget *parent=0, const char *name=0); + +private: + KAccel *accel; + Tron *tron; + QString playerName[2]; + int playerPoints[2]; + void updateStatusbar(); + +protected: + /** calls tron->updatePixmap to draw frame in the new colors */ + void paletteChange(const QPalette &oldPalette); + +private slots: + void loadSettings(); + /** updates players points in statusbar and checks if someone has won */ + void changeStatus(Player); + + void showWinner(Player winner); + void showSettings(); +}; + +#endif // KTRON_H + diff --git a/ktron/ktron.kcfg b/ktron/ktron.kcfg new file mode 100644 index 00000000..d46c2a76 --- /dev/null +++ b/ktron/ktron.kcfg @@ -0,0 +1,84 @@ + + + + + + + black + + + + red + + + + blue + + + + true + + + + false + + + + false + + + + 10 + + + + 5 + + + + + + + + + + Medium + + + + + + + + + + OLine + + + + + false + + + + + + + + true + + + + false + + + + + + + + + diff --git a/ktron/ktronui.rc b/ktron/ktronui.rc new file mode 100644 index 00000000..2d5a6c99 --- /dev/null +++ b/ktron/ktronui.rc @@ -0,0 +1,3 @@ + + + diff --git a/ktron/main.cpp b/ktron/main.cpp new file mode 100644 index 00000000..17be8fe1 --- /dev/null +++ b/ktron/main.cpp @@ -0,0 +1,59 @@ +/****************************************************************************** + This file is part of the game 'KTron' + + Copyright (C) 1998-2000 by Matthias Kiefer + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + ****************************************************************************/ +#include +#include +#include +#include + +#include "ktron.h" +#include "version.h" + +static const char description[] = I18N_NOOP("A race in hyperspace"); +static const char notice[] = I18N_NOOP("(c) 1998-2000, Matthias Kiefer\n\n" +"Parts of the algorithms for the computer player are from\n" +"xtron-1.1 by Rhett D. Jacobs "); + + +int main(int argc, char* argv[]) +{ + KAboutData aboutData( "ktron", I18N_NOOP("KTron"), + KTRON_VERSION, description, KAboutData::License_GPL, notice); + aboutData.addAuthor("Matthias Kiefer",I18N_NOOP("Original author"), "matthias.kiefer@gmx.de"); + aboutData.addAuthor("Benjamin Meyer",I18N_NOOP("Various improvements"), "ben+ktron@meyerhome.net"); + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + + // used for loading background pixmaps + KImageIO::registerFormats(); + + if(a.isRestored()){ + RESTORE(KTron) + } + else { + KTron *ktron = new KTron(); + a.setMainWidget(ktron); + ktron->show(); + } + return a.exec(); +} + diff --git a/ktron/pics/Makefile.am b/ktron/pics/Makefile.am new file mode 100644 index 00000000..751de516 --- /dev/null +++ b/ktron/pics/Makefile.am @@ -0,0 +1,15 @@ +KDE_ICON=AUTO + +generate: + @echo "Converting SVG files to PNG files. Sizes: 16x16; 22x22; 32x32; 48x48; 64x64; 128x128" + + @for file in *.svg*; do \ + echo "Processing: $$file..."; \ + ksvgtopng 16 16 $$file `basename $$file | sed -e 's/crsc/cr16/' | sed -e 's/svg\|svgz/png/'`; \ + ksvgtopng 22 22 $$file `basename $$file | sed -e 's/crsc/cr22/' | sed -e 's/svg\|svgz/png/'`; \ + ksvgtopng 32 32 $$file `basename $$file | sed -e 's/crsc/cr32/' | sed -e 's/svg\|svgz/png/'`; \ + ksvgtopng 48 48 $$file `basename $$file | sed -e 's/crsc/cr48/' | sed -e 's/svg\|svgz/png/'`; \ + ksvgtopng 64 64 $$file `basename $$file | sed -e 's/crsc/cr64/' | sed -e 's/svg\|svgz/png/'`; \ + ksvgtopng 128 128 $$file `basename $$file | sed -e 's/crsc/cr128/' | sed -e 's/svg\|svgz/png/'`; \ + done + diff --git a/ktron/pics/hi128-app-ktron.png b/ktron/pics/hi128-app-ktron.png new file mode 100644 index 00000000..a2433c02 Binary files /dev/null and b/ktron/pics/hi128-app-ktron.png differ diff --git a/ktron/pics/hi16-app-ktron.png b/ktron/pics/hi16-app-ktron.png new file mode 100644 index 00000000..c6ee2edf Binary files /dev/null and b/ktron/pics/hi16-app-ktron.png differ diff --git a/ktron/pics/hi22-app-ktron.png b/ktron/pics/hi22-app-ktron.png new file mode 100644 index 00000000..2058ee2e Binary files /dev/null and b/ktron/pics/hi22-app-ktron.png differ diff --git a/ktron/pics/hi32-app-ktron.png b/ktron/pics/hi32-app-ktron.png new file mode 100644 index 00000000..2d96ffec Binary files /dev/null and b/ktron/pics/hi32-app-ktron.png differ diff --git a/ktron/pics/hi48-app-ktron.png b/ktron/pics/hi48-app-ktron.png new file mode 100644 index 00000000..5cc088f1 Binary files /dev/null and b/ktron/pics/hi48-app-ktron.png differ diff --git a/ktron/pics/hi64-app-ktron.png b/ktron/pics/hi64-app-ktron.png new file mode 100644 index 00000000..8961e2d0 Binary files /dev/null and b/ktron/pics/hi64-app-ktron.png differ diff --git a/ktron/player.cpp b/ktron/player.cpp new file mode 100644 index 00000000..76cef9de --- /dev/null +++ b/ktron/player.cpp @@ -0,0 +1,57 @@ +/********************************************************************************** + This file is part of the game 'KTron' + + Copyright (C) 1998-2000 by Matthias Kiefer + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + *******************************************************************************/ + +#include "player.h" + +player::player() +{ + computer=false; + score=0; + reset(); + dir=Directions::Up; + last_dir=Directions::None; +} + + +void player::reset() +{ + alive=true; + accelerated=false; + if(computer) + keyPressed=true; + else + keyPressed=false; +} + +void player::setCoordinates(int x, int y) +{ + xCoordinate=x; + yCoordinate=y; +} + +void player::setComputer(bool isComputer) +{ + computer=isComputer; + if(computer) + keyPressed=true; + else keyPressed=false; +} + diff --git a/ktron/player.h b/ktron/player.h new file mode 100644 index 00000000..eefad62c --- /dev/null +++ b/ktron/player.h @@ -0,0 +1,56 @@ +/* ******************************************************************************** + This file is part of the kde-game 'KTron' + + Copyright (C) 1998-2000 by Matthias Kiefer + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + *******************************************************************************/ + +#ifndef PLAYER_H +#define PLAYER_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +namespace Directions +{ + enum Direction{ None, Up, Down, Left, Right}; +} + +/** +* @short This class represents a player with current position and several flags +*/ +class player +{ +public: + player(); + void reset(); + void setCoordinates(int x, int y); + void setComputer(bool computer); + + int xCoordinate,yCoordinate; + int score; + bool alive; + Directions::Direction dir; + Directions::Direction last_dir; + bool accelerated; + bool keyPressed; + bool computer; +}; + +#endif + diff --git a/ktron/settings.kcfgc b/ktron/settings.kcfgc new file mode 100644 index 00000000..de60e440 --- /dev/null +++ b/ktron/settings.kcfgc @@ -0,0 +1,5 @@ +# Code generation options for kconfig_compiler +File=ktron.kcfg +ClassName=Settings +Singleton=true +Mutators=false diff --git a/ktron/tron.cpp b/ktron/tron.cpp new file mode 100644 index 00000000..494d96bc --- /dev/null +++ b/ktron/tron.cpp @@ -0,0 +1,1654 @@ +/**************************************************************************** + This file is part of the game 'KTron' + + Copyright (C) 1998-2000 by Matthias Kiefer + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + ***************************************************************************/ +// Background +#include +#include + +// Normal class +#include + +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "tron.h" + +#define TRON_FRAMESIZE 2 + +/** + * init-functions + **/ + +Tron::Tron(QWidget *parent,const char *name) + : QWidget(parent,name) +{ + pixmap=0; + playfield=0; + beginHint=false; + lookForward=15; + + random.setSeed(0); + + setFocusPolicy(QWidget::StrongFocus); + setBackgroundMode(NoBackground); + + gameBlocked=false; + rectSize=10; + + timer = new QTimer(this,"timer"); + loadSettings(); + connect(timer, SIGNAL(timeout()), SLOT(doMove())); + QTimer::singleShot(15000, this,SLOT(showBeginHint())); +} + +void Tron::loadSettings(){ + setPalette(Settings::color_Background()); + + // Size + int newSize = Settings::rectSize(); + if(newSize!=rectSize){ + rectSize=newSize; + createNewPlayfield(); + } + + reset(); + + // Velocity + setVelocity( Settings::velocity() ); + + // Style + if(pixmap){ + updatePixmap(); + repaint(); + } + + // Backgroundimage + setBackgroundPix(NULL); + if(Settings::backgroundImageChoice()){ + KURL url ( Settings::backgroundImage() ); + if(!url.isEmpty()){ + QString tmpFile; + KIO::NetAccess::download(url, tmpFile, this); + QPixmap pix(tmpFile); + if(!pix.isNull()){ + setBackgroundPix(pix); + } else { + QString msg=i18n("Wasn't able to load wallpaper\n%1"); + msg=msg.arg(tmpFile); + KMessageBox::sorry(this, msg); + } + KIO::NetAccess::removeTempFile(tmpFile); + } + else setBackgroundPix(NULL); + } + setComputerplayer(One, Settings::computerplayer1()); + setComputerplayer(Two, Settings::computerplayer2()); +} + +Tron::~Tron() +{ + if(playfield) + { + delete [] playfield; + } + if(pixmap) + delete pixmap; + delete timer; + +} + +void Tron::createNewPlayfield() +{ + if(playfield) + delete [] playfield; + + if(pixmap) + delete pixmap; + + // field size + fieldWidth=(width()-2*TRON_FRAMESIZE)/rectSize; + fieldHeight=(height()-2*TRON_FRAMESIZE)/rectSize; + + // start positions + playfield=new QMemArray[fieldWidth]; + for(int i=0;ifill(Settings::color_Background()); + + //int min=(fieldWidthstart(velocity); +} + +void Tron::stopGame() +{ + timer->stop(); + gameEnded=true; + players[0].last_dir = Directions::None; + players[1].last_dir = Directions::None; +} + +void Tron::togglePause() // pause or continue game +{ + if(!gameEnded) + { + if(gamePaused) + { + gamePaused=false; + update(); + timer->start(velocity); + } + else + { + gamePaused=true; + timer->stop(); + update(); + } + } +} + +void Tron::showWinner(Player player) +{ + int i,j; + + if(player != Both && Settings::changeWinnerColor()) + { + int winner; + int loser; + if(player==One) + { + winner=PLAYER1; + loser=PLAYER2; + } + else + { + winner=PLAYER2; + loser=PLAYER1; + } + + for(i=0;ifill(Settings::color_Background()); + } + + QPainter p; + p.begin(pixmap); + + // alle Pixel prüfen und evt. zeichnen + for(i=0;icolorGroup().midlight(); + QColor dark=parentWidget()->colorGroup().mid(); + + p.setPen(NoPen); + p.setBrush(light); + p.drawRect(width()-TRON_FRAMESIZE,0,TRON_FRAMESIZE,height()); + p.drawRect(0,height()-TRON_FRAMESIZE,width(),TRON_FRAMESIZE); + p.setBrush(dark); + p.drawRect(0,0,width(),TRON_FRAMESIZE); + p.drawRect(0,0,TRON_FRAMESIZE,height()); + + p.end(); +} + +// draw new player rects +void Tron::paintPlayers() +{ + QPainter p; + p.begin(this); + drawRect(p,players[0].xCoordinate,players[0].yCoordinate); + drawRect(p,players[1].xCoordinate,players[1].yCoordinate); + p.end(); + + p.begin(pixmap); + drawRect(p,players[0].xCoordinate,players[0].yCoordinate); + drawRect(p,players[1].xCoordinate,players[1].yCoordinate); + p.end(); +} + +void Tron::drawRect(QPainter & p, int x, int y) +{ + int xOffset=x*rectSize+(width()-fieldWidth*rectSize)/2; + int yOffset=y*rectSize+(height()-fieldHeight*rectSize)/2; + + int type=playfield[x][y]; + + // find out which color to draw + QColor toDraw; + int player; + if(type&PLAYER1) // check player bit + { + toDraw=Settings::color_Player1(); + player=0; + } + else if(type&PLAYER2) + { + toDraw=Settings::color_Player2(); + player=1; + } + else + { + kdDebug() << "No player defined in Tron::drawRect(...)" << endl; + return; + } + + switch(Settings::style()) + { + case Settings::EnumStyle::Line: + p.setBrush(toDraw); + p.setPen(toDraw); + p.drawRect(xOffset,yOffset,rectSize,rectSize); + break; + case Settings::EnumStyle::OLine: + { + p.setBrush(toDraw); + p.setPen(toDraw); + p.drawRect(xOffset,yOffset,rectSize,rectSize); + p.setPen(toDraw.light()); + if(type&TOP) + { + p.drawLine(xOffset,yOffset,xOffset+rectSize-1,yOffset); + } + if(type&LEFT) + { + p.drawLine(xOffset,yOffset,xOffset,yOffset+rectSize-1); + } + p.setPen(toDraw.dark()); + if(type&RIGHT) + { + p.drawLine(xOffset+rectSize-1,yOffset,xOffset+rectSize-1,yOffset+rectSize-1); + } + if(type&BOTTOM) + { + p.drawLine(xOffset,yOffset+rectSize-1,xOffset+rectSize-1,yOffset+rectSize-1); + } + + break; + } + case Settings::EnumStyle::Circle: + p.setBrush(toDraw); + p.setPen(toDraw); + p.drawEllipse(xOffset ,yOffset ,rectSize,rectSize); + break; + case Settings::EnumStyle::ORect: + p.setBrush(toDraw); + p.setPen(toDraw.light()); + p.drawRect(xOffset,yOffset,rectSize,rectSize); + p.setPen(toDraw.dark()); + p.drawLine(xOffset,yOffset+rectSize-1,xOffset+rectSize-1 + ,yOffset+rectSize-1); + p.drawLine(xOffset+rectSize-1,yOffset,xOffset+rectSize-1,yOffset+rectSize-1); + break; + } +} + +/* *************************************************************** ** +** config functions ** +** *************************************************************** */ + +void Tron::setActionCollection(KActionCollection *a) +{ + actionCollection = a; +} + +void Tron::setBackgroundPix(QPixmap pix) +{ + bgPix=pix; + + if(pixmap!=0){ + updatePixmap(); + // most pictures have colors, that you can read white text + setPalette(QColor("black")); + } +} + +void Tron::setVelocity(int newVel) // set new velocity +{ + velocity=(10-newVel)*15; + + if(!gameEnded && !gamePaused) + timer->changeInterval(velocity); +} + +void Tron::setComputerplayer(Player player, bool flag) { + if(player==One) + players[0].setComputer(flag); + else if(player==Two) + players[1].setComputer(flag); + + if(isComputer(Both)) + QTimer::singleShot(1000,this,SLOT(computerStart())); +} + +bool Tron::isComputer(Player player) +{ + if(player==One) + return players[0].computer; + else if(player==Two) + return players[1].computer; + else if(player==Both) + { + if(players[0].computer && players[1].computer) + return true; + } + + return false; +} + +/* *************************************************************** ** +** moving functions ** +** *************************************************************** */ + +bool Tron::crashed(int playerNr,int xInc, int yInc) const +{ + bool flag; + int newX=players[playerNr].xCoordinate+xInc; + int newY=players[playerNr].yCoordinate+yInc; + + if(newX<0 || newY <0 || newX>=fieldWidth || newY>=fieldHeight) + flag=true; + else if(playfield[newX][newY] != BACKGROUND) + flag=true; + else flag=false; + + return flag; +} + +void Tron::switchDir(int playerNr,Directions::Direction newDirection) +{ + if(playerNr!=0 && playerNr != 1) + { + kdDebug() << "wrong playerNr" << endl; + return; + } + + if (Settings::oppositeDirCrashes()==false) + { + if (newDirection==Directions::Up && players[playerNr].last_dir==Directions::Down) + return; + if (newDirection==Directions::Down && players[playerNr].last_dir==Directions::Up) + return; + if (newDirection==Directions::Left && players[playerNr].last_dir==Directions::Right) + return; + if (newDirection==Directions::Right && players[playerNr].last_dir==Directions::Left) + return; + } + + players[playerNr].dir=newDirection; +} + +void Tron::updateDirections(int playerNr) +{ + if(playerNr==-1 || playerNr==0) + { + int x=players[0].xCoordinate; + int y=players[0].yCoordinate; + + // necessary for drawing the 3d-line + switch(players[0].dir) + { + // unset drawing flags in the moving direction + case Directions::Up: + { + playfield[x][y] &= (~TOP); + break; + } + case Directions::Down: + playfield[x][y] &= (~BOTTOM); + break; + case Directions::Right: + playfield[x][y] &= (~RIGHT); + break; + case Directions::Left: + playfield[x][y] &= (~LEFT); + break; + default: + break; + } + players[0].last_dir = players[0].dir; + + } + if(playerNr==-1 || playerNr==1) + { + int x=players[1].xCoordinate; + int y=players[1].yCoordinate; + + // necessary for drawing the 3d-line + switch(players[1].dir) + { + // unset drawing flags in the moving direction + case Directions::Up: + { + playfield[x][y] &= (~TOP); + break; + } + case Directions::Down: + playfield[x][y] &= (~BOTTOM); + break; + case Directions::Right: + playfield[x][y] &= (~RIGHT); + break; + case Directions::Left: + playfield[x][y] &= (~LEFT); + break; + default: + break; + } + players[1].last_dir = players[1].dir; + + } + + paintPlayers(); +} + +/* *************************************************************** ** +** Events ** +** *************************************************************** */ + +void Tron::paintEvent(QPaintEvent *e) +{ + bitBlt(this,e->rect().topLeft(),pixmap,e->rect()); + + // if game is paused, print message + if(gamePaused) + { + QString message=i18n("Game paused"); + QPainter p(this); + QFontMetrics fm=p.fontMetrics(); + int w=fm.width(message); + p.drawText(width()/2-w/2,height()/2,message); + } + + // If game ended, print "Crash!" + else if(gameEnded) + { + QString message=i18n("Crash!"); + QPainter p(this); + int w=p.fontMetrics().width(message); + int h=p.fontMetrics().height(); + for(int i=0;i<2;i++) + { + if(!players[i].alive) + { + int x=players[i].xCoordinate*rectSize; + int y=players[i].yCoordinate*rectSize; + while(x<0) x+=rectSize; + while(x+w>width()) x-=rectSize; + while(y-h<0) y+=rectSize; + while(y>height()) y-=rectSize; + p.drawText(x,y,message); + } + } + + // draw begin hint + if(beginHint) + { + QString hint=i18n("Press any of your direction keys to start!"); + int x=p.fontMetrics().width(hint); + x=(width()-x)/2; + int y=height()/2; + + p.drawText(x,y,hint); + } + } +} + +void Tron::resizeEvent(QResizeEvent *) +{ + createNewPlayfield(); + reset(); +} + +void Tron::keyPressEvent(QKeyEvent *e) +{ + KKey key(e); + if(!players[1].computer) + { + if(actionCollection->action("Pl2Up")->shortcut().contains(key)) + { + switchDir(1,Directions::Up); + players[1].keyPressed=true; + } + else if(actionCollection->action("Pl2Left")->shortcut().contains(key)) + { + switchDir(1,Directions::Left); + players[1].keyPressed=true; + } + else if(actionCollection->action("Pl2Right")->shortcut().contains(key)) + { + switchDir(1,Directions::Right); + players[1].keyPressed=true; + } + else if(actionCollection->action("Pl2Down")->shortcut().contains(key)) + { + switchDir(1,Directions::Down); + players[1].keyPressed=true; + } + else if(actionCollection->action("Pl2Ac")->shortcut().contains(key)) + { + if(!Settings::acceleratorBlocked()) + players[1].accelerated=true; + + } + } + + if(!players[0].computer) + { + if(actionCollection->action("Pl1Left")->shortcut().contains(key)) + { + switchDir(0,Directions::Left); + players[0].keyPressed=true; + } + else if(actionCollection->action("Pl1Right")->shortcut().contains(key)) + { + switchDir(0,Directions::Right); + players[0].keyPressed=true; + } + else if(actionCollection->action("Pl1Up")->shortcut().contains(key)) + { + switchDir(0,Directions::Up); + players[0].keyPressed=true; + } + else if(actionCollection->action("Pl1Down")->shortcut().contains(key)) + { + switchDir(0,Directions::Down); + players[0].keyPressed=true; + } + else if(actionCollection->action("Pl1Ac")->shortcut().contains(key)) + { + if(!Settings::acceleratorBlocked()) + players[0].accelerated=true; + } + } + + e->ignore(); // if key is unknown: ignore + + // if both players press keys at the same time, start game... + if(gameEnded && !gameBlocked) + { + if(players[0].keyPressed && players[1].keyPressed) + { + reset(); + startGame(); + } + } + // ...or continue + else if(gamePaused) + { + if(players[0].keyPressed && players[1].keyPressed) + { + togglePause(); + } + } +} + +void Tron::keyReleaseEvent(QKeyEvent * e) +{ + KKey key(e); + + if(!players[1].computer) + { + if(actionCollection->action("Pl2Ac")->shortcut().contains(key)) + { + players[1].accelerated=false; + return; + } + else if(actionCollection->action("Pl2Left")->shortcut().contains(key)) + { + players[1].keyPressed=false; + return; + } + else if(actionCollection->action("Pl2Right")->shortcut().contains(key)) + { + players[1].keyPressed=false; + return; + } + else if(actionCollection->action("Pl2Up")->shortcut().contains(key)) + { + players[1].keyPressed=false; + return; + } + else if(actionCollection->action("Pl2Down")->shortcut().contains(key)) + { + players[1].keyPressed=false; + return; + } + } + + if(!players[0].computer) + { + if(actionCollection->action("Pl1Left")->shortcut().contains(key)) + { + players[0].keyPressed=false; + return; + } + else if(actionCollection->action("Pl1Right")->shortcut().contains(key)) + { + players[0].keyPressed=false; + return; + } + else if(actionCollection->action("Pl1Up")->shortcut().contains(key)) + { + players[0].keyPressed=false; + return; + } + else if(actionCollection->action("Pl1Down")->shortcut().contains(key)) + { + players[0].keyPressed=false; + return; + } + else if(actionCollection->action("Pl1Ac")->shortcut().contains(key)) + { + players[0].accelerated=false; + return; + } + } + + e->ignore(); // if pressed key is unknown, ignore it + +} + +// if playingfield loses keyboard focus, pause game +void Tron::focusOutEvent(QFocusEvent *) +{ + if(!gameEnded && !gamePaused) + { + togglePause(); + } +} + +/* *************************************************************** ** +** slots ** +** *************************************************************** */ + +void Tron::unblockGame() +{ + gameBlocked=false; +} + +void Tron::showBeginHint() +{ + if(gameEnded) + { + // show only at the beginning of a game + if(players[0].score==0 && players[1].score==0) + { + beginHint=true; + repaint(); + } + } +} + +// doMove() is called from QTimer +void Tron::doMove() +{ + int i; + for(i=0;i<2;i++) + { + // Überprüfen, ob Acceleratortaste gedrückt wurde... + if(players[i].accelerated) + { + updateDirections(i); + + int newType; // determine type of rect to set + if(i==0) + { + newType=PLAYER1; + } + else + { + newType=PLAYER2; + } + switch(players[i].dir) + { + case Directions::Up: + if(crashed(i,0,-1)) + players[i].alive=false; + else + { + players[i].yCoordinate--; + newType|=(TOP | LEFT | RIGHT); + } + break; + case Directions::Down: + if(crashed(i,0,1)) + players[i].alive=false; + else + { + players[i].yCoordinate++; + newType |= (BOTTOM | LEFT | RIGHT); + } + break; + case Directions::Left: + if(crashed(i,-1,0)) + players[i].alive=false; + else + { + players[i].xCoordinate--; + newType |= (LEFT | TOP | BOTTOM); + } + break; + case Directions::Right: + if(crashed(i,1,0)) + players[i].alive=false; + else + { + players[i].xCoordinate++; + newType |= (RIGHT | TOP | BOTTOM); + } + break; + default: + break; + } + if(players[i].alive) + playfield[players[i].xCoordinate][players[i].yCoordinate]=newType; + } + } + + if(players[0].accelerated || players[1].accelerated) + { + /* player collision check */ + if(!players[1].alive) + { + int xInc=0,yInc=0; + switch(players[1].dir) + { + case Directions::Left: + xInc = -1; + break; + case Directions::Right: + xInc = 1; + break; + case Directions::Up: + yInc = -1; + break; + case Directions::Down: + yInc = 1; + break; + default: + break; + } + if ((players[1].xCoordinate+xInc) == players[0].xCoordinate) + if ((players[1].yCoordinate+yInc) == players[0].yCoordinate) + { + players[0].alive=false; + } + } + + paintPlayers(); + + // crashtest + if(!players[0].alive && !players[1].alive) + { + stopGame(); + players[0].score++; + players[1].score++; + showWinner(Both); + } + else + { + for(i=0;i<2;i++) + { + if(!players[i].alive) + { + stopGame(); + showWinner((i==0)? Two:One); + players[i].score++; + } + } + } + + + if(gameEnded) + { + //this is for waiting 0,5s before starting next game + gameBlocked=true; + QTimer::singleShot(1000,this,SLOT(unblockGame())); + return; + } + } + + // neue Spielerstandorte festlegen + for(i=0;i<2;i++) + { + if(players[i].computer) + think(i); + } + + updateDirections(); + + for(i=0;i<2;i++) + { + int newType; + if(i==0) + newType=PLAYER1; + else + newType=PLAYER2; + + switch(players[i].dir) + { + case Directions::Up: + if(crashed(i,0,-1)) + players[i].alive=false; + else + { + players[i].yCoordinate--; + newType |= (TOP | RIGHT | LEFT); + } + break; + case Directions::Down: + if(crashed(i,0,1)) + players[i].alive=false; + else + { + players[i].yCoordinate++; + newType |= (BOTTOM | RIGHT | LEFT); + } + break; + case Directions::Left: + if(crashed(i,-1,0)) + players[i].alive=false; + else + { + players[i].xCoordinate--; + newType |= (LEFT | TOP | BOTTOM); + } + break; + case Directions::Right: + if(crashed(i,1,0)) + players[i].alive=false; + else + { + players[i].xCoordinate++; + newType |= (RIGHT | TOP | BOTTOM); + } + break; + default: + break; + + } + if(players[i].alive) + playfield[players[i].xCoordinate][players[i].yCoordinate]=newType; + } + + /* player collision check */ + if(!players[1].alive) + { + int xInc=0,yInc=0; + switch(players[1].dir) + { + case Directions::Left: + xInc = -1; break; + case Directions::Right: + xInc = 1; break; + case Directions::Up: + yInc = -1; break; + case Directions::Down: + yInc = 1; break; + default: + break; + } + if ((players[1].xCoordinate+xInc) == players[0].xCoordinate) + if ((players[1].yCoordinate+yInc) == players[0].yCoordinate) + { + players[0].alive=false; + } + } + + paintPlayers(); + + if(!players[0].alive && !players[1].alive) + { + stopGame(); + players[0].score++; + players[1].score++; + showWinner(Both); + } + else + for(i=0;i<2;i++) + { + // crashtests + if(!players[i].alive) + { + stopGame(); + showWinner((i==0)? Two:One); + players[i].score++; + } + } + + if(gameEnded) + { + //this is for waiting 1s before starting next game + gameBlocked=true; + QTimer::singleShot(1000,this,SLOT(unblockGame())); + } + +} + +/* *************************************************************** ** +** algoritm for the computerplayer ** +** *************************************************************** */ + +// This part is partly ported from +// xtron-1.1 by Rhett D. Jacobs +void Tron::think(int playerNr) +{ +if(Settings::skill() != Settings::EnumSkill::Easy) +{ + int opponent=(playerNr==1)? 0 : 1; + + // determines left and right side + Directions::Direction sides[2]; + // increments for moving to the different sides + int flags[6]={0,0,0,0,0,0}; + int index[2]; + // distances to barrier + int dis_forward, dis_left, dis_right; + + dis_forward = dis_left = dis_right = 1; + + + switch (players[playerNr].dir) + { + case Directions::Left: + //forward flags + flags[0] = -1; + flags[1] = 0; + + //left flags + flags[2] = 0; + flags[3] = 1; + + // right flags + flags[4] = 0; + flags[5] = -1; + + //turns to either side + sides[0] = Directions::Down; + sides[1] = Directions::Up; + break; + case Directions::Right: + flags[0] = 1; + flags[1] = 0; + flags[2] = 0; + flags[3] = -1; + flags[4] = 0; + flags[5] = 1; + sides[0] = Directions::Up; + sides[1] = Directions::Down; + break; + case Directions::Up: + flags[0] = 0; + flags[1] = -1; + flags[2] = -1; + flags[3] = 0; + flags[4] = 1; + flags[5] = 0; + sides[0] = Directions::Left; + sides[1] = Directions::Right; + break; + case Directions::Down: + flags[0] = 0; + flags[1] = 1; + flags[2] = 1; + flags[3] = 0; + flags[4] = -1; + flags[5] = 0; + sides[0] = Directions::Right; + sides[1] = Directions::Left; + break; + default: + break; + + } + + // check forward + index[0] = players[playerNr].xCoordinate+flags[0]; + index[1] = players[playerNr].yCoordinate+flags[1]; + while (index[0] < fieldWidth && index[0] >= 0 && + index[1] < fieldHeight && index[1] >= 0 && + playfield[index[0]][index[1]] == BACKGROUND) + { + dis_forward++; + index[0] += flags[0]; + index[1] += flags[1]; + } + + // check left + index[0] = players[playerNr].xCoordinate+flags[2]; + index[1] = players[playerNr].yCoordinate+flags[3]; + while (index[0] < fieldWidth && index[0] >= 0 && + index[1] < fieldHeight && index[1] >= 0 && + playfield[index[0]][index[1]] == BACKGROUND) { + dis_left++; + index[0] += flags[2]; + index[1] += flags[3]; + } + + // check right + index[0] = players[playerNr].xCoordinate+flags[4]; + index[1] = players[playerNr].yCoordinate+flags[5]; + while (index[0] < fieldWidth && index[0] >= 0 && + index[1] < fieldHeight && index[1] >= 0 && + playfield[index[0]][index[1]] == BACKGROUND) { + dis_right++; + index[0] += flags[4]; + index[1] += flags[5]; + } + + // distances to opponent + int hor_dis=0; // negative is opponent to the right + int vert_dis=0; // negative is opponent to the bottom + hor_dis=players[playerNr].xCoordinate-players[opponent].xCoordinate; + vert_dis=players[playerNr].yCoordinate-players[opponent].yCoordinate; + + int opForwardDis=0; // negative is to the back + int opSideDis=0; // negative is to the left + bool opMovesOppositeDir=false; + bool opMovesSameDir=false; + bool opMovesRight=false; + bool opMovesLeft=false; + + switch(players[playerNr].dir) + { + case Directions::Up: + opForwardDis=vert_dis; + opSideDis=-hor_dis; + if(players[opponent].dir==Directions::Down) + opMovesOppositeDir=true; + else if(players[opponent].dir==Directions::Up) + opMovesSameDir=true; + else if(players[opponent].dir==Directions::Left) + opMovesLeft=true; + else if(players[opponent].dir==Directions::Right) + opMovesRight=true; + break; + case Directions::Down: + opForwardDis=-vert_dis; + opSideDis=hor_dis; + if(players[opponent].dir==Directions::Up) + opMovesOppositeDir=true; + else if(players[opponent].dir==Directions::Down) + opMovesSameDir=true; + else if(players[opponent].dir==Directions::Left) + opMovesRight=true; + else if(players[opponent].dir==Directions::Right) + opMovesLeft=true; + break; + case Directions::Left: + opForwardDis=hor_dis; + opSideDis=vert_dis; + if(players[opponent].dir==Directions::Right) + opMovesOppositeDir=true; + else if(players[opponent].dir==Directions::Left) + opMovesSameDir=true; + else if(players[opponent].dir==Directions::Down) + opMovesLeft=true; + else if(players[opponent].dir==Directions::Up) + opMovesRight=true; + break; + case Directions::Right: + opForwardDis=-hor_dis; + opSideDis=-vert_dis; + if(players[opponent].dir==Directions::Left) + opMovesOppositeDir=true; + else if(players[opponent].dir==Directions::Right) + opMovesSameDir=true; + else if(players[opponent].dir==Directions::Up) + opMovesLeft=true; + else if(players[opponent].dir==Directions::Down) + opMovesRight=true; + break; + default: + break; + + } + + int doPercentage = 100; + switch(Settings::skill()) + { + case Settings::EnumSkill::Easy: + // Never reached + break; + + case Settings::EnumSkill::Medium: + doPercentage=5; + break; + + case Settings::EnumSkill::Hard: + doPercentage=90; + break; + } + + // if opponent moves the opposite direction as we + if(opMovesOppositeDir) + { + // if opponent is in front + if(opForwardDis>0) + { + // opponent is to the right and we have the chance to block the way + if(opSideDis>0 && opSideDis < opForwardDis && opSideDis < dis_right && opForwardDis < lookForward) + { + if ((int)random.getLong(100) <= doPercentage || dis_forward==1) + switchDir(playerNr,sides[1]); // turn right + } + // opponent is to the left and we have the chance to block the way + else if(opSideDis<0 && -opSideDis < opForwardDis && -opSideDis < dis_left && opForwardDis < lookForward) + { + if ((int)random.getLong(100) <= doPercentage || dis_forward==1) + switchDir(playerNr,sides[0]); // turn left + } + // if we can do nothing, go forward + else if(dis_forward < lookForward) + { + dis_forward = 100 - 100/dis_forward; + + if(!(dis_left == 1 && dis_right == 1)) + if ((int)random.getLong(100) >= dis_forward || dis_forward == 1) + changeDirection(playerNr,dis_right,dis_left); + } + } + // opponent is in back of us and moves away: do nothing + else if(dis_forward < lookForward) + { + dis_forward = 100 - 100/dis_forward; + + if(!(dis_left == 1 && dis_right == 1)) + if ((int)random.getLong(100) >= dis_forward || dis_forward == 1) + changeDirection(playerNr,dis_right,dis_left); + } + } // end if(opMovesOppositeDir) + + else if(opMovesSameDir) + { + // if opponent is to the back + if(opForwardDis < 0) + { + // opponent is to the right and we have the chance to block the way + if(opSideDis>0 && opSideDis < -opForwardDis && opSideDis < dis_right) + { + if ((int)random.getLong(100) <= doPercentage || dis_forward==1) + switchDir(playerNr,sides[1]); // turn right + } + // opponent is to the left and we have the chance to block the way + else if(opSideDis<0 && -opSideDis < -opForwardDis && -opSideDis < dis_left) + { + if ((int)random.getLong(100) <= doPercentage || dis_forward==1) + switchDir(playerNr,sides[0]); // turn left + } + // if we can do nothing, go forward + else if(dis_forward < lookForward) + { + dis_forward = 100 - 100/dis_forward; + + if(!(dis_left == 1 && dis_right == 1)) + if ((int)random.getLong(100) >= dis_forward || dis_forward == 1) + changeDirection(playerNr,dis_right,dis_left); + } + } + // opponent is in front of us and moves away + else if(dis_forward < lookForward) + { + dis_forward = 100 - 100/dis_forward; + + if(!(dis_left == 1 && dis_right == 1)) + if ((int)random.getLong(100) >= dis_forward || dis_forward == 1) + changeDirection(playerNr,dis_right,dis_left); + } + } // end if(opMovesSameDir) + + else if(opMovesRight) + { + // opponent is in front of us + if(opForwardDis>0) + { + // opponent is to the left + if(opSideDis < 0 && -opSideDis < opForwardDis && -opSideDis < dis_left) + { + if(opForwardDis < lookForward && dis_left > lookForward) + { + if ((int)random.getLong(100) <= doPercentage/2 || dis_forward==1) + changeDirection(playerNr,dis_right,dis_left); + } + else if(dis_forward < lookForward) + { + dis_forward = 100 - 100/dis_forward; + + if(!(dis_left == 1 && dis_right == 1)) + if ((int)random.getLong(100) >= dis_forward || dis_forward == 1) + changeDirection(playerNr,dis_right,dis_left); + } + } + // op is to the right and moves away, but maybe we can block him + else if(opSideDis>=0 && opSideDis < dis_right) + { + if(opForwardDis < lookForward && dis_right>lookForward) + { + if ((int)random.getLong(100) <= doPercentage/2 || dis_forward==1) + switchDir(playerNr,sides[1]); // turn right + } + else if(dis_forward < lookForward) + { + dis_forward = 100 - 100/dis_forward; + + if(!(dis_left == 1 && dis_right == 1)) + if ((int)random.getLong(100) >= dis_forward || dis_forward == 1) + changeDirection(playerNr,dis_right,dis_left); + } + } + else if(dis_forward < lookForward) + { + dis_forward = 100 - 100/dis_forward; + + if(!(dis_left == 1 && dis_right == 1)) + if ((int)random.getLong(100) >= dis_forward || dis_forward == 1) + changeDirection(playerNr,dis_right,dis_left); + } + } + // opponent is in the back of us + else + { + // opponent is right from us and we already blocked him + if(opSideDis>0 && opForwardDis < lookForward && opSideDis < dis_right) + { + if ((int)random.getLong(100) <= doPercentage/2 || dis_forward==1) + changeDirection(playerNr,dis_right,dis_left); + } + else if(dis_forward= dis_forward || dis_forward == 1) + changeDirection(playerNr,dis_right,dis_left); + } + } + } // end if(opMovesRight) + + else if(opMovesLeft) + { + // opponent is in front of us + if(opForwardDis>0) + { + // opponent is to the right, moves towards us and could block us + if(opSideDis > 0 && opSideDis < opForwardDis && opSideDis < dis_right) + { + if(opForwardDis < lookForward && dis_right>lookForward) + { + if ((int)random.getLong(100) <= doPercentage/2 || dis_forward==1) + changeDirection(playerNr,dis_right,dis_left); + } + else if(dis_forward < lookForward) + { + dis_forward = 100 - 100/dis_forward; + + if(!(dis_left == 1 && dis_right == 1)) + if ((int)random.getLong(100) >= dis_forward || dis_forward == 1) + changeDirection(playerNr,dis_right,dis_left); + } + } + // op is to the left and moves away, but maybe we can block him + else if(opSideDis<=0 && opSideDis < dis_left) + { + if(opForwardDis < lookForward && dis_left>lookForward) + { + if ((int)random.getLong(100) <= doPercentage/2 || dis_forward==1) + switchDir(playerNr,sides[0]); // turn left + } + else if(dis_forward < lookForward) + { + dis_forward = 100 - 100/dis_forward; + + if(!(dis_left == 1 && dis_right == 1)) + if ((int)random.getLong(100) >= dis_forward || dis_forward == 1) + changeDirection(playerNr,dis_right,dis_left); + } + + } + else if(dis_forward < lookForward) + { + dis_forward = 100 - 100/dis_forward; + + if(!(dis_left == 1 && dis_right == 1)) + if ((int)random.getLong(100) >= dis_forward || dis_forward == 1) + changeDirection(playerNr,dis_right,dis_left); + } + } + // opponent is in the back of us + else //if(opForwardDis<=0) + { + // opponent is left from us and we already blocked him + if(opSideDis<0 && opForwardDis < lookForward && -opSideDis < dis_left) + { + if ((int)random.getLong(100) <= doPercentage/2 || dis_forward==1) + changeDirection(playerNr,dis_right,dis_left); + } + else if(dis_forward= dis_forward || dis_forward == 1) + changeDirection(playerNr,dis_right,dis_left); + } + } + } // end if(opMovesLeft) + +} +// This part is completely ported from +// xtron-1.1 by Rhett D. Jacobs +else // Settings::skill() == Settings::EnumSkill::Easy +{ + Directions::Direction sides[2]; + int flags[6] = {0,0,0,0,0,0}; + int index[2]; + int dis_forward, dis_left, dis_right; + + dis_forward = dis_left = dis_right = 1; + + switch (players[playerNr].dir) { + case Directions::Left: + + //forward flags + flags[0] = -1; + flags[1] = 0; + + //left flags + flags[2] = 0; + flags[3] = 1; + + // right flags + flags[4] = 0; + flags[5] = -1; + + //turns to either side + sides[0] = Directions::Down; + sides[1] = Directions::Up; + break; + case Directions::Right: + flags[0] = 1; + flags[1] = 0; + flags[2] = 0; + flags[3] = -1; + flags[4] = 0; + flags[5] = 1; + sides[0] = Directions::Up; + sides[1] = Directions::Down; + break; + case Directions::Up: + flags[0] = 0; + flags[1] = -1; + flags[2] = -1; + flags[3] = 0; + flags[4] = 1; + flags[5] = 0; + sides[0] = Directions::Left; + sides[1] = Directions::Right; + break; + case Directions::Down: + flags[0] = 0; + flags[1] = 1; + flags[2] = 1; + flags[3] = 0; + flags[4] = -1; + flags[5] = 0; + sides[0] = Directions::Right; + sides[1] = Directions::Left; + break; + default: + break; + } + + // check forward + index[0] = players[playerNr].xCoordinate+flags[0]; + index[1] = players[playerNr].yCoordinate+flags[1]; + while (index[0] < fieldWidth && index[0] >= 0 && + index[1] < fieldHeight && index[1] >= 0 && + playfield[index[0]][index[1]] == BACKGROUND) { + dis_forward++; + index[0] += flags[0]; + index[1] += flags[1]; + } + + if (dis_forward < lookForward) + { + dis_forward = 100 - 100/dis_forward; + + // check left + index[0] = players[playerNr].xCoordinate+flags[2]; + index[1] = players[playerNr].yCoordinate+flags[3]; + while (index[0] < fieldWidth && index[0] >= 0 && + index[1] < fieldHeight && index[1] >= 0 && + playfield[index[0]][index[1]] == BACKGROUND) { + dis_left++; + index[0] += flags[2]; + index[1] += flags[3]; + } + + // check right + index[0] = players[playerNr].xCoordinate+flags[4]; + index[1] = players[playerNr].yCoordinate+flags[5]; + while (index[0] < fieldWidth && index[0] >= 0 && + index[1] < fieldHeight && index[1] >= 0 && + playfield[index[0]][index[1]] == BACKGROUND) { + dis_right++; + index[0] += flags[4]; + index[1] += flags[5]; + } + if(!(dis_left == 1 && dis_right == 1)) + if ((int)random.getLong(100) >= dis_forward || dis_forward == 0) { + + // change direction + if ((int)random.getLong(100) <= (100*dis_left)/(dis_left+dis_right)) + if (dis_left != 1) + + // turn to the left + switchDir(playerNr,sides[0]); + else + + // turn to the right + switchDir(playerNr,sides[1]); + else + if (dis_right != 1) + + // turn to the right + switchDir(playerNr,sides[1]); + else + + // turn to the left + switchDir(playerNr,sides[0]); + } + } + } +} + +void Tron::changeDirection(int playerNr,int dis_right,int dis_left) +{ + Directions::Direction currentDir=players[playerNr].dir; + Directions::Direction sides[2]; + switch (currentDir) + { + case Directions::Left: + //turns to either side + sides[0] = Directions::Down; + sides[1] = Directions::Up; + break; + case Directions::Right: + sides[0] = Directions::Up; + sides[1] = Directions::Down; + break; + case Directions::Up: + sides[0] = Directions::Left; + sides[1] = Directions::Right; + break; + case Directions::Down: + sides[0] = Directions::Right; + sides[1] = Directions::Left; + break; + default: + break; + + } + + if(!(dis_left == 1 && dis_right == 1)) + { + // change direction + if ((int)random.getLong(100) <= (100*dis_left)/(dis_left+dis_right)) + { + if (dis_left != 1) + // turn to the left + switchDir(playerNr,sides[0]); + else + // turn to the right + switchDir(playerNr,sides[1]); + } + else + { + if (dis_right != 1) + // turn to the right + switchDir(playerNr,sides[1]); + else + // turn to the left + switchDir(playerNr,sides[0]); + } + } +} + +#include "tron.moc" + diff --git a/ktron/tron.h b/ktron/tron.h new file mode 100644 index 00000000..ebfc1d56 --- /dev/null +++ b/ktron/tron.h @@ -0,0 +1,178 @@ +/* ******************************************************************************** + This file is part of the game 'KTron' + + Copyright (C) 1998-2000 by Matthias Kiefer + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + *******************************************************************************/ + +#ifndef TRON_H +#define TRON_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +class KActionCollection; + +#include "player.h" + +enum Player{One,Two,Both,Nobody}; +// Bits that defines the rect and which sides to draw +enum {BACKGROUND=0, PLAYER1=1,PLAYER2=2,TOP=4,BOTTOM=8,LEFT=16,RIGHT=32}; + +/** +* @short The playingfield +*/ +class Tron : public QWidget +{ + Q_OBJECT + +public: + Tron(QWidget *parent=0, const char *name=0); + ~Tron(); + void setActionCollection(KActionCollection*); + void updatePixmap(); + void setBackgroundPix(QPixmap); + void setComputerplayer(Player player, bool); + bool isComputer(Player player); + void setVelocity(int); + void setRectSize(int newSize); + +public slots: + /** Starts a new game. The difference to reset is, that the players + * points are set to zero. Emits gameEnds(Nobody). + */ + void newGame(); + void togglePause(); + + /** + * checks if both players are computer and starts the game + */ + void computerStart(); + + void loadSettings(); + +signals: + void gameEnds(Player loser); + void gameReset(); + +protected: + /** bitBlt´s the rect that has to be updated from the + * bufferpixmap on the screen and writes eventually text + */ + void paintEvent(QPaintEvent *); + /** resets game and creates a new playingfield */ + void resizeEvent(QResizeEvent *); + void keyPressEvent(QKeyEvent *); + void keyReleaseEvent(QKeyEvent *); + /** pauses game */ + void focusOutEvent(QFocusEvent *); + +private: + /** Stores key shortcuts */ + KActionCollection* actionCollection; + /** Drawing buffer */ + QPixmap *pixmap; + /** The playingfield */ + QMemArray *playfield; + /** game status flag */ + bool gamePaused; + /** game status flag */ + bool gameEnded; + /** used for waiting after game ended */ + bool gameBlocked; + /** flag, if a string should be drawn, how to start the game */ + bool beginHint; + /** Height of the playingfield in number of rects*/ + int fieldHeight; + /** Width of the playingfield in number of rects*/ + int fieldWidth; + QTimer *timer; + player players[2]; + + /** Backgroundpixmap **/ + QPixmap bgPix; + + /** time in ms between two moves */ + int velocity; + /** size of the rects */ + int rectSize; + + /** The random sequence generator **/ + KRandomSequence random; + + // Options + /** determines level of computerplayer */ + int lookForward; + + // Funktionen + /** resets the game */ + void reset(); + /** starts the game timer */ + void startGame(); + /** stops the game timer */ + void stopGame(); + /** creates a new playfield and a bufferpixmap */ + void createNewPlayfield(); + /** paints players at current player coordinates */ + void paintPlayers(); + /** draws a rect in current TronStyle at position x,y of the playingfield */ + void drawRect(QPainter & p, int x, int y); + /** emits gameEnds(Player) and displays the winner by changing color*/ + void showWinner(Player winner); + + /** calculates if player playerNr would crash + * if he moves xInc in x-direction and yInc in y-direction + */ + bool crashed(int playerNr,int xInc, int yInc) const; + /** calculates if player playerNr should change direction */ + void think(int playerNr); + void changeDirection(int playerNr,int dis_right,int dis_left); + + /** sets the direction of player playerNr to newDirection */ + void switchDir(int playerNr,Directions::Direction newDirection); + /** + * updates the the rect at current position to not draw a + * border in the direction of the next step. + * (only used in mode OLine) + * + * -1 means update both players + */ + void updateDirections(int playerNr=-1); + +private slots: + /** + * This is the main function of KTron. + * It checkes if an accelerator is pressed and than moves this player + * forward. Then it checkes if a crash occurred. + * If no crash happen it moves both players forward and checks again + * if someone crashed. + */ + void doMove(); + void unblockGame(); + void showBeginHint(); +}; + + +#endif // TRON_H + diff --git a/ktron/version.h b/ktron/version.h new file mode 100644 index 00000000..71fdda43 --- /dev/null +++ b/ktron/version.h @@ -0,0 +1 @@ +#define KTRON_VERSION "1.1" diff --git a/ktuberling/Makefile.am b/ktuberling/Makefile.am new file mode 100644 index 00000000..55ed363e --- /dev/null +++ b/ktuberling/Makefile.am @@ -0,0 +1,38 @@ +# this 10 paths are KDE specific. Use them: +# kde_htmldir Where your docs should go to. (contains lang subdirs) +# kde_appsdir Where your application file (.desktop) should go to. +# kde_icondir Where your icon should go to. +# kde_sounddir Where system sounds should go to. +# kde_datadir Where you install application data. (Use a subdir) +# kde_locale Where translation files should go to.(contains lang subdirs) +# kde_cgidir Where cgi-bin executables should go to. +# kde_confdir Where config files should go to. +# kde_mimedir Where mimetypes should go to. +# kde_toolbardir Where general toolbar icons should go to. +# kde_wallpaperdir Where general wallpapers should go to. + +INCLUDES= -I$(top_srcdir)/libkdegames $(all_includes) +SUBDIRS = . museum sounds pics + +bin_PROGRAMS = ktuberling + +ktuberling_SOURCES = action.cpp main.cpp toplevel.cpp playground.cpp todraw.cpp soundfactory.cpp + +ktuberling_METASOURCES = AUTO +ktuberling_LDFLAGS = $(all_libraries) $(KDE_RPATH) +ktuberling_LDADD = $(LIB_KDEGAMES) $(LIB_KFILE) -lkdeprint +ktuberling_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +KDE_ICON = ktuberling + +xdg_apps_DATA = ktuberling.desktop + +mimetypeapplicationdata_DATA = x-tuberling.desktop +mimetypeapplicationdatadir = $(kde_mimedir)/application + +appsrc_DATA = ktuberlingui.rc +appsrcdir = $(kde_datadir)/ktuberling + +messages: rc.cpp + $(XGETTEXT) rc.cpp $(ktuberling_SOURCES) pics/layout.i18n -o $(podir)/ktuberling.pot + diff --git a/ktuberling/TODO b/ktuberling/TODO new file mode 100644 index 00000000..9bc83a70 --- /dev/null +++ b/ktuberling/TODO @@ -0,0 +1,40 @@ +- Allow point and click instead of drag and drop (KDE bug #68779) + +- Better format for .tuberling files (object names instead of numbers, XML) + +- Split layout.xml in two parts : layout and sounds. The sounds.xml part would + be distributed in the ktuberling-sounds package + +- Draw a small hand when grabbing objects + +- Write title of tuberling last opened / saved on top of the window + +- Choose the current language accordingly to locale settings + +- Add a "man" page for KTuberling + +- See what's wrong with wide cursors on old Tektronix X terminals and if + there's a workaround (KDE bug #68786) + +============= Things for which I need help from other people : ================ + +- More and better localized sounds (German, Spanish, etc) + +- More and better colour gameboards and objects (KDE bug #63211) + For example : Farm with animals + Criminal identification portrait + Clown with accessories + Lizard + Dragon + Gogo (the animal) + Iguana (the animal) + Boy-Barbie + Girl-Barbie + Money (different kind of) + House + Dogs + Cats + +- Sounds in OGG Vorbis format (KDE bug #37251) + +- Text above, not below images (KDE bug #53402) diff --git a/ktuberling/action.cpp b/ktuberling/action.cpp new file mode 100644 index 00000000..a55deb04 --- /dev/null +++ b/ktuberling/action.cpp @@ -0,0 +1,20 @@ +/* ------------------------------------------------------------- + KDE Tuberling + Action stored in the undo buffer + mailto:e.bischoff@noos.fr + ------------------------------------------------------------- */ + +#include "action.h" + +// Constructor +Action::Action + (const ToDraw *drawn1, int zOrder1, + const ToDraw *drawn2, int zOrder2) +{ + if (drawn1) drawnBefore = *drawn1; + zOrderBefore = zOrder1; + + if (drawn2) drawnAfter = *drawn2; + zOrderAfter = zOrder2; +} + diff --git a/ktuberling/action.h b/ktuberling/action.h new file mode 100644 index 00000000..8fdfeb8e --- /dev/null +++ b/ktuberling/action.h @@ -0,0 +1,28 @@ +/* ------------------------------------------------------------- + KDE Tuberling + Action stored in the undo buffer + mailto:e.bischoff@noos.fr + ------------------------------------------------------------- */ + + +#ifndef _ACTION_H_ +#define _ACTION_H_ + +#include "todraw.h" + +class Action +{ + public: + Action(const ToDraw *, int, + const ToDraw *, int); + inline const ToDraw &DrawnBefore() const {return drawnBefore;} + inline int ZOrderBefore() const {return zOrderBefore;} + inline const ToDraw &DrawnAfter() const {return drawnAfter;} + inline int ZOrderAfter() const {return zOrderAfter;} + + private: + ToDraw drawnBefore, drawnAfter; + int zOrderBefore, zOrderAfter; +}; + +#endif diff --git a/ktuberling/hi128-app-ktuberling.png b/ktuberling/hi128-app-ktuberling.png new file mode 100644 index 00000000..1d679c0f Binary files /dev/null and b/ktuberling/hi128-app-ktuberling.png differ diff --git a/ktuberling/hi16-app-ktuberling.png b/ktuberling/hi16-app-ktuberling.png new file mode 100644 index 00000000..16759609 Binary files /dev/null and b/ktuberling/hi16-app-ktuberling.png differ diff --git a/ktuberling/hi22-app-ktuberling.png b/ktuberling/hi22-app-ktuberling.png new file mode 100644 index 00000000..865bce26 Binary files /dev/null and b/ktuberling/hi22-app-ktuberling.png differ diff --git a/ktuberling/hi32-app-ktuberling.png b/ktuberling/hi32-app-ktuberling.png new file mode 100644 index 00000000..08dc6156 Binary files /dev/null and b/ktuberling/hi32-app-ktuberling.png differ diff --git a/ktuberling/hi48-app-ktuberling.png b/ktuberling/hi48-app-ktuberling.png new file mode 100644 index 00000000..140fab6e Binary files /dev/null and b/ktuberling/hi48-app-ktuberling.png differ diff --git a/ktuberling/hi64-app-ktuberling.png b/ktuberling/hi64-app-ktuberling.png new file mode 100644 index 00000000..abe60536 Binary files /dev/null and b/ktuberling/hi64-app-ktuberling.png differ diff --git a/ktuberling/ktuberling.desktop b/ktuberling/ktuberling.desktop new file mode 100644 index 00000000..b6f8e709 --- /dev/null +++ b/ktuberling/ktuberling.desktop @@ -0,0 +1,128 @@ +[Desktop Entry] +Exec=ktuberling %i %m -caption "%c" %u +Name=Potato Guy +Name[af]=Aartappel Man +Name[ar]=لعبة رجل البطاطا +Name[az]=Kartof Adam +Name[be]=БульбÑш +Name[bn]=পটেটো গাই +Name[br]=Paotr ar patatez +Name[cs]=Bramborový chlapík +Name[da]=Kartoffelfyren +Name[de]=Kartoffelknülch +Name[eo]=Terpomulo +Name[es]=Papá Patata +Name[et]=Kartulimees +Name[eu]=Patata jauna +Name[fi]=Perunamies +Name[fo]=Eplamaður +Name[fr]=Monsieur patate +Name[gl]=O Nacho Pataca +Name[he]=מר תפוח ×דמה +Name[hi]=पोटैटो गाइ +Name[hr]=Krumpirko +Name[hu]=Krumpli bácsi +Name[is]=Kartöflukall +Name[it]=Uomo patata +Name[ja]=ç¦ç¬‘ã„ +Name[km]=មនុស្ស​ដំឡូង +Name[lt]=Bulvinis Vyrukas +Name[lv]=Kartupeļu vÄ«rs +Name[mk]=Компирко +Name[nb]=Potetmannen +Name[nds]=Kantüffelfips +Name[ne]=पोटेटो गाई +Name[nl]=Aardappelmannetje +Name[nn]=Potetfyren +Name[pa]=ਆਲੂ ਮà©à©°à¨¡à¨¾ +Name[pl]=Ziemniaczany facet +Name[pt]=Homem Batata +Name[pt_BR]=Homem-Batata +Name[ro]=Domnul Cartof +Name[se]=BuÄ‘etolmmái +Name[sk]=Zemiakový chlapec +Name[sl]=KrompirÄek +Name[sv]=Potatismannen +Name[ta]=உரà¯à®³à¯ˆà®•à®¿à®´à®™à¯à®•à¯ வீரர௠+Name[tg]=ПиÑараки Картошкагин +Name[th]=นายมันà¸à¸£à¸±à¹ˆà¸‡ - K +Name[tr]=Patates Adam +Name[uk]=КартоплÑний хлопець +Name[ven]=Munna wa dabula +Name[wa]=Monsieu Crompire +Name[xh]=Umfana wetapile +Name[zh_CN]=土豆å°å­ +Name[zh_TW]=馬鈴薯å°å­ +Name[zu]=I-Potato Guy +GenericName=Game for Children +GenericName[af]=Speletjie vir Kinders +GenericName[ar]=لعبة للأطÙال +GenericName[be]=Ð“ÑƒÐ»ÑŒÐ½Ñ Ð´Ð»Ñ Ð´Ð·Ñцей +GenericName[bg]=Игра за деца +GenericName[bn]=পোলাপানের খেলা +GenericName[bs]=Igra za djecu +GenericName[ca]=Joc per a la canalla +GenericName[cs]=Hra pro dÄ›ti +GenericName[cy]=Gêm i Blant +GenericName[da]=Spil for børn +GenericName[de]=Spiel für Kinder +GenericName[el]=Παιχνίδι για παιδιά +GenericName[eo]=Infanludo +GenericName[es]=Juego para niños +GenericName[et]=Lastemäng +GenericName[eu]=Umeentzako jokoa +GenericName[fa]=بازی برای بچه‌‌‌ها +GenericName[fi]=Peli lapsille +GenericName[fo]=Barnaspæl +GenericName[fr]=Jeu pour les enfants +GenericName[ga]=Cluiche do phaistí +GenericName[gl]=Xogo para Nenos +GenericName[he]=משחק ×œ×™×œ×“×™× +GenericName[hi]=बचà¥à¤šà¥‹à¤‚ के लिठखेल +GenericName[hr]=Igra za djecu +GenericName[hu]=Gyermekjáték +GenericName[is]=Leikur fyrir börn +GenericName[it]=Gioco per i bambini +GenericName[ja]=å­ä¾›ç”¨ã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​សម្រាប់​ក្មáŸáž„ +GenericName[ko]=어린ì´ë¥¼ 위한 그림 게임 +GenericName[lt]=Žaidimas vaikams +GenericName[lv]=SpÄ“le bÄ“rniem +GenericName[mk]=Игра за деца +GenericName[nb]=Barnespill +GenericName[nds]=Speel för Kinners +GenericName[ne]=केटाकेटिका लागि खेल +GenericName[nl]=Spel voor kinderen +GenericName[nn]=Barnespel +GenericName[pa]=ਜਵਾਕਾਂ ਲਈ ਖੇਡ +GenericName[pl]=Gra dla dzieci +GenericName[pt]=Jogo para Crianças +GenericName[pt_BR]=Jogo para Crianças +GenericName[ro]=Joc pentru copii +GenericName[ru]=Клубень +GenericName[se]=Mánáidspeallu +GenericName[sk]=Hra pre deti +GenericName[sl]=Igra za otroke +GenericName[sr]=Игра за децу +GenericName[sr@Latn]=Igra za decu +GenericName[sv]=Spel för barn +GenericName[ta]=கà¯à®´à®¨à¯à®¤à¯ˆà®•à®³à¯à®•à¯à®•à®¾à®© விளையாடà¯à®Ÿà¯ +GenericName[tg]=Бозӣ барои бачаҳо +GenericName[th]=เà¸à¸¡à¸ªà¸³à¸«à¸£à¸±à¸šà¹€à¸”็ภ+GenericName[tr]=Çocuklar için bir oyun +GenericName[uk]=Гра Ð´Ð»Ñ Ð´Ñ–Ñ‚ÐµÐ¹ +GenericName[ven]=Mutambo wa Vhana +GenericName[vi]=Game cho trẻ em +GenericName[wa]=Djeu po ls efants +GenericName[xh]=Umdlalo wabantwana +GenericName[zh_CN]=å­©å­ä»¬çš„æ¸¸æˆ +GenericName[zh_TW]=å°å­©å°ˆå±¬éŠæˆ² +GenericName[zu]=Umdlalo wabantwana +Type=Application +DocPath=ktuberling/index.html +Terminal=false +Icon=ktuberling +MimeType=application/x-tuberling; +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;X-KDE-KidsGame; diff --git a/ktuberling/ktuberlingui.rc b/ktuberling/ktuberlingui.rc new file mode 100644 index 00000000..75bd3105 --- /dev/null +++ b/ktuberling/ktuberlingui.rc @@ -0,0 +1,41 @@ + + + + &Game + + + &Playground + + + + + &Speech + + + + + + + + + + + + + + + + + + + +Main Toolbar + + + + + + + + + diff --git a/ktuberling/main.cpp b/ktuberling/main.cpp new file mode 100644 index 00000000..5e508e66 --- /dev/null +++ b/ktuberling/main.cpp @@ -0,0 +1,60 @@ +/* ------------------------------------------------------------- + KDE Tuberling + Main program + mailto:e.bischoff@noos.fr + ------------------------------------------------------------- */ + +#include +#include +#include +#include +#include + +#include "toplevel.h" + +static KCmdLineOptions options[] = { + { "+", I18N_NOOP("Potato to open"), 0 }, + KCmdLineLastOption +}; + + + +static const char description[] = I18N_NOOP("Potato game for kids"); +static const char text[] = I18N_NOOP("A program by Eric Bischoff \nand John Calhoun.\n\nThis program is dedicated to my daughter Sunniva."); + +static const char version[] = "0.4"; + +// Main function +int main(int argc, char *argv[]) +{ + + KAboutData aboutData( "ktuberling", I18N_NOOP("KTuberling"), + version, description, KAboutData::License_GPL, + "(c) 1999-2003, The KTuberling Developers", text); + aboutData.addAuthor("Eric Bischoff", I18N_NOOP("Developer"), "e.bischoff@noos.fr"); + aboutData.addAuthor("John Calhoun", I18N_NOOP("Original concept and artwork")); + aboutData.addCredit("Agnieszka Czajkowska", I18N_NOOP("New artwork"), "agnieszka@imagegalaxy.de"); + aboutData.addCredit("Bas Willems", I18N_NOOP("New artwork"), "cybersurfer@euronet.nl"); + aboutData.addCredit("Roger Larsson", I18N_NOOP("Sounds tuning"), "roger.larsson@norran.net"); + KCmdLineArgs::init(argc, argv, &aboutData); + KCmdLineArgs::addCmdLineOptions(options); + + KApplication app; + KGlobal::locale()->insertCatalogue("libkdegames"); + KImageIO::registerFormats(); + + TopLevel *toplevel=0; + + if (app.isRestored()) + RESTORE(TopLevel) + else { + toplevel = new TopLevel(); + toplevel->show(); + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + if (args->count()) + toplevel->open(args->url(0)); + args->clear(); + } + + return app.exec(); +} diff --git a/ktuberling/museum/Makefile.am b/ktuberling/museum/Makefile.am new file mode 100644 index 00000000..50c11c10 --- /dev/null +++ b/ktuberling/museum/Makefile.am @@ -0,0 +1,10 @@ + +# add here all files +ktuberling_DATA = businessman.tuberling cool.tuberling cyclop.tuberling dali.tuberling \ + einstein.tuberling fly.tuberling grandpa.tuberling happy.tuberling \ + idiot.tuberling miss.tuberling mouse.tuberling picasso.tuberling \ + serious.tuberling \ + crazy.tuberling hippie.tuberling \ + sea.tuberling + +ktuberlingdir = $(kde_datadir)/ktuberling/museum diff --git a/ktuberling/museum/businessman.tuberling b/ktuberling/museum/businessman.tuberling new file mode 100644 index 00000000..fc26c97d --- /dev/null +++ b/ktuberling/museum/businessman.tuberling @@ -0,0 +1,28 @@ +0 +22 103 62 152 124 +12 77 110 124 139 +35 3 67 31 88 +36 126 228 160 249 +3 129 44 170 76 +3 87 45 128 77 +13 57 11 104 40 +17 165 19 212 48 +17 158 15 205 44 +17 151 12 198 41 +17 146 9 193 38 +17 140 7 187 36 +17 134 5 181 34 +13 62 7 109 36 +13 67 5 114 34 +13 73 5 120 34 +13 79 4 126 33 +13 84 3 131 32 +17 129 3 176 32 +19 137 25 184 54 +12 70 25 117 54 +25 57 58 84 98 +26 176 58 203 98 +39 106 148 150 180 +16 131 111 178 140 +32 94 126 160 156 +37 90 46 170 78 diff --git a/ktuberling/museum/cool.tuberling b/ktuberling/museum/cool.tuberling new file mode 100644 index 00000000..72ff5ec7 --- /dev/null +++ b/ktuberling/museum/cool.tuberling @@ -0,0 +1,8 @@ +0 +28 83 92 149 122 +35 66 95 94 116 +33 78 7 142 37 +3 75 41 116 73 +3 109 46 150 78 +24 150 54 177 94 +42 201 144 219 176 diff --git a/ktuberling/museum/crazy.tuberling b/ktuberling/museum/crazy.tuberling new file mode 100644 index 00000000..f32c07eb --- /dev/null +++ b/ktuberling/museum/crazy.tuberling @@ -0,0 +1,9 @@ +1 +11 102 28 147 87 +17 92 -20 155 43 +6 88 101 128 132 +4 109 100 149 131 +18 82 117 160 147 +8 81 149 176 268 +12 131 227 186 265 +13 84 244 138 282 diff --git a/ktuberling/museum/cyclop.tuberling b/ktuberling/museum/cyclop.tuberling new file mode 100644 index 00000000..fdf7ee89 --- /dev/null +++ b/ktuberling/museum/cyclop.tuberling @@ -0,0 +1,15 @@ +0 +0 109 44 150 76 +2 110 27 151 59 +32 94 149 160 179 +20 103 84 152 146 +23 58 63 84 103 +24 178 59 204 99 +17 112 19 159 48 +13 97 19 144 48 +17 175 55 222 84 +17 169 43 216 72 +17 158 29 205 58 +13 41 55 88 84 +13 43 45 90 74 +13 47 33 94 62 diff --git a/ktuberling/museum/dali.tuberling b/ktuberling/museum/dali.tuberling new file mode 100644 index 00000000..6b423c01 --- /dev/null +++ b/ktuberling/museum/dali.tuberling @@ -0,0 +1,11 @@ +0 +26 166 42 193 82 +0 82 32 123 64 +0 118 30 159 62 +15 72 72 119 101 +19 123 73 170 102 +17 168 26 215 55 +29 -3 69 63 99 +20 6 11 55 73 +6 -10 -4 31 28 +9 31 -4 72 28 diff --git a/ktuberling/museum/einstein.tuberling b/ktuberling/museum/einstein.tuberling new file mode 100644 index 00000000..9e276b4b --- /dev/null +++ b/ktuberling/museum/einstein.tuberling @@ -0,0 +1,10 @@ +0 +13 76 12 123 41 +17 140 12 187 41 +30 103 178 169 208 +34 103 163 167 185 +20 108 114 157 176 +4 143 77 184 109 +1 79 80 120 112 +24 179 80 206 120 +23 52 80 79 120 diff --git a/ktuberling/museum/fly.tuberling b/ktuberling/museum/fly.tuberling new file mode 100644 index 00000000..56e2ab82 --- /dev/null +++ b/ktuberling/museum/fly.tuberling @@ -0,0 +1,8 @@ +0 +14 42 87 89 116 +14 44 49 91 78 +21 62 25 111 87 +18 173 88 220 117 +18 171 44 218 73 +21 153 20 202 82 +22 103 151 152 213 diff --git a/ktuberling/museum/grandpa.tuberling b/ktuberling/museum/grandpa.tuberling new file mode 100644 index 00000000..81e15200 --- /dev/null +++ b/ktuberling/museum/grandpa.tuberling @@ -0,0 +1,9 @@ +0 +27 96 104 162 134 +34 99 93 163 115 +5 92 68 133 100 +5 130 70 171 102 +38 93 65 168 97 +35 4 67 32 88 +34 104 12 168 34 +33 87 4 151 34 diff --git a/ktuberling/museum/happy.tuberling b/ktuberling/museum/happy.tuberling new file mode 100644 index 00000000..265633fe --- /dev/null +++ b/ktuberling/museum/happy.tuberling @@ -0,0 +1,13 @@ +0 +21 107 76 156 138 +24 174 25 201 65 +23 59 28 86 68 +2 88 59 129 91 +2 141 59 182 91 +5 92 66 133 98 +5 137 64 178 96 +39 109 152 153 184 +13 76 49 123 78 +17 141 49 188 78 +27 99 130 165 160 +33 0 67 64 97 diff --git a/ktuberling/museum/hippie.tuberling b/ktuberling/museum/hippie.tuberling new file mode 100644 index 00000000..bc0d761f --- /dev/null +++ b/ktuberling/museum/hippie.tuberling @@ -0,0 +1,8 @@ +1 +10 75 43 173 102 +14 64 176 197 206 +15 61 168 194 198 +20 85 82 194 112 +1 89 106 129 136 +3 118 107 158 137 +18 152 107 230 137 diff --git a/ktuberling/museum/idiot.tuberling b/ktuberling/museum/idiot.tuberling new file mode 100644 index 00000000..9be66e14 --- /dev/null +++ b/ktuberling/museum/idiot.tuberling @@ -0,0 +1,10 @@ +0 +30 97 169 163 199 +22 105 62 154 124 +34 98 8 162 30 +21 4 40 53 102 +3 146 67 187 99 +0 74 67 115 99 +24 178 75 205 115 +23 52 77 79 117 +35 42 94 70 115 diff --git a/ktuberling/museum/miss.tuberling b/ktuberling/museum/miss.tuberling new file mode 100644 index 00000000..339b236a --- /dev/null +++ b/ktuberling/museum/miss.tuberling @@ -0,0 +1,11 @@ +0 +6 76 45 117 77 +9 145 47 186 79 +12 70 27 117 56 +16 144 24 191 53 +39 109 181 153 213 +42 203 145 221 177 +29 93 83 159 113 +40 68 79 83 111 +40 176 82 191 114 +36 10 113 44 134 diff --git a/ktuberling/museum/mouse.tuberling b/ktuberling/museum/mouse.tuberling new file mode 100644 index 00000000..3264c291 --- /dev/null +++ b/ktuberling/museum/mouse.tuberling @@ -0,0 +1,14 @@ +0 +13 76 49 123 78 +17 141 49 188 78 +41 95 58 122 90 +41 146 57 173 89 +19 96 113 143 142 +15 117 112 164 141 +5 112 84 153 116 +39 111 144 155 176 +5 88 59 129 91 +5 139 58 180 90 +24 175 28 201 68 +23 61 28 87 68 +15 56 189 103 218 diff --git a/ktuberling/museum/picasso.tuberling b/ktuberling/museum/picasso.tuberling new file mode 100644 index 00000000..56940ba9 --- /dev/null +++ b/ktuberling/museum/picasso.tuberling @@ -0,0 +1,10 @@ +0 +29 102 58 168 88 +24 179 92 206 132 +35 143 125 171 146 +41 104 91 131 123 +4 91 132 132 164 +23 66 145 93 185 +1 80 12 121 44 +13 89 39 136 68 +20 145 51 194 113 diff --git a/ktuberling/museum/sea.tuberling b/ktuberling/museum/sea.tuberling new file mode 100644 index 00000000..eea3ef51 --- /dev/null +++ b/ktuberling/museum/sea.tuberling @@ -0,0 +1,18 @@ +2 +11 -2 90 54 165 +1 119 27 182 74 +9 173 125 226 190 +9 197 130 250 195 +7 248 121 283 175 +1 96 23 159 70 +1 74 -1 137 46 +1 55 20 118 67 +1 70 40 133 87 +4 115 245 205 307 +1 48 -10 111 37 +1 39 -1 102 46 +1 28 44 91 91 +1 -31 6 32 53 +1 72 13 135 60 +1 104 5 167 52 +1 177 -2 240 45 diff --git a/ktuberling/museum/serious.tuberling b/ktuberling/museum/serious.tuberling new file mode 100644 index 00000000..4b5ffff3 --- /dev/null +++ b/ktuberling/museum/serious.tuberling @@ -0,0 +1,13 @@ +0 +23 75 74 102 114 +24 168 74 195 114 +32 104 154 170 184 +33 87 7 151 37 +41 135 185 162 217 +7 96 81 137 113 +10 133 83 174 115 +20 110 89 159 151 +14 85 66 132 95 +18 141 65 188 94 +35 5 72 33 93 +42 203 144 221 176 diff --git a/ktuberling/pics/Makefile.am b/ktuberling/pics/Makefile.am new file mode 100644 index 00000000..5e70846a --- /dev/null +++ b/ktuberling/pics/Makefile.am @@ -0,0 +1,8 @@ + +# add here all files +pics_DATA = penguin-game.png penguin-mask.png \ + potato-game.png potato-mask.png \ + aquarium-game.png aquarium-mask.png \ + layout.xml + +picsdir = $(kde_datadir)/ktuberling/pics diff --git a/ktuberling/pics/aquarium-game.png b/ktuberling/pics/aquarium-game.png new file mode 100644 index 00000000..089bdf7c Binary files /dev/null and b/ktuberling/pics/aquarium-game.png differ diff --git a/ktuberling/pics/aquarium-mask.png b/ktuberling/pics/aquarium-mask.png new file mode 100644 index 00000000..929d11be Binary files /dev/null and b/ktuberling/pics/aquarium-mask.png differ diff --git a/ktuberling/pics/layout.i18n b/ktuberling/pics/layout.i18n new file mode 100644 index 00000000..373203cb --- /dev/null +++ b/ktuberling/pics/layout.i18n @@ -0,0 +1,53 @@ +/* ------------------------------------------------------------- + KDE Tuberling + List of strings in layout.xml that are translatable + mailto:e.bischoff@noos.fr + ------------------------------------------------------------- */ + +i18n("Potato &Guy"); + +i18n("Eyes"); +i18n("Eyebrows"); +i18n("Noses"); +i18n("Ears"); +i18n("Mouths"); +i18n("Goodies"); + +i18n("&Penguin"); + +i18n("Eyes"); +i18n("Tie"); +i18n("Hair"); +i18n("Necklaces"); +i18n("Hats"); +i18n("Glasses"); +i18n("Scarf"); + +i18n("&Aquarium"); + +i18n("Fishes"); +i18n("Others"); + +i18n("&Danish"); +i18n("&German"); +i18n("&English"); +i18n("Sp&anish"); +i18n("Fi&nnish"); +i18n("&French"); +i18n("&Italian"); +i18n("Low Sa&xon"); +i18n("D&utch"); +i18n("&Portuguese"); +i18n("&Romanian"); +i18n("&Slovak"); +i18n("S&lovenian"); +i18n("S&wedish"); +i18n("Ser&bian"); + +i18n("NOTE_TO_THE_TRANSLATORS", +"The translators have the opportunity to translate the\n" +"sounds spoken in the game.\n" +"See the technical reference section in ktuberling's\n" +"documentation for more information on how to do that.\n" +"(translate this message as \"DONE\" when you have translated\n" +"the sounds; otherwise leave it untranslated as a reminder)"); diff --git a/ktuberling/pics/layout.xml b/ktuberling/pics/layout.xml new file mode 100644 index 00000000..c5b02fb8 --- /dev/null +++ b/ktuberling/pics/layout.xml @@ -0,0 +1,924 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + --> + + + + + + + + + diff --git a/ktuberling/pics/penguin-game.png b/ktuberling/pics/penguin-game.png new file mode 100644 index 00000000..6bbdfe74 Binary files /dev/null and b/ktuberling/pics/penguin-game.png differ diff --git a/ktuberling/pics/penguin-mask.png b/ktuberling/pics/penguin-mask.png new file mode 100644 index 00000000..efaf0654 Binary files /dev/null and b/ktuberling/pics/penguin-mask.png differ diff --git a/ktuberling/pics/potato-game.png b/ktuberling/pics/potato-game.png new file mode 100644 index 00000000..10132333 Binary files /dev/null and b/ktuberling/pics/potato-game.png differ diff --git a/ktuberling/pics/potato-mask.png b/ktuberling/pics/potato-mask.png new file mode 100644 index 00000000..838865d3 Binary files /dev/null and b/ktuberling/pics/potato-mask.png differ diff --git a/ktuberling/playground.cpp b/ktuberling/playground.cpp new file mode 100644 index 00000000..367c407c --- /dev/null +++ b/ktuberling/playground.cpp @@ -0,0 +1,609 @@ +/* ------------------------------------------------------------- + KDE Tuberling + Play ground widget + mailto:e.bischoff@noos.fr + ------------------------------------------------------------- */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "playground.moc" +#include "toplevel.h" + +#define XMARGIN 5 +#define YMARGIN 5 + +// Constructor +PlayGround::PlayGround(TopLevel *parent, const char *name, uint selectedGameboard) + : QWidget(parent, name) +{ + topLevel = parent; + + textsLayout = objectsLayout = 0; + textsList = soundsList = 0; + draggedCursor = 0; + + toDraw.setAutoDelete(true); + history.setAutoDelete(true); + + setBackgroundColor(white); + + QDomDocument layoutsDocument; + bool ok = topLevel->loadLayout(layoutsDocument); + if (ok) ok = registerPlayGrounds(layoutsDocument); + if (ok) ok = loadPlayGround(layoutsDocument, selectedGameboard); + if (!ok) loadFailure(); + + currentAction = 0; + setupGeometry(); +} + +// Destructor +PlayGround::~PlayGround() +{ + delete [] textsLayout; + delete [] objectsLayout; + + delete [] textsList; + delete [] soundsList; + + delete draggedCursor; +} + +// Reset the play ground +void PlayGround::reset() +{ + toDraw.clear(); + history.clear(); + currentAction = 0; +} + +// Change the gameboard +void PlayGround::change(uint selectedGameboard) +{ + QDomDocument layoutsDocument; + bool ok = topLevel->loadLayout(layoutsDocument); + if (ok) ok = loadPlayGround(layoutsDocument, selectedGameboard); + if (!ok) loadFailure(); + + toDraw.clear(); + history.clear(); + currentAction = 0; + + setupGeometry(); + + update(); +} + +// Repaint all the editable area +void PlayGround::repaintAll() +{ + QRect dirtyArea + (editableArea.left() - 10, + editableArea.top() - 10, + editableArea.width() + 20, + editableArea.height() + 20); + + repaint(dirtyArea, false); +} + +// Undo last action +// Returns true if everything went fine +bool PlayGround::undo() +{ + ToDraw *newObject; + Action *undone; + int zOrder; + + if (!(undone = history.at(--currentAction))) + return false; + + zOrder = undone->ZOrderAfter(); + if (zOrder != -1) + { + // Undo an "add" or a "move" action + if (!toDraw.remove(zOrder)) return false; + } + + zOrder = undone->ZOrderBefore(); + if (zOrder != -1) + { + // Undo a "delete" or a "move" action + newObject = new ToDraw(undone->DrawnBefore()); + if (!toDraw.insert(zOrder, newObject)) + return false; + } + + return true; +} + +// Redo next action +// Returns true if everything went fine +bool PlayGround::redo() +{ + ToDraw *newObject; + Action *undone; + int zOrder; + + if (!(undone = history.at(currentAction++))) + return false; + + zOrder = undone->ZOrderBefore(); + if (zOrder != -1) + { + // Redo a "delete" or a "move" action + if (!toDraw.remove(zOrder)) return false; + } + + zOrder = undone->ZOrderAfter(); + if (zOrder != -1) + { + // Redo an "add" or a "move" action + newObject = new ToDraw(undone->DrawnAfter()); + if (!toDraw.insert(zOrder, newObject)) + return false; + } + + return true; +} + +// Save objects laid down on the editable area +bool PlayGround::saveAs(const QString & name) +{ + FILE *fp; + const ToDraw *currentObject; + + if (!(fp = fopen((const char *) QFile::encodeName(name), "w"))) return false; + + fprintf(fp, "%d\n", topLevel->getSelectedGameboard()); + for (currentObject = toDraw.first(); currentObject; currentObject = toDraw.next()) + currentObject->save(fp); + + return !fclose(fp); +} + +// Print gameboard's picture +bool PlayGround::printPicture(KPrinter &printer) const +{ + QPainter artist; + QPixmap picture(getPicture()); + + if (!artist.begin(&printer)) return false; + artist.drawPixmap(QPoint(32, 32), picture); + if (!artist.end()) return false; + return true; +} + +// Get a pixmap containing the current picture +QPixmap PlayGround::getPicture() const +{ + QPixmap result(editableArea.size()); + QPainter artist(&result); + QRect transEditableArea(editableArea); + + transEditableArea.moveBy(-XMARGIN, -YMARGIN); + artist.translate(XMARGIN - editableArea.left(), + YMARGIN - editableArea.top()); + drawGameboard(artist, transEditableArea); + return result; +} + +// Draw some text +void PlayGround::drawText(QPainter &artist, QRect &area, QString &textId) const +{ + QString label; + + label=i18n(textId.latin1()); + + artist.drawText(area, AlignCenter, label); +} + +// Paint the current picture to the given device +void PlayGround::drawGameboard( QPainter &artist, const QRect &area ) const +{ + artist.drawPixmap(area.topLeft(), gameboard, area); + + artist.setPen(white); + for (int text = 0; text < texts; text++) + drawText(artist, textsLayout[text], textsList[text]); + + artist.setPen(black); + for (QPtrListIterator currentObject(toDraw); + currentObject.current(); + ++currentObject) + currentObject.current()->draw(artist, area, objectsLayout, &gameboard, &masks); +} + +// Painting event +void PlayGround::paintEvent( QPaintEvent *event ) +{ + QPoint destination(event->rect().topLeft()), + position(destination.x() - XMARGIN, + destination.y() - YMARGIN); + QRect area(position, QSize(event->rect().size())); + QPixmap cache(gameboard.size()); + QPainter artist(&cache); + + if (destination.x() < XMARGIN) destination.setX(XMARGIN); + if (destination.y() < YMARGIN) destination.setY(YMARGIN); + area = QRect(0, 0, gameboard.width(), gameboard.height()).intersect(area); + if (area.isEmpty()) return; + + drawGameboard(artist, area); + + bitBlt(this, destination, &cache, area, Qt::CopyROP); +} + +// Mouse pressed event +void PlayGround::mousePressEvent( QMouseEvent *event ) +{ + if (draggedCursor) return; + + QPoint position(event->x() - XMARGIN, + event->y() - YMARGIN); + if (!zone(position)) return; + + int draggedNumber = draggedObject.getNumber(); + QPixmap object(objectsLayout[draggedNumber].size()); + QBitmap shape(objectsLayout[draggedNumber].size()); + bitBlt(&object, QPoint(0, 0), &gameboard, objectsLayout[draggedNumber], Qt::CopyROP); + bitBlt(&shape, QPoint(0, 0), &masks, objectsLayout[draggedNumber], Qt::CopyROP); + object.setMask(shape); + + draggedCursor = new QCursor(object, position.x(), position.y()); + setCursor(*draggedCursor); + + topLevel->playSound(soundsList[draggedNumber]); +} + +// Mouse released event +void PlayGround::mouseReleaseEvent( QMouseEvent *event ) +{ + // If we are not dragging an object, ignore the event + if (!draggedCursor) return; + + QCursor arrow; + int draggedNumber = draggedObject.getNumber(); + QRect position( + event->x() - XMARGIN - draggedCursor->hotSpot().x(), + event->y() - YMARGIN - draggedCursor->hotSpot().y(), + objectsLayout[draggedNumber].width(), + objectsLayout[draggedNumber].height()); + QRect dirtyArea + (editableArea.left() - 10, + editableArea.top() - 10, + editableArea.width() + 20, + editableArea.height() + 20); + ToDraw *newObject; + Action *newAction; + + // We are not anymore dragging an object + delete draggedCursor; + draggedCursor = 0; + setCursor(arrow); + + // If we are not moving the object to the editable area + if (!dirtyArea.contains(event->pos())) + { + // ... then register its deletion (if coming from the editable area), and return + if (draggedZOrder == -1) return; + + while (history.count() > currentAction) history.removeLast(); + newAction = new Action(&draggedObject, draggedZOrder, 0, -1); + history.append(newAction); + currentAction++; + topLevel->enableUndo(true); + + return; + } + + // Register that we have one more object to draw + newObject = new ToDraw(draggedNumber, position); + toDraw.append(newObject); + + // Forget all subsequent actions in the undo buffer, and register object's addition (or its move) + while (history.count() > currentAction) history.removeLast(); + newAction = new Action(&draggedObject, draggedZOrder, newObject, toDraw.count()-1); + history.append(newAction); + currentAction++; + topLevel->enableUndo(true); + + // Repaint the editable area + position.moveBy(XMARGIN, YMARGIN); + repaint(position, false); + +} + +// Register the various playgrounds +bool PlayGround::registerPlayGrounds(QDomDocument &layoutDocument) +{ + QDomNodeList playGroundsList, menuItemsList, labelsList; + QDomElement playGroundElement, menuItemElement, labelElement; + QDomAttr actionAttribute; + + playGroundsList = layoutDocument.elementsByTagName("playground"); + if (playGroundsList.count() < 1) + return false; + + for (uint i = 0; i < playGroundsList.count(); i++) + { + playGroundElement = (const QDomElement &) playGroundsList.item(i).toElement(); + + menuItemsList = playGroundElement.elementsByTagName("menuitem"); + if (menuItemsList.count() != 1) + return false; + + menuItemElement = (const QDomElement &) menuItemsList.item(0).toElement(); + + labelsList = menuItemElement.elementsByTagName("label"); + if (labelsList.count() != 1) + return false; + + labelElement = (const QDomElement &) labelsList.item(0).toElement(); + actionAttribute = menuItemElement.attributeNode("action"); + topLevel->registerGameboard(labelElement.text(), actionAttribute.value().latin1()); + } + + return true; +} + +// Load background and draggable objects masks +bool PlayGround::loadPlayGround(QDomDocument &layoutDocument, uint toLoad) +{ + QDomNodeList playGroundsList, + editableAreasList, categoriesList, objectsList, + gameAreasList, maskAreasList, soundNamesList, labelsList; + QDomElement playGroundElement, + editableAreaElement, categoryElement, objectElement, + gameAreaElement, maskAreaElement, soundNameElement, labelElement; + QDomAttr gameboardAttribute, masksAttribute, + leftAttribute, topAttribute, rightAttribute, bottomAttribute, + refAttribute; + + playGroundsList = layoutDocument.elementsByTagName("playground"); + if (toLoad >= playGroundsList.count()) + return false; + + playGroundElement = (const QDomElement &) playGroundsList.item(toLoad).toElement(); + + gameboardAttribute = playGroundElement.attributeNode("gameboard"); + if (!gameboard.load(locate("data", "ktuberling/pics/" + gameboardAttribute.value()))) + return false; + + masksAttribute = playGroundElement.attributeNode("masks"); + if (!masks.load(locate("data", "ktuberling/pics/" + masksAttribute.value()))) + return false; + + editableAreasList = playGroundElement.elementsByTagName("editablearea"); + if (editableAreasList.count() != 1) + return false; + + editableAreaElement = (const QDomElement &) editableAreasList.item(0).toElement(); + + gameAreasList = editableAreaElement.elementsByTagName("position"); + if (gameAreasList.count() != 1) + return false; + + gameAreaElement = (const QDomElement &) gameAreasList.item(0).toElement(); + leftAttribute = gameAreaElement.attributeNode("left"); + topAttribute = gameAreaElement.attributeNode("top"); + rightAttribute = gameAreaElement.attributeNode("right"); + bottomAttribute = gameAreaElement.attributeNode("bottom"); + + editableArea.setCoords + (XMARGIN + leftAttribute.value().toInt(), + YMARGIN + topAttribute.value().toInt(), + XMARGIN + rightAttribute.value().toInt(), + YMARGIN + bottomAttribute.value().toInt()); + + soundNamesList = editableAreaElement.elementsByTagName("sound"); + if (soundNamesList.count() != 1) + return false; + + soundNameElement = (const QDomElement &) soundNamesList.item(0).toElement(); + refAttribute = soundNameElement.attributeNode("ref"); + + editableSound = refAttribute.value(); + + categoriesList = playGroundElement.elementsByTagName("category"); + texts = categoriesList.count(); + if (texts < 1) + return false; + + delete[] textsLayout; + textsLayout = new QRect[texts]; + delete[] textsList; + textsList = new QString[texts]; + + for (int text = 0; text < texts; text++) + { + categoryElement = (const QDomElement &) categoriesList.item(text).toElement(); + + gameAreasList = categoryElement.elementsByTagName("position"); + if (gameAreasList.count() != 1) + return false; + + gameAreaElement = (const QDomElement &) gameAreasList.item(0).toElement(); + leftAttribute = gameAreaElement.attributeNode("left"); + topAttribute = gameAreaElement.attributeNode("top"); + rightAttribute = gameAreaElement.attributeNode("right"); + bottomAttribute = gameAreaElement.attributeNode("bottom"); + + textsLayout[text].setCoords + (leftAttribute.value().toInt(), + topAttribute.value().toInt(), + rightAttribute.value().toInt(), + bottomAttribute.value().toInt()); + + labelsList = categoryElement.elementsByTagName("label"); + if (labelsList.count() != 1) + return false; + + labelElement = (const QDomElement &) labelsList.item(0).toElement(); + + textsList[text] = labelElement.text(); + } + + objectsList = playGroundElement.elementsByTagName("object"); + decorations = objectsList.count(); + if (decorations < 1) + return false; + + delete[] objectsLayout; + objectsLayout = new QRect[decorations]; + delete[] soundsList; + soundsList = new QString[decorations]; + + for (int decoration = 0; decoration < decorations; decoration++) + { + objectElement = (const QDomElement &) objectsList.item(decoration).toElement(); + + gameAreasList = objectElement.elementsByTagName("position"); + if (gameAreasList.count() != 1) + return false; + + gameAreaElement = (const QDomElement &) gameAreasList.item(0).toElement(); + leftAttribute = gameAreaElement.attributeNode("left"); + topAttribute = gameAreaElement.attributeNode("top"); + rightAttribute = gameAreaElement.attributeNode("right"); + bottomAttribute = gameAreaElement.attributeNode("bottom"); + + objectsLayout[decoration].setCoords + (leftAttribute.value().toInt(), + topAttribute.value().toInt(), + rightAttribute.value().toInt(), + bottomAttribute.value().toInt()); + + soundNamesList = objectElement.elementsByTagName("sound"); + if (soundNamesList.count() != 1) + return false; + + soundNameElement = (const QDomElement &) soundNamesList.item(0).toElement(); + + refAttribute = soundNameElement.attributeNode("ref"); + + soundsList[decoration] = refAttribute.value(); + } + + return true; +} + +// Report a load failure +void PlayGround::loadFailure() +{ + KMessageBox::error(topLevel, i18n("Fatal error:\n" + "Unable to load the pictures, aborting.")); + exit(-1); +} + +// Set up play ground's geometry +void PlayGround::setupGeometry() +{ + int width = gameboard.width() + 2 * XMARGIN, + height = gameboard.height() + 2 * YMARGIN; + setFixedWidth(width); + setFixedHeight(height); +} + +// In which decorative object are we? +// On return, the position is the location of the cursor's hot spot +// Returns false if we aren't in any zone +bool PlayGround::zone(QPoint &position) +{ + // Scan all available decorative objects on right side because we may be adding one + int draggedNumber; + for (draggedNumber = 0; + draggedNumber < decorations; + draggedNumber++) if (objectsLayout[draggedNumber].contains(position)) + { + position.setX(position.x() - objectsLayout[draggedNumber].x()); + position.setY(position.y() - objectsLayout[draggedNumber].y()); + + draggedObject.setNumber(draggedNumber); + draggedZOrder = -1; + + return true; + } + + // Scan all decorative objects already layed down on editable are because we may be moving or removing one + const ToDraw *currentObject; + + for (draggedZOrder = toDraw.count()-1; draggedZOrder >= 0; draggedZOrder--) + { + currentObject = toDraw.at(draggedZOrder); + if (!currentObject->getPosition().contains(position)) continue; + + QRect toUpdate(currentObject->getPosition()); + draggedObject = *currentObject; + draggedNumber = draggedObject.getNumber(); + + QBitmap shape(objectsLayout[draggedNumber].size()); + QPoint relative(position.x() - toUpdate.x(), + position.y() - toUpdate.y()); + bitBlt(&shape, QPoint(0, 0), &masks, objectsLayout[draggedNumber], Qt::CopyROP); + if (!shape.convertToImage().pixelIndex(relative.x(), relative.y())) continue; + + toDraw.remove(draggedZOrder); + toUpdate.moveBy(XMARGIN, YMARGIN); + repaint(toUpdate, false); + + position = relative; + + return true; + } + + // If we are on the gameboard itself, then play "tuberling" sound + if (editableArea.contains(position)) + topLevel->playSound(editableSound); + + return false; +} + +// Load objects and lay them down on the editable area +bool PlayGround::loadFrom(const QString &name) +{ + FILE *fp; + bool eof = false; + ToDraw readObject, *newObject; + Action *newAction; + + if (!(fp = fopen(QFile::encodeName(name), "r"))) return false; + + uint newGameboard; + int nitems = fscanf(fp, "%u\n", &newGameboard); + if (nitems == EOF) + { + fclose(fp); + return false; + } + topLevel->changeGameboard(newGameboard); + + for (;;) + { + if (!readObject.load(fp, decorations, eof)) + { + fclose(fp); + return false; + } + if (eof) + { + return !fclose(fp); + } + newObject = new ToDraw(readObject); + toDraw.append(newObject); + newAction = new Action(0, -1, newObject, toDraw.count()-1); + history.append(newAction); + currentAction++; + + } +} diff --git a/ktuberling/playground.h b/ktuberling/playground.h new file mode 100644 index 00000000..ee1a9a51 --- /dev/null +++ b/ktuberling/playground.h @@ -0,0 +1,86 @@ +/* ------------------------------------------------------------- + KDE Tuberling + Play ground widget + mailto:e.bischoff@noos.fr + ------------------------------------------------------------- */ + + +#ifndef _PLAYGROUND_H_ +#define _PLAYGROUND_H_ + +#include + +#include +#include +#include + +#include "todraw.h" +#include "action.h" + +class QDomDocument; +class TopLevel; + +class PlayGround : public QWidget +{ + Q_OBJECT + +public: + + PlayGround(TopLevel *parent, const char *name, uint selectedGameboard); + ~PlayGround(); + + void reset(); + void change(uint selectedGameboard); + void repaintAll(); + bool undo(); + bool redo(); + bool loadFrom(const QString &name); + bool saveAs(const QString &name); + bool printPicture(KPrinter &printer) const; + QPixmap getPicture() const; + + inline bool isFirstAction() const { return currentAction == 0; } + inline bool isLastAction() const { return currentAction >= history.count(); } + +protected: + + virtual void paintEvent(QPaintEvent *event); + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseReleaseEvent(QMouseEvent *event); + +private: + + bool registerPlayGrounds(QDomDocument &layoutDocument); + bool loadPlayGround(QDomDocument &layoutDocument, uint toLoad); + void loadFailure(); + void setupGeometry(); + bool zone(QPoint &position); + void drawText(QPainter &artist, QRect &area, QString &textId) const; + void drawGameboard(QPainter &artist, const QRect &area) const; + +private: + + QPixmap gameboard; // Picture of the game board + QBitmap masks; // Pictures of the objects' shapes + QRect editableArea; // Part of the gameboard where the player can lay down objects + QString menuItem, // Menu item describing describing this gameboard + editableSound; // Sound associated with this area + int texts, // Number of categories of objects names + decorations; // Number of draggable objects on the right side of the gameboard + QRect *textsLayout, // Positions of the categories names + *objectsLayout; // Position of the draggable objects on right side of the gameboard + QString *textsList, // List of the message numbers associated with categories + *soundsList; // List of sounds associated with each object + + QCursor *draggedCursor; // Cursor's shape for currently dragged object + ToDraw draggedObject; // Object currently dragged + int draggedZOrder; // Z-order (in to-draw buffer) of this object + + QPtrList toDraw; // List of objects in z-order + QPtrList history; // List of actions in chronological order + unsigned int currentAction; // Number of current action (not the last one if used "undo" button!) + + TopLevel *topLevel; // Top-level window +}; + +#endif diff --git a/ktuberling/soundfactory.cpp b/ktuberling/soundfactory.cpp new file mode 100644 index 00000000..d9bdf48d --- /dev/null +++ b/ktuberling/soundfactory.cpp @@ -0,0 +1,148 @@ +/* ------------------------------------------------------------- + KDE Tuberling + Play ground widget + mailto:e.bischoff@noos.fr + ------------------------------------------------------------- */ + +#include + +#include +#include +#include +#include + +#include + +#include "soundfactory.h" +#include "soundfactory.moc" +#include "toplevel.h" + +// Constructor +SoundFactory::SoundFactory(TopLevel *parent, const char *name, uint selectedLanguage) + : QObject(parent, name) +{ + topLevel = parent; + + namesList = filesList = 0; + + QDomDocument layoutsDocument; + bool ok = topLevel->loadLayout(layoutsDocument); + if (ok) ok = registerLanguages(layoutsDocument); + if (ok) ok = loadLanguage(layoutsDocument, selectedLanguage); + if (!ok) loadFailure(); +} + +// Destructor +SoundFactory::~SoundFactory() +{ + if (namesList) delete [] namesList; + if (filesList) delete [] filesList; +} + +// Change the language +void SoundFactory::change(uint selectedLanguage) +{ + QDomDocument layoutsDocument; + bool ok = topLevel->loadLayout(layoutsDocument); + if (ok) ok = loadLanguage(layoutsDocument, selectedLanguage); + if (!ok) loadFailure(); +} + +// Play some sound +void SoundFactory::playSound(const QString &soundRef) const +{ + int sound; + QString soundFile; + + if (!topLevel->isSoundEnabled()) return; + + for (sound = 0; sound < sounds; sound++) + if (!namesList[sound].compare(soundRef)) break; + if (sound == sounds) return; + + soundFile = locate("data", "ktuberling/sounds/" + filesList[sound]); + if (soundFile == 0) return; + +//printf("%s\n", (const char *) soundFile); + KAudioPlayer::play(soundFile); +} + +// Report a load failure +void SoundFactory::loadFailure() +{ + KMessageBox::error(topLevel, i18n("Error while loading the sound names.")); +} + +// Register the various languages +bool SoundFactory::registerLanguages(QDomDocument &layoutDocument) +{ + QDomNodeList languagesList, menuItemsList, labelsList; + QDomElement languageElement, menuItemElement, labelElement; + QDomAttr codeAttribute, actionAttribute; + bool enabled; + + languagesList = layoutDocument.elementsByTagName("language"); + if (languagesList.count() < 1) + return false; + + for (uint i = 0; i < languagesList.count(); i++) + { + languageElement = (const QDomElement &) languagesList.item(i).toElement(); + codeAttribute = languageElement.attributeNode("code"); + enabled = locate("data", "ktuberling/sounds/" + codeAttribute.value() + "/") != 0; + + menuItemsList = languageElement.elementsByTagName("menuitem"); + if (menuItemsList.count() != 1) + return false; + + menuItemElement = (const QDomElement &) menuItemsList.item(0).toElement(); + + labelsList = menuItemElement.elementsByTagName("label"); + if (labelsList.count() != 1) + return false; + + labelElement = (const QDomElement &) labelsList.item(0).toElement(); + actionAttribute = menuItemElement.attributeNode("action"); + topLevel->registerLanguage(labelElement.text(), actionAttribute.value().latin1(), enabled); + } + + return true; +} + +// Load the sounds of one given language +bool SoundFactory::loadLanguage(QDomDocument &layoutDocument, uint toLoad) +{ + QDomNodeList languagesList, + soundNamesList; + QDomElement languageElement, + soundNameElement; + QDomAttr nameAttribute, fileAttribute; + + languagesList = layoutDocument.elementsByTagName("language"); + if (toLoad >= languagesList.count()) + return false; + + languageElement = (const QDomElement &) languagesList.item(toLoad).toElement(); + + soundNamesList = languageElement.elementsByTagName("sound"); + sounds = soundNamesList.count(); + if (sounds < 1) + return false; + + if (!(namesList = new QString[sounds])) + return false; + if (!(filesList = new QString[sounds])) + return false; + + for (uint sound = 0; sound < sounds; sound++) + { + soundNameElement = (const QDomElement &) soundNamesList.item(sound).toElement(); + + nameAttribute = soundNameElement.attributeNode("name"); + namesList[sound] = nameAttribute.value(); + fileAttribute = soundNameElement.attributeNode("file"); + filesList[sound] = fileAttribute.value(); + } + return true; +} + diff --git a/ktuberling/soundfactory.h b/ktuberling/soundfactory.h new file mode 100644 index 00000000..1eb0abd5 --- /dev/null +++ b/ktuberling/soundfactory.h @@ -0,0 +1,46 @@ +/* ------------------------------------------------------------- + KDE Tuberling + Sound factory + mailto:e.bischoff@noos.fr + ------------------------------------------------------------- */ + + +#ifndef _SOUNDFACTORY_H_ +#define _SOUNDFACTORY_H_ + +#include "qobject.h" + +class QDomDocument; +class TopLevel; + +class SoundFactory : public QObject +{ + Q_OBJECT + +public: + + SoundFactory(TopLevel *parent, const char *name, uint selectedLanguage); + ~SoundFactory(); + + void change(uint selectedLanguage); + void playSound(const QString &soundRef) const; + +protected: + + bool registerLanguages(QDomDocument &layoutDocument); + bool loadLanguage(QDomDocument &layoutDocument, uint toLoad); + +private: + + void loadFailure(); + +private: + + int sounds; // Number of sounds + QString *namesList, // List of sound names + *filesList; // List of sound files associated with each sound name + + TopLevel *topLevel; // Top-level window +}; + +#endif diff --git a/ktuberling/sounds/Makefile.am b/ktuberling/sounds/Makefile.am new file mode 100644 index 00000000..3f0d46b7 --- /dev/null +++ b/ktuberling/sounds/Makefile.am @@ -0,0 +1,9 @@ + +# add here all files +sound_DATA = badge.wav bow.wav cigar.wav ear.wav earring.wav eye.wav eyebrow.wav\ + hat.wav moustache.wav mouth.wav nose.wav spectacles.wav \ + sunglasses.wav tuberling.wav watch.wav + +sounddir = $(kde_datadir)/ktuberling/sounds/en + +# DONT ADD TRANSLATIONS HERE diff --git a/ktuberling/sounds/NO_TRANSLATIONS_HERE b/ktuberling/sounds/NO_TRANSLATIONS_HERE new file mode 100644 index 00000000..e69de29b diff --git a/ktuberling/sounds/badge.wav b/ktuberling/sounds/badge.wav new file mode 100644 index 00000000..2e436e3a Binary files /dev/null and b/ktuberling/sounds/badge.wav differ diff --git a/ktuberling/sounds/bow.wav b/ktuberling/sounds/bow.wav new file mode 100644 index 00000000..28703029 Binary files /dev/null and b/ktuberling/sounds/bow.wav differ diff --git a/ktuberling/sounds/cigar.wav b/ktuberling/sounds/cigar.wav new file mode 100644 index 00000000..dbc72e25 Binary files /dev/null and b/ktuberling/sounds/cigar.wav differ diff --git a/ktuberling/sounds/ear.wav b/ktuberling/sounds/ear.wav new file mode 100644 index 00000000..901bac74 Binary files /dev/null and b/ktuberling/sounds/ear.wav differ diff --git a/ktuberling/sounds/earring.wav b/ktuberling/sounds/earring.wav new file mode 100644 index 00000000..e708caaf Binary files /dev/null and b/ktuberling/sounds/earring.wav differ diff --git a/ktuberling/sounds/eye.wav b/ktuberling/sounds/eye.wav new file mode 100644 index 00000000..b596d4c6 Binary files /dev/null and b/ktuberling/sounds/eye.wav differ diff --git a/ktuberling/sounds/eyebrow.wav b/ktuberling/sounds/eyebrow.wav new file mode 100644 index 00000000..d3c1b3b4 Binary files /dev/null and b/ktuberling/sounds/eyebrow.wav differ diff --git a/ktuberling/sounds/hat.wav b/ktuberling/sounds/hat.wav new file mode 100644 index 00000000..b6d92e5a Binary files /dev/null and b/ktuberling/sounds/hat.wav differ diff --git a/ktuberling/sounds/moustache.wav b/ktuberling/sounds/moustache.wav new file mode 100644 index 00000000..e671f1c8 Binary files /dev/null and b/ktuberling/sounds/moustache.wav differ diff --git a/ktuberling/sounds/mouth.wav b/ktuberling/sounds/mouth.wav new file mode 100644 index 00000000..90613e9c Binary files /dev/null and b/ktuberling/sounds/mouth.wav differ diff --git a/ktuberling/sounds/nose.wav b/ktuberling/sounds/nose.wav new file mode 100644 index 00000000..058e718f Binary files /dev/null and b/ktuberling/sounds/nose.wav differ diff --git a/ktuberling/sounds/spectacles.wav b/ktuberling/sounds/spectacles.wav new file mode 100644 index 00000000..bf0802ae Binary files /dev/null and b/ktuberling/sounds/spectacles.wav differ diff --git a/ktuberling/sounds/sunglasses.wav b/ktuberling/sounds/sunglasses.wav new file mode 100644 index 00000000..0b8f8c6d Binary files /dev/null and b/ktuberling/sounds/sunglasses.wav differ diff --git a/ktuberling/sounds/tuberling.wav b/ktuberling/sounds/tuberling.wav new file mode 100644 index 00000000..c85418b1 Binary files /dev/null and b/ktuberling/sounds/tuberling.wav differ diff --git a/ktuberling/sounds/watch.wav b/ktuberling/sounds/watch.wav new file mode 100644 index 00000000..87c03278 Binary files /dev/null and b/ktuberling/sounds/watch.wav differ diff --git a/ktuberling/todraw.cpp b/ktuberling/todraw.cpp new file mode 100644 index 00000000..e12c9e1b --- /dev/null +++ b/ktuberling/todraw.cpp @@ -0,0 +1,90 @@ +/* ------------------------------------------------------------- + KDE Tuberling + Object to draw on the game board + mailto:e.bischoff@noos.fr + ------------------------------------------------------------- */ + +#include +#include + +#include "todraw.h" + +// Constructor with no arguments +ToDraw::ToDraw() + : position() +{ + number = -1; +} + +// Copy constructor +ToDraw::ToDraw(const ToDraw &model) + : position(model.position) +{ + number = model.number; +} + +// Constructor with arguments +ToDraw::ToDraw(int declaredNumber, const QRect &declaredPosition) + : position(declaredPosition) +{ + number = declaredNumber; +} + +// Affectation operator +ToDraw &ToDraw::operator=(const ToDraw &model) +{ + if (&model == this) return *this; + + position = model.position; + number = model.number; + + return *this; +} + +// Draw an object previously laid down on the game board +void ToDraw::draw(QPainter &artist, const QRect &area, + const QRect *objectsLayout, + const QPixmap *gameboard, const QBitmap *masks) const +{ + if (!position.intersects(area)) return; + + QPixmap objectPixmap(objectsLayout[number].size()); + QBitmap shapeBitmap(objectsLayout[number].size()); + + bitBlt(&objectPixmap, QPoint(0, 0), gameboard, objectsLayout[number], Qt::CopyROP); + bitBlt(&shapeBitmap, QPoint(0, 0), masks, objectsLayout[number], Qt::CopyROP); + objectPixmap.setMask(shapeBitmap); + artist.drawPixmap(position.topLeft(), objectPixmap); +} + +// Load an object from a file +bool ToDraw::load(FILE *fp, int decorations, bool &eof) +{ + int nitems; + int pl, pt, pr, pb; + + nitems = fscanf(fp, "%d\t%d %d %d %d\n", &number, &pl, &pt, &pr, &pb); + + if (nitems == EOF) + { + eof = true; + return true; + } + eof = false; + if (nitems != 5) return false; + + if (number < 0 || number >= decorations) return false; + + position.setCoords(pl, pt, pr, pb); + + return true; +} + +// Save an object to a file +void ToDraw::save(FILE *fp) const +{ + fprintf(fp, "%d\t%d %d %d %d\n", + number, + position.left(), position.top(), position.right(), position.bottom()); +} + diff --git a/ktuberling/todraw.h b/ktuberling/todraw.h new file mode 100644 index 00000000..4e46200a --- /dev/null +++ b/ktuberling/todraw.h @@ -0,0 +1,35 @@ +/* ------------------------------------------------------------- + KDE Tuberling + Object to draw on the game board + mailto:e.bischoff@noos.fr + ------------------------------------------------------------- */ + + +#ifndef _TODRAW_H_ +#define _TODRAW_H_ + +#include + +#include + +class ToDraw +{ + public: + ToDraw(); + ToDraw(const ToDraw &); + ToDraw(int, const QRect &); + ToDraw &operator=(const ToDraw &); + void draw(QPainter &, const QRect &, const QRect *, const QPixmap *, const QBitmap *) const; + void save(FILE *) const; + bool load(FILE *, int, bool &); + + inline int getNumber() const { return number; } + inline void setNumber(int newValue) { number = newValue; } + inline const QRect &getPosition() const { return position; } + + private: + int number; + QRect position; +}; + +#endif diff --git a/ktuberling/toplevel.cpp b/ktuberling/toplevel.cpp new file mode 100644 index 00000000..d4ad647c --- /dev/null +++ b/ktuberling/toplevel.cpp @@ -0,0 +1,611 @@ +/* ------------------------------------------------------------- + KDE Tuberling + Top level window + mailto:e.bischoff@noos.fr + ------------------------------------------------------------- */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "toplevel.moc" +#include "playground.h" +#include "soundfactory.h" + +// Constructor +TopLevel::TopLevel() + : KMainWindow(0) +{ + readOptions(); + + gameboards = 0; + languages = 0; + playGround = new PlayGround(this, "playground", selectedGameboard); + soundFactory = new SoundFactory(this, "sounds", selectedLanguage); + + setCentralWidget(playGround); + + setupKAction(); +} + +// Destructor +TopLevel::~TopLevel() +{ +} + +// Enable or disable "undo" button and menu item +void TopLevel::enableUndo(bool enable) const +{ + actionCollection()->action(KStdAction::stdName(KStdAction::Undo))->setEnabled(enable); +} + +// Enable or disable "redo" button and menu item +void TopLevel::enableRedo(bool enable) const +{ + actionCollection()->action(KStdAction::stdName(KStdAction::Redo))->setEnabled(enable); +} + +// Register an available gameboard +void TopLevel::registerGameboard(const QString &menuItem, const char *actionId) +{ + KToggleAction *t = 0; + + switch (gameboards) + { + case 0: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(gameboard0()), actionCollection(), actionId); + break; + case 1: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(gameboard1()), actionCollection(), actionId); + break; + case 2: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(gameboard2()), actionCollection(), actionId); + break; + case 3: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(gameboard3()), actionCollection(), actionId); + break; + case 4: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(gameboard4()), actionCollection(), actionId); + break; + case 5: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(gameboard5()), actionCollection(), actionId); + break; + case 6: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(gameboard6()), actionCollection(), actionId); + break; + case 7: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(gameboard7()), actionCollection(), actionId); + break; + } + + if( t ) { + if (gameboards == selectedGameboard) t->setChecked(true); + gameboardActions[gameboards] = actionId; + gameboards++; + } +} + +// Register an available language +void TopLevel::registerLanguage(const QString &menuItem, const char *actionId, bool enabled) +{ + KToggleAction *t = 0; + + switch (languages) + { + case 0: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(language0()), actionCollection(), actionId); + break; + case 1: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(language1()), actionCollection(), actionId); + break; + case 2: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(language2()), actionCollection(), actionId); + break; + case 3: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(language3()), actionCollection(), actionId); + break; + case 4: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(language4()), actionCollection(), actionId); + break; + case 5: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(language5()), actionCollection(), actionId); + break; + case 6: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(language6()), actionCollection(), actionId); + break; + case 7: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(language7()), actionCollection(), actionId); + break; + case 8: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(language8()), actionCollection(), actionId); + break; + case 9: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(language9()), actionCollection(), actionId); + break; + case 10: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(language10()), actionCollection(), actionId); + break; + case 11: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(language11()), actionCollection(), actionId); + break; + case 12: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(language12()), actionCollection(), actionId); + break; + case 13: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(language13()), actionCollection(), actionId); + break; + case 14: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(language14()), actionCollection(), actionId); + break; + case 15: t = new KToggleAction(i18n(menuItem.latin1()), 0, this, SLOT(language15()), actionCollection(), actionId); + break; + } + + if( t ) { + if (languages == selectedLanguage) t->setChecked(true); + t->setEnabled(enabled); + languageActions[languages] = actionId; + languages++; + } +} + +// Switch to another gameboard +void TopLevel::changeGameboard(uint newGameboard) +{ + // Do not accept to switch to same gameboard + if (newGameboard == selectedGameboard) { + // select this gameboard again + ((KToggleAction*) actionCollection()->action(gameboardActions[newGameboard].latin1()))->setChecked(true); + return; + } + + // Unselect preceding gameboard + ((KToggleAction*) actionCollection()->action(gameboardActions[selectedGameboard].latin1()))->setChecked(false); + + // Change gameboard in the remembered options + selectedGameboard = newGameboard; + writeOptions(); + + if( !((KToggleAction*) actionCollection()->action(gameboardActions[selectedGameboard].latin1()))->isChecked() ) + ((KToggleAction*) actionCollection()->action(gameboardActions[selectedGameboard].latin1()))->setChecked(true); + + // Change gameboard effectively + playGround->change(newGameboard); + + // Disable undo and redo actions + enableUndo(false); + enableRedo(false); +} + +// Switch to another language +void TopLevel::changeLanguage(uint newLanguage) +{ + // Do not accept to switch to same language + if (newLanguage == selectedLanguage && soundEnabled) { + // newLanguage should stay checked + ((KToggleAction*) actionCollection()->action(languageActions[newLanguage].latin1()))->setChecked(true); + return; + } + + // Unselect preceding language + if (!soundEnabled) ((KToggleAction*) actionCollection()->action("speech_no_sound"))->setChecked(false); + ((KToggleAction*) actionCollection()->action(languageActions[selectedLanguage].latin1()))->setChecked(false); + + // Change language in the remembered options + soundEnabled = true; + selectedLanguage = newLanguage; + writeOptions(); + + // Change language effectively + soundFactory->change(newLanguage); +} + +// Load the layouts file +bool TopLevel::loadLayout(QDomDocument &layoutDocument) +{ + QFile layoutFile(QFile::encodeName(locate("data", "ktuberling/pics/layout.xml"))); + if (!layoutFile.open(IO_ReadOnly)) + return false; + + if (!layoutDocument.setContent(&layoutFile)) + { + layoutFile.close(); + return false; + } + layoutFile.close(); + + return true; +} + +// Play a sound +void TopLevel::playSound(const QString &ref) const +{ + soundFactory->playSound(ref); +} + +// Read options from preferences file +void TopLevel::readOptions() +{ + KConfig *config; + QString option; + + config = KApplication::kApplication()->config(); + + config->setGroup("General"); + option = config->readEntry("Sound", "on"); + soundEnabled = option.find("on") == 0; + + option = config->readEntry("GameboardNumber", "0"); + selectedGameboard = option.toInt(); + if (selectedGameboard <= 0) selectedGameboard = 0; + if (selectedGameboard > 7) selectedGameboard = 7; + + option = config->readEntry("LanguageNumber", "2"); + selectedLanguage = option.toInt(); + if (selectedLanguage <= 0) selectedLanguage = 0; + if (selectedLanguage > 15) selectedLanguage = 15; + +} + +// Write options to preferences file +void TopLevel::writeOptions() +{ + KConfig *config; + + config = KApplication::kApplication()->config(); + + config->setGroup("General"); + config->writeEntry("Sound", soundEnabled? "on": "off"); + + config->writeEntry("GameboardNumber", selectedGameboard); + + config->writeEntry("LanguageNumber", selectedLanguage); + + config->sync(); +} + +// KAction initialization (aka menubar + toolbar init) +void TopLevel::setupKAction() +{ +//Game + KStdGameAction::gameNew(this, SLOT(fileNew()), actionCollection()); + KStdGameAction::load(this, SLOT(fileOpen()), actionCollection()); + KStdGameAction::save(this, SLOT(fileSave()), actionCollection()); + KStdGameAction::print(this, SLOT(filePrint()), actionCollection()); + KStdGameAction::quit(kapp, SLOT(quit()), actionCollection()); + (void) new KAction(i18n("Save &as Picture..."), 0, this, SLOT(filePicture()), actionCollection(), "game_save_picture"); + +//Edit + KStdAction::copy(this, SLOT(editCopy()), actionCollection()); + KStdAction::undo(this, SLOT(editUndo()), actionCollection()); + KStdAction::redo(this, SLOT(editRedo()), actionCollection()); + enableUndo(false); + enableRedo(false); + +//Speech + KToggleAction* t = new KToggleAction(i18n("&No Sound"), 0, this, SLOT(soundOff()), actionCollection(), "speech_no_sound"); + if (!soundEnabled) t->setChecked(true); + + setupGUI(); +} + +// Reset gameboard +void TopLevel::fileNew() +{ + playGround->reset(); + + enableUndo(false); + enableRedo(false); + + playGround->repaintAll(); +} + +// Load gameboard +void TopLevel::fileOpen() +{ + QString dir = locate("data", "ktuberling/museum/miss.tuberling"); + dir.truncate(dir.findRev('/') + 1); + + KURL url = KFileDialog::getOpenURL(dir, "*.tuberling"); + + open(url); +} + +void TopLevel::open(const KURL &url) +{ + if (url.isEmpty()) + return; + + QString name; + + KIO::NetAccess::download(url, name, this); + + playGround->reset(); + + if (!playGround->loadFrom(name)) + KMessageBox::error(this, i18n("Could not load file.")); + + enableUndo(!playGround->isFirstAction()); + enableRedo(false); + + playGround->repaintAll(); + + KIO::NetAccess::removeTempFile( name ); +} + +// Save gameboard +void TopLevel::fileSave() +{ + KURL url = KFileDialog::getSaveURL + ( QString::null, + "*.tuberling"); + + if (url.isEmpty()) + return; + + if( !url.isLocalFile() ) + { + KMessageBox::sorry(this, + i18n("Only saving to local files is currently " + "supported.")); + return; + } + + QString name = url.path(); + int suffix; + + suffix = name.findRev('.'); + if (suffix == -1) + { + name += ".tuberling"; + } + + if( !playGround->saveAs( name ) ) + KMessageBox::error(this, i18n("Could not save file.")); +} + +// Save gameboard as picture +void TopLevel::filePicture() +{ + QPixmap picture(playGround->getPicture()); + + KURL url = KFileDialog::getSaveURL + ( QString::null, + i18n( "*.xpm|UNIX Pixmaps (*.xpm)\n" + "*.jpg|JPEG Compressed Files (*.jpg)\n" + "*.png|Next Generation Pictures (*.png)\n" + "*.bmp|Windows Bitmaps (*.bmp)\n" + "*|All Picture Formats")); + + if( url.isEmpty() ) + return; + + if( !url.isLocalFile() ) + { + KMessageBox::sorry(this, + i18n("Only saving to local files is currently " + "supported.")); + return; + } + + QString name = url.path(); + const char *format; + int suffix; + QString end; + + suffix = name.findRev('.'); + if (suffix == -1) + { + name += ".xpm"; + end = "xpm"; + } + else end = name.mid(suffix + 1, name.length()); + + if (end == "xpm") format = "XPM"; + else if (end == "jpg") format = "JPEG"; + else if (end == "png") format = "PNG"; + else if (end == "bmp") format = "BMP"; + else + { + KMessageBox::error(this, i18n("Unknown picture format.")); + return; + } + + if (!picture.save(name, format)) + KMessageBox::error + (this, i18n("Could not save file.")); +} + +// Save gameboard as picture +void TopLevel::filePrint() +{ + KPrinter printer; + bool ok; + + ok = printer.setup(this, i18n("Print %1").arg(actionCollection()->action(gameboardActions[selectedGameboard].latin1())->plainText())); + if (!ok) return; + playGround->repaint(true); + if (!playGround->printPicture(printer)) + KMessageBox::error(this, + i18n("Could not print picture.")); + else + KMessageBox::information(this, + i18n("Picture successfully printed.")); +} + +// Copy modified area to clipboard +void TopLevel::editCopy() +{ + QClipboard *clipboard = QApplication::clipboard(); + QPixmap picture(playGround->getPicture()); + + clipboard->setPixmap(picture); +} + +// Undo last action +void TopLevel::editUndo() +{ + if (playGround->isFirstAction()) return; + + if (!playGround->undo()) return; + + if (playGround->isFirstAction()) enableUndo(false); + enableRedo(true); + + playGround->repaintAll(); +} + +// Redo last action +void TopLevel::editRedo() +{ + if (playGround->isLastAction()) return; + + if (!playGround->redo()) return; + + if (playGround->isLastAction()) enableRedo(false); + enableUndo(true); + + playGround->repaintAll(); +} + +// Switch to gameboard #0 +void TopLevel::gameboard0() +{ + changeGameboard(0); +} + +// Switch to gameboard #1 +void TopLevel::gameboard1() +{ + changeGameboard(1); +} + +// Switch to gameboard #2 +void TopLevel::gameboard2() +{ + changeGameboard(2); +} + +// Switch to gameboard #3 +void TopLevel::gameboard3() +{ + changeGameboard(3); +} + +// Switch to gameboard #4 +void TopLevel::gameboard4() +{ + changeGameboard(4); +} + +// Switch to gameboard #5 +void TopLevel::gameboard5() +{ + changeGameboard(5); +} + +// Switch to gameboard #6 +void TopLevel::gameboard6() +{ + changeGameboard(6); +} + +// Switch to gameboard #7 +void TopLevel::gameboard7() +{ + changeGameboard(7); +} + +// Toggle sound off +void TopLevel::soundOff() +{ + if (!soundEnabled) return; + + soundEnabled = false; + ((KToggleAction*) actionCollection()->action(languageActions[selectedLanguage].latin1()))->setChecked(false); + ((KToggleAction*) actionCollection()->action("speech_no_sound"))->setChecked(true); + + writeOptions(); +} + +// Switch to language #0 +void TopLevel::language0() +{ + changeLanguage(0); +} + +// Switch to language #1 +void TopLevel::language1() +{ + changeLanguage(1); +} + +// Switch to language #2 +void TopLevel::language2() +{ + changeLanguage(2); +} + +// Switch to language #3 +void TopLevel::language3() +{ + changeLanguage(3); +} + +// Switch to language #4 +void TopLevel::language4() +{ + changeLanguage(4); +} + +// Switch to language #5 +void TopLevel::language5() +{ + changeLanguage(5); +} + +// Switch to language #6 +void TopLevel::language6() +{ + changeLanguage(6); +} + +// Switch to language #7 +void TopLevel::language7() +{ + changeLanguage(7); +} + +// Switch to language #8 +void TopLevel::language8() +{ + changeLanguage(8); +} + +// Switch to language #9 +void TopLevel::language9() +{ + changeLanguage(9); +} + +// Switch to language #10 +void TopLevel::language10() +{ + changeLanguage(10); +} + +// Switch to language #11 +void TopLevel::language11() +{ + changeLanguage(11); +} + +// Switch to language #12 +void TopLevel::language12() +{ + changeLanguage(12); +} + +// Switch to language #13 +void TopLevel::language13() +{ + changeLanguage(13); +} + +// Switch to language #14 +void TopLevel::language14() +{ + changeLanguage(14); +} + +// Switch to language #15 +void TopLevel::language15() +{ + changeLanguage(15); +} diff --git a/ktuberling/toplevel.h b/ktuberling/toplevel.h new file mode 100644 index 00000000..8d67a252 --- /dev/null +++ b/ktuberling/toplevel.h @@ -0,0 +1,105 @@ +/* ------------------------------------------------------------- + KDE Tuberling + Top level window + mailto:e.bischoff@noos.fr + ------------------------------------------------------------- */ + + +#ifndef _TOPLEVEL_H_ +#define _TOPLEVEL_H_ + +#include +#include + +class QDomDocument; +class PlayGround; +class SoundFactory; + +class TopLevel : public KMainWindow +{ + Q_OBJECT + +public: + + TopLevel(); + ~TopLevel(); + + void open(const KURL &url); + void enableUndo(bool enable) const; + void enableRedo(bool enable) const; + void registerGameboard(const QString &menuItem, const char *actionId); + void registerLanguage(const QString &menuItem, const char *actionId, bool enabled); + void changeGameboard(uint newGameboard); + void changeLanguage(uint newLanguage); + bool loadLayout(QDomDocument &layoutDocument); + void playSound(const QString &ref) const; + + inline bool isSoundEnabled() const {return soundEnabled;} + inline uint getSelectedGameboard() const {return selectedGameboard;} + +protected: + + void readOptions(); + void writeOptions(); + void setupKAction(); + +private slots: + + void fileNew(); + void fileOpen(); + void fileSave(); + void filePicture(); + void filePrint(); + void editCopy(); + void editUndo(); + void editRedo(); + void gameboard0(); + void gameboard1(); + void gameboard2(); + void gameboard3(); + void gameboard4(); + void gameboard5(); + void gameboard6(); + void gameboard7(); + void soundOff(); + void language0(); + void language1(); + void language2(); + void language3(); + void language4(); + void language5(); + void language6(); + void language7(); + void language8(); + void language9(); + void language10(); + void language11(); + void language12(); + void language13(); + void language14(); + void language15(); + +private: + + int // Menu items identificators + newID, openID, saveID, pictureID, printID, quitID, + copyID, undoID, redoID, + gameboardID, speechID; + enum { // Tool bar buttons identificators + ID_NEW, ID_OPEN, ID_SAVE, ID_PRINT, + ID_UNDO, ID_REDO, + ID_HELP }; + + bool soundEnabled; // True if the sound is enabled by user, even if there is no audio server + uint selectedGameboard, // Number of currently selected gameboard + gameboards; // Total number of gameboards + uint selectedLanguage, // Number of selected language + languages; // Total number of languages + QString gameboardActions[8], // Name of actions for registered gameboards + languageActions[16]; // Name of actions for registered languages + + PlayGround *playGround; // Play ground central widget + SoundFactory *soundFactory; // Speech organ +}; + +#endif diff --git a/ktuberling/x-tuberling.desktop b/ktuberling/x-tuberling.desktop new file mode 100644 index 00000000..9d62b1ab --- /dev/null +++ b/ktuberling/x-tuberling.desktop @@ -0,0 +1,61 @@ +# KDE Config File +[Desktop Entry] +MimeType=application/x-tuberling +Comment=Potato +Comment[ar]=بطاطا +Comment[be]=Клубень +Comment[bg]=Картоф +Comment[bn]=আলৠ+Comment[br]=Patatez +Comment[bs]=Krompir +Comment[cs]=Brambora +Comment[da]=Kartoffel +Comment[de]=Kartoffel +Comment[el]=Πατάτα +Comment[eo]=Terpomo +Comment[es]=Papá Patata +Comment[et]=Kartulimees +Comment[eu]=Patata +Comment[fa]=سیب‌زمینی +Comment[fi]=Peruna +Comment[fr]=Patate +Comment[ga]=Práta +Comment[gl]=Pataca +Comment[he]=תפוח ×דמה +Comment[hi]=पोटैटो +Comment[hr]=Krumpirko +Comment[hu]=Krumpli +Comment[is]=Kartafla +Comment[it]=Patata +Comment[ja]=ãƒãƒ†ãƒˆ +Comment[km]=ដំឡូង +Comment[lt]=BulvÄ— +Comment[lv]=Kartupelis +Comment[mk]=Компир +Comment[nb]=Potet +Comment[nds]=Kantüffel +Comment[ne]=पोटेटो +Comment[nl]=Aardappel +Comment[nn]=Potet +Comment[pa]=ਆਲੂ +Comment[pl]=Ziemniak +Comment[pt]=Batata +Comment[pt_BR]=Batata +Comment[se]=BuÄ‘et +Comment[sk]=Zemiak +Comment[sl]=KrompirÄek +Comment[sv]=Potatis +Comment[ta]=உரà¯à®³à¯ˆà®•à®¿à®´à®™à¯à®•à¯ +Comment[tg]=Картошка +Comment[tr]=Patates +Comment[uk]=ÐšÐ°Ñ€Ñ‚Ð¾Ð¿Ð»Ñ +Comment[wa]=Crompire +Comment[zh_CN]=土豆 +Comment[zh_TW]=馬鈴薯 +Icon=ktuberling +Type=MimeType +Patterns=*.tuberling; +X-KDE-AutoEmbed=false +[Property::X-KDE-NativeExtension] +Type=QString +Value=.tuberling diff --git a/kwin4/AUTHORS b/kwin4/AUTHORS new file mode 100644 index 00000000..944b9a7d --- /dev/null +++ b/kwin4/AUTHORS @@ -0,0 +1 @@ +Martin Heni diff --git a/kwin4/COPYING b/kwin4/COPYING new file mode 100644 index 00000000..54754ab4 --- /dev/null +++ b/kwin4/COPYING @@ -0,0 +1,341 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/kwin4/INSTALL b/kwin4/INSTALL new file mode 100644 index 00000000..02a4a074 --- /dev/null +++ b/kwin4/INSTALL @@ -0,0 +1,167 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes a while. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Type `make install' to install the programs and any data files and + documentation. + + 4. You can remove the program binaries and object files from the + source code directory by typing `make clean'. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff --git a/kwin4/Makefile.am b/kwin4/Makefile.am new file mode 100644 index 00000000..98008b8e --- /dev/null +++ b/kwin4/Makefile.am @@ -0,0 +1,7 @@ +SUBDIRS = kwin4 grafix + +AUTOMAKE_OPTIONS = foreign + +KDE_ICON = kwin4 + +xdg_apps_DATA = kwin4.desktop diff --git a/kwin4/README b/kwin4/README new file mode 100644 index 00000000..88cf297b --- /dev/null +++ b/kwin4/README @@ -0,0 +1,26 @@ +GENERAL NOTE: + + This is a small KDE 2.0 game, I wrote to practise + programming in KDE. + It should work quite stable. But if you find any bugs + please contact me. + It would be also nice to give me some general feedback + about the program, so that it can be approved or it + can be considered in other programs. + (Email: martin@heni-online.de) + +INSTALLATION: + +# unpack the archive +tar xzf kwin4-0.9.tar.gz + +# build the package +cd kwin4-0.9 +./configure +# or if KDE is in /opt/kde2 +./configure --prefix=/opt/kde2 +make + +# become superuser for installing +su -c 'make install' + diff --git a/kwin4/grafix/Makefile.am b/kwin4/grafix/Makefile.am new file mode 100644 index 00000000..6390e071 --- /dev/null +++ b/kwin4/grafix/Makefile.am @@ -0,0 +1,22 @@ +GRAFIXDIRS = default + +grafixdir = $(kde_datadir)/kwin4/grafix + +uninstall-local: + rm -rf $(DESTDIR)$(grafixdir) + +install-data-local: + $(mkinstalldirs) $(DESTDIR)$(grafixdir) + for i in $(GRAFIXDIRS); do \ + if test -d $(DESTDIR)$(grafixdir)/$$i; then \ + echo "refreshing and removing $$i" ;\ + rm -rf $(DESTDIR)$(grafixdir)/$$i;\ + fi ;\ + echo "installing $$i" ;\ + if test ! -d $(DESTDIR)$(grafixdir)/$$i; then \ + cp -r $(srcdir)/$$i $(DESTDIR)$(grafixdir)/$$i ;\ + rm -rf $(DESTDIR)$(grafixdir)/$$i/CVS ;\ + rm -rf $(DESTDIR)$(grafixdir)/$$i/.svn ;\ + fi ;\ + done + diff --git a/kwin4/grafix/default/aboute.png b/kwin4/grafix/default/aboute.png new file mode 100644 index 00000000..78c8f4c1 Binary files /dev/null and b/kwin4/grafix/default/aboute.png differ diff --git a/kwin4/grafix/default/arrow0.png b/kwin4/grafix/default/arrow0.png new file mode 100644 index 00000000..c93e7e63 Binary files /dev/null and b/kwin4/grafix/default/arrow0.png differ diff --git a/kwin4/grafix/default/arrow1.png b/kwin4/grafix/default/arrow1.png new file mode 100644 index 00000000..81a2b5e7 Binary files /dev/null and b/kwin4/grafix/default/arrow1.png differ diff --git a/kwin4/grafix/default/arrow2.png b/kwin4/grafix/default/arrow2.png new file mode 100644 index 00000000..89c9b815 Binary files /dev/null and b/kwin4/grafix/default/arrow2.png differ diff --git a/kwin4/grafix/default/background.png b/kwin4/grafix/default/background.png new file mode 100644 index 00000000..7edcd84e Binary files /dev/null and b/kwin4/grafix/default/background.png differ diff --git a/kwin4/grafix/default/board.png b/kwin4/grafix/default/board.png new file mode 100644 index 00000000..d27ff61f Binary files /dev/null and b/kwin4/grafix/default/board.png differ diff --git a/kwin4/grafix/default/crnt.png b/kwin4/grafix/default/crnt.png new file mode 100644 index 00000000..5a3c0c69 Binary files /dev/null and b/kwin4/grafix/default/crnt.png differ diff --git a/kwin4/grafix/default/empty.png b/kwin4/grafix/default/empty.png new file mode 100644 index 00000000..acb05be3 Binary files /dev/null and b/kwin4/grafix/default/empty.png differ diff --git a/kwin4/grafix/default/empty2.png b/kwin4/grafix/default/empty2.png new file mode 100644 index 00000000..94ec038d Binary files /dev/null and b/kwin4/grafix/default/empty2.png differ diff --git a/kwin4/grafix/default/game_over.png b/kwin4/grafix/default/game_over.png new file mode 100644 index 00000000..45f030f7 Binary files /dev/null and b/kwin4/grafix/default/game_over.png differ diff --git a/kwin4/grafix/default/grafix.rc b/kwin4/grafix/default/grafix.rc new file mode 100644 index 00000000..afd8237c --- /dev/null +++ b/kwin4/grafix/default/grafix.rc @@ -0,0 +1,148 @@ +[about] +file=aboute.png +rtti=32 +x=150.0 +y=50.0 +z=0.0 + +[arrow] +file=arrow%d.png +number=3 +offset=19,5 +rtti=32 +z=9.0 + +[board] +file=board.png +rtti=32 +x=15.0 +y=40.0 +z=0.0 + +# more then max(z piece) +[empty] +file=empty2.png +offset=19,12 +rtti=32 +z=60.0 + +# Less then z piece +[empty2] +file=empty.png +offset=19,12 +rtti=32 +z=5.0 + +[game] +scorewidget=390,25 +spread_x=8 +spread_y=8 +statuswidget=350,250 + +[gameover] +desty=140 +file=game_over.png +offset=0,0 +rtti=32 +speed=2.0 +x=25.0 +y=20.0 +z=150.0 + +[hint] +file=crnt.png +offset=19,20 +rtti=32 +z=100.0 + +[intro1] +color=255,255,0 +# Family, pointsize, pixelsize,stylehint,weight,italic, +# ... undeline,strikeout,fixedpitch,rawmode +# If you want pixelsize. Otherwise just OMIT the pixelsize +# argument +font=Sans Serif,-1,25,default,50,0,0,0,0,0 +rtti=3 +text=Dummy +x=230.0 +y=70.0 +z=150.0 + +[intro2] +color=255,255,0 +font=Sans Serif,-1,20,default,50,0,0,0,0,0 +rtti=3 +text=Dummy +x=275.0 +y=115.0 +z=150.0 + +[intro3] +color=255,255,0 +font=Sans Serif,-1,25,default,50,0,0,0,0,0 +rtti=3 +text=Dummy +x=245.0 +y=160.0 +z=150.0 + +# z = z..z+21 +[piece] +file=piece%d.png +number=2 +offset=19,20 +rtti=32 +speed=4.0 +z=10.0 + +[star] +anim0=0,4,2,8 +file=star%d.png +number=5 +offset=19,20 +rtti=32 +z=100.0 + +[text1] +color=255,255,255 +font=Sans Serif,-1,18,default,50,0,0,0,0,0 +rtti=3 +text=Dummy +x=30.0 +y=170.0 +z=150.0 + +[text2] +color=255,255,255 +font=Sans Serif,-1,18,default,50,0,0,0,0,0 +rtti=3 +text=Dummy +x=100.0 +y=170.0 +z=150.0 + +[text3] +color=255,255,255 +font=Sans Serif,-1,18,default,50,0,0,0,0,0 +rtti=3 +text=Dummy +x=35.0 +y=170.0 +z=150.0 + +[text4] +color=255,255,255 +font=Sans Serif,-1,18,default,50,0,0,0,0,0 +rtti=3 +text=Dummy +x=40.0 +y=170.0 +z=150.0 + +[win4about] +file=introabout.png +rtti=32 +x=170.0 +x2=340.0 +y=265.0 +z=1.0 diff --git a/kwin4/grafix/default/introabout.png b/kwin4/grafix/default/introabout.png new file mode 100644 index 00000000..23e4eeeb Binary files /dev/null and b/kwin4/grafix/default/introabout.png differ diff --git a/kwin4/grafix/default/piece0.png b/kwin4/grafix/default/piece0.png new file mode 100644 index 00000000..a2570b54 Binary files /dev/null and b/kwin4/grafix/default/piece0.png differ diff --git a/kwin4/grafix/default/piece1.png b/kwin4/grafix/default/piece1.png new file mode 100644 index 00000000..80dbba36 Binary files /dev/null and b/kwin4/grafix/default/piece1.png differ diff --git a/kwin4/grafix/default/star0.png b/kwin4/grafix/default/star0.png new file mode 100644 index 00000000..e125cc53 Binary files /dev/null and b/kwin4/grafix/default/star0.png differ diff --git a/kwin4/grafix/default/star1.png b/kwin4/grafix/default/star1.png new file mode 100644 index 00000000..bce75e13 Binary files /dev/null and b/kwin4/grafix/default/star1.png differ diff --git a/kwin4/grafix/default/star2.png b/kwin4/grafix/default/star2.png new file mode 100644 index 00000000..11175dd4 Binary files /dev/null and b/kwin4/grafix/default/star2.png differ diff --git a/kwin4/grafix/default/star3.png b/kwin4/grafix/default/star3.png new file mode 100644 index 00000000..862f457e Binary files /dev/null and b/kwin4/grafix/default/star3.png differ diff --git a/kwin4/grafix/default/star4.png b/kwin4/grafix/default/star4.png new file mode 100644 index 00000000..40bf4bad Binary files /dev/null and b/kwin4/grafix/default/star4.png differ diff --git a/kwin4/hi128-app-kwin4.png b/kwin4/hi128-app-kwin4.png new file mode 100644 index 00000000..10ba8a47 Binary files /dev/null and b/kwin4/hi128-app-kwin4.png differ diff --git a/kwin4/hi16-app-kwin4.png b/kwin4/hi16-app-kwin4.png new file mode 100644 index 00000000..e908a50c Binary files /dev/null and b/kwin4/hi16-app-kwin4.png differ diff --git a/kwin4/hi22-app-kwin4.png b/kwin4/hi22-app-kwin4.png new file mode 100644 index 00000000..31956ad2 Binary files /dev/null and b/kwin4/hi22-app-kwin4.png differ diff --git a/kwin4/hi32-app-kwin4.png b/kwin4/hi32-app-kwin4.png new file mode 100644 index 00000000..e7ae6152 Binary files /dev/null and b/kwin4/hi32-app-kwin4.png differ diff --git a/kwin4/hi48-app-kwin4.png b/kwin4/hi48-app-kwin4.png new file mode 100644 index 00000000..54a4e4a0 Binary files /dev/null and b/kwin4/hi48-app-kwin4.png differ diff --git a/kwin4/hi64-app-kwin4.png b/kwin4/hi64-app-kwin4.png new file mode 100644 index 00000000..69911333 Binary files /dev/null and b/kwin4/hi64-app-kwin4.png differ diff --git a/kwin4/index.html b/kwin4/index.html new file mode 100644 index 00000000..ea969510 --- /dev/null +++ b/kwin4/index.html @@ -0,0 +1,213 @@ + + + + +Four wins manual + + + + +

Four wins

+

 

+ How do you play "four wins" ? + +

+Four wins is a game for two player. +Each player is represented by a colour (yellow and red). +The goal of the game is to get four connected pieces of your +colour into a row, column or any diagonal. +This is done by placing one of your pieces into any of the +seven columns. +A piece will begin to fill a column from the bottom, i.e. it +will fall down until it reaches the ground level or another stone. +After a move is done it is the turn of the other player. This is +repeated until the game is over, i.e. one of the players has +four pieces in a row, column or diagonal or no more moves are possbile +because the board is filled. +

+ +

 

+ The board +

The board is separated into three regions.

+ +
    +
  • The game board:
    +It is constructed out of 7x6 fields which will be filled from bottom +to top. The fields are marked in the colour of the player who made the +current move.
    +On top of each column a coloured arrow shows were the last piece had been +put. +
    +
  • +
  • The status display:
    +The status display shows which player colour starts and which colour is +played by whom (player,computer,remote connection). It further shows the +level of the computer opponent, the number of moves done as well as the +computer calculated chance of winning. This chance is calculated only if +the computer opponent makes a move. A positive number means that the player +has an advantage, a negative number means that the computer thinks +he is better. +
    +
  • +
  • The table display:
    +Here the number of won, lost and drawn games is noted for both player. +Also the number of aborted games (Brk) and the sum of games is shown. +
    +
  • +
+ +

 

+ The File menu +

+ +

    +
  • New game
    +Start a new game.
    +
  • +
  • End Game
    +Immediately end a game. This will raise the break counter in the statistics +by one.
    +
  • +
  • Statistics
    +Show the all time statistic of all games. This will be saved, +but can be cleared in this menu as well.
    +
  • +
  • Send remote message...:
    +Opens a dialog window which lets you send a message to a remote player. +
    +
  • +
  • Hint
    +The computer will calculate the best possible move and mark it with a small +circle on the board. +How good the move is depends on the level of the computer.
    +
  • +
  • Exit
    +Exit the program and save all statistical data +
    +
  • +
+ + + +

 

+The Edit menu +

+ +

    +
  • Undo move
    +Undo the last move. If the previous player is played by the computer +two moves are taken back so that it is the player's turn again.
    +
  • +
  • Redo move
    +Replay a move which had been undone. +
    +
  • +
+ +

 

+The View menu +

+ +

    +
  • Show statusbar
    +Displays the status bar +
    +
  • +
+ + +

 

+ The Options menu +

+

    +
  • Startcolour
    +Determines which colour has the first move.
    +
  • +
  • Yellow played by
    +Choose here who shall play for the yellow side. This can either be +the local player, the computer or a remote player. Connecting to +a remote host will pop up a conenction dialog which is explained +later.
    +During game only the player who is not moving can be changed. +
    +
  • +
  • Red played by
    +This is analogous to the yellow side. It is possible that there are +two local players just as the computer can take both local players. +Only one remote player is possible though. If the client does +not choose one remote player he will be asked as soon as a connection +is built. It does not matter what colour the remote side +chooses. If both remote parties choose to play the same colour the +computer will handle this and transform the other players colour +appropriately! +
    +
  • +
  • Level
    +The level determines who well the computr plays. A higher level makes +the computer play better but think longer. For levels larger than 5 or 6 +you need a fast computer! +
    +
  • +
  • Change Names
    +Change the names of the two players. The names will be saved and reloaded +in the next game! +
    +
  • +
  • Network server
    +If this menu item is selected your computer tries to behave as +game network server. This is of course only of any importance if +you are doing a network game. +Only the computer acting as server will be able to start a new game +or transfer a started game to the client's side. +
    +If both computers want to be server or none of them it is randomly +selected. +
    +
  • +
+ +

 

+ The Help menu +

This menu displays the help text as well as information +about the program and the operation system.

+ + +

 

+ Remote connections +

+ +It is possible to play the game over a network connection +with another computer. To do so both player on both computers +have to select one colour played by a human player and the +other by the remote player. Who chooses which colour does not +matter. It even does not matter if both choose to play the same +colour as this will transparentely be interchanged by the game. +

+One of the computers will act as game server. Only this one can +start a nbew network game. Also all its game data will be transfered +to the client computer. This includes games already in play - this +means a remote player can join a game already begun. Who will be +server can be selected by the network server menu item in +the options menu. If both choose to be server or client the game +randomly selects one. +

+ +When a network connection is build you are ask to enter a remote +host and a port. The port can usually just been left untouched. But +if you now what you do replace it by another number, which has to +be the same in both player games of course. The hostname should be +the name of the remote host to which you are connecting. Only one +of the two players has to supply a hostname, the other one need not +to, but can. +

+ + + +

+  +

+


+Author: +

© 1995-2000 Martin Heni (martin@heni-online.de)

+ + diff --git a/kwin4/install-sh b/kwin4/install-sh new file mode 100755 index 00000000..67c94290 --- /dev/null +++ b/kwin4/install-sh @@ -0,0 +1,238 @@ +#! /bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. +# + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f "$src" -o -d "$src" ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd "$src" $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/kwin4/kwin4.desktop b/kwin4/kwin4.desktop new file mode 100644 index 00000000..2071cc5a --- /dev/null +++ b/kwin4/kwin4.desktop @@ -0,0 +1,94 @@ +[Desktop Entry] +Name=KWin4 +Name[af]=Kwin4 +Name[be]=Перамога чатырох +Name[bn]=কে-উইন-ফোর +Name[de]=Vier gewinnt +Name[eo]=Kvar venkas +Name[es]=Kwin4 +Name[hi]=के-विन4 +Name[hu]=Négy a nyerÅ‘ +Name[is]=Kwin4 +Name[nds]=Veer winnt +Name[ne]=केडीई विन ४ +Name[sv]=Kwin4 +Name[ta]=Kவிணà¯4 +Name[tg]=K4 Ғолиб мешавад +Name[th]=เรียงหมาภ4 - K +Name[tr]=4 Kazanır +Name[zu]=I-KWin4 +GenericName=Strategy Game +GenericName[af]=Strategie Speletjie +GenericName[ar]=لعبة استراتيجية +GenericName[be]=СтратÑÐ³Ñ–Ñ‡Ð½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ +GenericName[bg]=МорÑки шах +GenericName[bn]=কৌশলের খেলা +GenericName[br]=C'hoari a gadouriezh +GenericName[bs]=StrateÅ¡ka igra +GenericName[ca]=Joc d'estratègia +GenericName[cs]=Strategická hra +GenericName[cy]=Gêm Strategaeth +GenericName[da]=Strategispil +GenericName[de]=Strategiespiel +GenericName[el]=Παιχνίδι στÏατηγικής +GenericName[eo]=Strategiludo +GenericName[es]=Juego de estrategia +GenericName[et]=Strateegiamäng +GenericName[eu]=Estrategia jokoa +GenericName[fa]=بازی Strategy +GenericName[fi]=Strategiapeli +GenericName[fo]=Strategispæl +GenericName[fr]=Jeu de stratégie +GenericName[ga]=Cluiche Straitéise +GenericName[gl]=Xogo de estratexia +GenericName[he]=משחק ×סטרטגיה +GenericName[hi]=कौशल का खेल +GenericName[hr]=Igra strategije +GenericName[hu]=Stratégiai +GenericName[is]=Herkænskuleikur +GenericName[it]=Gioco di strategia +GenericName[ja]=戦略ゲーム +GenericName[km]=ល្បែង​យុទ្ធសាស្ážáŸ’ážš +GenericName[ko]=우주 ì „ëžµ 게임 +GenericName[lt]=Strateginis žaidimas +GenericName[lv]=StratÄ“Ä£iskÄ Game +GenericName[mk]=Стратешка игра +GenericName[mt]=Logħba ta' strateÄ¡ija +GenericName[nb]=Strategispill +GenericName[nds]=Strategiespeel +GenericName[ne]=रणनीति खेल +GenericName[nl]=Strategisch spel +GenericName[nn]=Strategispel +GenericName[pa]=ਨੀਤੀ ਖੇਡ +GenericName[pl]=Gra strategiczna +GenericName[pt]=Jogo de Estratégia +GenericName[pt_BR]=Jogo de Estratégia +GenericName[ro]=Joc de strategie +GenericName[ru]=Четыре побеждают +GenericName[rw]=Umukino w'Ingamba +GenericName[se]=Strategiijaspeallu +GenericName[sk]=Strategická hra +GenericName[sl]=StrateÅ¡ka igra +GenericName[sr]=Стратешка игра +GenericName[sr@Latn]=StrateÅ¡ka igra +GenericName[sv]=Strategispel +GenericName[ta]=தநà¯à®¤à®¿à®° விளையாடà¯à®Ÿà¯ +GenericName[tg]=Бозии Стратегӣ +GenericName[th]=เà¸à¸¡à¸à¸¥à¸¢à¸¸à¸—ธ์ +GenericName[tr]=Strateji Oyunu +GenericName[uk]=Стратегічна гра "Чотири виграють" +GenericName[uz]=Strategiya oÊ»yini +GenericName[uz@cyrillic]=Ð¡Ñ‚Ñ€Ð°Ñ‚ÐµÐ³Ð¸Ñ ÑžÐ¹Ð¸Ð½Ð¸ +GenericName[ven]=Mutambo wa Maitele +GenericName[wa]=Djeu di stratedjeye +GenericName[xh]=Indlela yokudlala umdlalo +GenericName[zh_CN]=ç­–ç•¥æ¸¸æˆ +GenericName[zh_TW]=ç­–ç•¥éŠæˆ² +GenericName[zu]=Umdlalo wamaqhinga +Exec=kwin4 +Icon=kwin4 +MimeType= +Terminal=false +Type=Application +DocPath=kwin4/index.html +Categories=Qt;KDE;Game;BoardGame; diff --git a/kwin4/kwin4.kdelnk b/kwin4/kwin4.kdelnk new file mode 100644 index 00000000..c2703b9b --- /dev/null +++ b/kwin4/kwin4.kdelnk @@ -0,0 +1,24 @@ +# KDE Config File +[KDE Desktop Entry] +Type=Application +Exec=kwin4 -caption "%c" %i %m +Icon=kwin4.xpm +MiniIcon=kwin4.xpm +DocPath=kwin4/index.html +Comment= +Terminal=0 +Name=Kwin4 +Name[be]=Перамога чатырох +Name[bn]=কে-উইন-ফোর +Name[de]=Vier gewinnt +Name[eo]=Kvar venkas +Name[hi]=के-विन4 +Name[hu]=Négy a nyerÅ‘ +Name[it]=KWin4 +Name[nds]=Veer winnt +Name[nl]=Vier op een rij +Name[sl]=Å tiri v vrsto +Name[ta]=Kவிணà¯4 +Name[tg]=K4 ғолиб мешавад +Name[th]=เรียงหมาภ4 - K +Name[zu]=I-Kwin4 diff --git a/kwin4/kwin4.lsm b/kwin4/kwin4.lsm new file mode 100644 index 00000000..0f4ace19 --- /dev/null +++ b/kwin4/kwin4.lsm @@ -0,0 +1,14 @@ +Begin3 +Title: Kwin4 +Version: 0.9 +Entered-date: +Description: A small game for the KDE 2 desktop +Keywords: four wins,game,computer,network,kde +Author: Martin Heni +Maintained-by: Martin Heni +Primary-site: +Home-page: http://www.heni-online.de/linux +Original-site: +Platforms: Linux and other Unices +Copying-policy: GNU Public License +End diff --git a/kwin4/kwin4/AboutDlg.kdevdlg b/kwin4/kwin4/AboutDlg.kdevdlg new file mode 100644 index 00000000..dc87a8b2 --- /dev/null +++ b/kwin4/kwin4/AboutDlg.kdevdlg @@ -0,0 +1,138 @@ +// KDevelop Dialog Editor File (.kdevdlg) +// +// Created by KDlgEdit Version 0.1alpha (C) 1999 by Pascal Krahmer +// Get KDevelop including KDlgEdit at "www.beast.de/kdevelop" +// +data Information +{ + Filename="/home/martin/mgames/kwin4/kwin4/AboutDlg.kdevdlg" + KDevelopVersion="1.1" + DlgEditVersion="0.1alpha" + LastChanged="Mon Apr 17 09:29:55 2000" +} + +data SessionManagement +{ + OpenedRoot_1="Appearance" + OpenedRoot_2="C++ Code" + OpenedRoot_3="General" + OpenedRoot_4="Geometry" + OpenedRootCount="4" +} + +item QWidget +{ + Name="KWin4" + VarName="this" + X="0" + Y="0" + Width="320" + Height="290" + MinWidth="0" + MinHeight="0" + + item QGroupBox + { + Name="NoName" + VarName="QGroupBox_1" + X="20" + Y="10" + Width="280" + Height="230" + MinWidth="0" + MinHeight="0" + + item QLabel + { + Name="NoName" + VarName="QLabel_1" + X="10" + Y="90" + Width="260" + Height="40" + MinWidth="0" + MinHeight="0" + Text="Mail: martin@heni-online.de" + } + + item QLabel + { + Name="NoName" + VarName="QLabel_7" + X="160" + Y="10" + Width="90" + Height="30" + MinWidth="0" + MinHeight="0" + Text="Four wins" + } + + item QLabel + { + Name="NoName" + VarName="QLabel_2" + X="10" + Y="10" + Width="120" + Height="70" + MinWidth="0" + MinHeight="0" + BgPixmap=*mPixmap + } + + item QLabel + { + Name="NoName" + VarName="QLabel_8" + X="10" + Y="70" + Width="200" + Height="25" + MinWidth="0" + MinHeight="0" + Text="(c) 1995-2000 by Martin Heni" + } + + item QLabel + { + Name="NoName" + VarName="QLabel_5" + X="10" + Y="120" + Width="260" + Height="100" + MinWidth="0" + MinHeight="0" + Text="Gamefeatures:\n- Multiplayer network game\n- Computerplayer with ten levels\n\nThanks to Laura for betatesting!\n" + } + + item QLabel + { + Name="NoName" + VarName="QLabel_11" + X="160" + Y="40" + Width="100" + Height="30" + MinWidth="0" + MinHeight="0" + Text="Version 0.9" + } + + } + + item QPushButton + { + Name="NoName" + VarName="QPushButton_1" + HasFocus="true" + X="110" + Y="250" + Width="100" + Height="30" + MinWidth="0" + MinHeight="0" + Text="Ok" + } +} diff --git a/kwin4/kwin4/Makefile.am b/kwin4/kwin4/Makefile.am new file mode 100644 index 00000000..c5392326 --- /dev/null +++ b/kwin4/kwin4/Makefile.am @@ -0,0 +1,28 @@ + +bin_PROGRAMS = kwin4 kwin4proc +kwin4_SOURCES = main.cpp kwin4.cpp kwin4view.cpp kwin4doc.cpp \ + kwin4player.cpp kspritecache.cpp \ + scorewidget.cpp prefs.kcfgc settings.ui statistics.ui statuswidget.ui + +kwin4_LDADD = $(LIB_KFILE) $(LIB_KDEGAMES) +kwin4_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +kwin4proc_SOURCES = kwin4proc.cpp +kwin4proc_LDADD = $(LIB_KFILE) $(LIB_KDEGAMES) +kwin4proc_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/kgame $(all_includes) + +METASOURCES = AUTO + +rcdir = $(kde_datadir)/kwin4 +rc_DATA = kwin4ui.rc + +kwin4_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kwin4proc_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +kde_kcfg_DATA = kwin4.kcfg + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kwin4.pot + diff --git a/kwin4/kwin4/kspritecache.cpp b/kwin4/kwin4/kspritecache.cpp new file mode 100644 index 00000000..5be5538d --- /dev/null +++ b/kwin4/kwin4/kspritecache.cpp @@ -0,0 +1,812 @@ +/*************************************************************************** + Kwin4 - Four in a Row for KDE + ------------------- + begin : March 2000 + copyright : (C) 1995-2001 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kspritecache.h" + +#include +#include +#include +#include +#include +#include + +// KSprite +#include + +KSpriteCache::KSpriteCache(QString grafixdir, QObject* parent,const char * name) + : QObject(parent,name) +{ + kdDebug(11002) << "KSpriteCache:: grafixdir=" << grafixdir << endl; + mConfig=0; + mCanvas=0; + setRcFile(QString("grafix.rc")); + setGrafixDir(grafixdir); + kdDebug(11002) << "Grafixdir=" << grafixDir() << " rcfile=" << rcFile() << endl; + reset(); +} + +KSpriteCache::~KSpriteCache() +{ + kdDebug(11002) << "KSpriteCache: ItemDict=" << mItemDict.count() << endl; + kdDebug(11002) << "KSpriteCache: CloneDict=" << mCloneDict.count() << endl; + reset(); + delete mConfig; +} + +void KSpriteCache::setRcFile(QString name) +{ + mRcFile=name; +} +bool KSpriteCache::setGrafixDir(QString name) +{ + delete mConfig; + + QDir dir(name); + QString d; + d=dir.absPath()+QString("/"); + QString file=d+rcFile(); + + // TODO check for filename + kdDebug(11002) << "Opening config " << file << endl; + mConfig=new KConfig(file,false,false); + mGrafixDir=d; + return true; +} + +void KSpriteCache::reset() +{ + mItemDict.setAutoDelete(false); + mCloneDict.setAutoDelete(false); + mItemDict.clear(); + mCloneDict.clear(); +} + + +void KSpriteCache::deleteAllItems() +{ + QDictIterator it( mItemDict ); + //kdDebug(11002) << "KSpriteCache::deleteAllItems items in cache=" << mItemDict.size() << endl; + while ( it.current() ) + { + QCanvasItem *item=it.current(); + mItemDict.remove(it.currentKey()); + delete item; + } +} +void KSpriteCache::deleteItem(QString s,int no) +{ + QCanvasItem *item; + QString name=s+QString("_%1").arg(no); + //kdDebug(11002) << "KSpriteCache::deleteItem name=" << name << endl; + item=mItemDict[name]; + if (item) + { + mItemDict.remove(name); +// kdDebug(11002) << "deleteitem "< getItem("<"<hasGroup(name)) + { + kdError() << "Item "<setGroup(name); + item=loadItem(mConfig,name); + // kdDebug(11002) << "Inserting sprite="<load(dir+file); + } + if (result1 && !mask.isNull()) + { + QBitmap bitmask; + if (mask==QString("auto")) // self mask..slow but quick to create + { + newP->setMask( newP->createHeuristicMask() ); + result2=true; + } + else + { + result2=bitmask.load(dir+mask); + if (result2) newP->setMask(bitmask); + } + } + //kdDebug(11002) << "KSpriteCache::loadPixmap: file="<readListEntry("pixmaps"); + // Append default entry (empty string) + if (operationList.count()==0) + { + operationList.append(QString::null); + } + + + // Prepare for the reading of the pixmaps + QPixmap *pixmap=0; + QPtrList pixlist; + pixlist.setAutoDelete(true); + QPtrList hotlist; + hotlist.setAutoDelete(true); + + // work through the operations list and create pixmaps + for ( QStringList::Iterator it = operationList.begin(); it !=operationList.end(); ++it ) + { + QString name=*it; + // Try to find out what we want to do, e.g. load, scale, ... + QString type=config->readEntry(name+"method"); + if (type.isNull()) type=QString("load"); // default load + //kdDebug(11002) << " Processing operation " << (name.isNull()?"default":name) << "type="<readNumEntry(name+"number",1); + //kdDebug(11002) << " Reading " << number << " frames " << endl; + + QString pixfile=config->readPathEntry(name+"file"); + QString maskfile=config->readPathEntry(name+"mask"); + + // Load a given set of images or replace a %d by a sequence if there are + // less image names than number given + if (type==QString("load")) + { + // Read images + for (unsigned int i=0;ireadNumEntry(name+"axis",0); + double finalscale=config->readDoubleNumEntry(name+"final",0.0); + double step; + if (number>1) step=(100.0-finalscale)/100.0/(double)(number-1); + else step=1.0; + //kdDebug(11002) << " Scaling " << number << " pics axis="< transformList; + transformList=config->readIntListEntry(name+"transformfilter"); + + // apply transformation filter + if (transformList.count()>0) + { + if (transformList[0]==1 && transformList.count()==2) // rotate + { + QWMatrix rotate; + rotate.rotate(transformList[1]); + *pixmap=pixmap->xForm(rotate); + } + else if (transformList[0]==2 && transformList.count()==3) // scale + { + QWMatrix scale; + scale.scale((double)transformList[1]/100.0,(double)transformList[2]/100.0); + *pixmap=pixmap->xForm(scale); + } + } + // apply colorfilter + if (filterList.count()>0) + { + // Only filter 1 HSV and 2: grey are implemented + if (filterList[0]==1 && filterList.count()==4) changeHSV(pixmap,filterList[1],filterList[2],filterList[3]); + else if (filterList[0]==2 && filterList.count()==2) changeGrey(pixmap,filterList[1]); + else if (filterList[0]==2 && filterList.count()==1) changeGrey(pixmap); + else kdWarning(11002) << "WARNING: Colorfilter parameter incorrect "<< endl; + } +} + +void KSpriteCache::changeHSV(QPixmap *pixmap,int dh,int ds,int dv) +{ + if (!pixmap || (dh==0 && ds==0 && dv==0)) return ; + if (pixmap->isNull()) return ; + if (pixmap->width()==0 && pixmap->height()==0) return ; + + int h,s,v; + QColor black=QColor(0,0,0); + QImage img=pixmap->convertToImage(); // slow + + for (int y=0;yconvertFromImage(img); // slow +} +void KSpriteCache::changeGrey(QPixmap *pixmap,int lighter) +{ + if (!pixmap) return ; + if (pixmap->isNull()) return ; + if (pixmap->width()==0 && pixmap->height()==0) return ; + + QImage img=pixmap->convertToImage(); // slow + + for (int y=0;y0) col=col.light(lighter); + if (lighter<0) col=col.dark(-lighter); + img.setPixel(x,y,qRgba(col.red(),col.green(),col.blue(),qAlpha(pix))); + } + } + pixmap->convertFromImage(img); // slow +} + +QCanvasItem *KSpriteCache::loadItem(KConfig *config,QString name) +{ + if (!config) return 0; + int rtti=config->readNumEntry("rtti",0); + QCanvasItem *item=0; + switch(rtti) + { + case QCanvasItem::Rtti_Text: + { + QCanvasText *sprite=new QCanvasText(canvas()); + //kdDebug(11002) << "new CanvasText =" << sprite << endl; + QString text=config->readEntry("text"); + sprite->setText(text); + QColor color=config->readColorEntry("color"); + sprite->setColor(color); + QFont font=config->readFontEntry("font"); + sprite->setFont(font); + item=(QCanvasItem *)sprite; + configureCanvasItem(config,item); + } + break; + case 32: + { + QCanvasPixmapArray *pixmaps=createPixmapArray(config,name); + KSprite *sprite=new KSprite(pixmaps,canvas()); + //kdDebug(11002) << "new sprite =" << sprite << endl; + double speed=config->readDoubleNumEntry("speed",0.0); + sprite->setSpeed(speed); + //kdDebug(11002) << "speed=" << sprite->speed() << endl; + createAnimations(config,sprite); + + item=(QCanvasItem *)sprite; + configureCanvasItem(config,item); + + } + break; + default: + { + kdError() << "KSpriteCache::loadItem: Should create unkwown rtti " << rtti << "...overwrite this function" << endl; + } + break; + } + return item; +} + +QCanvasItem *KSpriteCache::cloneItem(QCanvasItem *original) +{ + if (!original) return 0; + int rtti=original->rtti(); + QCanvasItem *item=0; + switch(rtti) + { + case QCanvasItem::Rtti_Text: + { + QCanvasText *sprite=(QCanvasText *)original; + QCanvasText *copy=new QCanvasText(canvas()); + configureCanvasItem(original,(QCanvasItem *)copy); + copy->setText(sprite->text()); + copy->setColor(sprite->color()); + copy->setFont(sprite->font()); + item=(QCanvasItem *)copy; + } + break; + case 32: + { + KSprite *sprite=(KSprite *)original; + KSprite *copy=new KSprite(sprite->images(),canvas()); + configureCanvasItem(original,(QCanvasItem *)copy); + copy->setSpeed(sprite->speed()); + createAnimations(sprite,copy); + item=(QCanvasItem *)copy; + } + break; + default: + { + kdError() << "KSpriteCache::cloneItem: Should create unkwown rtti " << rtti << "...overwrite this function" << endl; + } + break; + } + return item; +} + + +void KSpriteCache::configureCanvasItem(KConfig *config, QCanvasItem *sprite) +{ + double x=config->readDoubleNumEntry("x",0.0); + double y=config->readDoubleNumEntry("y",0.0); + double z=config->readDoubleNumEntry("z",0.0); + sprite->setX(x); + sprite->setY(y); + sprite->setZ(z); + //kdDebug(11002) << "x=" << sprite->x() << endl; + //kdDebug(11002) << "y=" << sprite->y() << endl; + //kdDebug(11002) << "z=" << sprite->z() << endl; +} + +void KSpriteCache::configureCanvasItem(QCanvasItem *original, QCanvasItem *copy) +{ + copy->setX(original->x()); + copy->setY(original->y()); + copy->setZ(original->z()); +} + + +void KSpriteCache::createAnimations(KSprite *original,KSprite *sprite) +{ + unsigned int no=original->animationCount(); + for (unsigned int i=0;igetAnimation(i,start,end,mode,delay); + sprite->createAnimation(i,start,end,mode,delay); + } +} + +void KSpriteCache::createAnimations(KConfig *config,KSprite *sprite) +{ + if (!sprite) return ; + for (int i=0;i<1000;i++) + { + QString anim=QString("anim%1").arg(i); + if (config->hasKey(anim)) + { + //kdDebug(11002) << "Found animation key " << anim << endl; + QValueList animList=config->readIntListEntry(anim); + if (animList.count()!=4) + { + kdWarning(11002) << "KSpriteCache::createAnimations:: warning animation parameter " << anim << " needs four arguments" << endl; + } + else + { + sprite->createAnimation(i,animList[0],animList[1],animList[2],animList[3]); + } + } + else + { + break; + } + } +} + + +// ----------------------- KSPRITE -------------------------------- +KSprite::KSprite(QCanvasPixmapArray* array, QCanvas* canvas) + :QCanvasSprite(array,canvas) +{ + mImages=array; + mSpeed=0.0; + mNotify=0; + mAnimationNumber=-1; + mAnimSpeedCnt=0; + mMoveObj=0; + +} + +void KSprite::moveTo(double tx,double ty,double speed) +{ + if (speed>0.0) + { + mSpeed=speed; + } + + //kdDebug(11002) <<"KSprite::moveTo x=" << tx << " y="<b + // -1: single shot b->a + // 2: cycle a->b->a + // -2: cycle b->a->b + // 3: cycle a->b + // -3: cycle b->a + mAnimSpeedCnt++; + if (mAnimationNumber<0 || mAnimDelay[mAnimationNumber]==0) + { + // nothing to do? + isAnimated=false; + mAnimSpeedCnt=0; + } + if (mAnimationNumber>=0 && mAnimSpeedCnt>=mAnimDelay[mAnimationNumber]) + { + switch(mAnimDirection[mAnimationNumber]) + { + case 1: + if (frame()+1 <= mAnimTo[mAnimationNumber]) setFrame(frame()+1); + else emitsignal=2; + break; + case -1: + if (frame()-1 >= mAnimFrom[mAnimationNumber]) setFrame(frame()-1); + else emitsignal=2; + break; + case 2: + if (mAnimDirection[mAnimationNumber]==mCurrentAnimDir) + { + if (frame()+1 <= mAnimTo[mAnimationNumber]) setFrame(frame()+1); + else + { + mCurrentAnimDir=-mCurrentAnimDir; + if (frame()>0) setFrame(frame()-1); + } + } + else + { + if (frame()-1 >= mAnimFrom[mAnimationNumber]) setFrame(frame()-1); + else + { + mCurrentAnimDir=-mCurrentAnimDir; + if (frame()+1= mAnimFrom[mAnimationNumber]) setFrame(frame()-1); + else + { + mCurrentAnimDir=-mCurrentAnimDir; + if (frame()+10) setFrame(frame()-1); + } + } + break; + case 3: + if (frame()+1 <= mAnimTo[mAnimationNumber]) setFrame(frame()+1); + else setFrame(mAnimFrom[mAnimationNumber]); + break; + case -3: + if (frame()-1 >= mAnimFrom[mAnimationNumber]) setFrame(frame()-1); + else setFrame(mAnimTo[mAnimationNumber]); + break; + default: //0 + isAnimated=false; + setFrame(0); + } + if (emitsignal) isAnimated=false; + + mAnimSpeedCnt=0; + } + + + + // Movement to target + if (!moveObject() && (fabs(mTargetX-x())+fabs(mTargetY-y())) >0.0 && mSpeed>0.0) + { + isMoving=spriteMove(mTargetX,mTargetY); + if (!isMoving) emitsignal=1; + } + else if (moveObject()) + { + isMoving=moveObject()->spriteMove(mTargetX,mTargetY,this); + if (!isMoving) emitsignal=1; + } + + + // Final checks + if (!isAnimated && !isMoving) + { + //kdDebug(11002) << "Animation over" << endl; + setAnimated(false); + } + + if (mNotify && emitsignal) + { + //kdDebug(11002) << " ADVANCE emits signal " << emitsignal << " for item "<< this << endl; + mNotify->emitSignal((QCanvasItem *)this,emitsignal); + } +} + +// Generates linear movement to tx,ty +bool KSprite::spriteMove(double tx,double ty) +{ + bool isMoving=false; + double dx,dy; + double vx,vy; + dx=tx-x(); + dy=ty-y(); + vx=0.0; + vy=0.0; + + // first pure x,y movements + if (fabs(dy)<=0.0001) + { + if (dx>0.0) vx=mSpeed; + else if (dx<0.0) vx=-mSpeed; + else vx=0.0; + } + else if (fabs(dx)<=0.0001) + { + if (dy>0.0) vy=mSpeed; + else if (dy<0.0) vy=-mSpeed; + else vy=0.0; + } + else // diagonal + { + double alpha=atan2(dy,dx); + vx=cos(alpha)*mSpeed; + vy=sin(alpha)*mSpeed; + } + + if (fabs(dx)<=fabs(vx) && fabs(dy)<=fabs(vy)) + { + move(tx,ty); + isMoving=false; + } + else if (fabs(dx)<=fabs(vx)) + { + moveBy(dx,vy); + isMoving=true; + } + else if (fabs(dy)<=fabs(vy)) + { + moveBy(vx,dy); + isMoving=true; + } + else + { + moveBy(vx,vy); + isMoving=true; + } + return isMoving; +} + +void KSprite::emitNotify(int mode) +{ + if (!mNotify) return ; + //kdDebug(11002) << " ADVANCE emits DIRECT signal " << mode << " for item "<< this << endl; + mNotify->emitSignal((QCanvasItem *)this,mode); +} +QObject *KSprite::createNotify() +{ + if (!mNotify) mNotify=new KSpriteNotify; + mNotify->incRefCnt(); + return (QObject *)mNotify; +} + +void KSprite::deleteNotify() +{ + if (!mNotify) return ; + mNotify->decRefCnt(); + if (mNotify->refCnt()<=0) + { + //kdDebug(11002) << "REALLY deleting notify" << endl; + delete mNotify; + mNotify=0; + } +} + +KSprite::~KSprite() +{ + delete mNotify; + mNotify=0; +} + +void KSprite::setAnimation(int no) +{ + if ((int)mAnimFrom.count()<=no) + { + kdError(11002) << "KSprite::setAnimation:: Animation " << no << " not defined " << endl; + return ; + } + mAnimationNumber=no; + if (no<0) return ; + mAnimSpeedCnt=0; + mCurrentAnimDir=mAnimDirection[no]; + + // Start frame + if (mCurrentAnimDir>0) setFrame(mAnimFrom[no]); + else if (mCurrentAnimDir<0) setFrame(mAnimTo[no]); + + // animated + if (mCurrentAnimDir!=0 && mAnimTo[no]>=mAnimFrom[no]) setAnimated(true); + else setAnimated(false); + + //kdDebug(11002) << this << " setAnimation("<"< +#include + +class KConfig; + +class KSprite; + + /** + * this is an internal class to provide a @ref QObject to emit + * a signal from a sprite if a notify object is created + * You do not need this directly. + * TODO: Can be part of the KSprite class + **/ + class KSpriteNotify : public QObject + { + Q_OBJECT + + public: + KSpriteNotify() :QObject(0,0) {mRefCnt=0;} + void emitSignal(QCanvasItem *parent,int mode) {emit signalNotify(parent,mode);} + void incRefCnt() {mRefCnt++;} + void decRefCnt() {mRefCnt--;} + int refCnt() {return mRefCnt;} + signals: + void signalNotify(QCanvasItem *,int); + private: + int mRefCnt; + }; + + class KSpriteMove + { + public: + KSpriteMove() {} + virtual ~KSpriteMove() {} + virtual bool spriteMove(double ,double ,KSprite *) {return false;} + private: + }; + +/** + * The KSprite class is an advance QCanvasSprite class which + * is usable with the @ref KSpriteCache. It furthermore contains a + * few useful functions like advanced movement and animations which + * go beyond the QCanvasSprite versions of them. Also it provides + * a signal which is emitted when movement or animation are finished. + * + * @short The main KDE game object + * @author Martin Heni + * + */ +class KSprite : public QCanvasSprite +{ + public: + /** + * Contructs a KSprite object. It is anlogous to the @ref QCanvasSprite + * constructor + * + * @param array - the frames of the sprite + * @param canvas - the canvas the sprites lives on + **/ + KSprite(QCanvasPixmapArray* array, QCanvas* canvas); + + /** + * Destructs the sprite + **/ + virtual ~KSprite(); + + /** + * The sprites runtime idendification (32) + **/ + int rtti() const {return 32;} + + /** + * returns a pointer to the pixmap array which holds the + * frames of the sprite. + **/ + QCanvasPixmapArray* images() const {return mImages;} + + /** + * Moves the sprite to the given position with the given speed. + * When it reaches its desitnation a signal is emmited if the + * emmiter @ref createNotify is enabled + * + * @param x - the x coordinate + * @param y - the y coordinate + * @param speed - the speed to move . If zero the last set speed is taken + **/ + void moveTo(double x,double y,double speed=0.0); + + /** + * Generates a linear move to the target tx,ty from the current + * position of the sprite with its set speed @ref setSpeed + * Upon arrival the function returns false to indicate an end of the + * movment. Otherwise true is returned. + * The sprite is moved in this function. + **/ + bool spriteMove(double tx,double ty); + + /** + * The sprites advance function. See the qt @ref QcanvasSprite advance + **/ + void advance(int stage); + + /** + * Sets the speed for the next move. Can be set with moveTo too. + * + * @param v - the speed in pixel per animation cycle + **/ + void setSpeed(double v) {mSpeed=v;} + + /** + * returns the speed + **/ + double speed() {return mSpeed;} + + /** + * returns the notification QObject. You probably do not need this but + * @ref createNotify instead + **/ + QObject *notify() {return (QObject *)mNotify;} + + /** + * Directly emits the notification signal with the given parameter + * + * @param the notification parameter + **/ + void emitNotify(int mode); + + /** + * Creates a notification object. You can connect to it and it will emit + * the signal signalNotify(QCanvasItem *parent, intmode) when a move or + * animation is finished. + * Example: + *
+    *  connect(sprite->createNotify(),SIGNAL(signalNotify(QCanvasItem *,int)),
+    *          this,SLOT(moveDone(QCanvasItem *,int)));
+    * 
+ * In the move done function you best delete the notify again with + * @ref deleteNotify + **/ + QObject *createNotify(); + + /** + * Deletes the sprite notify if it is no longer used. The notify keeps a + * reference count which deletes the QObject when no reference to it is in + * use. + **/ + void deleteNotify(); + + /** + * Reads the animation parameters into the given variables for the given + * animation. Mostly used by @ref KSpriteCache + * + * @param no - the animation number + * @param startframe - the first frame of the animation + * @param endframe - the last frame of the animation + * @param mode - the mode of the animation see @ref creaetAnimation + * @param delay - the delay in QCanvas animation cycles between two frames + **/ + void getAnimation(int no,int &startframe,int &endframe,int &mode,int &delay); + + /** + * Creates an animation of the sprite between two frames in one of the + * following modes + * 0: no animation + * 1: single shot a->b + *-1: single shot b->a + * 2: cycle a->b->a + *-2: cycle b->a->b + * 3: cycle a->b + *-3: cycle b->a + * + * The single shot animations will emit the above mentioned signal over the + * notify object if it is created. + * If you load the sprite over the KSpriteCache's config file you need not + * bother about calling this function. + **/ + void createAnimation(int no,int startframe,int endframe,int mode,int delay); + + /** + * Switches on the animation of the given number. Of course it needs to be + * defined beforehand either via loading the sprite with the + * @ref KSpriteCache or be calling @ref createAnimation + * + * @param no - the number of the animation + **/ + void setAnimation(int no); + + /** + * Returns how many different animations are stored + **/ + unsigned int animationCount() {return mAnimFrom.count();} + + void setMoveObject(KSpriteMove *m) {mMoveObj=m;} + KSpriteMove *moveObject() {return mMoveObj;} + + protected: + KSpriteMove *mMoveObj; + + private: + KSpriteNotify *mNotify; + QCanvasPixmapArray* mImages; + QByteArray mAnimFrom; + QByteArray mAnimTo; + QByteArray mAnimDirection; + QByteArray mAnimDelay; + + double mTargetX,mTargetY; + double mSpeed; + int mAnimationNumber; + int mAnimSpeedCnt; + int mCurrentAnimDir; +}; + + +/** + * The KSpriteCache class is used to load and cache sprites. Loading + * is done via a @ref KConfig file which contains the definitions of the + * sprite in text form. Usng this approach allows you to tun the sprites + * without chaning the sourcecode of the program. This is especially useful if + * the graphics team is independent of the programmer or if you want to write + * external themes for your game. + * Furhtermore the class keeps sprites in memory so that they are fastly + * reloaded when you use more than one sprite of a given type. + * + * Example: + *
+ * # Sprite with three frames and a common mask for them
+ * # z position given but x,y set manually in the program
+ *  [arrow]
+ *  file=arrow%d.png
+ *  mask=arrow_mask.png
+ *  number=3
+ *  offset=19,5
+ *  rtti=32
+ *  z=9.0
+ *
+ * # Simple sprite which is already positioned correcly. No mask just one file
+ *  [board]
+ *  file=board.png
+ *  rtti=32
+ *  x=15.0
+ *  y=40.0
+ *  z=0.0
+ * 
+ * # Sprite with one cyclic (2) animation of  5 frames (0-4) and
+ * # a slow delay of 8
+ *  [star]
+ *  anim0=0,4,2,8
+ *  file=star%d.png
+ *  mask=star%d_mask.png
+ *  number=5
+ *  offset=19,20
+ *  rtti=32
+ *  z=100.0
+ *
+ * 
+ * + * @todo Support single sprites (only one copy in memory) + * Support more sprite types (currently KSprite and QCanvasText) + * + * @short The main KDE game object + * @author Martin Heni + * + */ +class KSpriteCache : public QObject +{ + Q_OBJECT + + public: + /** + * Create a sprite cache. Usuzally you will need one per program only. + * + * @param grafixdir - the directory where the configuration file and the graphics reside + **/ + KSpriteCache(QString grafixdir, QObject* parent=0,const char * name=0); + + /** + * Delete the sprite cache + **/ + ~KSpriteCache(); + + /** + * Change the grafichs directory. + * + * @todo this does not flush the cache or so... + **/ + bool setGrafixDir(QString dir); // dir and load config + + /** + * Change the name of the config file. Its default is grafix.rc + **/ + void setRcFile(QString file); + + /** + * return the graphics directory + **/ + QString grafixDir() {return mGrafixDir;} + + /** + * return the rc/configuration file + **/ + QString rcFile() {return mRcFile;} + + /** + * returns the canvas which belongs to the cache + **/ + QCanvas *canvas() const {return mCanvas;} + + /** + * sets the canvas belonging to the cache + * + * @todo could be done in the constructor + **/ + void setCanvas(QCanvas *c) {mCanvas=c;} + + /** + * returns the @ref KConfig configuration file where thegraphical data is + * read. Access to this is necessary if you want to store general game infos + * in theis file to or if you want to read additional sprite data which are + * not read be the default functions. + **/ + KConfig *config() {return mConfig;} + + /** + * Main function to create a sprite. You call this like + *
+  * KSprite *sprite=(KSprite *)(yourcahce->getItem("hello",1));
+  * if (sprite) sprite->show();
+  * 
+ * Calling this function will load the given sprite from the + * configuration file. Its type is determined by the rtti= entry + * of the section [hello] in that file. Default is a KSprite. + * This file defines all data of the sprite so that you just have to show it. + * Each copy of the sprite gets its own number (1,2,...) + * Note: The sprite is hidden upon creation and you need to show it + * explicitly. + * TODO: What definitions are possible in the rc file + * + * @param name - the name of the sprite resp. the secion in the rc file + * @param no - the unique number of the sprite + * @return sprite - returns the sprite pointer as @ref QCanvasItem + * + * @todo support call without number argument as single sprite's + * @todo support loading of frame sequence via one big pixmap + * + **/ + QCanvasItem *getItem(QString name, int no); + + /** + * This function loads a pixmap from the given file. Optional you can also + * provide a mask file. Also optinal you can provide the directory. Default + * is the directory which is set with this @ref KSpriteCache + **/ + QPixmap * loadPixmap(QString file,QString mask=QString::null,QString dir=QString::null); + + /** + * Deletes a item form the sprite cache given as a pointer to it + **/ + void deleteItem(QCanvasItem *item); + + /** + * Same as above but delete the item with the name and number + **/ + void deleteItem(QString s,int no); + + /** + * Deletes all items in the cache + **/ + void deleteAllItems(); + + protected: + /** + * Loads the default properties for all QCanvasItems from the given config + * file. This is at the moment + *
+  * x=(double)
+  * y=(double)
+  * z=(double)
+  * 
+ **/ + void configureCanvasItem(KConfig *config,QCanvasItem *item); + + /** + * Copies the default properties for all QCanvasItems from another sprite. + * Same as above. + **/ + void configureCanvasItem(QCanvasItem *original,QCanvasItem *item); + + /** + * Loads an item with the given name form the given config file. From the + * rtti entry it is determined what type it is and then it is loaded. + **/ + virtual QCanvasItem *loadItem(KConfig *config,QString name); + + /** + * Clone the sprite from another sprite, mostly from the copy stored in the + * cache. + **/ + virtual QCanvasItem *cloneItem(QCanvasItem *original); + + /** + * Creates a pixmap array for a @ref KSprite from the given config file + * for the sprite with the given name (is the name necessary?). + * Parameters are + *
+  *   offset=(QPoint)       : The sprites offset (where 0,0 is)
+  *   pixmaps=(QStringList) : List of operations to create frames (TODO *   rename)
+  *                           if ommited one operation without name is used
+  * 
+ * All following calls have to be preceded by every given string of the + * pixmaps section. If this section is not supplied they can be used without + * prefix but only one frame sequence is created. + *
+  *   method=(QString)  : load, scale (default=load)
+  *                       load: loads number frames from file
+  *                       scale: scales  number frames from one loaded file
+  *   number=(int)      : how many frames to generate
+  *   file=(Qstring)    : the filename to load (can contain printf format args
+  *                       as %d which are replaced, e.g. file=hello_%d.png
+  *   mask=(QString)    : Same for the mask of the pixmaps
+  *   axis=(int)        : (scale only): scale axis (1=x,2=y,3=x+y)
+  *   final=(double)    : final scale in percent (default 0.0, i.e. complete scaling)
+  *   colorfilter=1,h,s,v: make a HSV transform of all sprite images
+  *   colorfilter=2     :  make it gray (lighter=100 is default)
+  *   colorfilter=2,g   : make it gray and lighter (positiv) or darker (negative)
+  * 
+ **/ + virtual QCanvasPixmapArray *createPixmapArray(KConfig *config,QString name); + + /** + * Reads the animations from the config file and calls the corresponding + * KSprite function to create them. + *
+  *  anim0=a,b,c,d
+  *  anim1=e,f,g,h
+  * 
+ * Where the animations have to to be in sequence starting with 0 (i.e. + * anim0). a is the first frame of the animation. b is + * the last frame of the animation. c is the mode of the animations, + * see @ref KSprite::createAnimation and d is the delay in cycles + * of the qcanvas animnation. + * + * @param config - the config file the sprite is read from + * @param sprite - the sprite whose animations are set + **/ + void createAnimations(KConfig *config,KSprite *sprite); + + /** + * Same as above but to copy the animations from an existing sprite + **/ + void createAnimations(KSprite *original,KSprite *sprite); + + /** + * Change a pixmap to grey values. If the second argument is bigger + * than 100 the pixmap is made lighter and if it less then 100 it is + * made darker too + **/ + virtual void changeGrey(QPixmap *pixmap,int lighter=100); + + /** + * Change the HAS value of the pixmap by dH, dS and dV + **/ + virtual void changeHSV(QPixmap *pixmap,int dh,int ds,int dv); + + /** + * Apply the filters as defined in the config file to the sprite name + * (TODO is this argument needed) to the pixmap. + */ + virtual void applyFilter(QPixmap *pixmap,KConfig *config,QString name); + + /** + * resets the cache (?) + */ + void reset(); + + protected: + QDict mItemDict; // Spritename lookup + QDict mCloneDict; // clone Items lookup + + QString mGrafixDir; + QString mRcFile; + KConfig *mConfig; + QCanvas *mCanvas; + +}; + +#endif diff --git a/kwin4/kwin4/kwin4.cpp b/kwin4/kwin4/kwin4.cpp new file mode 100644 index 00000000..fb66e687 --- /dev/null +++ b/kwin4/kwin4/kwin4.cpp @@ -0,0 +1,626 @@ +/*************************************************************************** + kwin4 - Boardgame for KDE + ------------------- + begin : Sun Mar 26 12:50:12 CEST 2000 + copyright : (C) |1995-2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +// include files for QT +#include +#include +#include +#include +#include +#include +#include + +// include files for KDE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// application specific includes +#include "kwin4.h" +#include "kwin4view.h" +#include "kwin4doc.h" +#include "prefs.h" +#include "settings.h" +#include "statistics.h" + +#define ACTION(x) (actionCollection()->action(x)) +#define ID_STATUS_MSG 1003 +#define ID_STATUS_MOVER 1002 + +/** + * Constructor for the chat widget. This widget + * is derived from the libkdegames chat widget + */ +ChatDlg::ChatDlg(KGame *game,QWidget *parent) + : KDialogBase(Plain,i18n("Chat Dlg"),Ok,Ok,parent,0,false,true),mChat(0), mChatDlg(0) +{ + setMinimumSize(QSize(200,200)); + + QGridLayout* mGridLayout=new QGridLayout(plainPage()); + QHBoxLayout* h = new QHBoxLayout(plainPage()); + QHGroupBox* b = new QHGroupBox(i18n("Chat"), plainPage()); + mChat = new KGameChat(game, 10000, b); + h->addWidget(b, 1); + h->addSpacing(10); + mGridLayout->addLayout(h,0,0); + + QPushButton *mButton=new QPushButton(i18n("Configure..."),plainPage()); + mGridLayout->addWidget(mButton,1,1); + + adjustSize(); + + mChatDlg=new KChatDialog(mChat,plainPage(),true); + connect(mButton,SIGNAL(clicked()),mChatDlg,SLOT(show())); +} + +/** + * Set the player in who does the chat. This should be + * the local player. + */ +void ChatDlg::setPlayer(Kwin4Player *p) +{ + if (!mChat) + { + kdError() << "ChatDlg::setPlayer::Chat not defined can't set player" << endl; + return ; + } + if (!p) + { + kdError() << "ChatDlg::setPlayer::Player not defined can't set player" << endl; + return ; + } + mChat->setFromPlayer(p); +} + +/** + * Construct the main application window + */ +Kwin4App::Kwin4App(QWidget *parent, const char *name) : KMainWindow(parent,name), view(0), doc(0), mChat(0), mMyChatDlg(0) +{ + initGUI(); + initStatusBar(); + initDocument(); + + view = new Kwin4View(doc,this); + doc->setView(view); + setCentralWidget(view); + doc->initPlayers(); + + setMinimumSize(640,400); // TODO + setMaximumSize(800,600); + + setupGUI(); + + doc->ReadConfig(kapp->config()); + + checkMenus(); +} + +/** + * This method is called from various places + * and signals to check, uncheck and enable + * or disable all menu items. + * The menu parameter can limit this operation + * to one or more of the main menues (File,View,...) + */ +void Kwin4App::checkMenus(CheckFlags menu) +{ + bool localgame=(!doc->isNetwork()); + bool isRunning = (doc->gameStatus()==KGame::Run); + if (!menu || (menu&CheckFileMenu)) + { + changeAction("hint", !(!isRunning && localgame)); + changeAction("new_game", !isRunning); + changeAction("save", isRunning); + changeAction("end_game", isRunning); + } + + if (!menu || (menu&CheckEditMenu)) + { + if (!isRunning || !localgame) + { + disableAction("edit_undo"); + } + else if (doc->QueryHistoryCnt()==0) + { + disableAction("edit_undo"); + } + else if (doc->QueryCurrentMove()<1 ) + { + disableAction("edit_undo"); + } + else + { + enableAction("edit_undo"); + } + + // Show redo + if (!isRunning || !localgame) + { + disableAction("edit_redo"); + } + else if (doc->QueryHistoryCnt()==doc->QueryMaxMove()) + { + disableAction("edit_redo"); + } + else + { + enableAction("edit_redo"); + } + } +} + +/** + * Function to create the actions for the menu. This + * works together with the xml guirc file + */ +void Kwin4App::initGUI() +{ + KStdGameAction::gameNew(this, SLOT(newGame()), actionCollection(), "new_game"); + ACTION("new_game")->setStatusText(i18n("Start a new game")); + + KStdGameAction::load(this, SLOT(slotOpenGame()), actionCollection(), "open"); + ACTION("open")->setStatusText(i18n("Open a saved game...")); + + KStdGameAction::save(this, SLOT(slotSaveGame()), actionCollection(), "save"); + ACTION("save")->setStatusText(i18n("Save a game...")); + + KStdGameAction::end(this, SLOT(endGame()), actionCollection(), "end_game"); + ACTION("end_game")->setStatusText(i18n("Ending the current game...")); + ACTION("end_game")->setWhatsThis(i18n("Aborts a currently played game. No winner will be declared.")); + + new KAction(i18n("&Network Configuration..."),0, this, SLOT(slotInitNetwork()), + actionCollection(), "network_conf"); + + new KAction(i18n("Network Chat..."),0, this, SLOT(slotChat()), + actionCollection(), "network_chat"); + + if (global_debug>0) + new KAction(i18n("Debug KGame"), 0, this, SLOT(slotDebugKGame()), + actionCollection(), "file_debug"); + + new KAction(i18n("&Show Statistics"),"flag", 0, this, + SLOT(showStatistics()), actionCollection(), "statistics"); + ACTION("statistics")->setStatusText(i18n("Show statistics.")); + + KStdGameAction::hint(doc, SLOT(calcHint()), actionCollection(), "hint"); + ACTION("hint")->setStatusText(i18n("Shows a hint on how to move.")); + + KStdGameAction::quit(this, SLOT(close()), actionCollection(), "game_exit"); + ACTION("game_exit")->setStatusText(i18n("Quits the program.")); + + KStdGameAction::undo(this, SLOT(slotUndo()), actionCollection(), "edit_undo"); + ACTION("edit_undo")->setStatusText(i18n("Undo last move.")); + + KStdGameAction::redo(this, SLOT(slotRedo()), actionCollection(), "edit_redo"); + ACTION("edit_redo")->setStatusText(i18n("Redo last move.")); + + actionCollection()->setHighlightingEnabled(true); + connect(actionCollection(), SIGNAL(actionStatusText(const QString &)), SLOT(slotStatusMsg(const QString &))); + connect(actionCollection(), SIGNAL(clearStatusText()), SLOT(slotClearStatusText())); + + KStdAction::preferences(this, SLOT(showSettings()), actionCollection()); +} + +/** + * Set the status message to Ready + */ +void Kwin4App::slotClearStatusText() +{ + slotStatusMsg(i18n("Ready")); +} + +/** + * Create the status bar with the message part, the + * player part + */ +void Kwin4App::initStatusBar() +{ + statusBar()->insertItem(i18n("This leaves space for the mover"),ID_STATUS_MOVER,0,true); + statusBar()->insertItem(i18n("Ready"), ID_STATUS_MSG); + + slotStatusMover(i18n("(c) Martin Heni ")); + slotStatusMsg(i18n("Welcome to KWin4")); +} + +/** + * Set up the document, i.e. the KGame object + * and connect all signals emitted by it + */ +void Kwin4App::initDocument() +{ + doc = new Kwin4Doc(this); + // Game Over signal + connect(doc,SIGNAL(signalGameOver(int, KPlayer *,KGame *)), + this,SLOT(slotGameOver(int, KPlayer *,KGame *))); + connect(doc,SIGNAL(signalMoveDone(int, int)), + this,SLOT(slotMoveDone(int, int))); + connect(doc,SIGNAL(signalClientLeftGame(int, int,KGame *)), + this,SLOT(slotNetworkBroken(int, int, KGame *))); + connect(doc,SIGNAL(signalNextPlayer()), + this,SLOT(slotStatusNames())); + + connect(doc,SIGNAL(signalGameRun()), + this,SLOT(slotNewGame())); +} + +void Kwin4App::changeAction(const char *action, bool enable){ + if (!action) + return; + + KAction *act=actionCollection()->action(action); + if (act) + act->setEnabled(enable); +} + +/** + * Store the current game + */ +void Kwin4App::saveProperties(KConfig *cfg) +{ + QString tempfile = kapp->tempSaveName(QDir::currentDirPath()+"kwin4"); + cfg->writePathEntry("filename", tempfile ); + doc->save(tempfile); +} + +/** + * Load game back + */ +void Kwin4App::readProperties(KConfig* cfg) +{ + QString filename = cfg->readPathEntry("filename"); + if(!filename.isEmpty()) + doc->load(filename); +} + +/** + * Load a game + */ +void Kwin4App::slotOpenGame() +{ + QString dir(":"); + QString filter("*"); + QString file("/tmp/kwin.save"); + if (global_debug < 10) + file=KFileDialog::getOpenFileName(dir,filter,this); + doc->load(file,true); + checkMenus(); +} + +/** + * Save game + */ +void Kwin4App::slotSaveGame() +{ + QString dir(":"); + QString filter("*"); + QString file("/tmp/kwin.save"); + if (global_debug < 10) + file=KFileDialog::getSaveFileName(dir,filter,this); + doc->save(file); +} + +/** + * Start a new game + */ +void Kwin4App::newGame() +{ + // End the intro if it is running + doc->setGameStatus(Kwin4Doc::End); + // Init the board and Clear the old game out + doc->setGameStatus(Kwin4Doc::Init); + // Run it + doc->setGameStatus(Kwin4Doc::Run); +} + +/** + * Slot: Noticed that a new game started...update menues + */ +void Kwin4App::slotNewGame() +{ + slotStatusNames(); + //checkMenus(CheckFileMenu|CheckEditMenu); + checkMenus(All); +} + +/** + * Abort a running game + */ +void Kwin4App::endGame() +{ + doc->setGameStatus(Kwin4Doc::Abort); +} + +/** + * Show statistics dialog + */ +void Kwin4App::showStatistics() +{ + Statistics *dlg=new Statistics(this,"Game statistics"); + + dlg->p1_name->setText(doc->QueryName(Gelb)); + dlg->p1_won->display(doc->QueryStat(Gelb, TWin)); + dlg->p1_drawn->display(doc->QueryStat(Gelb, TRemis)); + dlg->p1_lost->display(doc->QueryStat(Gelb, TLost)); + dlg->p1_aborted->display(doc->QueryStat(Gelb, TBrk)); + dlg->p1_sum->display(doc->QueryStat(Gelb, TSum)); + + dlg->p2_name->setText(doc->QueryName(Rot)); + dlg->p2_won->display(doc->QueryStat(Rot, TWin)); + dlg->p2_drawn->display(doc->QueryStat(Rot, TRemis)); + dlg->p2_lost->display(doc->QueryStat(Rot, TLost)); + dlg->p2_aborted->display(doc->QueryStat(Rot, TBrk)); + dlg->p2_sum->display(doc->QueryStat(Rot, TSum)); + + if(dlg->exec() == QDialog::Rejected) + doc->ResetStat(); +} + +/** + * Undo menu call + */ +void Kwin4App::slotUndo() +{ + doc->UndoMove(); + // Undo twice if computer is moving then + if (doc->playedBy(doc->QueryCurrentPlayer())==KGameIO::ProcessIO) + doc->UndoMove(); + + // Prepare menus + slotStatusNames(); + checkMenus(CheckEditMenu); +} + +/** + * Redo menu call + */ +void Kwin4App::slotRedo() +{ + doc->RedoMove(); + if (doc->playedBy(doc->QueryCurrentPlayer())==KGameIO::ProcessIO) + doc->RedoMove(); + slotStatusNames(); + checkMenus(CheckEditMenu); +} + +/** + * Set the given text into the statusbar + * change status message permanently + */ +void Kwin4App::slotStatusMsg(const QString &text) +{ + statusBar()->clear(); + statusBar()->changeItem(text, ID_STATUS_MSG); +} + +/** + * Set the string in the statusbar window for + * the player currently moving + * change status mover permanently + */ +void Kwin4App::slotStatusMover(const QString &text) +{ + statusBar()->clear(); + statusBar()->changeItem(text, ID_STATUS_MOVER); +} + +/** + * Ends the current game + * Called by the gameover signal + */ +void Kwin4App::EndGame(TABLE mode) +{ + doc->EndGame(mode); + doc->SwitchStartPlayer(); + slotStatusNames(); + checkMenus(); +} + +/** + * Set the names in the mover field + */ +void Kwin4App::slotStatusNames(){ + QString msg; + if (!(doc->gameStatus()==KGame::Run)) + msg=i18n("No game "); + else if (doc->QueryCurrentPlayer()==Gelb) + msg=QString(" ")+doc->QueryName(Gelb)+ i18n(" - Yellow "); + else if (doc->QueryCurrentPlayer()) + msg=QString(" ")+doc->QueryName(Rot)+ i18n(" - Red "); + else + msg=i18n("Nobody "); + slotStatusMover(msg); +} + +/** + * The network connection is lost + */ +void Kwin4App::slotNetworkBroken(int /*id*/, int oldstatus ,KGame * /*game */) +{ + kdDebug(12010) << "Kwin4App::slotNetworkBroken" << endl; + if (doc->playedBy(Gelb)==0) + doc->setPlayedBy(Gelb,KGameIO::MouseIO); + if (doc->playedBy(Rot)==0) + doc->setPlayedBy(Rot,KGameIO::MouseIO); + + kdDebug(12010) << "CurrrentPlayer=" << doc->QueryCurrentPlayer() << endl; + kdDebug(12010) << " " << doc->getPlayer(doc->QueryCurrentPlayer()) << endl; + doc->getPlayer(doc->QueryCurrentPlayer())->setTurn(true,true); + + KMessageBox::information(this,i18n("The network game ended!\n")); + doc->setGameStatus(oldstatus); +} + +/** + * A move is done. update status + */ +void Kwin4App::slotMoveDone(int /* x */ ,int /* y */ ) +{ + checkMenus(CheckEditMenu); + slotStatusNames(); + slotStatusMsg(i18n("Game running...")); +} + +/** + * The game is over or aborted + */ +void Kwin4App::slotGameOver(int status, KPlayer * p, KGame * /*me*/) +{ + if (status==-1) // remis + { + EndGame(TRemis); + slotStatusMsg(i18n("The game is drawn. Please restart next round.")); + } + else if (status==1) + { + if (p->userId()==Gelb) + EndGame(TWin); + else + EndGame(TLost); + QString msg=i18n("%1 won the game. Please restart next round.").arg(doc->QueryName(((FARBE)p->userId()))); + slotStatusMsg(msg); + } + else if (status==2) // Abort + { + EndGame(TBrk); + QString m=i18n(" Game aborted. Please restart next round."); + slotStatusMsg(m); + } + else + { + kdError() << "Gameover with status " << status << ". This is unexpected and a serious problem" << endl; + } + checkMenus(CheckEditMenu); +} + +void Kwin4App::slotInitNetwork() +{ + if (doc->gameStatus()==Kwin4Doc::Intro) doc->setGameStatus(Kwin4Doc::Pause); + + QString host = Prefs::host(); + int port=Prefs::port(); + + // just for testing - should be non-modal + KGameDialog dlg(doc, 0, i18n("Network Configuration"), this, + KGameDialog::NetworkConfig, 20000, true); + dlg.networkConfig()->setDefaultNetworkInfo(host, port); + dlg.networkConfig()->setDiscoveryInfo("_kwin4._tcp",Prefs::gamename()); + + QVBox *box=dlg.configPage(KGameDialog::NetworkConfig); + QVBoxLayout *l=(QVBoxLayout *)(box->layout()); + + mColorGroup=new QVButtonGroup(box); + connect(mColorGroup, SIGNAL(clicked(int)), this, SLOT(slotRemoteChanged(int))); + connect(dlg.networkConfig(), SIGNAL(signalServerTypeChanged(int)), this, SLOT(slotServerTypeChanged(int))); + + new QRadioButton(i18n("Yellow should be played by remote"), mColorGroup); + new QRadioButton(i18n("Red should be played by remote"), mColorGroup); + l->addWidget(mColorGroup); + mColorGroup->setButton(0); + slotRemoteChanged(0); + + dlg.exec();// note: we don't have to check for the result - maybe a bug +} + +/** + * Arg can't get rid of this function in KGame's current state. + * Can't pass a int signal to a bool slot, so this must be here + */ +void Kwin4App::slotServerTypeChanged(int t) +{ + mColorGroup->setDisabled(t); +} + +/** + * The remove player changed + */ +void Kwin4App::slotRemoteChanged(int button) +{ + if (button==0) + { + doc->getPlayer(Gelb)->setNetworkPriority(0); + doc->getPlayer(Rot)->setNetworkPriority(10); + } + else + { + doc->getPlayer(Gelb)->setNetworkPriority(10); + doc->getPlayer(Rot)->setNetworkPriority(0); + } +} + +void Kwin4App::slotChat() +{ + if (!mMyChatDlg) + { + mMyChatDlg=new ChatDlg(doc,this); + Kwin4Player *p=doc->getPlayer(Gelb); + if (!p->isVirtual()) + mMyChatDlg->setPlayer(doc->getPlayer(Gelb)); + else + mMyChatDlg->setPlayer(doc->getPlayer(Rot)); + connect(doc,SIGNAL(signalChatChanged(Kwin4Player *)), + mMyChatDlg,SLOT(setPlayer(Kwin4Player *))); + } + + if (mMyChatDlg->isHidden()) + mMyChatDlg->show(); + else + mMyChatDlg->hide(); +} + +/** + * Show the KGame debug window + */ +void Kwin4App::slotDebugKGame() +{ + KGameDebugDialog* debugWindow = new KGameDebugDialog(doc, this); + debugWindow->show(); +} + +/** + * Show Configure dialog. + */ +void Kwin4App::showSettings(){ + if(KConfigDialog::showDialog("settings")) + return; + + KConfigDialog *dialog = new KConfigDialog(this, "settings", Prefs::self(), KDialogBase::Swallow); + Settings *general = new Settings(0, "General"); + dialog->addPage(general, i18n("General"), "package_settings"); + connect(dialog, SIGNAL(settingsChanged()), doc, SLOT(loadSettings())); + dialog->show(); +} + +#include "kwin4.moc" diff --git a/kwin4/kwin4/kwin4.h b/kwin4/kwin4/kwin4.h new file mode 100644 index 00000000..572381f1 --- /dev/null +++ b/kwin4/kwin4/kwin4.h @@ -0,0 +1,126 @@ +/*************************************************************************** + Kwin4 - Four in a Row for KDE + ------------------- + begin : March 2000 + copyright : (C) 1995-2001 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KWIN4_H +#define KWIN4_H + +#include +#include +#include "kwin4doc.h" +#include +class Kwin4Player; +class KGameChat; +class KChatDialog; +class Kwin4Doc; +class Kwin4View; +class QVButtonGroup; +class KPlayer; + +/** + * Subclass of the chat dialog provided by the KGame lib. + * It supports a user defined chat and the setting of the + * owner player + **/ +class KDE_EXPORT ChatDlg : public KDialogBase +{ + Q_OBJECT +public: + ChatDlg(KGame *game,QWidget* parent=0); + +public slots: + void setPlayer(Kwin4Player *p); + +private: + KGameChat *mChat; + KChatDialog *mChatDlg; +}; + +/** + * The base class for Kwin4 application window. + */ +class Kwin4App : public KMainWindow +{ + +Q_OBJECT + +public: + Kwin4App(QWidget *parent=0, const char *name=0); + +protected: + void EndGame(TABLE mode); + + // Flags which menus should be checked and set by below functions + enum CheckFlags {All=0,CheckFileMenu=1,CheckEditMenu=2}; + + void changeAction(const char *,bool); + // Enabled/Disabled menu/toolbar items + void enableAction(const char *action) {changeAction(action, true); } + void disableAction(const char *action) {changeAction(action, false); } + // Checks all menus..usually done on init programm + void checkMenus(CheckFlags menu=All); + + void initGUI(); + void initStatusBar(); + void initDocument(); + + virtual void saveProperties(KConfig *cfg); + virtual void readProperties(KConfig *cfg); + +public slots: + void slotServerTypeChanged(int t); + + void slotRemoteChanged(int who); + void slotGameOver(int status, KPlayer * p, KGame * me); + void slotMoveDone(int x, int y); + + void slotNetworkBroken(int id, int oldstatus ,KGame *game); + /** Being noticed that a new game started */ + void slotNewGame(); + void slotStatusNames(); + + void slotInitNetwork(); + void slotChat(); + void slotDebugKGame(); + + void newGame(); + void slotOpenGame(); + void slotSaveGame(); + void endGame(); + void showStatistics(); + + void slotUndo(); + void slotRedo(); + + void slotStatusMover(const QString &text); + void slotStatusMsg(const QString &text); + +private: + Kwin4View *view; + Kwin4Doc *doc; + + QVButtonGroup *mColorGroup; + KGameChat *mChat; + ChatDlg *mMyChatDlg; + +protected slots: + void slotClearStatusText(); + + void showSettings(); +}; + +#endif // KWIN4_H + diff --git a/kwin4/kwin4/kwin4.kcfg b/kwin4/kwin4/kwin4.kcfg new file mode 100644 index 00000000..2ac852e2 --- /dev/null +++ b/kwin4/kwin4/kwin4.kcfg @@ -0,0 +1,46 @@ + + + + + + + 7442 + + + + + + + localhost + + + + 3 + 0 + 5 + + + + + Player 1 + + + + Player 2 + + + 0 + + + 0 + + + + 0 + + + + diff --git a/kwin4/kwin4/kwin4doc.cpp b/kwin4/kwin4/kwin4doc.cpp new file mode 100644 index 00000000..4c85f70e --- /dev/null +++ b/kwin4/kwin4/kwin4doc.cpp @@ -0,0 +1,1322 @@ +/*************************************************************************** + kwin4doc.cpp - Boardgame for KDE + ------------------- + begin : Sun Mar 26 12:50:12 CEST 2000 + copyright : (C) |1995-2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kwin4doc.h" + +// include files for Qt +#include +#include + +// include files for KDE +#include +#include +#include +#include +#include +#include + +// application specific includes +#include "kspritecache.h" +#include "kwin4view.h" +#include "scorewidget.h" +#include "prefs.h" +#include "statuswidget.h" + +Kwin4Doc::Kwin4Doc(QWidget *parent, const char *) : KGame(1234,parent), pView(0), mHintProcess(0) +{ + connect(this,SIGNAL(signalPropertyChanged(KGamePropertyBase *,KGame *)), + this,SLOT(slotPropertyChanged(KGamePropertyBase *,KGame *))); + + dataHandler()->Debug(); + //kdDebug(12010) << "Property 7 policy=" << dataHandler()->find(7)->policy() << endl; + setPolicy(KGame::PolicyDirty,true); + + //kdDebug(12010) << "Property 7 policy=" << dataHandler()->find(7)->policy() << endl; + + // Game design + setMaxPlayers(2); + setMinPlayers(2); + + // Game initialization + mField.resize(42); + + // **************************************** + // NOTE: Do not i18n the strings here. They + // are for debugging only + // **************************************** + + // The field array needs not be updated as any move will change it + // Careful only in new ResetGame! Maybe unlocal it there! + // mField.setPolicy(KGamePropertyBase::PolicyLocal); + mField.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QString("mField")); + + mFieldFilled.resize(7); + mHistory.resize(43); + mHistory.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QString("mHistory")); + + mAmzug.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QString("mAmzug")); + mCurrentMove.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QString("mCurrentMove")); + mMaxMove.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QString("mMaxMove")); + mFieldFilled.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QString("mFieldFilled")); + mHistoryCnt.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QString("mHistoryCnt")); + mLastColumn.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QString("mLastColumn")); + mLastHint.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QString("mLastHint")); + mLastColour.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QString("mLastColour")); + mScore.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QString("mScore")); + + // game startup parameter + mStartPlayer=Gelb; + mStartPlayer.registerData(dataHandler(),KGamePropertyBase::PolicyDirty,QString("mStartPlayer")); + SetCurrentPlayer((FARBE)mStartPlayer.value()); + if (global_debug>1) kdDebug(12010) << "amZug policy=" << mAmzug.policy() << endl; + + mPlayedBy[Gelb]=KGameIO::MouseIO; + mPlayedBy[Rot]=KGameIO::MouseIO; + + // last in init + ResetGame(false); + + setGameStatus(Intro); + + // Listen to network + connect(this,SIGNAL(signalMessageUpdate(int,Q_UINT32,Q_UINT32)), + this,SLOT(slotMessageUpdate(int, Q_UINT32,Q_UINT32))); + connect(this,SIGNAL(signalClientJoinedGame(Q_UINT32,KGame *)), + this,SLOT(slotClientConnected(Q_UINT32, KGame *))); + + // Debug only + connect(this,SIGNAL(signalGameOver(int, KPlayer *,KGame *)), + this,SLOT(slotGameOver(int, KPlayer *,KGame *))); + + // Change global KGame policy + //dataHandler()->setPolicy(KGamePropertyBase::PolicyDirty,false); + dataHandler()->Debug(); +} + +/** + * Player initialization + */ +void Kwin4Doc::initPlayers() +{ + // Create yellow + Kwin4Player *yellow = (Kwin4Player *)createPlayer(1, mPlayedBy[Gelb], false); + yellow->setUserId(Gelb); + yellow->setName(Prefs::name1()); + addPlayer(yellow); + setPlayedBy(Gelb,mPlayedBy[Gelb]); + + // Create Red + Kwin4Player *red = (Kwin4Player *)createPlayer(1, mPlayedBy[Rot], false); + red->setUserId(Rot); + red->setName(Prefs::name1()); + addPlayer(red); + setPlayedBy(Rot,mPlayedBy[Rot]); +} + +Kwin4Doc::~Kwin4Doc() +{ + WriteConfig(kapp->config()); + if (mHintProcess) + delete mHintProcess; +} + +void Kwin4Doc::setView(Kwin4View *view) +{ + pView=view; +} + +/** + * Returns colour + */ +FARBE Kwin4Doc::QueryColour(int x,int y){ + return (FARBE)mField.at(x+y*FIELD_SIZE_X); +} + +/** + * Set the colour + */ +void Kwin4Doc::SetColour(int x,int y,FARBE c){ + if (x<0 || x>=FIELD_SIZE_X || y<0 || y>=FIELD_SIZE_Y) + { + kdDebug(12010) << "ERROR: SetColour auf falsche Poition " << x << " " << y << endl; + return ; + } + //kdDebug(12010) << "SetColor::mField["<=0;y--) + SetColour(x,y,Niemand); + } + mFieldFilled.fill(0); + + // Reset game vars + mHistoryCnt=0; + mCurrentMove=0; + mMaxMove=0; + mLastColumn=-1; + mLastColour=Niemand; + SetScore(0); + mLastHint=-1; + + // Reset the view + if (initview) + pView->initView(false); + + // Who starts this game + SetCurrentPlayer((FARBE)mStartPlayer.value()); +} + +/** + * Set current player to setTurn true + */ +void Kwin4Doc::preparePlayerTurn() +{ + if (global_debug>1) + kdDebug(12010) << "Setting the current player to turn"<setTurn(true,true); +} + +/** + * End a game + */ +void Kwin4Doc::EndGame(TABLE mode) +{ + setGameStatus(End); + pView->clearError(); + pView->EndGame(); + Kwin4Player *yellow=getPlayer(Gelb); + Kwin4Player *red=getPlayer(Rot); + + switch(mode) + { + case TWin: yellow->incWin(); + red->incLost(); + break; + case TLost: yellow->incLost(); + red->incWin(); + break; + case TRemis: yellow->incRemis(); + red->incRemis(); + break; + default: + // Only break if moves have been made + if (mMaxMove>0) + { + yellow->incBrk(); + red->incBrk(); + } + break; + } + // switch start player +} + +void Kwin4Doc::moveDone(QCanvasItem *item,int ) +{ + //kdDebug(12010) << "########################## SPRITE MOVE DONE ################# " << endl; + //Debug(); + //for (KPlayer* p=playerList()->first(); p!= 0; p=playerList()->next() ) + //{ + // p->Debug(); + //} + + if (playerCount()>1) + playerInputFinished(getPlayer(QueryCurrentPlayer())); + + pView->clearError(); + + KSprite *sprite=(KSprite *)item; + sprite->deleteNotify(); +} + +/** + * Calcualte the next players turn + */ +KPlayer * Kwin4Doc::nextPlayer(KPlayer *last,bool /*exclusive*/) +{ + if (global_debug>1) + kdDebug(12010) << k_funcinfo << "nextPlayer last="<id() << " admin=" << isAdmin() <userId()==Gelb) + SetCurrentPlayer(Rot); + else + SetCurrentPlayer(Gelb); + if (global_debug>1) + kdDebug(12010) <<" Current set to "<setTurn(true,true); + emit signalMoveDone(0,0); + return getPlayer(QueryCurrentPlayer()); +} + +/** + * Make a game move + * mode=0 normal move, =1: redo move + */ +MOVESTATUS Kwin4Doc::MakeMove(int x,int mode){ + if (x<0 || x>=FIELD_SIZE_X) + { + kdDebug(12010) << "ERROR: MakeMove auf falsche Position " << x << endl; + return GNotAllowed; + } + + int y=mFieldFilled.at(x); + + if (y>=FIELD_SIZE_Y) + { + return GIllMove; // no space left in column + } + + if (mLastHint>=0) + { + int hy; + hy=mFieldFilled.at(mLastHint); + SetColour(mLastHint,hy,Niemand); + mLastHint=-1; + } + if (mode==Tip) + { + mLastHint=x; + SetColour(x,y,Tip); + return GTip ; // no real move + } + + mFieldFilled.setAt(x,mFieldFilled.at(x)+1); + SetColour(x,y,QueryCurrentPlayer()); + + mHistory.setAt(QueryHistoryCnt(),x); + mHistoryCnt=mHistoryCnt.value()+1; + + mLastColour=QueryCurrentPlayer(); + //if (QueryCurrentPlayer()==Gelb) SetCurrentPlayer(Rot); + //else SetCurrentPlayer(Gelb); + + mCurrentMove=mCurrentMove.value()+1; + + // only if a real move isdone the maxmove is raised + if (mode==0) mMaxMove=mCurrentMove.value(); + mLastColumn=x; + + pView->setArrow(x,mLastColour); + // animation onyl if no redo + pView->setPiece(x,y,mLastColour,mCurrentMove-1,mode==1?false:true); + pView->setHint(0,0,false); + + return GNormal; +} + + +/** + * Undo a move + */ +bool Kwin4Doc::UndoMove(){ + //kdDebug(12010) <<" undo: current player="<setGroup("YellowPlayer"); + getPlayer(Gelb)->readConfig(config); + + config->setGroup("RedPlayer"); + getPlayer(Rot)->readConfig(config); +} + +/** + * write config file + */ +void Kwin4Doc::WriteConfig(KConfig *config) +{ + config->setGroup("YellowPlayer"); + getPlayer(Gelb)->writeConfig(config); + + config->setGroup("RedPlayer"); + getPlayer(Rot)->writeConfig(config); + + config->sync(); +} + +/** + * Returns the current player, resp amzug + */ +FARBE Kwin4Doc::QueryCurrentPlayer(){ + return (FARBE)mAmzug.value(); +} + +void Kwin4Doc::SetCurrentPlayer(FARBE i) +{ + mAmzug.setValue(i); +} + +/** + * Swtich the starting player and return the new started + */ +FARBE Kwin4Doc::SwitchStartPlayer() +{ + if (mStartPlayer.value()==Gelb) + mStartPlayer.setValue(Rot); + else + mStartPlayer.setValue(Gelb); + + return (FARBE)mStartPlayer.value(); +} + +int Kwin4Doc::QueryLastcolumn() +{ + return mLastColumn; +} + +FARBE Kwin4Doc::QueryLastcolour() +{ + return (FARBE)(mLastColour.value()); +} + +int Kwin4Doc::QueryCurrentMove() +{ + return mCurrentMove; +} + +void Kwin4Doc::SetCurrentMove(int i) +{ + mCurrentMove=i; +} + +int Kwin4Doc::QueryMaxMove() +{ + return mMaxMove; +} + +int Kwin4Doc::QueryHistoryCnt() +{ + return mHistoryCnt; +} + +/** + * Return the name of the computer player process + */ +QString Kwin4Doc::QueryProcessName() +{ + // First try a local dir override + QDir dir; + QString filename=dir.path()+QString("/kwin4/kwin4proc"); + QFile flocal(filename); + if (flocal.exists()) + { + if (global_debug>1) kdDebug(12010) << " Found local process " << filename << endl; + return filename; + } + QString path=kapp->dirs()->findExe("kwin4proc"); + if (!path.isNull()) + { + if (global_debug>1) kdDebug(12010) << " Found system process " << path << endl; + return path; + } + QString empty; + kdError() << "Could not locate the computer player" << endl; + return empty; +} + +void Kwin4Doc::slotMessageUpdate(int /*id*/,Q_UINT32 /*sender*/,Q_UINT32 /*recv*/) +{ +// kdDebug(12010) << "MSG: id=" << id << " sender=" << sender << " receiver="<setWidget(pView->statusWidget()); + return player; +} + +/** + * Called when a player input is received from the KGame object + * this is e-.g. a mouse event + */ +bool Kwin4Doc::playerInput(QDataStream &msg, KPlayer * /*player*/) +{ + int move, pl; + msg >> pl >> move; + if (!Move(move,pl)) + QTimer::singleShot(0, this,SLOT(slotRepeatMove())); + + return false; +} + +/** + * Reactivate player in case of a move which could not pe performed + */ +void Kwin4Doc::slotRepeatMove() +{ + getPlayer(QueryCurrentPlayer())->setTurn(true); +} + +/** + * Performs a game move + */ +bool Kwin4Doc::Move(int x,int id) +{ + if (global_debug>1) + kdDebug(12010) <<" Kwin4Doc::Move("<1) + kdDebug(12010) <<"kwin4doc::checkGameOver::"<userId()<1) + kdDebug(12010) << " Kwin4Doc::setPlayedBy(int "<isVirtual()) + { + mPlayedBy[col]=io; + player->removeGameIO(0); // remove all IO's + createIO(player,io); + } +} + +/* Get the io values right after a load game as the io the playedby + * is not set there + */ +void Kwin4Doc::recalcIO() +{ + mPlayedBy[Gelb]=(KGameIO::IOMode)getPlayer(Gelb)->calcIOValue(); + mPlayedBy[Rot]=(KGameIO::IOMode)getPlayer(Rot)->calcIOValue(); +} + +void Kwin4Doc::createIO(KPlayer *player,KGameIO::IOMode io) +{ + if (!player) + return; + + if (global_debug>1) + kdDebug(12010) << " Kwin4Doc::createIO(KPlayer *player("<userId()<<"),KGameIO::IOMode "<1) kdDebug(12010) << "Creating MOUSE IO to "<1) kdDebug(12010) << "MOUSE IO added " << endl; + // Connect mouse input to a function to process the actual input + connect(input,SIGNAL(signalMouseEvent(KGameIO *,QDataStream &,QMouseEvent *,bool *)), + pView,SLOT(slotMouseInput(KGameIO *,QDataStream &,QMouseEvent *,bool *))); + player->addGameIO(input); + } + else if (io&KGameIO::ProcessIO) + { + QString file=QueryProcessName(); + if (global_debug>1) kdDebug(12010) << "Creating PROCESS IO " << file << endl; + + KGameProcessIO *input; + // We want a computer player + input=new KGameProcessIO(file); + // Connect computer player to the setTurn + connect(input,SIGNAL(signalPrepareTurn(QDataStream &,bool,KGameIO *,bool *)), + this,SLOT(slotPrepareTurn(QDataStream &,bool,KGameIO *,bool *))); + + connect(input,SIGNAL(signalProcessQuery(QDataStream &,KGameProcessIO *)), + this,SLOT(slotProcessQuery(QDataStream &,KGameProcessIO *))); + player->addGameIO(input); + } + else if (io&KGameIO::KeyIO) + { + if (global_debug>1) kdDebug(12010) << "Creating KEYBOARD IO " << endl; + // We want the player to work over keyboard + KGameKeyIO *input; + input=new KGameKeyIO(pView->parentWidget()); + // Connect keys input to a function to process the actual input + connect((KGameKeyIO *)input,SIGNAL(signalKeyEvent(KGameIO *,QDataStream &,QKeyEvent *,bool *)), + pView,SLOT(slotKeyInput(KGameIO *,QDataStream &,QKeyEvent *,bool *))); + player->addGameIO(input); + } +} + +/** + * This slot is called when a computer move should be generated + */ +void Kwin4Doc::slotPrepareTurn(QDataStream &stream,bool b,KGameIO *input,bool *sendit) +{ + if (global_debug>1) + kdDebug(12010) << " Kwin4Doc::slotPrepareTurn b="<player(); + if (!player->myTurn()) return ; + if (!b) return ; // only on setTurn(true) + + Q_INT32 pl; + if (global_debug>1) kdDebug(12010) << "slotPrepareComputerTurn for player id= " << player->id() << endl; + pl=player->userId(); + + prepareGameMessage(stream,pl); + + *sendit=true; +} + +/** + * Sends the current game status to the computer player + * Careful: The data needs to be the same than the computer + * player reading on the other side + **/ +void Kwin4Doc::prepareGameMessage(QDataStream &stream, Q_INT32 pl) +{ + if (global_debug>1) kdDebug(12010) << " sending col=" << pl << endl; + stream << pl ; + // This needs to be the same than the computer player reads! + stream << (Q_INT32)QueryCurrentMove(); + stream << (Q_INT32)QueryCurrentPlayer(); + stream << (Q_INT32)QueryPlayerColour(0); + stream << (Q_INT32)QueryPlayerColour(1); + stream << (Q_INT32)Prefs::level(); + + int i,j; + for (i=0;i1) kdDebug(12010) + << QueryColour(0,i) << " " + << QueryColour(1,i) << " " + << QueryColour(2,i) << " " + << QueryColour(3,i) << " " + << QueryColour(4,i) << " " + << QueryColour(5,i) << " " + << QueryColour(6,i) << endl; + } + stream << (Q_INT32)421256; +} + +void Kwin4Doc::slotProcessQuery(QDataStream &in,KGameProcessIO * /*me*/) +{ + Q_INT8 cid; + in >> cid; + switch(cid) + { + case 1: // value + long value; + in >> value; + if (global_debug>1) kdDebug(12010) << "#### Computer thinks value is " << value << endl; + SetScore(value); + break; + default: + kdError() << "Kwin4Doc::slotProcessQuery: Unknown id " << cid << endl; + break; + } +} + +/** + * This slot is called by the signal of KGame to indicated + * that the network connection is done and a new client is + * connected + * cid is the id of the client connected. if this is equal + * gameId() WE are the client + */ +void Kwin4Doc::slotClientConnected(Q_UINT32 cid,KGame *) +{ + if (global_debug>1) kdDebug(12010) << " void Kwin4Doc::slotClientConnected id="<count()!=2) + { + kdError() << "SERIOUS ERROR: We do not have two players...Trying to disconnect!"<at(0); + Kwin4Player *p2=(Kwin4Player *)playerList()->at(1); + if (!p1->isVirtual()) + { + emit signalChatChanged(p1); + if (global_debug>1) kdDebug(12010) << "CHAT to player 0 " << endl; + } + else + { + emit signalChatChanged(p2); + if (global_debug>1) kdDebug(12010) << "CHAT to player 1 " << endl; + } + + // Now check whose turn it is. The Admin will rule this + if (isAdmin()) + { + if (global_debug>1) kdDebug(12010) << "WE are ADMIN == COOL ! " << endl; + // p1 is local + if (!p1->isVirtual()) + { + if (global_debug>1) kdDebug(12010) << "p1 id=" << p1->userId() << " is local turn="<myTurn()<< endl; + // Exclusive setting of the turn + p1->setTurn(p1->myTurn(),true); + p2->setTurn(!p1->myTurn(),true); + } + else if (!p2->isVirtual()) + { + if (global_debug>1) kdDebug(12010) << "p2 id=" << p2->userId() << " is local turn="<myTurn()<< endl; + // Exclusive setting of the turn + p2->setTurn(p2->myTurn(),true); + p1->setTurn(!p2->myTurn(),true); + } + } +} + +/** + * Get the KPlayer from the color by searching all players + * users id's + **/ +Kwin4Player *Kwin4Doc::getPlayer(FARBE col) +{ + Kwin4Player *p; + for ( p=(Kwin4Player *)playerList()->first(); p!= 0; p=(Kwin4Player *)playerList()->next() ) + { + if (p->userId()==col) + return p; + } + kdError() << "SERIOUS ERROR: Cannot find player with colour " << col << ". CRASH imminent" << endl; + return 0; +} + +/** + * We create a process which calulcates a computer move + * which is shown as hint + **/ +void Kwin4Doc::calcHint() +{ + // We allocate the hint process only if it is needed + if (!mHintProcess) + { + QString file=QueryProcessName(); + if (global_debug>1) kdDebug(12010) << "Creating HINT PROCESS IO " << endl; + + // We want a computer player + mHintProcess=new KGameProcessIO(file); + + connect(mHintProcess,SIGNAL(signalProcessQuery(QDataStream &,KGameProcessIO *)), + this,SLOT(slotProcessHint(QDataStream &,KGameProcessIO *))); + } + Q_INT32 pl; + QByteArray buffer; + QDataStream stream(buffer,IO_WriteOnly); + pl=QueryCurrentPlayer(); + prepareGameMessage(stream,pl); + mHintProcess->sendMessage(stream,2,0,gameId()); +} + +/** + * The compute rprocess sent a hint which we show in the + * game board + **/ +void Kwin4Doc::slotProcessHint(QDataStream &in,KGameProcessIO * /*me*/) +{ + Q_INT8 cid; + in >> cid; + switch(cid) + { + case 2: // Hint + { + Q_INT32 pl; + Q_INT32 move; + long value; + in >> pl >> move >> value; + if (global_debug>1) kdDebug(12010) << "#### Computer thinks pl=" << pl << " move =" << move << endl; + if (global_debug>1) kdDebug(12010) << "#### Computer thinks hint is " << move << " and value is " << value << endl; + int x=move; + int y=mFieldFilled.at(x); + pView->setHint(x,y,true); + } + break; + default: + kdError() << "Kwin4Doc::slotProcessHint: Unknown id " << cid << endl; + break; + } +} + +/** + * Called when a player property has changed. We check whether the name + * changed and then update the score widget + * We should maybe do this for the other properties too to update + * the status widget...I am not sure here...we'll see + **/ +void Kwin4Doc::slotPlayerPropertyChanged(KGamePropertyBase *prop,KPlayer *player) +{ + if (!pView) return ; + if (prop->id()==KGamePropertyBase::IdName) + { + if (global_debug>1) kdDebug(12010) << "Player name id=" << player->userId() << " changed to " << player->name()<scoreWidget()->setPlayer(player->name(),player->userId()); + } +} + +void Kwin4Doc::slotPropertyChanged(KGamePropertyBase *prop,KGame *) +{ + if (!pView) + return ; + + if (prop->id()==mCurrentMove.id()) + { + pView->scoreWidget()->setMove(mCurrentMove); + } + else if (prop->id()==mScore.id()) + { + int sc=mScore/10000; + if (sc==0 && mScore.value()>0) sc=1; + else if (sc==0 && mScore.value()<0) sc=-1; + pView->scoreWidget()->setChance(sc); + } + else if (prop->id()==mAmzug.id()) + { + if (global_debug>1) kdDebug(12010) << "Amzug changed to " << mAmzug.value()<scoreWidget()->setTurn(mAmzug); + } + else if (prop->id()==KGamePropertyBase::IdGameStatus) + { + if (gameStatus()==Abort) + { + if (global_debug>1) kdDebug(12010) << "PropertyChanged::status signal game abort +++" << endl; + emit signalGameOver(2,getPlayer(QueryCurrentPlayer()),0); // 2 indicates Abort + } + else if (gameStatus()==Run) + { + if (global_debug>1) kdDebug(12010) << "PropertyChanged::status signal game run +++" << endl; + preparePlayerTurn(); // Set the current player to play + emit signalGameRun(); + } + else if (gameStatus()==Init) + { + if (global_debug>1) kdDebug(12010) << "PropertyChanged::status signal game INIT +++" << endl; + ResetGame(true); + } + else + { + if (global_debug>1) kdDebug(12010) << "PropertyChanged::other status signal +++" << endl; + } + + } +} + +/** + * Called by KGame if the game has ended. + * DEBUG only as we do not need any extension to + * the KGame behavior + */ +void Kwin4Doc::slotGameOver(int status, KPlayer * p, KGame * /*me*/) +{ + if (global_debug>1) kdDebug(12010) << "SlotGameOver: status="<1) kdDebug(12010) << "amzug loaded to ="<1) kdDebug(12010) << "REDRAW GAME using undo/redo" << endl; + if (global_debug>1) kdDebug(12010) << "history cnt="<1) kdDebug(12010) << "amzug ="<1) kdDebug(12010) << "Undoing move "<1) kdDebug(12010) << "amzug ="<0) + { + RedoMove(); + cnt--; + if (global_debug>1) kdDebug(12010) << "Redoing move "<1) kdDebug(12010) << "amzug ="<1) + kdDebug(12010) << "loadgame done +++" << endl; + return res; +} + +/** + * This is also an overwritten function of KGame. It is + * Called in the game negotiation upon connect. Here + * the games have to determine what player is remote and + * what is local + * This function is only called in the Admin. + */ +void Kwin4Doc::newPlayersJoin(KGamePlayerList * /*oldList*/,KGamePlayerList *newList,QValueList &inactivate) +{ + if (global_debug>1) + kdDebug(12010) << "newPlayersJoin: START"<networkPriority()>red->networkPriority()) + { + // Deactivate the lower one + inactivate.append(red->id()); + if (global_debug>1) kdDebug(12010) << "ADMIN keeps yellow and kicks red= " << red->id()<<" userId/col="<userId()<first(); player != 0; player=newList->next() ) + { + if (player->userId()==yellow->userId()) + { + inactivate.append(player->id()); + if (global_debug>1) kdDebug(12010) << "Deactivate C1 " << player->id()<<" col="<userId()<id()); + if (global_debug>1) kdDebug(12010) << "ADMIN keeps red and kicks yellow= " << yellow->id()<<" userId/col="<userId()<first(); player != 0; player=newList->next() ) + { + if (player->userId()==red->userId()) + { + inactivate.append(player->id()); + if (global_debug>1) kdDebug(12010) << "Deactivate C2 " << player->id()<<" col="<userId()<1) + kdDebug(12010) << "newPlayersJoin: DONE"< +#include +#include + +#include "kwin4player.h" + +class Kwin4View; +class QCanvasItem; + +extern int global_debug; + +// The user or color? +typedef enum e_Farbe {Niemand=-1,Gelb=0,Rot=1,Tip=3,Rand=4,GelbWin=8,RotWin=9} FARBE; +// The type of player +typedef enum {Men=0,Computer=1,Remote=2} PLAYER; +typedef enum {TSum,TWin,TRemis,TLost,TBrk} TABLE; +typedef enum {GIllMove=-2,GNotAllowed=-1,GNormal=0,GYellowWin=1,GRedWin=2,GRemis=3,GTip=4} MOVESTATUS; + +#define NOOFPLAYER 2 + +#define FIELD_SIZE_X 7 +#define FIELD_SIZE_Y 6 +#define FIELD_SPACING 40 + +/** + * The board "engine" + */ +class Kwin4Doc : public KGame +{ +Q_OBJECT + +public: + Kwin4Doc(QWidget *parent, const char *name=0); + ~Kwin4Doc(); + + /** adds a view to the document which represents the document contents. Usually this is your main view. */ + void setView(Kwin4View *view); + /** initializes the players */ + void initPlayers(); + /** saves the document under filename and format.*/ + bool loadgame(QDataStream &stream, bool network, bool reset); + + int QueryLastHint(); + int QueryHeight(int x); + void SetScore(long i); + void ResetStat(); + int CheckGameOver(int x, FARBE col); + FARBE QueryPlayerColour(int player); + int QueryStat(FARBE i, TABLE mode); + QString QueryName(FARBE i); + void SetName(FARBE i, const QString &n); + + /** + * Set and query the IO mode of player Gelb/Rot + */ + KGameIO::IOMode playedBy(int col); + void setPlayedBy(int col,KGameIO::IOMode mode); + + /** + * create and add an IO device to an given player. + * The old ones have to be removed manually before + */ + void createIO(KPlayer *player,KGameIO::IOMode io); + + Kwin4Player *getPlayer(FARBE col); + + bool RedoMove(); + bool UndoMove(); + + /** Make a game move */ + MOVESTATUS MakeMove(int x,int mode); + /** End a game */ + void EndGame(TABLE mode); + /** Reset the whole game */ + void ResetGame(bool initview); + /** Set the colour */ + void SetColour(int x,int y,FARBE c); + /** Returns colour */ + FARBE QueryColour(int x,int y); + + void ReadConfig(KConfig *config); + void WriteConfig(KConfig *config); + + FARBE QueryCurrentPlayer(); + void SetCurrentPlayer(FARBE i); + + FARBE SwitchStartPlayer(); + + int QueryLastcolumn(); // last x moved + FARBE QueryLastcolour(); // last colour moved + int QueryCurrentMove(); // 0..42 + void SetCurrentMove(int ); // 0..42 + int QueryMaxMove(); // 0..42 + int QueryHistoryCnt(); // 0..42 + QString QueryProcessName(); + + KPlayer *createPlayer(int rtti, int io, bool isvirtual); + KPlayer * nextPlayer(KPlayer *last, bool exclusive=true); + + void newPlayersJoin(KGamePlayerList *,KGamePlayerList *,QValueList &); + +protected: + bool Move(int x,int id); + /** Check whether the field has a game over situation */ + int checkGameOver(KPlayer *); + /** Send to the computer player */ + void prepareGameMessage(QDataStream &stream, Q_INT32 pl); + /** Main function to do player input */ + bool playerInput(QDataStream &msg,KPlayer *player); + /** Set the IO devices new */ + void recalcIO(); + /** Set the turn of the current player to true */ + void preparePlayerTurn(); + + +public slots: + void calcHint(); + + void slotPropertyChanged(KGamePropertyBase *,KGame *); + void slotPlayerPropertyChanged(KGamePropertyBase *,KPlayer *); + void moveDone(QCanvasItem *,int); + void slotMessageUpdate(int,Q_UINT32,Q_UINT32); + void slotPrepareTurn(QDataStream &stream,bool b,KGameIO *input,bool *eatevent); + void slotClientConnected(Q_UINT32,KGame *); + void slotProcessQuery(QDataStream &,KGameProcessIO *); + void slotProcessHint(QDataStream &,KGameProcessIO *); + void slotGameOver(int status, KPlayer * p, KGame * me); + void slotRepeatMove(); + void loadSettings(); + +signals: + /** + * emmitted if the game status changes to run + */ + void signalGameRun(); + /** + * Emmitted if the chat origin changes + */ + void signalChatChanged(Kwin4Player *); + /** + * emmitted after a sprite move ends + **/ + void signalMoveDone(int,int); + void signalNextPlayer(); + /** + * emmitted if the game ends + **/ + void GameOver(int,KPlayer *,KGame *); + +private: + Kwin4View *pView; + + KGamePropertyInt mLastColumn; // last x moved + KGamePropertyInt mLastColour; // last colour moved + + KGamePropertyInt mHistoryCnt; + KGamePropertyArray mField; // 42 pieces + Kwin4Player *yellowPlayer; + KGamePropertyInt mStartPlayer; // Player started game + KGamePropertyInt mAmzug; // Player's to move + KGamePropertyInt mMaxMove; // maximal move made in a game before undo + KGamePropertyInt mCurrentMove; // current move in the game + KGamePropertyArray mFieldFilled; // to what height is the column filled + KGamePropertyInt mLastHint; + KGamePropertyInt mScore; // Computer score + KGamePropertyArray mHistory; // to what height is the column filled + + KGameIO::IOMode mPlayedBy[NOOFPLAYER]; + KGameProcessIO *mHintProcess; + +}; + +#endif // KWIN4DOC_H + diff --git a/kwin4/kwin4/kwin4player.cpp b/kwin4/kwin4/kwin4player.cpp new file mode 100644 index 00000000..65a7947c --- /dev/null +++ b/kwin4/kwin4/kwin4player.cpp @@ -0,0 +1,160 @@ +/*************************************************************************** + KWin4Player + ------------------- + begin : August 2001 + copyright : (C) |1995-2001 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + + +// include files for KDE +#include +#include +#include + +#include "kwin4player.h" +#include "statuswidget.h" + +Kwin4Player::Kwin4Player() : KPlayer(), sWidget(0) +{ + int id; + id=mWin.registerData(dataHandler(),KGamePropertyBase::PolicyDirty,QString("mWin")); + id=mRemis.registerData(dataHandler(),KGamePropertyBase::PolicyDirty,QString("mRemis")); + id=mLost.registerData(dataHandler(),KGamePropertyBase::PolicyDirty,QString("mLost")); + id=mBrk.registerData(dataHandler(),KGamePropertyBase::PolicyDirty,QString("mBrk")); + id=mAllWin.registerData(dataHandler(),KGamePropertyBase::PolicyDirty,QString("mAllWin")); + id=mAllRemis.registerData(dataHandler(),KGamePropertyBase::PolicyDirty,QString("mAllRemis")); + id=mAllLost.registerData(dataHandler(),KGamePropertyBase::PolicyDirty,QString("mAllLost")); + id=mAllBrk.registerData(dataHandler(),KGamePropertyBase::PolicyDirty,QString("mAllBrk")); + + dataHandler()->setPolicy(KGamePropertyBase::PolicyDirty,false); + + resetStats(); + connect(this,SIGNAL(signalPropertyChanged(KGamePropertyBase *,KPlayer *)), + this,SLOT(slotPlayerPropertyChanged(KGamePropertyBase *,KPlayer *))); +} + +#include +#include + +void Kwin4Player::slotPlayerPropertyChanged(KGamePropertyBase *prop, KPlayer * /*player*/) +{ + if (!sWidget) return ; + if (!isActive()) return ; + if (prop->id()==KGamePropertyBase::IdName) + { + if(userId()) + sWidget->p1_name->setText(name()); + else + sWidget->p2_name->setText(name()); + } + else if (prop->id()==mWin.id()) + { + if(userId()){ + sWidget->p1_w->display(mWin); + sWidget->p1_n->display(mWin+mRemis+mLost); + } + else{ + sWidget->p2_w->display(mWin); + sWidget->p2_n->display(mWin+mRemis+mLost); + } + } + else if (prop->id()==mRemis.id()) + { + if(userId()){ + sWidget->p1_d->display(mRemis); + sWidget->p1_n->display(mWin+mRemis+mLost); + } + else{ + sWidget->p2_d->display(mRemis); + sWidget->p2_n->display(mWin+mRemis+mLost); + } + } + else if (prop->id()==mLost.id()) + { + if(userId()){ + sWidget->p1_l->display(mLost); + sWidget->p1_n->display(mWin+mRemis+mLost); + } + else{ + sWidget->p2_l->display(mLost); + sWidget->p2_n->display(mWin+mRemis+mLost); + } + } + else if (prop->id()==mBrk.id()) + { + if(userId()) + sWidget->p1_b->display(mBrk); + else + sWidget->p2_b->display(mBrk); + } +} + +void Kwin4Player::readConfig(KConfig *config) +{ + mAllWin.setValue(config->readNumEntry("win",0)); + mAllRemis.setValue(config->readNumEntry("remis",0)); + mAllLost.setValue(config->readNumEntry("lost",0)); + mAllBrk.setValue(config->readNumEntry("brk",0)); +} + +void Kwin4Player::writeConfig(KConfig *config) +{ + config->writeEntry("win",mAllWin.value()); + config->writeEntry("remis",mAllRemis.value()); + config->writeEntry("lost",mAllLost.value()); + config->writeEntry("brk",mAllBrk.value()); +} + +void Kwin4Player::incWin() +{ + mWin.setValue(mWin.value()+1); + mAllWin.setValue(mAllWin.value()+1); +} + +void Kwin4Player::incLost() +{ + mLost.setValue(mLost.value()+1); + mAllLost.setValue(mAllLost.value()+1); +} + +void Kwin4Player::incRemis() +{ + mRemis.setValue(mRemis.value()+1); + mAllRemis.setValue(mAllRemis.value()+1); +} + +void Kwin4Player::incBrk() +{ + mBrk.setValue(mBrk.value()+1); + mAllBrk.setValue(mAllBrk.value()+1); +} + +void Kwin4Player::resetStats(bool all) +{ + mWin=0; + mLost=0; + mBrk=0; + mRemis=0; + if (all) + { + mAllWin=0; + mAllLost=0; + mAllBrk=0; + mAllRemis=0; + } +} + +#include "kwin4player.moc" + + + diff --git a/kwin4/kwin4/kwin4player.h b/kwin4/kwin4/kwin4player.h new file mode 100644 index 00000000..d659b3f5 --- /dev/null +++ b/kwin4/kwin4/kwin4player.h @@ -0,0 +1,72 @@ +/*************************************************************************** + Kwin4Player + ------------------- + begin : August 2001 + copyright : (C) 1995-2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KWIN4PLAYER_H +#define KWIN4PLAYER_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +class StatusWidget; +class KConfig; + +class Kwin4Player : public KPlayer +{ + Q_OBJECT + + signals: + + public: + Kwin4Player(); + int rtti() const {return 1;} + void setWidget(StatusWidget *w) {sWidget=w;} + void readConfig(KConfig *config); + void writeConfig(KConfig *config); + void incWin(); + void incRemis(); + void incLost(); + void incBrk(); + int win() {return mAllWin.value();} + int lost() {return mAllLost.value();} + int brk() {return mAllBrk.value();} + int remis() {return mAllRemis.value();} + void resetStats(bool all=true); + + protected slots: + void slotPlayerPropertyChanged(KGamePropertyBase *prop,KPlayer *player); + + private: + // One session + KGamePropertyInt mWin; + KGamePropertyInt mRemis; + KGamePropertyInt mLost; + KGamePropertyInt mBrk; + + // all time + KGamePropertyInt mAllWin; + KGamePropertyInt mAllRemis; + KGamePropertyInt mAllLost; + KGamePropertyInt mAllBrk; + + StatusWidget *sWidget; +}; + +#endif // KWIN4PLAYER_H + diff --git a/kwin4/kwin4/kwin4proc.cpp b/kwin4/kwin4/kwin4proc.cpp new file mode 100644 index 00000000..604600ec --- /dev/null +++ b/kwin4/kwin4/kwin4proc.cpp @@ -0,0 +1,432 @@ +/*************************************************************************** + kproc4.cpp - description + ------------------- + begin : Sun Apr 9 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kwin4proc.h" + +#include +#include +#include +#include +#include +#include + +#define MIN_TIME 1 // sec + +#define NOOFPLAYER 2/* Zwei Spieler */ +#define MAXANZAHL 6 /* Maximal 6 Steine pro Reihe */ +#define WIN4 4 /* 4er Reihe hat gewonnen */ +#define MAXZUG 42 /* Soviele Zuege moeglich */ +#define FELD_OFF 10 +#define LOWERT -999999999L +#define SIEG_WERT 9999999L + + +#define START_REK 1 // (0) 1:Nur Stellungsbewertung bei Level 1 + // 0:Level 1 schon eine Rekursion + +KComputer::KComputer() : QObject(0,0) +{ + InitField(); + + const char *s1="7777776666666123456654321123456654321"; + const char *s2="0000000000000000000123456000000123456"; + + unsigned int i; + for (i=0;i\nKComputer::Computer\n"); +} + +void KComputer::slotInit(QDataStream &in,int id) +{ + fprintf(stderr,"----------------->\nKComputer::slotInit\nid:%d\n",id); + /* + QByteArray buffer; + QDataStream out(buffer,IO_WriteOnly); + int msgid=KGameMessage::IdProcessQuery; + out << (int)1; + proc.sendSystemMessage(out,msgid,0); + */ +} + +void KComputer::slotTurn(QDataStream &in,bool turn) +{ + QByteArray buffer; + QDataStream out(buffer,IO_WriteOnly); + fprintf(stderr,"----------------->\nKComputer::slotTurn\nturn:%d\n",turn); + if (turn) + { + // Create a move + long value=think(in,out,false); + int id=KGameMessage::IdPlayerInput; + proc.sendSystemMessage(out,id,0); + sendValue(value); + } +} + +void KComputer::sendValue(long value) +{ + Q_INT8 cid=1; // notifies our KGameIO that this is a value message + int id=KGameMessage::IdProcessQuery; + QByteArray buffer; + QDataStream out(buffer,IO_WriteOnly); + out << cid << value; + proc.sendSystemMessage(out,id,0); +} + +long KComputer::think(QDataStream &in,QDataStream &out,bool hint) +{ + Q_INT32 pl; + Q_INT32 move; + Q_INT32 tmp; + in >> pl ; + in >> tmp; + aktzug=tmp; + in >> tmp; + // We need all the +1 because the main programm has different defines + // for the colours. And chaning it here seems not to work.... + amZug=(Farbe)(tmp+1); + in >> tmp; + beginner=(Farbe)(tmp+1); + in >> tmp; + second=(Farbe)(tmp+1); + in >> tmp; + mymaxreklev=tmp; + fprintf(stderr,"think: pl=%d, aktzug=%d amzug=%d begin=%d second=%d level=%d\n", + pl,aktzug,amZug,beginner,second,mymaxreklev); + + InitField(); + + // Field as 42 Q_INT8's + int i,j; + for (i=0;i<=SIZE_Y;i++) + { + for (j=0;j<=SIZE_X;j++) + { + Q_INT8 col; + in >> col; + Farbe colour; + if (col<2) colour=(Farbe)(col+1); + else colour=(Farbe)col; + DoMove(j,colour,feldmatrix,anzahlmatrix); + } + } + + for (i=0;i<=SIZE_Y;i++) + { + char tstr[1024]; + tstr[0]=0; + for (j=0;j<=SIZE_X;j++) + { + sprintf(tstr+strlen(tstr),"%02d ", feldmatrix[i][j]); + } + fprintf(stderr,"%s\n",tstr); + } + + in >> tmp; + fprintf(stderr,"CHECKSUM=%ld should be 421256\n",(long)tmp); + + time_t timea,timee; + timea=time(0); + + int mymove; + mymove= GetCompMove(); + + fprintf(stderr,"Computermove to %d value=%ld\n",mymove,aktwert); + + timee=time(0); + // Sleep a minimum amount to slow down moves + if (timee-timea < MIN_TIME) sleep((MIN_TIME-(timee-timea))); + + + move=mymove; + if (hint) + { + out << pl << move; + } + else + { + out << pl << move; + } + return aktwert; +} + +void KComputer::slotCommand(QDataStream &in,int msgid,int receiver,int sender) +{ + fprintf(stderr,"----------------->\nKComputer::slotCommand\nMsgid:%d\n",msgid); + QByteArray buffer; + QDataStream out(buffer,IO_WriteOnly); + switch(msgid) + { + case 2: // hint + { + Q_INT8 cid=2; + Q_INT32 pl=0; + Q_INT32 move=3; + out << cid; + long value=think(in,out,true); + out << value; + int id=KGameMessage::IdProcessQuery; + proc.sendSystemMessage(out,id,0); + } + break; + default: + fprintf(stderr,"KComputer:: unknown command Msgid:%d\n",msgid); + } +} + +/** + * Computer Routinen + */ +int KComputer::GetCompMove() +{ + int cmove; + long cmax,wert; + int x; + FARBE lfeld[SIZE_Y_ALL+1][SIZE_X+1]; + char lanzahl[SIZE_Y_ALL+1]; + Farbe farbe; + + + farbe=amZug; + cmove=-1; /* Kein Zug */ + cmax=LOWERT; + for (x=0;x<=SIZE_X;x++) + { + if (anzahlmatrix[6+x]>=MAXANZAHL) continue; + memcpy(lanzahl,anzahlmatrix,sizeof(lanzahl)); + memcpy(lfeld,feldmatrix,sizeof(lfeld)); + + DoMove(x,farbe,lfeld,lanzahl); + wert=Wertung(farbe,lfeld,lanzahl,START_REK,aktzug+1); + + if (wert>=cmax) + { + cmax=wert; + cmove=x; + if (cmax>=SIEG_WERT) break; + } + }/*next x*/ + aktwert=cmax; + amZug=farbe; // Wertung changes amZug! +return cmove; +} + +long KComputer::Wertung(Farbe farbe,FARBE feld[][SIZE_X+1],char anzahl[],int reklev,int zug) +{ + static long gaus[]={10,50,300,500,300,50,10}; + FARBE lfeld[SIZE_Y_ALL+1][SIZE_X+1]; + char lanzahl[SIZE_Y_ALL+1]; + long max,wert; + int x; + Farbe winner; + + winner=GameOver(feld,anzahl); + if (winner!=Niemand) + { + if (winner==farbe) return(SIEG_WERT); + else return(-SIEG_WERT); + } + if (zug>=MAXZUG) return(0); /* Remis */ + if (reklev>=mymaxreklev) return Bewertung(farbe,feld); + + + farbe=SwitchPlayer(farbe); + max=LOWERT; + for (x=0;x<=SIZE_X;x++) + { + if (anzahl[6+x]>=MAXANZAHL) continue; + memcpy(lfeld,feld,sizeof(lfeld)); + memcpy(lanzahl,anzahl,sizeof(lanzahl)); + DoMove(x,farbe,lfeld,lanzahl); + wert=Wertung(farbe,lfeld,lanzahl,reklev+1,zug+1)+gaus[x]; + if (wert>=max) + { + max=wert; + if (max>=SIEG_WERT) break; + } + }/*next x*/ + return(-max); +}/*end wertung*/ + +long KComputer::Bewertung(Farbe farbe,FARBE feld[][SIZE_X+1]) +{ +/* Abstand: 0 1 2 3 4 5 */ +static long myWERT[]={2200,600, 300, 75, 20, 0}; +//static long myWERT[]={0,0,0,0,0,0}; +/* Wieviele von Farbe: 0 1 2 3 4 */ +static long steinWERT[4][5]= +{ + { 0, 500L, 40000L,200000L,SIEG_WERT}, // Leerfelder=0 + { 0, 500L, 8000L, 40000L,SIEG_WERT}, // =1 + { 0, 00L, 4000L, 25000L,SIEG_WERT}, // =2 + { 0, 00L, 2000L, 12500L,SIEG_WERT}, // =3 +}; + long gelb_wert,rot_wert,wert; + int cntcol,cnt; + Farbe color; + FARBE field; + int y,i,j; + gelb_wert=random(2500); + rot_wert=random(2500); + for (y=0;y<=SIZE_Y_ALL;y++) + { + if (lenofrow[y]3) cnt=3; + if (color==Rot) rot_wert+=(wert+steinWERT[cnt][cntcol]); + else if (color==Gelb) gelb_wert+=(wert+steinWERT[cnt][cntcol]); + }/*next i*/ + }/*next y*/ + if (farbe==Rot) wert=rot_wert-gelb_wert; + else wert=gelb_wert-rot_wert; +return(wert); +} + +Farbe KComputer::GameOver(FARBE feld[][SIZE_X+1],char anzahl[]) +{ + Farbe thiscolor,field; + int x,y,cnt; + for (y=0;y<=SIZE_Y_ALL;y++) + { + if (anzahl[y]=WIN4)&&( (thiscolor==Gelb)||(thiscolor==Rot) ) ) return(thiscolor); + }/*next x */ + }/*next y*/ + return(Niemand); +} + +Farbe KComputer::SwitchPlayer(Farbe m_amZug) +{ + if (m_amZug==Niemand) + m_amZug=amZug; + if (m_amZug==Rot) + amZug=Gelb; + else if (m_amZug==Gelb) + amZug=Rot; + else amZug=beginner; + return amZug; +} + +void KComputer::DoMove(char move,Farbe farbe,FARBE feld[][SIZE_X+1],char anzahl[]) +{ + int x,i,y; + + if (farbe==Tip || farbe==Niemand) return ; // no real move + x=move; + y=anzahl[6+move]; + feld[y][x]=farbe; + + //if (farbe==Tip || farbe==Niemand) return ; // no real move + + feld[6+x][y]=farbe; + feld[13+x+y][x]=farbe; + feld[30+x-y][x]=farbe; + anzahl[y]++; + anzahl[6+x]++; + anzahl[13+x+y]++; + anzahl[30+x-y]++; + for (i=y+1;i<=SIZE_Y;i++) + { + feld[i][x]--; + feld[6+x][i]--; + feld[13+x+i][x]--; + feld[30+x-i][x]--; + } +} + +void KComputer::InitField() { + int x,y; + for (y=0;y<=SIZE_Y_ALL;y++) + anzahlmatrix[y]=0; + for (y=0;y<=SIZE_Y;y++) + { + for (x=0;x<=SIZE_X;x++) + { + feldmatrix[y][x]=(FARBE)(y+FELD_OFF); + feldmatrix[6+x][y]=(FARBE)(y+FELD_OFF); + feldmatrix[13+x+y][x]=(FARBE)(y+FELD_OFF); + feldmatrix[30+x-y][x]=(FARBE)(y+FELD_OFF); + } + }/* next y */ +} + +long KComputer::random(long max) +{ + long wert; + wert=proc.random()->getLong(max); + return wert; +} + +// Main startup +int main(int argc ,char * argv[]) +{ + // This is the computer player...it should do the calulation + // It doesn't do much here + fprintf(stderr,"Vor KComputer\n"); + fflush(stderr); + KComputer comp; + fprintf(stderr,"Vor exec\n"); + // And start the event loop + comp.proc.exec(argc,argv); + fprintf(stderr,"nach exec\n"); + return 1; +} +#include "kwin4proc.moc" diff --git a/kwin4/kwin4/kwin4proc.h b/kwin4/kwin4/kwin4proc.h new file mode 100644 index 00000000..b865b5cb --- /dev/null +++ b/kwin4/kwin4/kwin4proc.h @@ -0,0 +1,83 @@ +/*************************************************************************** + Kwin4 - Four in a Row for KDE + ------------------- + begin : March 2000 + copyright : (C) 1995-2001 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef _KWIN4PROC_H_ +#define _KWIN4PROC_H_ + +#include + +// TODO match up with what is in kwin4doc.h +typedef enum e_farbe {Niemand=0,Gelb=1,Rot=2,Tip=3,Rand=4} Farbe; +typedef char FARBE; + +#define SIZE_Y_ALL 36 +#define SIZE_X 6 +#define SIZE_Y 5 + +class KComputer : public QObject +{ + +Q_OBJECT + +public: + KComputer(); + // The KGameProcess is the main program and event loop + KGameProcess proc; + +public slots: + void slotCommand(QDataStream &, int msgid,int receiver,int sender); + void slotInit(QDataStream &, int id); + void slotTurn(QDataStream &, bool turn); + +protected: + void sendValue(long value); + long random(long max); + long think(QDataStream &in,QDataStream &out,bool hint); + + // Old computer stuff + Farbe SwitchPlayer(Farbe amZug=Niemand); + Farbe GameOver(FARBE feld[][SIZE_X+1],char anzahl[]); + void DoMove(char move,Farbe farbe,FARBE feld[][SIZE_X+1],char anzahl[]); + int GetCompMove(); + long Wertung(Farbe farbe,FARBE feld[][SIZE_X+1],char anzahl[],int reklev,int zug); + long Bewertung(Farbe farbe,FARBE feld[][SIZE_X+1]); + void InitField(); + +private: + /* rows: 0-5 =6 : horiz(i:0-6) */ + /* 6-12 =7 : vert(i:0-5) */ + /* 13-24 =12: diag-45(i:...) */ + /* 25-36 =12: diag45(i:...) */ + + char lenofrow[38]; + char startofrow[38]; + + Farbe beginner,second; // Welche Farbe faengt an und zieht nach + + Farbe amZug; // wer ist am Zug + Farbe winner; // who won thee game + FARBE feldmatrix[SIZE_Y_ALL+1][SIZE_X+1]; + char anzahlmatrix[SIZE_Y_ALL+1]; + + int aktzug; // welcher Zug ist getade gemacht 0..42 + int mymaxreklev; // maximale Rekursion + long aktwert; // Stellungsbewertung + +}; + +#endif // _KWIN4PROC_H_ + diff --git a/kwin4/kwin4/kwin4ui.rc b/kwin4/kwin4/kwin4ui.rc new file mode 100644 index 00000000..1d31536a --- /dev/null +++ b/kwin4/kwin4/kwin4ui.rc @@ -0,0 +1,28 @@ + + + + + &Game + + + + + + + + + + + + + + + + +Main Toolbar + + + + + + diff --git a/kwin4/kwin4/kwin4view.cpp b/kwin4/kwin4/kwin4view.cpp new file mode 100644 index 00000000..20d83dee --- /dev/null +++ b/kwin4/kwin4/kwin4view.cpp @@ -0,0 +1,729 @@ +/*************************************************************************** + kwin4view.cpp - View of the kwin4 program + ------------------- + begin : Sun Mar 26 12:50:12 CEST 2000 + copyright : (C) |1995-2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kwin4view.h" + +#include +#include + +#include +#include +#include +#include + +#include "kwin4doc.h" +#include "scorewidget.h" +#include "statuswidget.h" +#include "kspritecache.h" + +#include +#include + +#define COL_STATUSLIGHT QColor(210,210,255) +#define COL_STATUSFIELD QColor(130,130,255) +#define COL_STATUSDARK QColor(0,0,65) + +#define COL_STATUSBORDER black +#define COL_PLAYER QColor(255,255,0) +#define COL_RED red +#define COL_YELLOW yellow + +class KIntroMove : public KSpriteMove +{ + public: + KIntroMove() : KSpriteMove() {mode=0;cnt=0;} + virtual bool spriteMove(double tx,double ty,KSprite *sp) + { + double sign=1.0; + if (!dir) sign=-1.0; + if (mode==0) + { + cnt++; + if (sp->x()<120.0) + { + sp->spriteMove(tx,ty); + return true; + } + else + { + cnt=0; + mode=1; + cx=sp->x(); + cy=sp->y()-sign*50; + } + } + if (mode==1) + { + if (cnt<360) + { + double x,y; + x=cx+50*cos((sign*90.0-sign*(double)cnt)/180.0*M_PI); + y=cy+50*sin((sign*90.0-sign*(double)cnt)/180.0*M_PI); + sp->move(x,y); + cnt+=5; + } + else + { + cnt=0; + mode=2; + } + } + if (mode==2) + { + return sp->spriteMove(tx,ty); + } + + return true; + } + + void setDir(bool d) {dir=d;} + +private: + + bool dir; + int mode; + int cnt; + double cx,cy; + +}; + +Kwin4View::Kwin4View(Kwin4Doc *theDoc, QWidget *parent, const char *name) + : QCanvasView(0,parent, name), doc(theDoc) +{ + mLastArrow=-1; + + + // localise data file + QString file="kwin4/grafix/default/grafix.rc"; + QString mGrafix=kapp->dirs()->findResourceDir("data",file); + if (mGrafix.isNull()) + mGrafix="grafix/default/"; + else + mGrafix+="kwin4/grafix/default/"; + if (global_debug>3) + kdDebug(12010) << "Localised grafix dir " << mGrafix << endl; + + // Allow overriding of the grafix directory + // This is a cheap and dirty way for theming + kapp->config()->setGroup("Themes"); + mGrafix = kapp->config()->readPathEntry("grafixdir", mGrafix); + + + setVScrollBarMode(AlwaysOff); + setHScrollBarMode(AlwaysOff); + + //setBackgroundMode(PaletteBase); + setBackgroundColor(QColor(0,0,128)); + + mCanvas=new QCanvas(parent); + mCanvas->resize(parent->width(),parent->height()); + mCanvas->setDoubleBuffering(true); + mCanvas->setBackgroundColor(QColor(0,0,128)); + setCanvas(mCanvas); + + mCache=new KSpriteCache(mGrafix,this); + mCache->setCanvas(mCanvas); + KConfig *config=mCache->config(); + + QPoint pnt; + config->setGroup("game"); + + pnt=config->readPointEntry("scorewidget"); + mScoreWidget=new ScoreWidget(viewport()); + addChild(mScoreWidget); + mScoreWidget->move(pnt); + + pnt=config->readPointEntry("statuswidget"); + mStatusWidget=new StatusWidget(this); + mStatusWidget->move(pnt); + QPalette pal; + pal.setColor(QColorGroup::Light, COL_STATUSLIGHT); + pal.setColor(QColorGroup::Mid, COL_STATUSFIELD); + pal.setColor(QColorGroup::Dark, COL_STATUSDARK); + mStatusWidget->setPalette(pal); + mStatusWidget->setBackgroundColor(COL_STATUSFIELD); + + mStatusWidget->wins->setBackgroundColor(COL_STATUSFIELD); + mStatusWidget->draws->setBackgroundColor(COL_STATUSFIELD); + mStatusWidget->loses->setBackgroundColor(COL_STATUSFIELD); + mStatusWidget->num->setBackgroundColor(COL_STATUSFIELD); + mStatusWidget->bk->setBackgroundColor(COL_STATUSFIELD); + + mStatusWidget->p1_name->setBackgroundColor(COL_STATUSFIELD); + mStatusWidget->p1_w->setBackgroundColor(COL_STATUSFIELD); + mStatusWidget->p1_d->setBackgroundColor(COL_STATUSFIELD); + mStatusWidget->p1_l->setBackgroundColor(COL_STATUSFIELD); + mStatusWidget->p1_n->setBackgroundColor(COL_STATUSFIELD); + mStatusWidget->p1_b->setBackgroundColor(COL_STATUSFIELD); + + mStatusWidget->p2_name->setBackgroundColor(COL_STATUSFIELD); + mStatusWidget->p2_w->setBackgroundColor(COL_STATUSFIELD); + mStatusWidget->p2_d->setBackgroundColor(COL_STATUSFIELD); + mStatusWidget->p2_l->setBackgroundColor(COL_STATUSFIELD); + mStatusWidget->p2_n->setBackgroundColor(COL_STATUSFIELD); + mStatusWidget->p2_b->setBackgroundColor(COL_STATUSFIELD); + + mScoreWidget->hide(); + mStatusWidget->hide(); + + move(0,0); + adjustSize(); + + initView(false); +} + +void Kwin4View::initView(bool deleteall) +{ + KSprite *sprite=0; + // mCanvas->setAdvancePeriod(period); + mCanvas->setAdvancePeriod(15); + + KConfig *config=mCache->config(); + config->setGroup("game"); + mSpreadX=config->readNumEntry("spread_x",0); + mSpreadY=config->readNumEntry("spread_y",0); + //kdDebug(12010) << "Spread : x=" << mSpreadX << " y=" << mSpreadY << endl; + + QPixmap *pixmap=loadPixmap("background.png"); + if (pixmap) + mCanvas->setBackgroundPixmap(*pixmap); + else + mCanvas->setBackgroundColor(QColor(0,0,128)); + delete pixmap; + + if (doc->gameStatus()==KGame::Intro) + { + mScoreWidget->hide(); + mStatusWidget->hide(); + drawIntro(deleteall); + } + else + { + // TODO in start functions to distinguish from intro + kdDebug(12010) << "Clearing board" <show(); + mStatusWidget->show(); + // Hide pieces in any case + for (int i=0;i<42;i++) + { + sprite=(KSprite *)(mCache->getItem("piece",i)); + if (sprite) + { + introMoveDone(sprite,0 ); + sprite->hide(); + } + } + hideIntro(); + } + + // Hide stars in any case + for (int i=0;i<8;i++) + { + sprite=(KSprite *)(mCache->getItem("star",i)); + if (sprite) sprite->hide(); + } + // Hide GameOver in any case + sprite=(KSprite *)(mCache->getItem("gameover",1)); + if (sprite) sprite->hide(); + + + // Hide hint in any case + setHint(0,0,false); + + // Clear error text + clearError(); +} + +QPixmap *Kwin4View::loadPixmap(QString name) +{ + if (!mCache) + return 0; + return mCache->loadPixmap(name); +} + +/** + * Called by the doc/app to signal the end of the game + */ +void Kwin4View::EndGame() +{ + KSprite *sprite; + sprite=(KSprite *)(mCache->getItem("gameover",1)); + KConfig *config=mCache->config(); + int dest=config->readNumEntry("destY",150); + int src=config->readNumEntry("y",0); + //kdDebug(12010) << "MOVING gameover to " << dest << endl; + + if (sprite) + { + sprite->show(); + sprite->setY(src); + sprite->moveTo(sprite->x(),dest); + } +} + +/** + * Draw Sprites + */ +void Kwin4View::drawStar(int x,int y,int no) +{ + int dx,dy; + y=5-y; + KSprite *piece=(KSprite *)(mCache->getItem("piece",0)); + if (piece) + { + dx=piece->width(); + dy=piece->height(); + } + else + { + dx=0; + dy=0; + } + + KSprite *sprite=(KSprite *)(mCache->getItem("star",no)); + //kdDebug(12010) << " setStar("<hide(); + sprite=(KSprite *)(mCache->getItem("win4about",1)); + if (sprite) sprite->hide(); + sprite=(KSprite *)(mCache->getItem("win4about",2)); + if (sprite) sprite->hide(); + + QCanvasText *text; + text=(QCanvasText *)(mCache->getItem("intro1",1)); + if (text) text->hide(); + text=(QCanvasText *)(mCache->getItem("intro2",1)); + if (text) text->hide(); + text=(QCanvasText *)(mCache->getItem("intro3",1)); + if (text) text->hide(); +} + +void Kwin4View::drawIntro(bool /*remove*/) +{ + KSprite *sprite=0; + // background + sprite=(KSprite *)(mCache->getItem("about",1)); + if (sprite) sprite->show(); + + sprite=(KSprite *)(mCache->getItem("win4about",1)); + if (sprite) sprite->show(); + sprite=(KSprite *)(mCache->getItem("win4about",2)); + if (sprite) + { + KConfig *config=mCache->config(); + double dest=config->readDoubleNumEntry("x2",250.0); + sprite->setX(dest); + sprite->show(); + } + + QCanvasText *text; + text=(QCanvasText *)(mCache->getItem("intro1",1)); + if (text) + { + text->setText(i18n("1. intro line, welcome to win4","Welcome")); + text->show(); + } + text=(QCanvasText *)(mCache->getItem("intro2",1)); + if (text) + { + text->setText(i18n("2. intro line, welcome to win4","to")); + text->show(); + } + text=(QCanvasText *)(mCache->getItem("intro3",1)); + if (text) + { + text->setText(i18n("3. intro line, welcome to win4","KWin4")); + text->show(); + } + // text + + // animation + for (int no=0;no<42;no++) + { + sprite=(KSprite *)(mCache->getItem("piece",no)); + if (sprite) + { + KIntroMove *move=new KIntroMove; + connect(sprite->createNotify(),SIGNAL(signalNotify(QCanvasItem *,int)), + this,SLOT(introMoveDone(QCanvasItem *,int))); + sprite->setMoveObject(move); + if (no%2==0) + { + sprite->move(0-20*no,0); + sprite->moveTo(150+2*no,105+4*no); + move->setDir(true); + sprite->setFrame((no/2)%2); + } + else + { + sprite->move(0-20*no,height()); + sprite->moveTo(340-2*(no-1),105+4*(no-1)); + move->setDir(false); + sprite->setFrame(((no-1)/2)%2); + } + // Increase the nz coord for consecutive peices + // to allow proper intro + // Carefule: The number must be more then the + // z coord of [empty] and less than [empty2] + sprite->setZ(sprite->z()+no/2); + // kdDebug(12010) << "Z("<z()<show(); + } + } +} + +/** + * received after the movment of an intro sprite is finished + **/ +void Kwin4View::introMoveDone(QCanvasItem *item,int ) +{ + KSprite *sprite=(KSprite *)item; + sprite->deleteNotify(); + KIntroMove *move=(KIntroMove *)sprite->moveObject(); + if (move) + { + delete move; + sprite->setMoveObject(0); + } +} + +void Kwin4View::drawBoard(bool remove) +{ + KSprite *sprite=0; + KSprite *board=0; + int x,y; + + // Board + // TODO: Without number as it is unique item + board=(KSprite *)(mCache->getItem("board",1)); + if (board) + { + if (remove) board->hide(); + else if (!board->isVisible()) board->show(); + mBoardX=(int)(board->x()); + mBoardY=(int)(board->y()); + } + else + { + mBoardX=0; + mBoardY=0; + } + //kdDebug(12010) << "Board X=" << mBoardX << " y="<getItem("arrow",x)); + if (sprite) + { + sprite->setFrame(0); + sprite->setX(x*(sprite->width()+mSpreadX)+mBoardX); + if (remove) sprite->hide(); + else if (!sprite->isVisible()) sprite->show(); + } + }/* end arrows */ + + // Field + for (y=5;y>=0;y--) + { + for (x=0;x<7;x++) + { + // Lower layer + sprite=(KSprite *)(mCache->getItem("empty2",x+7*y)); + if (sprite) + { + sprite->move(x*(sprite->width()+mSpreadX)+mBoardX, + y*(sprite->height())+mBoardY); + if (remove) sprite->hide(); + else if (!sprite->isVisible()) sprite->show(); + } + // upper layer + sprite=(KSprite *)(mCache->getItem("empty",x+7*y)); + if (sprite) + { + sprite->move(x*(sprite->width()+mSpreadX)+mBoardX, + y*(sprite->height())+mBoardY); + if (remove) sprite->hide(); + else if (!sprite->isVisible()) sprite->show(); + } + } + }/* end field */ +} + +void Kwin4View::setSprite(int no, int x, int col, bool enable) +{ + KSprite *sprite; + sprite=(KSprite *)(mCache->getItem("piece",no)); + if (sprite) sprite->setVisible(enable); + setArrow(x,col); +} + +void Kwin4View::setHint(int x,int y,bool enabled) +{ + KSprite *sprite; + sprite=(KSprite *)(mCache->getItem("hint",1)); + y=5-y; + if (sprite) + { + if (enabled) + { + sprite->move(x*(sprite->width()+mSpreadX)+mBoardX, + y*(sprite->height()+mSpreadY)+mBoardY); + } + sprite->setVisible(enabled); + } +} + +void Kwin4View::setPiece(int x,int y,int color,int no,bool animation) +{ + KSprite *sprite=0; + + y=5-y; + + sprite=(KSprite *)(mCache->getItem("piece",no)); + + //kdDebug(12010) << " setPiece("<setFrame(0); + + sprite=(KSprite *)(mCache->getItem("arrow",x)); + + //kdDebug(12010) << " setArrow("<getItem(m,1)); + if (text) + { + text->setText(ms); + text->show(); + } + return true; +} + +/** + * This slot is called when a key event is received. It then prduces a + * valid move for the game. + **/ +// This is analogous to the mouse event only it is called when a key is +// pressed +void Kwin4View::slotKeyInput(KGameIO *input,QDataStream &stream,QKeyEvent *e,bool *eatevent) +{ + // Ignore non running + if (!doc->isRunning()) + return; + // kdDebug(12010) << "KEY EVENT" << e->ascii() << endl; + + // Ignore non key press + if (e->type() != QEvent::KeyPress) return ; + + // Our player + KPlayer *player=input->player(); + if (!player->myTurn()) + { + *eatevent=wrongPlayer(player,KGameIO::KeyIO); + return; + } + + int code=e->ascii(); + if (code<'1' || code>'7') + { + //kdDebug(12010) << "Key not supported\n"; + return ; + } + + // Create a move + Q_INT32 move,pl; + move=code-'1'; + pl=player->userId(); + stream << pl << move; + *eatevent=true; +} + +/** + * This slot is called when a mouse key is pressed. As the mouse is used as + * input for all players + * this slot is called to generate a player move out of a mouse input, i.e. + * it converts a QMouseEvent into a move for the game. We do here some + * simple nonsense and use the position of the mouse to generate + * moves containing the keycodes + */ +void Kwin4View::slotMouseInput(KGameIO *input,QDataStream &stream,QMouseEvent *mouse,bool *eatevent) +{ + // Only react to key pressed not released + if (mouse->type() != QEvent::MouseButtonPress ) return ; + if (!doc->isRunning()) + return; + + // Our player + KPlayer *player=input->player(); + if (!player->myTurn()) + { + *eatevent=wrongPlayer(player,KGameIO::MouseIO); + return; + } + + if (mouse->button()!=LeftButton) return ; + //if (!doc->IsRunning()) return ; + + QPoint point; + int x,y; + + point=mouse->pos() - QPoint(15,40) - QPoint(20,20); + if (point.x()<0) return ; + + x=point.x()/FIELD_SPACING; + y=point.y()/FIELD_SPACING; + + if (y>=FIELD_SIZE_Y) return ; + if (x<0 || x>=FIELD_SIZE_X) return; + + // Create a move + Q_INT32 move,pl; + move=x; + pl=player->userId(); + stream << pl << move; + *eatevent=true; + // kdDebug(12010) << "Mouse input done..eatevent=true" << endl; +} + +/** + * Hide all the error sprites + */ +void Kwin4View::clearError() +{ + QCanvasText *text; + + text=(QCanvasText *)(mCache->getItem("text1",1)); + if (text) text->hide(); + text=(QCanvasText *)(mCache->getItem("text2",1)); + if (text) text->hide(); + text=(QCanvasText *)(mCache->getItem("text3",1)); + if (text) text->hide(); + text=(QCanvasText *)(mCache->getItem("text4",1)); + if (text) text->hide(); +} + +void Kwin4View::resizeEvent(QResizeEvent *e) +{ + if (mCanvas) mCanvas->resize(e->size().width(),e->size().height()); +} + +#include "kwin4view.moc" diff --git a/kwin4/kwin4/kwin4view.h b/kwin4/kwin4/kwin4view.h new file mode 100644 index 00000000..48b60706 --- /dev/null +++ b/kwin4/kwin4/kwin4view.h @@ -0,0 +1,82 @@ +/*************************************************************************** + Kwin4 - Four in a Row for KDE + ------------------- + begin : March 2000 + copyright : (C) 1995-2001 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KWIN4VIEW_H +#define KWIN4VIEW_H + +#include +#include + +class Kwin4Doc; +class ScoreWidget; +class StatusWidget; +class KSpriteCache; + +/** + * The Kwin4View class provides the view widget for the Kwin4App instance. + */ +class Kwin4View : public QCanvasView +{ +Q_OBJECT + +public: + Kwin4View(Kwin4Doc *theDoc, QWidget *parent = 0, const char *name=0); + + void initView(bool deleteall=true); + void drawBoard(bool remove=false); + void drawIntro(bool remove=false); + void hideIntro(); + void drawStar(int x,int y,int no); + void setArrow(int x,int color); + void setPiece(int x,int y,int color,int no,bool animation=true); + void setHint(int x,int y,bool enabled); + ScoreWidget *scoreWidget() {return mScoreWidget;} + StatusWidget *statusWidget() {return mStatusWidget;} + void setSprite(int no,int x, int col, bool enable); + void clearError(); + void EndGame(); + +public slots: + void slotMouseInput(KGameIO *input,QDataStream &stream,QMouseEvent *e,bool *eatevent); + void slotKeyInput(KGameIO *input,QDataStream &stream,QKeyEvent *e,bool *eatevent); + void introMoveDone(QCanvasItem *item,int mode); + +protected: + QPixmap *loadPixmap(QString name); + void resizeEvent(QResizeEvent *e); + bool wrongPlayer(KPlayer *player,KGameIO::IOMode io); + +private: + Kwin4Doc *doc; + QCanvas *mCanvas; // our drawing canvas + KSpriteCache *mCache; // The sprite cache + QString mGrafix; // grafix dir + + int mLastArrow; // last drawn arrow + int mLastX; // last piece + int mLastY; + int mSpreadX; // spread x,y board pieces + int mSpreadY; + int mBoardX; // board offset + int mBoardY; + + ScoreWidget *mScoreWidget; // score widget + StatusWidget *mStatusWidget; // score widget +}; + +#endif // KWIN4VIEW_H + diff --git a/kwin4/kwin4/main.cpp b/kwin4/kwin4/main.cpp new file mode 100644 index 00000000..8f0ddaa5 --- /dev/null +++ b/kwin4/kwin4/main.cpp @@ -0,0 +1,75 @@ +/*************************************************************************** + Kwin4 - Four in a Row for KDE + ------------------- + begin : March 2000 + copyright : (C) 1995-2001 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include + +#include "kwin4.h" + +#define KWIN4_VERSION "v1.10" + +static KCmdLineOptions options[] = +{ + { "d", 0, 0}, + { "debug ", I18N_NOOP("Enter debug level"), 0 }, + KCmdLineLastOption +}; + +int global_debug; + +int main(int argc, char *argv[]) +{ + global_debug=0; + KAboutData aboutData( "kwin4", I18N_NOOP("KWin4"), + KWIN4_VERSION, + I18N_NOOP("KWin4: Two player network game"), + KAboutData::License_GPL, + "(c) 1995-2000, Martin Heni"); + aboutData.addAuthor("Martin Heni",0, "martin@heni-online.de"); + aboutData.addCredit("Laura", I18N_NOOP("Beta testing"), 0); + aboutData.addAuthor("Benjamin Meyer", I18N_NOOP("Code Improvements"), 0); + KCmdLineArgs::init( argc, argv, &aboutData ); + KCmdLineArgs::addCmdLineOptions( options ); // Add our own options. + + /* command line handling */ + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + if (args->isSet("debug")) + { + global_debug=QString(args->getOption("debug")).toInt(); + kdDebug(12010) << "Debug level set to " << global_debug << endl; + } + args->clear(); + KApplication app(argc, argv); + KGlobal::locale()->insertCatalogue("libkdegames"); + + if (app.isRestored()) + { + RESTORE(Kwin4App); + } + else + { + Kwin4App *kwin4 = new Kwin4App(); + app.setMainWidget(kwin4); + kwin4->show(); + } + + return app.exec(); +} + diff --git a/kwin4/kwin4/prefs.kcfgc b/kwin4/kwin4/prefs.kcfgc new file mode 100644 index 00000000..6abe752a --- /dev/null +++ b/kwin4/kwin4/prefs.kcfgc @@ -0,0 +1,7 @@ +# Code generation options for kconfig_compiler +File=kwin4.kcfg +#IncludeFiles=defines.h +ClassName=Prefs +Singleton=true +#CustomAdditions=true +#Mutators=Zoom diff --git a/kwin4/kwin4/scorewidget.cpp b/kwin4/kwin4/scorewidget.cpp new file mode 100644 index 00000000..7d0586fb --- /dev/null +++ b/kwin4/kwin4/scorewidget.cpp @@ -0,0 +1,193 @@ +/*************************************************************************** + kwin4 program + ------------------- + begin : Sun Mar 26 12:50:12 CEST 2000 + copyright : (C) |1995-2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "scorewidget.h" + +#include "prefs.h" + +#include +#include +#include +#include +#include +#include + +#define COL_STATUSBORDER black +#define COL_STATUSFIELD QColor(130,130,255) +#define COL_STATUSDARK QColor(0,0,65) +#define COL_STATUSLIGHT QColor(210,210,255) + +ScoreWidget::ScoreWidget( QWidget* parent, const char* name, WFlags fl ) + : QFrame( parent, name, fl ) +{ + setFrameStyle( QFrame::Box | QFrame::Raised ); + setLineWidth( 2 ); + setMidLineWidth( 4 ); + + setBackgroundColor( COL_STATUSFIELD ); + + resize( 255, 187 ); + int row=0; + + setCaption( i18n( "Form1" ) ); + //LayoutB = new QGridLayout( this,4,3,15,5 ); + LayoutB = new QGridLayout( this); + LayoutB->setSpacing( 3 ); + LayoutB->setMargin( 15 ); + + TextLabel7 = new QLabel( this, "TextLabel7" ); + setPlayer("-----",0); + TextLabel7->setBackgroundColor( COL_STATUSFIELD ); + TextLabel7->setAlignment(Qt::AlignHCenter); + LayoutB->addMultiCellWidget( TextLabel7, row, row,0,2 ); + row++; + + TextLabel8 = new QLabel( this, "TextLabel8" ); + TextLabel8->setText( i18n( "vs" ) ); + TextLabel8->setBackgroundColor( COL_STATUSFIELD ); + TextLabel8->setAlignment(Qt::AlignHCenter); + LayoutB->addMultiCellWidget( TextLabel8, row, row,0,2 ); + row++; + + TextLabel9 = new QLabel( this, "TextLabel9" ); + setPlayer("-----",1); + // TextLabel9->setFrameShape(QFrame::Box ); + // TextLabel9->setLineWidth(5); + TextLabel9->setBackgroundColor( COL_STATUSFIELD ); + TextLabel9->setAlignment(Qt::AlignHCenter); + LayoutB->addMultiCellWidget( TextLabel9, row, row,0,2 ); + row++; + + QSpacerItem *Spacer2=new QSpacerItem(0,16,QSizePolicy::Preferred,QSizePolicy::Preferred); + LayoutB->addMultiCell( Spacer2, row, row,0,2 ); + row++; + + QSpacerItem *Spacer1=new QSpacerItem(25,0,QSizePolicy::Preferred,QSizePolicy::Preferred); + LayoutB->addMultiCell( Spacer1, row, row+2,1,1 ); + + TextLabel1 = new QLabel( this, "Level" ); + TextLabel1->setText( i18n( "Level" ) ); + TextLabel1->setBackgroundColor( COL_STATUSFIELD ); + LayoutB->addWidget( TextLabel1, row, 0 ); + + TextLabel4 = new QLabel( this, "L" ); + setLevel(Prefs::level()); + TextLabel4->setAlignment(Qt::AlignRight); + TextLabel4->setBackgroundColor( COL_STATUSFIELD ); + LayoutB->addWidget( TextLabel4, row, 2 ); + + row++; + + + TextLabel2 = new QLabel( this, "Move" ); + TextLabel2->setText( i18n("number of MOVE in game", "Move" ) ); + TextLabel2->setBackgroundColor( COL_STATUSFIELD ); + LayoutB->addWidget( TextLabel2, row, 0 ); + + TextLabel5 = new QLabel( this, "M" ); + setMove(0); + TextLabel5->setAlignment(Qt::AlignRight); + TextLabel5->setBackgroundColor( COL_STATUSFIELD ); + LayoutB->addWidget( TextLabel5, row, 2 ); + + row++; + + + TextLabel3 = new QLabel( this, "Chance" ); + TextLabel3->setText( i18n( "Chance" ) ); + TextLabel3->setBackgroundColor( COL_STATUSFIELD ); + LayoutB->addWidget( TextLabel3, row, 0 ); + + TextLabel6 = new QLabel( this, "C" ); + setChance(0); + TextLabel6->setAlignment(Qt::AlignRight); + TextLabel6->setBackgroundColor( COL_STATUSFIELD ); + LayoutB->addWidget( TextLabel6, row, 2 ); + + row++; + + QSpacerItem *Spacer3=new QSpacerItem(0,8,QSizePolicy::Preferred,QSizePolicy::Preferred); + LayoutB->addMultiCell( Spacer3, row, row,0,2 ); + row++; + + + + adjustSize(); +} + +void ScoreWidget::paintEvent( QPaintEvent * p) +{ + QPainter paint( this ); + paint.setClipRect(p->rect()); + Paint( &paint, p->rect() ); +} + +void ScoreWidget::Paint(QPainter *p,QRect /*cliprect*/) +{ + QPalette pal; + pal.setColor(QColorGroup::Light, COL_STATUSLIGHT); + pal.setColor(QColorGroup::Mid, COL_STATUSFIELD); + pal.setColor(QColorGroup::Dark, COL_STATUSDARK); + setPalette(pal); + drawFrame(p); +} + +void ScoreWidget::setMove(int i) +{ + TextLabel5->setText( QString("%1").arg(i)); +} + +void ScoreWidget::setLevel(int i) +{ + TextLabel4->setText( QString("%1").arg(i)); +} + +void ScoreWidget::setChance(int i) +{ + if (i==0) + TextLabel6->setText(" ----"); + else if (i>=999) + TextLabel6->setText(i18n("Winner")); + else if (i<=-999) + TextLabel6->setText(i18n("Loser")); + else + TextLabel6->setText(QString("%1").arg(i)); +} + +void ScoreWidget::setPlayer(QString s,int no) +{ + if (no==0) TextLabel7->setText(s); + else TextLabel9->setText(s); +} + +void ScoreWidget::setTurn(int i) +{ + if (i==0) + { + TextLabel7->setPaletteForegroundColor ( yellow); + TextLabel9->setPaletteForegroundColor (black); + } + else + { + TextLabel9->setPaletteForegroundColor ( red); + TextLabel7->setPaletteForegroundColor (black); + } + TextLabel7->update(); + TextLabel9->update(); +} + +#include "scorewidget.moc" diff --git a/kwin4/kwin4/scorewidget.h b/kwin4/kwin4/scorewidget.h new file mode 100644 index 00000000..82648b2c --- /dev/null +++ b/kwin4/kwin4/scorewidget.h @@ -0,0 +1,45 @@ +#ifndef _SCOREWIDGET_H +#define _SCOREWIDGET_H + +#include +class QVBoxLayout; +class QHBoxLayout; +class QGridLayout; +class QGroupBox; +class QLabel; + +class ScoreWidget : public QFrame +{ + Q_OBJECT + +public: + ScoreWidget( QWidget* parent = 0, const char* name = 0, WFlags fl = 0 ); + void setMove(int i); + void setLevel(int i); + void setChance(int i); + void setPlayer(QString s,int no); + void setTurn(int i); + +protected: + QGroupBox* GroupBox1; + QLabel* TextLabel4; + QLabel* TextLabel5; + QLabel* TextLabel6; + QLabel* TextLabel1; + QLabel* TextLabel2; + QLabel* TextLabel3; + QLabel* TextLabel7; + QLabel* TextLabel8; + QLabel* TextLabel9; + +protected: + void paintEvent( QPaintEvent * ); + void Paint(QPainter *p,QRect rect); + void drawBorder(QPainter *p,QRect rect,int offset,int width,int mode); + +protected: + QGridLayout* LayoutB; +}; + +#endif // _SCOREWIDGET_H + diff --git a/kwin4/kwin4/settings.ui b/kwin4/kwin4/settings.ui new file mode 100644 index 00000000..130ce4ee --- /dev/null +++ b/kwin4/kwin4/settings.ui @@ -0,0 +1,248 @@ + +Settings + + + Settings + + + + 0 + 0 + 411 + 319 + + + + + unnamed + + + + groupBox2 + + + Player Names + + + + unnamed + + + + textLabel3 + + + Player 1: + + + + + textLabel4 + + + Player 2: + + + + + kcfg_Name1 + + + + + kcfg_Name2 + + + + + + + groupBox1 + + + Computer Difficulty + + + + unnamed + + + + textLabel1 + + + Easy + + + + + textLabel2 + + + Hard + + + AlignVCenter|AlignRight + + + + + kcfg_level + + + 1 + + + 10 + + + 1 + + + 3 + + + Horizontal + + + Below + + + Change the strength of the computer player. + + + + + + + kcfg_Colour1 + + + Starting Player Color + + + + unnamed + + + + radioButton1 + + + Red + + + true + + + + + radioButton2 + + + Yellow + + + + + + + kcfg_Input2 + + + Red Plays With + + + + unnamed + + + + radioButton6 + + + Mouse + + + true + + + + + radioButton7 + + + Computer + + + + + radioButton8 + + + Keyboard + + + + + + + kcfg_Input1 + + + Yellow Plays With + + + + unnamed + + + + radioButton3 + + + Mouse + + + true + + + + + radioButton4 + + + Computer + + + + + radioButton5 + + + Keyboard + + + + + + + spacer1 + + + Vertical + + + Expanding + + + + 20 + 20 + + + + + + + diff --git a/kwin4/kwin4/statistics.ui b/kwin4/kwin4/statistics.ui new file mode 100644 index 00000000..d6d39c07 --- /dev/null +++ b/kwin4/kwin4/statistics.ui @@ -0,0 +1,249 @@ + +Statistics + + + Statistics + + + + 0 + 0 + 439 + 164 + + + + Statistics + + + + unnamed + + + + line1 + + + HLine + + + Sunken + + + Horizontal + + + + + spacer1 + + + Vertical + + + Expanding + + + + 20 + 16 + + + + + + p2_lost + + + + + p1_name + + + Player 1 + + + + + Name + + + Name + + + AlignCenter + + + + + won + + + Won + + + AlignCenter + + + + + p2_drawn + + + + + lost + + + Lost + + + AlignCenter + + + + + p2_aborted + + + + + p2_sum + + + + + sum + + + Sum + + + AlignCenter + + + + + aborted + + + Aborted + + + AlignCenter + + + + + layout1 + + + + unnamed + + + + pushButton1 + + + Clear All Statistics + + + + + spacer2 + + + Horizontal + + + Expanding + + + + 51 + 20 + + + + + + pushButton2 + + + &Close + + + + + + + p1_won + + + + + p2_name + + + Player 2 + + + + + p2_won + + + + + drawn + + + Drawn + + + AlignCenter + + + + + p1_drawn + + + + + p1_lost + + + + + p1_aborted + + + + + p1_sum + + + + + + + pushButton1 + clicked() + Statistics + reject() + + + pushButton2 + clicked() + Statistics + accept() + + + + pushButton2 + pushButton1 + + + diff --git a/kwin4/kwin4/statuswidget.ui b/kwin4/kwin4/statuswidget.ui new file mode 100644 index 00000000..0c9613b5 --- /dev/null +++ b/kwin4/kwin4/statuswidget.ui @@ -0,0 +1,263 @@ + +StatusWidget + + + StatusWidget + + + + 0 + 0 + 304 + 120 + + + + Box + + + Raised + + + 2 + + + 4 + + + + unnamed + + + + spacer1 + + + Vertical + + + Expanding + + + + 20 + 41 + + + + + + spacer2 + + + Horizontal + + + Expanding + + + + 90 + 20 + + + + + + wins + + + W + + + AlignCenter + + + + + draws + + + D + + + AlignCenter + + + + + loses + + + L + + + AlignCenter + + + + + num + + + No + + + AlignCenter + + + + + bk + + + Bk + + + AlignCenter + + + + + p1_name + + + Player 1 + + + + + p1_w + + + 0 + + + 2 + + + + + p1_d + + + 0 + + + 2 + + + + + p1_l + + + 0 + + + 2 + + + + + p1_n + + + 0 + + + 2 + + + + + p1_b + + + 0 + + + 2 + + + + + p2_d + + + 0 + + + 2 + + + + + p2_b + + + 0 + + + 2 + + + + + p2_l + + + 0 + + + 2 + + + + + p2_n + + + 0 + + + 2 + + + + + p2_w + + + 0 + + + 2 + + + + + p2_name + + + Player 2 + + + + + line1 + + + HLine + + + Raised + + + Horizontal + + + + + + diff --git a/libkdegames/Makefile.am b/libkdegames/Makefile.am new file mode 100644 index 00000000..89477c44 --- /dev/null +++ b/libkdegames/Makefile.am @@ -0,0 +1,23 @@ + +lib_LTLIBRARIES = libkdegames.la +libkdegames_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) -no-undefined -version-info 3:0:2 +libkdegames_la_LIBADD = highscore/libkhighscore.la kgame/libkgame.la kgame/dialogs/libkgamedialogs.la \ + $(LIB_KSYCOCA) $(LIB_KDNSSD) + +libkdegames_la_SOURCES = kcarddialog.cpp kstdgameaction.cpp \ + kgamemisc.cpp kchatbase.cpp kchat.cpp \ + kchatdialog.cpp kgameprogress.cpp \ + kcanvasrootpixmap.cpp kgamelcd.cpp +include_HEADERS = kgamemisc.h kcarddialog.h kstdgameaction.h \ + kchatbase.h kchat.h kchatdialog.h \ + kgameprogress.h kcanvasrootpixmap.h kgamelcd.h kgrid2d.h + +INCLUDES = $(all_includes) +METASOURCES = AUTO + +SUBDIRS = carddecks highscore kgame pics + +messages: + $(XGETTEXT) `find . -name \*.h -o -name \*.cpp -o -name \*.cc` -o $(podir)/libkdegames.pot + +include ../admin/Doxyfile.am diff --git a/libkdegames/README b/libkdegames/README new file mode 100644 index 00000000..fae0e7ee --- /dev/null +++ b/libkdegames/README @@ -0,0 +1,25 @@ +This directory contains the library for the kdegames packege. +It is a collection of functions used by some games or which +are useful for other games. + +Packagers note: it is recommended to put the directory "carddecks" into a separate +package, as not all games using libkdegames use the carddecks as well. + +Contents: + kcarddialog: Access to carddeck selection and administration + for cardgames + kstdgameaction: just like kstdaction this provides some default action for + kde games. games often use different entries than other apps + (like "game" instead of "file"), so use this if possible + kgamemisc: some static method i didn't know a good class name for. it + currently features "randomName()" which will just give you a random name from + a list (translators note: happy translating ;) i copied kde-common/accounts + for this so there are nearly 300 entries...) + kgame: this is a complete network/game handling library. it constists of a lot + of classes which are explained in the kgame docu (as soon as it is + written) +if you use libkdegames in your game please also add +KGlobal::locale()->insertCatalogue("libkdegames"); +to main() +This will add libkdegames.pot to your game and therefore all libkdegames +strings get translated. diff --git a/libkdegames/TODO b/libkdegames/TODO new file mode 100644 index 00000000..136c4fdd --- /dev/null +++ b/libkdegames/TODO @@ -0,0 +1,10 @@ +- 17.04.2001: change version number of the kdenonbeta one +- 10.07.2001: scaling has been added to KCard and KCardDialog. Find out if there + is a memory leak!! +- 10.07.2001: better layout for the resize box +- 10.07.2001: global decks/carddirs need extensive testing + +-These pertain to the highscore widget +The Ok button when adding a name should be enabled when the lineedit is filled with a QString of length() > 0 + +The lineedit by default should come up with the user name that is loged in. diff --git a/libkdegames/carddecks/Makefile.am b/libkdegames/carddecks/Makefile.am new file mode 100644 index 00000000..8effea93 --- /dev/null +++ b/libkdegames/carddecks/Makefile.am @@ -0,0 +1,24 @@ +DECKDIRS = cards-aisleriot cards-dondorf-whist-b cards-gdkcard-bonded cards-hard-a-port \ + cards-penguins cards-spaced cards-xskat-french cards-default decks cards-konqi-modern cards-warwick cards-xskat-german + +deckdir = $(kde_datadir)/carddecks + +uninstall-local: + rm -rf $(DESTDIR)$(deckdir) + +install-data-local: + @$(mkinstalldirs) $(DESTDIR)$(deckdir) + @for i in $(DECKDIRS); do \ + if test -d $(DESTDIR)$(deckdir)/$$i; then \ + echo "refreshing and removing $$i" ;\ + rm -rf $(DESTDIR)$(deckdir)/$$i;\ + fi ;\ + echo "installing $$i" ;\ + mkdir $(DESTDIR)$(deckdir)/$$i ;\ + files=`cd $(srcdir)/$$i && ls -1d *` ;\ + for f in $$files; do \ + if test -f $(srcdir)/$$i/$$f; then \ + $(INSTALL_DATA) $(srcdir)/$$i/$$f $(DESTDIR)$(deckdir)/$$i/$$f ;\ + fi \ + done \ + done diff --git a/libkdegames/carddecks/README b/libkdegames/carddecks/README new file mode 100644 index 00000000..9beb8a87 --- /dev/null +++ b/libkdegames/carddecks/README @@ -0,0 +1,12 @@ +This directory contains all carddecks for KDE cardgames. + +The backsides of the cards are stored as PNG images in +the directory decks as deck0.png, deck1.png, ... + +The cardsets with 52 cards are stored in the directories +cards1,cards2,cards3,... where each directory contains 52 +PNG images of the cards labels 1.png,2.png,...52.png + +Access to these cards works by using the kcardialog library +in libkdegames. See for documentation there. + diff --git a/libkdegames/carddecks/cards-aisleriot/1.png b/libkdegames/carddecks/cards-aisleriot/1.png new file mode 100644 index 00000000..d08a67d8 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/1.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/10.png b/libkdegames/carddecks/cards-aisleriot/10.png new file mode 100644 index 00000000..f9f00749 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/10.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/11.png b/libkdegames/carddecks/cards-aisleriot/11.png new file mode 100644 index 00000000..036807ec Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/11.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/12.png b/libkdegames/carddecks/cards-aisleriot/12.png new file mode 100644 index 00000000..2945ea93 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/12.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/13.png b/libkdegames/carddecks/cards-aisleriot/13.png new file mode 100644 index 00000000..86ac688c Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/13.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/14.png b/libkdegames/carddecks/cards-aisleriot/14.png new file mode 100644 index 00000000..e5b64e0f Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/14.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/15.png b/libkdegames/carddecks/cards-aisleriot/15.png new file mode 100644 index 00000000..3522414c Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/15.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/16.png b/libkdegames/carddecks/cards-aisleriot/16.png new file mode 100644 index 00000000..098a50f4 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/16.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/17.png b/libkdegames/carddecks/cards-aisleriot/17.png new file mode 100644 index 00000000..a4abb694 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/17.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/18.png b/libkdegames/carddecks/cards-aisleriot/18.png new file mode 100644 index 00000000..b231456b Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/18.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/19.png b/libkdegames/carddecks/cards-aisleriot/19.png new file mode 100644 index 00000000..d77ff9c8 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/19.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/2.png b/libkdegames/carddecks/cards-aisleriot/2.png new file mode 100644 index 00000000..5d610da7 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/2.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/20.png b/libkdegames/carddecks/cards-aisleriot/20.png new file mode 100644 index 00000000..bf9d586f Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/20.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/21.png b/libkdegames/carddecks/cards-aisleriot/21.png new file mode 100644 index 00000000..7ae9b457 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/21.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/22.png b/libkdegames/carddecks/cards-aisleriot/22.png new file mode 100644 index 00000000..86dee123 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/22.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/23.png b/libkdegames/carddecks/cards-aisleriot/23.png new file mode 100644 index 00000000..0f6aba24 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/23.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/24.png b/libkdegames/carddecks/cards-aisleriot/24.png new file mode 100644 index 00000000..5cf8d35c Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/24.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/25.png b/libkdegames/carddecks/cards-aisleriot/25.png new file mode 100644 index 00000000..a46de767 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/25.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/26.png b/libkdegames/carddecks/cards-aisleriot/26.png new file mode 100644 index 00000000..a21f883d Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/26.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/27.png b/libkdegames/carddecks/cards-aisleriot/27.png new file mode 100644 index 00000000..724146ca Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/27.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/28.png b/libkdegames/carddecks/cards-aisleriot/28.png new file mode 100644 index 00000000..f446432e Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/28.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/29.png b/libkdegames/carddecks/cards-aisleriot/29.png new file mode 100644 index 00000000..81060669 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/29.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/3.png b/libkdegames/carddecks/cards-aisleriot/3.png new file mode 100644 index 00000000..9bc9a017 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/3.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/30.png b/libkdegames/carddecks/cards-aisleriot/30.png new file mode 100644 index 00000000..d506216a Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/30.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/31.png b/libkdegames/carddecks/cards-aisleriot/31.png new file mode 100644 index 00000000..e6643631 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/31.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/32.png b/libkdegames/carddecks/cards-aisleriot/32.png new file mode 100644 index 00000000..225d9c3e Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/32.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/33.png b/libkdegames/carddecks/cards-aisleriot/33.png new file mode 100644 index 00000000..bb5bd4ec Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/33.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/34.png b/libkdegames/carddecks/cards-aisleriot/34.png new file mode 100644 index 00000000..a5a0f38d Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/34.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/35.png b/libkdegames/carddecks/cards-aisleriot/35.png new file mode 100644 index 00000000..72cca325 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/35.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/36.png b/libkdegames/carddecks/cards-aisleriot/36.png new file mode 100644 index 00000000..8c805d93 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/36.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/37.png b/libkdegames/carddecks/cards-aisleriot/37.png new file mode 100644 index 00000000..36746eee Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/37.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/38.png b/libkdegames/carddecks/cards-aisleriot/38.png new file mode 100644 index 00000000..58de88ae Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/38.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/39.png b/libkdegames/carddecks/cards-aisleriot/39.png new file mode 100644 index 00000000..879c0f78 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/39.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/4.png b/libkdegames/carddecks/cards-aisleriot/4.png new file mode 100644 index 00000000..6b723627 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/4.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/40.png b/libkdegames/carddecks/cards-aisleriot/40.png new file mode 100644 index 00000000..e20553ee Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/40.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/41.png b/libkdegames/carddecks/cards-aisleriot/41.png new file mode 100644 index 00000000..9a023831 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/41.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/42.png b/libkdegames/carddecks/cards-aisleriot/42.png new file mode 100644 index 00000000..363e44b2 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/42.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/43.png b/libkdegames/carddecks/cards-aisleriot/43.png new file mode 100644 index 00000000..e2e7b08c Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/43.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/44.png b/libkdegames/carddecks/cards-aisleriot/44.png new file mode 100644 index 00000000..aeb617f6 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/44.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/45.png b/libkdegames/carddecks/cards-aisleriot/45.png new file mode 100644 index 00000000..fee4dba0 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/45.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/46.png b/libkdegames/carddecks/cards-aisleriot/46.png new file mode 100644 index 00000000..f5948439 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/46.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/47.png b/libkdegames/carddecks/cards-aisleriot/47.png new file mode 100644 index 00000000..0079b371 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/47.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/48.png b/libkdegames/carddecks/cards-aisleriot/48.png new file mode 100644 index 00000000..62a487da Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/48.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/49.png b/libkdegames/carddecks/cards-aisleriot/49.png new file mode 100644 index 00000000..87ede197 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/49.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/5.png b/libkdegames/carddecks/cards-aisleriot/5.png new file mode 100644 index 00000000..8c5df649 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/5.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/50.png b/libkdegames/carddecks/cards-aisleriot/50.png new file mode 100644 index 00000000..a8a01853 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/50.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/51.png b/libkdegames/carddecks/cards-aisleriot/51.png new file mode 100644 index 00000000..9f2d83b9 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/51.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/52.png b/libkdegames/carddecks/cards-aisleriot/52.png new file mode 100644 index 00000000..c5c754b3 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/52.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/6.png b/libkdegames/carddecks/cards-aisleriot/6.png new file mode 100644 index 00000000..d399ebbb Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/6.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/7.png b/libkdegames/carddecks/cards-aisleriot/7.png new file mode 100644 index 00000000..754240cd Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/7.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/8.png b/libkdegames/carddecks/cards-aisleriot/8.png new file mode 100644 index 00000000..805854c0 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/8.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/9.png b/libkdegames/carddecks/cards-aisleriot/9.png new file mode 100644 index 00000000..9c032e48 Binary files /dev/null and b/libkdegames/carddecks/cards-aisleriot/9.png differ diff --git a/libkdegames/carddecks/cards-aisleriot/COPYRIGHT b/libkdegames/carddecks/cards-aisleriot/COPYRIGHT new file mode 100644 index 00000000..bfbeeca7 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/COPYRIGHT @@ -0,0 +1,10 @@ +This PySol cardset was adapted from gnome-games 0.27 (aisleriot). +http://www.gnome.org + +Copyright (C) 1998 Jonathan Blandford +Copyright (C) 1998 Markus F.X.J. Oberhumer + +This cardset is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 of +the License, or (at your option) any later version. diff --git a/libkdegames/carddecks/cards-aisleriot/index.desktop b/libkdegames/carddecks/cards-aisleriot/index.desktop new file mode 100644 index 00000000..e70c235b --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/index.desktop @@ -0,0 +1,20 @@ +[KDE Backdeck] +Name=AisleRiot +Name[af]=Aisleriot +Name[be]=ЭÑлрыёт (паÑÑŒÑнÑÑ‹) +Name[bg]=ÐйÑълриот +Name[bn]=à¦à¦‡à¦¸à¦²à¦°à¦¾à§Ÿà§‹à¦Ÿ +Name[cs]=Pozdvižení v uliÄce +Name[eo]=Simpla +Name[hi]=à¤à¤¸à¥à¤²à¥‡à¤°à¤¿à¤¯à¥‰à¤Ÿ +Name[hr]=Pobuna u prolazu +Name[ne]=आइसà¥à¤²à¥‡ रिओट +Name[pa]=ਇਲਸੀਰਓਟ +Name[ru]=ЭшлРиот (ÐºÐ¾Ð»Ð»ÐµÐºÑ†Ð¸Ñ Ð¿Ð°ÑÑŒÑнÑов) +Name[sv]=Kyrkouppror +Name[ta]= அயà¯à®¸à®¿à®²à¯à®°à®¿à®¯à®¾à®Ÿà¯ +Name[uk]=Заколот на човні +Name[vi]= AisleRiot +Name[zu]=I-AisleRiot +Preview=11.png +PySol=yes diff --git a/libkdegames/carddecks/cards-default/1.png b/libkdegames/carddecks/cards-default/1.png new file mode 100644 index 00000000..8f5de97f Binary files /dev/null and b/libkdegames/carddecks/cards-default/1.png differ diff --git a/libkdegames/carddecks/cards-default/10.png b/libkdegames/carddecks/cards-default/10.png new file mode 100644 index 00000000..6c7b87ea Binary files /dev/null and b/libkdegames/carddecks/cards-default/10.png differ diff --git a/libkdegames/carddecks/cards-default/11.png b/libkdegames/carddecks/cards-default/11.png new file mode 100644 index 00000000..a5938708 Binary files /dev/null and b/libkdegames/carddecks/cards-default/11.png differ diff --git a/libkdegames/carddecks/cards-default/12.png b/libkdegames/carddecks/cards-default/12.png new file mode 100644 index 00000000..4d43a730 Binary files /dev/null and b/libkdegames/carddecks/cards-default/12.png differ diff --git a/libkdegames/carddecks/cards-default/13.png b/libkdegames/carddecks/cards-default/13.png new file mode 100644 index 00000000..c7d51207 Binary files /dev/null and b/libkdegames/carddecks/cards-default/13.png differ diff --git a/libkdegames/carddecks/cards-default/14.png b/libkdegames/carddecks/cards-default/14.png new file mode 100644 index 00000000..2efb9b18 Binary files /dev/null and b/libkdegames/carddecks/cards-default/14.png differ diff --git a/libkdegames/carddecks/cards-default/15.png b/libkdegames/carddecks/cards-default/15.png new file mode 100644 index 00000000..8208b271 Binary files /dev/null and b/libkdegames/carddecks/cards-default/15.png differ diff --git a/libkdegames/carddecks/cards-default/16.png b/libkdegames/carddecks/cards-default/16.png new file mode 100644 index 00000000..73c5fce5 Binary files /dev/null and b/libkdegames/carddecks/cards-default/16.png differ diff --git a/libkdegames/carddecks/cards-default/17.png b/libkdegames/carddecks/cards-default/17.png new file mode 100644 index 00000000..611b7aff Binary files /dev/null and b/libkdegames/carddecks/cards-default/17.png differ diff --git a/libkdegames/carddecks/cards-default/18.png b/libkdegames/carddecks/cards-default/18.png new file mode 100644 index 00000000..8b7d1a1e Binary files /dev/null and b/libkdegames/carddecks/cards-default/18.png differ diff --git a/libkdegames/carddecks/cards-default/19.png b/libkdegames/carddecks/cards-default/19.png new file mode 100644 index 00000000..0a9dac18 Binary files /dev/null and b/libkdegames/carddecks/cards-default/19.png differ diff --git a/libkdegames/carddecks/cards-default/2.png b/libkdegames/carddecks/cards-default/2.png new file mode 100644 index 00000000..b0c67169 Binary files /dev/null and b/libkdegames/carddecks/cards-default/2.png differ diff --git a/libkdegames/carddecks/cards-default/20.png b/libkdegames/carddecks/cards-default/20.png new file mode 100644 index 00000000..0657a091 Binary files /dev/null and b/libkdegames/carddecks/cards-default/20.png differ diff --git a/libkdegames/carddecks/cards-default/21.png b/libkdegames/carddecks/cards-default/21.png new file mode 100644 index 00000000..fe376ba1 Binary files /dev/null and b/libkdegames/carddecks/cards-default/21.png differ diff --git a/libkdegames/carddecks/cards-default/22.png b/libkdegames/carddecks/cards-default/22.png new file mode 100644 index 00000000..2a0e5d30 Binary files /dev/null and b/libkdegames/carddecks/cards-default/22.png differ diff --git a/libkdegames/carddecks/cards-default/23.png b/libkdegames/carddecks/cards-default/23.png new file mode 100644 index 00000000..52403931 Binary files /dev/null and b/libkdegames/carddecks/cards-default/23.png differ diff --git a/libkdegames/carddecks/cards-default/24.png b/libkdegames/carddecks/cards-default/24.png new file mode 100644 index 00000000..5af94ec7 Binary files /dev/null and b/libkdegames/carddecks/cards-default/24.png differ diff --git a/libkdegames/carddecks/cards-default/25.png b/libkdegames/carddecks/cards-default/25.png new file mode 100644 index 00000000..cc437aea Binary files /dev/null and b/libkdegames/carddecks/cards-default/25.png differ diff --git a/libkdegames/carddecks/cards-default/26.png b/libkdegames/carddecks/cards-default/26.png new file mode 100644 index 00000000..d034f06f Binary files /dev/null and b/libkdegames/carddecks/cards-default/26.png differ diff --git a/libkdegames/carddecks/cards-default/27.png b/libkdegames/carddecks/cards-default/27.png new file mode 100644 index 00000000..a23fe6ff Binary files /dev/null and b/libkdegames/carddecks/cards-default/27.png differ diff --git a/libkdegames/carddecks/cards-default/28.png b/libkdegames/carddecks/cards-default/28.png new file mode 100644 index 00000000..dcccaac2 Binary files /dev/null and b/libkdegames/carddecks/cards-default/28.png differ diff --git a/libkdegames/carddecks/cards-default/29.png b/libkdegames/carddecks/cards-default/29.png new file mode 100644 index 00000000..e19070bd Binary files /dev/null and b/libkdegames/carddecks/cards-default/29.png differ diff --git a/libkdegames/carddecks/cards-default/3.png b/libkdegames/carddecks/cards-default/3.png new file mode 100644 index 00000000..1854faba Binary files /dev/null and b/libkdegames/carddecks/cards-default/3.png differ diff --git a/libkdegames/carddecks/cards-default/30.png b/libkdegames/carddecks/cards-default/30.png new file mode 100644 index 00000000..d9b285c0 Binary files /dev/null and b/libkdegames/carddecks/cards-default/30.png differ diff --git a/libkdegames/carddecks/cards-default/31.png b/libkdegames/carddecks/cards-default/31.png new file mode 100644 index 00000000..b77a5d8d Binary files /dev/null and b/libkdegames/carddecks/cards-default/31.png differ diff --git a/libkdegames/carddecks/cards-default/32.png b/libkdegames/carddecks/cards-default/32.png new file mode 100644 index 00000000..0e0649bf Binary files /dev/null and b/libkdegames/carddecks/cards-default/32.png differ diff --git a/libkdegames/carddecks/cards-default/33.png b/libkdegames/carddecks/cards-default/33.png new file mode 100644 index 00000000..d1344b0f Binary files /dev/null and b/libkdegames/carddecks/cards-default/33.png differ diff --git a/libkdegames/carddecks/cards-default/34.png b/libkdegames/carddecks/cards-default/34.png new file mode 100644 index 00000000..9ff5f5aa Binary files /dev/null and b/libkdegames/carddecks/cards-default/34.png differ diff --git a/libkdegames/carddecks/cards-default/35.png b/libkdegames/carddecks/cards-default/35.png new file mode 100644 index 00000000..b247d95e Binary files /dev/null and b/libkdegames/carddecks/cards-default/35.png differ diff --git a/libkdegames/carddecks/cards-default/36.png b/libkdegames/carddecks/cards-default/36.png new file mode 100644 index 00000000..f5284e87 Binary files /dev/null and b/libkdegames/carddecks/cards-default/36.png differ diff --git a/libkdegames/carddecks/cards-default/37.png b/libkdegames/carddecks/cards-default/37.png new file mode 100644 index 00000000..e4a2fa70 Binary files /dev/null and b/libkdegames/carddecks/cards-default/37.png differ diff --git a/libkdegames/carddecks/cards-default/38.png b/libkdegames/carddecks/cards-default/38.png new file mode 100644 index 00000000..859e4a52 Binary files /dev/null and b/libkdegames/carddecks/cards-default/38.png differ diff --git a/libkdegames/carddecks/cards-default/39.png b/libkdegames/carddecks/cards-default/39.png new file mode 100644 index 00000000..b4392112 Binary files /dev/null and b/libkdegames/carddecks/cards-default/39.png differ diff --git a/libkdegames/carddecks/cards-default/4.png b/libkdegames/carddecks/cards-default/4.png new file mode 100644 index 00000000..136c3100 Binary files /dev/null and b/libkdegames/carddecks/cards-default/4.png differ diff --git a/libkdegames/carddecks/cards-default/40.png b/libkdegames/carddecks/cards-default/40.png new file mode 100644 index 00000000..2a30304a Binary files /dev/null and b/libkdegames/carddecks/cards-default/40.png differ diff --git a/libkdegames/carddecks/cards-default/41.png b/libkdegames/carddecks/cards-default/41.png new file mode 100644 index 00000000..2c619dc3 Binary files /dev/null and b/libkdegames/carddecks/cards-default/41.png differ diff --git a/libkdegames/carddecks/cards-default/42.png b/libkdegames/carddecks/cards-default/42.png new file mode 100644 index 00000000..5200e878 Binary files /dev/null and b/libkdegames/carddecks/cards-default/42.png differ diff --git a/libkdegames/carddecks/cards-default/43.png b/libkdegames/carddecks/cards-default/43.png new file mode 100644 index 00000000..ebd6db3f Binary files /dev/null and b/libkdegames/carddecks/cards-default/43.png differ diff --git a/libkdegames/carddecks/cards-default/44.png b/libkdegames/carddecks/cards-default/44.png new file mode 100644 index 00000000..8cdcd68f Binary files /dev/null and b/libkdegames/carddecks/cards-default/44.png differ diff --git a/libkdegames/carddecks/cards-default/45.png b/libkdegames/carddecks/cards-default/45.png new file mode 100644 index 00000000..9297d188 Binary files /dev/null and b/libkdegames/carddecks/cards-default/45.png differ diff --git a/libkdegames/carddecks/cards-default/46.png b/libkdegames/carddecks/cards-default/46.png new file mode 100644 index 00000000..1b47218e Binary files /dev/null and b/libkdegames/carddecks/cards-default/46.png differ diff --git a/libkdegames/carddecks/cards-default/47.png b/libkdegames/carddecks/cards-default/47.png new file mode 100644 index 00000000..8fd3853d Binary files /dev/null and b/libkdegames/carddecks/cards-default/47.png differ diff --git a/libkdegames/carddecks/cards-default/48.png b/libkdegames/carddecks/cards-default/48.png new file mode 100644 index 00000000..01eea71b Binary files /dev/null and b/libkdegames/carddecks/cards-default/48.png differ diff --git a/libkdegames/carddecks/cards-default/49.png b/libkdegames/carddecks/cards-default/49.png new file mode 100644 index 00000000..d6860e04 Binary files /dev/null and b/libkdegames/carddecks/cards-default/49.png differ diff --git a/libkdegames/carddecks/cards-default/5.png b/libkdegames/carddecks/cards-default/5.png new file mode 100644 index 00000000..64029ed0 Binary files /dev/null and b/libkdegames/carddecks/cards-default/5.png differ diff --git a/libkdegames/carddecks/cards-default/50.png b/libkdegames/carddecks/cards-default/50.png new file mode 100644 index 00000000..79c1ed05 Binary files /dev/null and b/libkdegames/carddecks/cards-default/50.png differ diff --git a/libkdegames/carddecks/cards-default/51.png b/libkdegames/carddecks/cards-default/51.png new file mode 100644 index 00000000..f396b946 Binary files /dev/null and b/libkdegames/carddecks/cards-default/51.png differ diff --git a/libkdegames/carddecks/cards-default/52.png b/libkdegames/carddecks/cards-default/52.png new file mode 100644 index 00000000..dcdc90e5 Binary files /dev/null and b/libkdegames/carddecks/cards-default/52.png differ diff --git a/libkdegames/carddecks/cards-default/6.png b/libkdegames/carddecks/cards-default/6.png new file mode 100644 index 00000000..1749b472 Binary files /dev/null and b/libkdegames/carddecks/cards-default/6.png differ diff --git a/libkdegames/carddecks/cards-default/7.png b/libkdegames/carddecks/cards-default/7.png new file mode 100644 index 00000000..2d33965c Binary files /dev/null and b/libkdegames/carddecks/cards-default/7.png differ diff --git a/libkdegames/carddecks/cards-default/8.png b/libkdegames/carddecks/cards-default/8.png new file mode 100644 index 00000000..165bd76b Binary files /dev/null and b/libkdegames/carddecks/cards-default/8.png differ diff --git a/libkdegames/carddecks/cards-default/9.png b/libkdegames/carddecks/cards-default/9.png new file mode 100644 index 00000000..9860c535 Binary files /dev/null and b/libkdegames/carddecks/cards-default/9.png differ diff --git a/libkdegames/carddecks/cards-default/index.desktop b/libkdegames/carddecks/cards-default/index.desktop new file mode 100644 index 00000000..63894a1b --- /dev/null +++ b/libkdegames/carddecks/cards-default/index.desktop @@ -0,0 +1,115 @@ +[KDE Backdeck] +Name=Standard +Name[af]=Standaard +Name[az]=Standart +Name[be]=Ð—Ð²Ñ‹Ñ‡Ð°Ð¹Ð½Ð°Ñ +Name[bg]=Стандарт +Name[bn]=পà§à¦°à¦®à¦¿à¦¤ +Name[br]=Reoliek +Name[ca]=Estàndard +Name[cs]=Standardní +Name[cy]=Safonol +Name[eo]=Normala +Name[es]=Estándar +Name[eu]=Estandarra +Name[fa]=استاندارد +Name[fi]=Normaali +Name[ga]=Caighdeánach +Name[gl]=Estándar +Name[hi]=मानक +Name[is]=Staðlað +Name[ja]=標準 +Name[km]=ážáŸ’នាážâ€‹áž‚ំរូ +Name[ko]=표준 +Name[lt]=Standartas +Name[lv]=Standarta +Name[mk]=Стандард +Name[ne]=मानक +Name[nl]=Standaard +Name[nso]=Lekanetse +Name[pa]=ਮਿਆਰ +Name[pt]=Normal +Name[pt_BR]=Padrão +Name[ru]=Стандарт +Name[se]=Standárda +Name[sk]=Å tandard +Name[sr]=Стандардни +Name[sr@Latn]=Standardni +Name[ta]= தரம௠+Name[tg]=Ðизоммеъёр +Name[th]=มาตรà¸à¸²à¸™ +Name[tr]=Standart +Name[uk]=Типові +Name[uz]=Andoza +Name[uz@cyrillic]=Ðндоза +Name[ven]=Murole +Name[vi]=Tiêu chuẩn +Name[wa]=SitandÃ¥rd +Name[xh]=Umgangatho +Name[zh_CN]=标准 +Name[zh_TW]=標準 +Name[zu]=Okulingeneyo +Preview=11.png +PySol=false +Comment=Standard KDE card set\nGPL license +Comment[af]=Standaard Kde kaart stel\nGPL lisensie +Comment[az]=Standart KDE kart dÉ™stÉ™si\nGPL license +Comment[be]=Ð—Ð²Ñ‹Ñ‡Ð°Ð¹Ð½Ð°Ñ ÐºÐ°Ð»Ð¾Ð´Ð° картаў KDE\nGPL license +Comment[bg]=Стандартна колода карти KDE\nОПЛ лиценз(GPL) +Comment[bn]=কে.ডি.ই.-র সাধারণ তাস সেট\nজি.পি.à¦à¦². লাইসেনà§à¦¸ +Comment[bs]=Standardni KDE Å¡pil karata\nGPL license +Comment[ca]=Joc de cartes estàndard per al KDE sota\nLlicència GPL +Comment[cs]=Standardní sada karet KDE\nGPL licence +Comment[cy]=Trwydded GPL safonol\nar gyfer set cerdiau KDE +Comment[da]=Standard KDE-kortspil\nGPL-licens +Comment[de]=Standardmäßige KDE-Karten\nGPL-Lizenz +Comment[el]=ΠÏοκαθοÏισμένο σετ καÏτών του KDE\nΆδεια GPL +Comment[en_GB]=Standard KDE card set\nGPL licence +Comment[eo]=Normala KDEa kartaro\nGPL licenco +Comment[es]=Juego de cartas estándar de KDE\nLicencia GPL +Comment[et]=Standardne KDE kaardikomplekt\nGPL litsents +Comment[eu]=KDE-ren karta-sorta estandarra\nGPL lizentzia +Comment[fa]=مجموعه استاندارد کارت KDE\مجوز nGPL +Comment[fi]=Normaali KDE:n korttipakka\nGPL lisenssi +Comment[fr]=Jeu de cartes standard de KDE\nLicence GPL +Comment[gl]=Baralla de cartas estándar de KDE\nGPL license +Comment[he]=ערכת ×”×§×œ×¤×™× ×”×¡×˜× ×“×¨×˜×™×ª של KDE\nרישיון GPL +Comment[hi]=मानक केडीई ताश सेट\nजीपीà¤à¤² अनà¥à¤œà¥à¤žà¤¾à¤ªà¤¤à¥à¤° +Comment[hr]=Standardni KDE komplet karata\nGPL licenca +Comment[hu]=Standard KDE kártyacsomag\n(GPL licencű) +Comment[is]=Venjulegi KDE spilastokkurinn\nGPL leyfi +Comment[it]=Insieme di carte standard di KDE\nLicenza GPL +Comment[ja]=標準 KDE カードセット\nGPL ライセンス +Comment[km]=បណ្ដុំ​បៀ KDE ážáŸ’នាážâ€‹áž‚ំរូ\nអជ្ញាបណ្ណ GPL +Comment[ko]=표준 KDE ì¹´ë“œ 모ìŒ\nGPL ë¼ì´ì„ ìŠ¤ +Comment[lt]=Standartinis KDE kortų rinkinys\nGPL licencija +Comment[lv]=Standarta KDE kÄrÅ¡u komplekts\n GPL licenze +Comment[mk]=Стандарден комплет на карти во KDE\nGPL-лиценца +Comment[mt]=Mazz karti standard KDE\nliÄ‹enzja GPL +Comment[nb]=Standard KDE sett med kort\nGPL-lisens +Comment[nds]=KDE-Standardkoorten\nGPL-Lizenz +Comment[ne]=मानक केडीई कारà¥à¤¡ सेट\nGPL इजाजतपतà¥à¤° +Comment[nl]=Standaard KDE-kaartrug\nGPL-licentie +Comment[nn]=Standard KDE-kortstokk\nGPL-lisens +Comment[pl]=Standardowy zestaw kart KDE\nLicencja GPL +Comment[pt]=Baralho de cartas normal do KDE\nLicença GPL +Comment[pt_BR]=Jogo de cartas padrão do KDE\nLicença GPL +Comment[ro]=Set de cărÅ£i KDE standard\nLicenţă GPL +Comment[ru]=Ð¡Ñ‚Ð°Ð½Ð´Ð°Ñ€Ñ‚Ð½Ð°Ñ ÐºÐ¾Ð»Ð¾Ð´Ð° карт KDE\nЛицензиÑ: GPL +Comment[sk]=Å tandardný balíÄek kariet KDE\nlicencia GPL +Comment[sl]=Standardni nabor kart v KDE\nLicenca GPL +Comment[sr]=Стандардни KDE-ов шпил карата\nGPL лиценца +Comment[sr@Latn]=Standardni KDE-ov Å¡pil karata\nGPL licenca +Comment[sv]=Förvald KDE-kortlek\nGPL-licens +Comment[ta]=நிலையான கேடிஇ சீடà¯à®Ÿà¯ அமைபà¯à®ªà¯ \nGPL இயகà¯à®• ஆணை +Comment[tg]=Маҷмӯи кортҳои низомии KDE\nлитÑензиÑи GPL +Comment[th]=ชุดของเà¸à¸¡à¹„พ่ของ KDE\nลิขสิทธิ์à¹à¸šà¸š GPL +Comment[tr]=Standart KDE kart seti\nGPL lisansı +Comment[uk]=Типовий набір карт KDE\nÐ»Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñ GPL +Comment[ven]=U vhekanya ha magarata a KDE ya Murole\tendelo ya nGPL +Comment[vi]=Thẻ KDE tiêu chuẩn đặt \nGPL license +Comment[wa]=CwÃ¥rdjeus standÃ¥rd di KDE\ndizo licince GPL +Comment[xh]=Umgangatho wekhadi le KDE iqela lekhadi \nGPL imvumelwano +Comment[zh_CN]=标准 KDE 牌é¢\nGPL æŽˆæƒ +Comment[zh_TW]=標準 KDE 牌局\nGPL 授權 +Comment[zu]=Iqoqo lamakhadi elilingeneyo le-KDE\n ilayisensi ye-GPL diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/1.png b/libkdegames/carddecks/cards-dondorf-whist-b/1.png new file mode 100644 index 00000000..89c86909 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/1.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/10.png b/libkdegames/carddecks/cards-dondorf-whist-b/10.png new file mode 100644 index 00000000..5113cad8 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/10.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/11.png b/libkdegames/carddecks/cards-dondorf-whist-b/11.png new file mode 100644 index 00000000..11fc7e05 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/11.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/12.png b/libkdegames/carddecks/cards-dondorf-whist-b/12.png new file mode 100644 index 00000000..a2307ddc Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/12.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/13.png b/libkdegames/carddecks/cards-dondorf-whist-b/13.png new file mode 100644 index 00000000..0b9e20cd Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/13.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/14.png b/libkdegames/carddecks/cards-dondorf-whist-b/14.png new file mode 100644 index 00000000..1c49f750 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/14.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/15.png b/libkdegames/carddecks/cards-dondorf-whist-b/15.png new file mode 100644 index 00000000..ab295e25 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/15.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/16.png b/libkdegames/carddecks/cards-dondorf-whist-b/16.png new file mode 100644 index 00000000..d8bd8402 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/16.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/17.png b/libkdegames/carddecks/cards-dondorf-whist-b/17.png new file mode 100644 index 00000000..d3a3f6ed Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/17.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/18.png b/libkdegames/carddecks/cards-dondorf-whist-b/18.png new file mode 100644 index 00000000..12831991 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/18.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/19.png b/libkdegames/carddecks/cards-dondorf-whist-b/19.png new file mode 100644 index 00000000..e7199dce Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/19.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/2.png b/libkdegames/carddecks/cards-dondorf-whist-b/2.png new file mode 100644 index 00000000..93ec9944 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/2.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/20.png b/libkdegames/carddecks/cards-dondorf-whist-b/20.png new file mode 100644 index 00000000..90b043f0 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/20.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/21.png b/libkdegames/carddecks/cards-dondorf-whist-b/21.png new file mode 100644 index 00000000..905ccd6a Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/21.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/22.png b/libkdegames/carddecks/cards-dondorf-whist-b/22.png new file mode 100644 index 00000000..2eee5bbc Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/22.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/23.png b/libkdegames/carddecks/cards-dondorf-whist-b/23.png new file mode 100644 index 00000000..b717110e Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/23.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/24.png b/libkdegames/carddecks/cards-dondorf-whist-b/24.png new file mode 100644 index 00000000..4c05cd1d Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/24.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/25.png b/libkdegames/carddecks/cards-dondorf-whist-b/25.png new file mode 100644 index 00000000..c6701d0d Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/25.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/26.png b/libkdegames/carddecks/cards-dondorf-whist-b/26.png new file mode 100644 index 00000000..a737f469 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/26.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/27.png b/libkdegames/carddecks/cards-dondorf-whist-b/27.png new file mode 100644 index 00000000..c904309b Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/27.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/28.png b/libkdegames/carddecks/cards-dondorf-whist-b/28.png new file mode 100644 index 00000000..8da263d7 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/28.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/29.png b/libkdegames/carddecks/cards-dondorf-whist-b/29.png new file mode 100644 index 00000000..8ff7822d Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/29.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/3.png b/libkdegames/carddecks/cards-dondorf-whist-b/3.png new file mode 100644 index 00000000..7f5644f8 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/3.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/30.png b/libkdegames/carddecks/cards-dondorf-whist-b/30.png new file mode 100644 index 00000000..c93c64dc Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/30.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/31.png b/libkdegames/carddecks/cards-dondorf-whist-b/31.png new file mode 100644 index 00000000..912cbf79 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/31.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/32.png b/libkdegames/carddecks/cards-dondorf-whist-b/32.png new file mode 100644 index 00000000..9b72443c Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/32.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/33.png b/libkdegames/carddecks/cards-dondorf-whist-b/33.png new file mode 100644 index 00000000..20f863ac Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/33.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/34.png b/libkdegames/carddecks/cards-dondorf-whist-b/34.png new file mode 100644 index 00000000..7763d965 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/34.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/35.png b/libkdegames/carddecks/cards-dondorf-whist-b/35.png new file mode 100644 index 00000000..29eaef82 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/35.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/36.png b/libkdegames/carddecks/cards-dondorf-whist-b/36.png new file mode 100644 index 00000000..fd72b718 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/36.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/37.png b/libkdegames/carddecks/cards-dondorf-whist-b/37.png new file mode 100644 index 00000000..15a2b979 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/37.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/38.png b/libkdegames/carddecks/cards-dondorf-whist-b/38.png new file mode 100644 index 00000000..91551173 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/38.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/39.png b/libkdegames/carddecks/cards-dondorf-whist-b/39.png new file mode 100644 index 00000000..dcb66d00 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/39.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/4.png b/libkdegames/carddecks/cards-dondorf-whist-b/4.png new file mode 100644 index 00000000..1033ffd0 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/4.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/40.png b/libkdegames/carddecks/cards-dondorf-whist-b/40.png new file mode 100644 index 00000000..a1b9b2e9 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/40.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/41.png b/libkdegames/carddecks/cards-dondorf-whist-b/41.png new file mode 100644 index 00000000..8d7ca55b Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/41.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/42.png b/libkdegames/carddecks/cards-dondorf-whist-b/42.png new file mode 100644 index 00000000..4570430b Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/42.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/43.png b/libkdegames/carddecks/cards-dondorf-whist-b/43.png new file mode 100644 index 00000000..c889b795 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/43.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/44.png b/libkdegames/carddecks/cards-dondorf-whist-b/44.png new file mode 100644 index 00000000..b73bd4d1 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/44.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/45.png b/libkdegames/carddecks/cards-dondorf-whist-b/45.png new file mode 100644 index 00000000..de6730cd Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/45.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/46.png b/libkdegames/carddecks/cards-dondorf-whist-b/46.png new file mode 100644 index 00000000..f20edcc9 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/46.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/47.png b/libkdegames/carddecks/cards-dondorf-whist-b/47.png new file mode 100644 index 00000000..50ec6b95 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/47.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/48.png b/libkdegames/carddecks/cards-dondorf-whist-b/48.png new file mode 100644 index 00000000..c0bfdfbd Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/48.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/49.png b/libkdegames/carddecks/cards-dondorf-whist-b/49.png new file mode 100644 index 00000000..c8803ffa Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/49.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/5.png b/libkdegames/carddecks/cards-dondorf-whist-b/5.png new file mode 100644 index 00000000..0fb9ffcf Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/5.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/50.png b/libkdegames/carddecks/cards-dondorf-whist-b/50.png new file mode 100644 index 00000000..6d8c891e Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/50.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/51.png b/libkdegames/carddecks/cards-dondorf-whist-b/51.png new file mode 100644 index 00000000..fc2c76d0 Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/51.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/52.png b/libkdegames/carddecks/cards-dondorf-whist-b/52.png new file mode 100644 index 00000000..6aef81aa Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/52.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/6.png b/libkdegames/carddecks/cards-dondorf-whist-b/6.png new file mode 100644 index 00000000..f70072cf Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/6.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/7.png b/libkdegames/carddecks/cards-dondorf-whist-b/7.png new file mode 100644 index 00000000..1dd352dc Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/7.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/8.png b/libkdegames/carddecks/cards-dondorf-whist-b/8.png new file mode 100644 index 00000000..087906ca Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/8.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/9.png b/libkdegames/carddecks/cards-dondorf-whist-b/9.png new file mode 100644 index 00000000..9bb290ee Binary files /dev/null and b/libkdegames/carddecks/cards-dondorf-whist-b/9.png differ diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/COPYRIGHT b/libkdegames/carddecks/cards-dondorf-whist-b/COPYRIGHT new file mode 100644 index 00000000..917bc1e3 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/COPYRIGHT @@ -0,0 +1,12 @@ +This PySol cardset was created by T. Kirk. + +Copyright (C) 2000 T. Kirk + +This card set uses the court and Ace images from a deck published +by Dondorf and Co. of Frankfurt in 1896. The cards are double ended +and have different images on each end. Two card sets were produced. + +This card set is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 of +the License, or (at your option) any later version. diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/index.desktop b/libkdegames/carddecks/cards-dondorf-whist-b/index.desktop new file mode 100644 index 00000000..6626e825 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/index.desktop @@ -0,0 +1,17 @@ +[KDE Backdeck] +Name=Dondorf +Name[be]=Дандорф +Name[bg]=Дондроф +Name[bn]=ডনডরà§à¦« +Name[eo]=Bela +Name[hi]=डॉनडà¥à¤°à¥‰à¤« +Name[lv]=Dondorfa +Name[mk]=Дондорф +Name[ne]=डनडोरà¥à¤« +Name[ru]=Дондорф +Name[sr]=Дондорф +Name[ta]=டேனà¯à®Ÿà®¾à®°à¯à®ƒà®ªà¯ +Name[tg]=Дондорф +Name[zu]=I-Dondorf +Preview=11.png +PySol=yes diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/1.png b/libkdegames/carddecks/cards-gdkcard-bonded/1.png new file mode 100644 index 00000000..17c1ac79 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/1.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/10.png b/libkdegames/carddecks/cards-gdkcard-bonded/10.png new file mode 100644 index 00000000..f9fe505b Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/10.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/11.png b/libkdegames/carddecks/cards-gdkcard-bonded/11.png new file mode 100644 index 00000000..9fa6e5ec Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/11.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/12.png b/libkdegames/carddecks/cards-gdkcard-bonded/12.png new file mode 100644 index 00000000..09089e70 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/12.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/13.png b/libkdegames/carddecks/cards-gdkcard-bonded/13.png new file mode 100644 index 00000000..cb6b0e6f Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/13.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/14.png b/libkdegames/carddecks/cards-gdkcard-bonded/14.png new file mode 100644 index 00000000..027217be Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/14.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/15.png b/libkdegames/carddecks/cards-gdkcard-bonded/15.png new file mode 100644 index 00000000..b9054f4f Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/15.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/16.png b/libkdegames/carddecks/cards-gdkcard-bonded/16.png new file mode 100644 index 00000000..7b14d54a Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/16.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/17.png b/libkdegames/carddecks/cards-gdkcard-bonded/17.png new file mode 100644 index 00000000..e4d9b798 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/17.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/18.png b/libkdegames/carddecks/cards-gdkcard-bonded/18.png new file mode 100644 index 00000000..c0fe1954 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/18.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/19.png b/libkdegames/carddecks/cards-gdkcard-bonded/19.png new file mode 100644 index 00000000..7f24d523 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/19.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/2.png b/libkdegames/carddecks/cards-gdkcard-bonded/2.png new file mode 100644 index 00000000..7d8cbbcb Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/2.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/20.png b/libkdegames/carddecks/cards-gdkcard-bonded/20.png new file mode 100644 index 00000000..1a6e01c9 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/20.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/21.png b/libkdegames/carddecks/cards-gdkcard-bonded/21.png new file mode 100644 index 00000000..c217b982 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/21.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/22.png b/libkdegames/carddecks/cards-gdkcard-bonded/22.png new file mode 100644 index 00000000..82ce9196 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/22.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/23.png b/libkdegames/carddecks/cards-gdkcard-bonded/23.png new file mode 100644 index 00000000..03850bef Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/23.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/24.png b/libkdegames/carddecks/cards-gdkcard-bonded/24.png new file mode 100644 index 00000000..990289f6 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/24.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/25.png b/libkdegames/carddecks/cards-gdkcard-bonded/25.png new file mode 100644 index 00000000..04ea602f Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/25.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/26.png b/libkdegames/carddecks/cards-gdkcard-bonded/26.png new file mode 100644 index 00000000..705a22a2 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/26.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/27.png b/libkdegames/carddecks/cards-gdkcard-bonded/27.png new file mode 100644 index 00000000..cf5ea112 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/27.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/28.png b/libkdegames/carddecks/cards-gdkcard-bonded/28.png new file mode 100644 index 00000000..d613413c Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/28.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/29.png b/libkdegames/carddecks/cards-gdkcard-bonded/29.png new file mode 100644 index 00000000..8838c750 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/29.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/3.png b/libkdegames/carddecks/cards-gdkcard-bonded/3.png new file mode 100644 index 00000000..563a6385 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/3.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/30.png b/libkdegames/carddecks/cards-gdkcard-bonded/30.png new file mode 100644 index 00000000..4fb2901b Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/30.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/31.png b/libkdegames/carddecks/cards-gdkcard-bonded/31.png new file mode 100644 index 00000000..8bcd3090 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/31.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/32.png b/libkdegames/carddecks/cards-gdkcard-bonded/32.png new file mode 100644 index 00000000..c5fb66db Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/32.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/33.png b/libkdegames/carddecks/cards-gdkcard-bonded/33.png new file mode 100644 index 00000000..43d4df04 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/33.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/34.png b/libkdegames/carddecks/cards-gdkcard-bonded/34.png new file mode 100644 index 00000000..1bf6c975 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/34.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/35.png b/libkdegames/carddecks/cards-gdkcard-bonded/35.png new file mode 100644 index 00000000..ba85c37e Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/35.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/36.png b/libkdegames/carddecks/cards-gdkcard-bonded/36.png new file mode 100644 index 00000000..c45008af Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/36.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/37.png b/libkdegames/carddecks/cards-gdkcard-bonded/37.png new file mode 100644 index 00000000..05cac5a9 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/37.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/38.png b/libkdegames/carddecks/cards-gdkcard-bonded/38.png new file mode 100644 index 00000000..7c52ecbf Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/38.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/39.png b/libkdegames/carddecks/cards-gdkcard-bonded/39.png new file mode 100644 index 00000000..1404295a Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/39.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/4.png b/libkdegames/carddecks/cards-gdkcard-bonded/4.png new file mode 100644 index 00000000..fbf1b7d2 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/4.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/40.png b/libkdegames/carddecks/cards-gdkcard-bonded/40.png new file mode 100644 index 00000000..b1c0f317 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/40.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/41.png b/libkdegames/carddecks/cards-gdkcard-bonded/41.png new file mode 100644 index 00000000..a59a245c Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/41.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/42.png b/libkdegames/carddecks/cards-gdkcard-bonded/42.png new file mode 100644 index 00000000..104836a5 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/42.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/43.png b/libkdegames/carddecks/cards-gdkcard-bonded/43.png new file mode 100644 index 00000000..90b51009 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/43.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/44.png b/libkdegames/carddecks/cards-gdkcard-bonded/44.png new file mode 100644 index 00000000..66960d0d Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/44.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/45.png b/libkdegames/carddecks/cards-gdkcard-bonded/45.png new file mode 100644 index 00000000..7aabe88f Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/45.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/46.png b/libkdegames/carddecks/cards-gdkcard-bonded/46.png new file mode 100644 index 00000000..adfb2516 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/46.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/47.png b/libkdegames/carddecks/cards-gdkcard-bonded/47.png new file mode 100644 index 00000000..0f48a363 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/47.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/48.png b/libkdegames/carddecks/cards-gdkcard-bonded/48.png new file mode 100644 index 00000000..368c593a Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/48.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/49.png b/libkdegames/carddecks/cards-gdkcard-bonded/49.png new file mode 100644 index 00000000..a783b506 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/49.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/5.png b/libkdegames/carddecks/cards-gdkcard-bonded/5.png new file mode 100644 index 00000000..9f809535 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/5.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/50.png b/libkdegames/carddecks/cards-gdkcard-bonded/50.png new file mode 100644 index 00000000..dacc26cd Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/50.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/51.png b/libkdegames/carddecks/cards-gdkcard-bonded/51.png new file mode 100644 index 00000000..13a6e924 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/51.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/52.png b/libkdegames/carddecks/cards-gdkcard-bonded/52.png new file mode 100644 index 00000000..cce8c9f6 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/52.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/6.png b/libkdegames/carddecks/cards-gdkcard-bonded/6.png new file mode 100644 index 00000000..c679e0c3 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/6.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/7.png b/libkdegames/carddecks/cards-gdkcard-bonded/7.png new file mode 100644 index 00000000..53b35fbf Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/7.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/8.png b/libkdegames/carddecks/cards-gdkcard-bonded/8.png new file mode 100644 index 00000000..389d0069 Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/8.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/9.png b/libkdegames/carddecks/cards-gdkcard-bonded/9.png new file mode 100644 index 00000000..59551a1a Binary files /dev/null and b/libkdegames/carddecks/cards-gdkcard-bonded/9.png differ diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/COPYRIGHT b/libkdegames/carddecks/cards-gdkcard-bonded/COPYRIGHT new file mode 100644 index 00000000..36052f95 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/COPYRIGHT @@ -0,0 +1,13 @@ +This PySol cardset was adapted from gnome-games 1.0.2 (gdk-card-image). +http://www.gnome.org + +Copyright (C) 1994 Heiko Eissfeldt +Copyright (C) 1994 Michael Bischoff +Copyright (C) 1998 Felix Bellaby +Copyright (C) 1998 Ryu Changwoo +Copyright (C) 1999 Markus F.X.J. Oberhumer + +This cardset is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 of +the License, or (at your option) any later version. diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/index.desktop b/libkdegames/carddecks/cards-gdkcard-bonded/index.desktop new file mode 100644 index 00000000..68f3ff21 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/index.desktop @@ -0,0 +1,37 @@ +[KDE Backdeck] +Name=Bonded +Name[af]=Geheg +Name[az]=BaÄŸlı +Name[be]=ПераплÑценні +Name[bg]=Завързан +Name[bn]=আবদà§à¦§ +Name[ca]=Unides +Name[cs]=Lepenka +Name[de]=Verbunden +Name[eo]=Benda +Name[es]=Unidas +Name[eu]=Estekatua +Name[fi]=Sidottu +Name[fr]=Compact +Name[gl]=Unidas +Name[hi]=बॉनà¥à¤¡à¥‡à¤¡ +Name[hr]=Povezano +Name[is]=Bundið +Name[lv]=VienkÄrÅ¡s +Name[mk]=Врзани +Name[mt]=Mehmuż +Name[ne]=बनà¥à¤¡à¥‡à¤¡ +Name[pt]=Unidos +Name[pt_BR]=Laçado +Name[ru]=ÐŸÐµÑ€ÐµÐ¿Ð»ÐµÑ‚ÐµÐ½Ð¸Ñ +Name[sr]=Везан +Name[sr@Latn]=Vezan +Name[ta]=பிணைகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿ +Name[tg]=Печдарпечшавӣ +Name[tr]=BaÄŸlı +Name[uk]=Бони +Name[ven]=Zwo khwathiswa +Name[xh]=Dibeneyo +Name[zu]=Kuxhumene +Preview=11.png +PySol=yes diff --git a/libkdegames/carddecks/cards-hard-a-port/1.png b/libkdegames/carddecks/cards-hard-a-port/1.png new file mode 100644 index 00000000..05c56268 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/1.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/10.png b/libkdegames/carddecks/cards-hard-a-port/10.png new file mode 100644 index 00000000..ee2d2072 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/10.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/11.png b/libkdegames/carddecks/cards-hard-a-port/11.png new file mode 100644 index 00000000..4fd72b89 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/11.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/12.png b/libkdegames/carddecks/cards-hard-a-port/12.png new file mode 100644 index 00000000..7dd80ce7 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/12.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/13.png b/libkdegames/carddecks/cards-hard-a-port/13.png new file mode 100644 index 00000000..62edf27c Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/13.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/14.png b/libkdegames/carddecks/cards-hard-a-port/14.png new file mode 100644 index 00000000..5cfa05ed Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/14.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/15.png b/libkdegames/carddecks/cards-hard-a-port/15.png new file mode 100644 index 00000000..a950a87c Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/15.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/16.png b/libkdegames/carddecks/cards-hard-a-port/16.png new file mode 100644 index 00000000..8b0d76f3 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/16.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/17.png b/libkdegames/carddecks/cards-hard-a-port/17.png new file mode 100644 index 00000000..9625563e Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/17.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/18.png b/libkdegames/carddecks/cards-hard-a-port/18.png new file mode 100644 index 00000000..715c8443 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/18.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/19.png b/libkdegames/carddecks/cards-hard-a-port/19.png new file mode 100644 index 00000000..7a3cf664 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/19.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/2.png b/libkdegames/carddecks/cards-hard-a-port/2.png new file mode 100644 index 00000000..bf4d601f Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/2.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/20.png b/libkdegames/carddecks/cards-hard-a-port/20.png new file mode 100644 index 00000000..331abc3b Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/20.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/21.png b/libkdegames/carddecks/cards-hard-a-port/21.png new file mode 100644 index 00000000..59adfb08 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/21.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/22.png b/libkdegames/carddecks/cards-hard-a-port/22.png new file mode 100644 index 00000000..a1ca8484 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/22.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/23.png b/libkdegames/carddecks/cards-hard-a-port/23.png new file mode 100644 index 00000000..d97de844 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/23.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/24.png b/libkdegames/carddecks/cards-hard-a-port/24.png new file mode 100644 index 00000000..65e3da3c Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/24.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/25.png b/libkdegames/carddecks/cards-hard-a-port/25.png new file mode 100644 index 00000000..14f82b7d Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/25.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/26.png b/libkdegames/carddecks/cards-hard-a-port/26.png new file mode 100644 index 00000000..e2601df6 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/26.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/27.png b/libkdegames/carddecks/cards-hard-a-port/27.png new file mode 100644 index 00000000..d18b29d7 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/27.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/28.png b/libkdegames/carddecks/cards-hard-a-port/28.png new file mode 100644 index 00000000..3dcc0842 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/28.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/29.png b/libkdegames/carddecks/cards-hard-a-port/29.png new file mode 100644 index 00000000..a0cc5866 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/29.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/3.png b/libkdegames/carddecks/cards-hard-a-port/3.png new file mode 100644 index 00000000..b78502b9 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/3.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/30.png b/libkdegames/carddecks/cards-hard-a-port/30.png new file mode 100644 index 00000000..e659dc83 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/30.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/31.png b/libkdegames/carddecks/cards-hard-a-port/31.png new file mode 100644 index 00000000..db6f599c Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/31.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/32.png b/libkdegames/carddecks/cards-hard-a-port/32.png new file mode 100644 index 00000000..9e42582f Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/32.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/33.png b/libkdegames/carddecks/cards-hard-a-port/33.png new file mode 100644 index 00000000..c19f1520 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/33.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/34.png b/libkdegames/carddecks/cards-hard-a-port/34.png new file mode 100644 index 00000000..139dc9b4 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/34.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/35.png b/libkdegames/carddecks/cards-hard-a-port/35.png new file mode 100644 index 00000000..62d8a9bc Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/35.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/36.png b/libkdegames/carddecks/cards-hard-a-port/36.png new file mode 100644 index 00000000..510d150e Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/36.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/37.png b/libkdegames/carddecks/cards-hard-a-port/37.png new file mode 100644 index 00000000..2e03c9e2 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/37.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/38.png b/libkdegames/carddecks/cards-hard-a-port/38.png new file mode 100644 index 00000000..e12131f0 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/38.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/39.png b/libkdegames/carddecks/cards-hard-a-port/39.png new file mode 100644 index 00000000..eae23793 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/39.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/4.png b/libkdegames/carddecks/cards-hard-a-port/4.png new file mode 100644 index 00000000..9d7f80c1 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/4.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/40.png b/libkdegames/carddecks/cards-hard-a-port/40.png new file mode 100644 index 00000000..733be70f Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/40.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/41.png b/libkdegames/carddecks/cards-hard-a-port/41.png new file mode 100644 index 00000000..b3e51b07 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/41.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/42.png b/libkdegames/carddecks/cards-hard-a-port/42.png new file mode 100644 index 00000000..a6f69d9f Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/42.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/43.png b/libkdegames/carddecks/cards-hard-a-port/43.png new file mode 100644 index 00000000..49c20875 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/43.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/44.png b/libkdegames/carddecks/cards-hard-a-port/44.png new file mode 100644 index 00000000..6fed6ead Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/44.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/45.png b/libkdegames/carddecks/cards-hard-a-port/45.png new file mode 100644 index 00000000..81be70a2 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/45.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/46.png b/libkdegames/carddecks/cards-hard-a-port/46.png new file mode 100644 index 00000000..d82c5f60 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/46.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/47.png b/libkdegames/carddecks/cards-hard-a-port/47.png new file mode 100644 index 00000000..f6056465 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/47.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/48.png b/libkdegames/carddecks/cards-hard-a-port/48.png new file mode 100644 index 00000000..6369f53b Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/48.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/49.png b/libkdegames/carddecks/cards-hard-a-port/49.png new file mode 100644 index 00000000..49b5b53e Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/49.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/5.png b/libkdegames/carddecks/cards-hard-a-port/5.png new file mode 100644 index 00000000..6ff98a13 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/5.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/50.png b/libkdegames/carddecks/cards-hard-a-port/50.png new file mode 100644 index 00000000..ce8e46db Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/50.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/51.png b/libkdegames/carddecks/cards-hard-a-port/51.png new file mode 100644 index 00000000..46552bee Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/51.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/52.png b/libkdegames/carddecks/cards-hard-a-port/52.png new file mode 100644 index 00000000..b03db4a0 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/52.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/6.png b/libkdegames/carddecks/cards-hard-a-port/6.png new file mode 100644 index 00000000..24fa84b0 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/6.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/7.png b/libkdegames/carddecks/cards-hard-a-port/7.png new file mode 100644 index 00000000..11561878 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/7.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/8.png b/libkdegames/carddecks/cards-hard-a-port/8.png new file mode 100644 index 00000000..08d124b7 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/8.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/9.png b/libkdegames/carddecks/cards-hard-a-port/9.png new file mode 100644 index 00000000..14962b11 Binary files /dev/null and b/libkdegames/carddecks/cards-hard-a-port/9.png differ diff --git a/libkdegames/carddecks/cards-hard-a-port/COPYRIGHT b/libkdegames/carddecks/cards-hard-a-port/COPYRIGHT new file mode 100644 index 00000000..f5575c7b --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/COPYRIGHT @@ -0,0 +1,14 @@ +This PySol cardset was created by T. Kirk. + +Copyright (C) 2000 T. Kirk + +This transformation card set uses images from a set of trade +cards produced in the late 19th century in the United States +of America. Cards of this type were given away as premiums +with the purchase of various products. Each of the cards +has a different image. + +This card set is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 of +the License, or (at your option) any later version. diff --git a/libkdegames/carddecks/cards-hard-a-port/index.desktop b/libkdegames/carddecks/cards-hard-a-port/index.desktop new file mode 100644 index 00000000..d8e49aa5 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/index.desktop @@ -0,0 +1,29 @@ +[KDE Backdeck] +Name=Hard a Port +Name[af]=Hard 'n Poort +Name[be]=Пампушка +Name[bg]=ДамÑки +Name[bn]=হারà§à¦¡ ঠপোরà§à¦Ÿ +Name[de]=Hart Backbord +Name[eo]=Tenta +Name[hi]=हारà¥à¤¡ ठपोरà¥à¤Ÿ +Name[hr]=Tvrdi na portu +Name[is]=Hart á bakborða +Name[it]=Donnine +Name[km]=ច្រក​រឹង +Name[lv]=Klasisks +Name[nds]=Hatt Backboord +Name[ne]=कडा à¤à¤‰à¤Ÿà¤¾ पोरà¥à¤Ÿ +Name[pt_BR]=Duramente um Porto +Name[ro]=LicenÅ£ioase +Name[ru]=Пышка +Name[sr]=Хард-а-порт +Name[sr@Latn]=Hard-a-port +Name[sv]=Dikt babord +Name[ta]=à®®à¯à®©à¯ˆà®¯à®¤à¯à®¤à¯ˆ கடினமாகà¯à®•à¯ +Name[tg]=Даргоҳи Сахт +Name[ven]=Port yo Khwathaho +Name[xh]=Izibuko Esport eqinileyo +Name[zu]=Qinisa isikhumulo +Preview=11.png +PySol=yes diff --git a/libkdegames/carddecks/cards-konqi-modern/1.png b/libkdegames/carddecks/cards-konqi-modern/1.png new file mode 100644 index 00000000..68dfb0ff Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/1.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/10.png b/libkdegames/carddecks/cards-konqi-modern/10.png new file mode 100644 index 00000000..6df3ad8d Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/10.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/11.png b/libkdegames/carddecks/cards-konqi-modern/11.png new file mode 100644 index 00000000..665cb2a6 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/11.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/12.png b/libkdegames/carddecks/cards-konqi-modern/12.png new file mode 100644 index 00000000..1f23d99e Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/12.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/13.png b/libkdegames/carddecks/cards-konqi-modern/13.png new file mode 100644 index 00000000..f1027fdc Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/13.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/14.png b/libkdegames/carddecks/cards-konqi-modern/14.png new file mode 100644 index 00000000..80d11335 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/14.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/15.png b/libkdegames/carddecks/cards-konqi-modern/15.png new file mode 100644 index 00000000..72913f7b Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/15.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/16.png b/libkdegames/carddecks/cards-konqi-modern/16.png new file mode 100644 index 00000000..225f7774 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/16.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/17.png b/libkdegames/carddecks/cards-konqi-modern/17.png new file mode 100644 index 00000000..0444e010 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/17.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/18.png b/libkdegames/carddecks/cards-konqi-modern/18.png new file mode 100644 index 00000000..f325c781 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/18.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/19.png b/libkdegames/carddecks/cards-konqi-modern/19.png new file mode 100644 index 00000000..570e951e Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/19.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/2.png b/libkdegames/carddecks/cards-konqi-modern/2.png new file mode 100644 index 00000000..89b2b4d5 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/2.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/20.png b/libkdegames/carddecks/cards-konqi-modern/20.png new file mode 100644 index 00000000..d4b47c7d Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/20.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/21.png b/libkdegames/carddecks/cards-konqi-modern/21.png new file mode 100644 index 00000000..9ab04cc5 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/21.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/22.png b/libkdegames/carddecks/cards-konqi-modern/22.png new file mode 100644 index 00000000..387c3203 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/22.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/23.png b/libkdegames/carddecks/cards-konqi-modern/23.png new file mode 100644 index 00000000..fcec2343 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/23.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/24.png b/libkdegames/carddecks/cards-konqi-modern/24.png new file mode 100644 index 00000000..48c9ed02 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/24.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/25.png b/libkdegames/carddecks/cards-konqi-modern/25.png new file mode 100644 index 00000000..80bbfdf1 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/25.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/26.png b/libkdegames/carddecks/cards-konqi-modern/26.png new file mode 100644 index 00000000..2a879ec5 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/26.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/27.png b/libkdegames/carddecks/cards-konqi-modern/27.png new file mode 100644 index 00000000..1e6141c0 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/27.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/28.png b/libkdegames/carddecks/cards-konqi-modern/28.png new file mode 100644 index 00000000..1403437e Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/28.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/29.png b/libkdegames/carddecks/cards-konqi-modern/29.png new file mode 100644 index 00000000..415b48cc Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/29.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/3.png b/libkdegames/carddecks/cards-konqi-modern/3.png new file mode 100644 index 00000000..43067744 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/3.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/30.png b/libkdegames/carddecks/cards-konqi-modern/30.png new file mode 100644 index 00000000..8a1b885d Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/30.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/31.png b/libkdegames/carddecks/cards-konqi-modern/31.png new file mode 100644 index 00000000..c7de49ed Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/31.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/32.png b/libkdegames/carddecks/cards-konqi-modern/32.png new file mode 100644 index 00000000..57e0015f Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/32.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/33.png b/libkdegames/carddecks/cards-konqi-modern/33.png new file mode 100644 index 00000000..a502ae94 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/33.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/34.png b/libkdegames/carddecks/cards-konqi-modern/34.png new file mode 100644 index 00000000..524768dc Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/34.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/35.png b/libkdegames/carddecks/cards-konqi-modern/35.png new file mode 100644 index 00000000..d15a240e Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/35.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/36.png b/libkdegames/carddecks/cards-konqi-modern/36.png new file mode 100644 index 00000000..b8dce3f8 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/36.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/37.png b/libkdegames/carddecks/cards-konqi-modern/37.png new file mode 100644 index 00000000..2f193cc1 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/37.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/38.png b/libkdegames/carddecks/cards-konqi-modern/38.png new file mode 100644 index 00000000..097d63ff Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/38.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/39.png b/libkdegames/carddecks/cards-konqi-modern/39.png new file mode 100644 index 00000000..a94a6009 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/39.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/4.png b/libkdegames/carddecks/cards-konqi-modern/4.png new file mode 100644 index 00000000..b4890c03 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/4.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/40.png b/libkdegames/carddecks/cards-konqi-modern/40.png new file mode 100644 index 00000000..d533469a Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/40.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/41.png b/libkdegames/carddecks/cards-konqi-modern/41.png new file mode 100644 index 00000000..f524f7ea Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/41.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/42.png b/libkdegames/carddecks/cards-konqi-modern/42.png new file mode 100644 index 00000000..9e6ff5af Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/42.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/43.png b/libkdegames/carddecks/cards-konqi-modern/43.png new file mode 100644 index 00000000..59e9e273 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/43.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/44.png b/libkdegames/carddecks/cards-konqi-modern/44.png new file mode 100644 index 00000000..a3114bd9 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/44.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/45.png b/libkdegames/carddecks/cards-konqi-modern/45.png new file mode 100644 index 00000000..2614080b Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/45.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/46.png b/libkdegames/carddecks/cards-konqi-modern/46.png new file mode 100644 index 00000000..a3118981 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/46.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/47.png b/libkdegames/carddecks/cards-konqi-modern/47.png new file mode 100644 index 00000000..423ec06e Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/47.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/48.png b/libkdegames/carddecks/cards-konqi-modern/48.png new file mode 100644 index 00000000..5153c779 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/48.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/49.png b/libkdegames/carddecks/cards-konqi-modern/49.png new file mode 100644 index 00000000..99f8ae96 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/49.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/5.png b/libkdegames/carddecks/cards-konqi-modern/5.png new file mode 100644 index 00000000..5e167b1a Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/5.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/50.png b/libkdegames/carddecks/cards-konqi-modern/50.png new file mode 100644 index 00000000..a9c41757 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/50.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/51.png b/libkdegames/carddecks/cards-konqi-modern/51.png new file mode 100644 index 00000000..b322fc1b Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/51.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/52.png b/libkdegames/carddecks/cards-konqi-modern/52.png new file mode 100644 index 00000000..d614733f Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/52.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/6.png b/libkdegames/carddecks/cards-konqi-modern/6.png new file mode 100644 index 00000000..d614e646 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/6.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/7.png b/libkdegames/carddecks/cards-konqi-modern/7.png new file mode 100644 index 00000000..961ac50f Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/7.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/8.png b/libkdegames/carddecks/cards-konqi-modern/8.png new file mode 100644 index 00000000..1769d3db Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/8.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/9.png b/libkdegames/carddecks/cards-konqi-modern/9.png new file mode 100644 index 00000000..f86dcd48 Binary files /dev/null and b/libkdegames/carddecks/cards-konqi-modern/9.png differ diff --git a/libkdegames/carddecks/cards-konqi-modern/index.desktop b/libkdegames/carddecks/cards-konqi-modern/index.desktop new file mode 100644 index 00000000..4dc5fe5c --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/index.desktop @@ -0,0 +1,62 @@ +[KDE Backdeck] +Name=Konqi +Name[be]=Конкі +Name[bg]=Конки +Name[bn]=কনকি +Name[cs]=Konqui +Name[eo]=Konĉja +Name[hi]=के-ऑनà¥à¤—ी +Name[it]=Konqui +Name[lv]=Konvi +Name[ne]=कोनà¥à¤•à¥à¤µà¥€ +Name[ru]=Конки +Name[sr]=Конки +Name[sr@Latn]=Konki +Name[ta]=கானà¯à®•à®¿ +Name[tg]=Конки +Name[uk]=Конкі +Name[zu]=I-Konqi +Preview=11.png +PySol=false +Comment=Modern Konqi - play the family carddeck\nDesign: Laura Layland\n \nKatie by Agnieszka Czajkowska\n \nKonqi by Stefan Spatz\n +Comment[bn]=আধà§à¦¨à¦¿à¦• কনকি - à¦à¦•à¦Ÿà¦¿ পারিবারিক তাস খেলা\nডিজাইন: লরা লেলà§à¦¯à¦¾à¦¨à§à¦¡\n \n Katie: Agnieszka Czajkowska\n \nকনকি: সà§à¦Ÿà¦¿à¦«à§‡à¦¨ সà§à¦ªà¦¾à¦œ\n +Comment[bs]=Moderni Konqi - igrajte sa porodiÄnim Å¡pilom\nDesign: Laura Layland\n \nKatie by Agnieszka Czajkowska\n \nKonqi by Stefan Spatz\n +Comment[ca]=Konqi modern - jugueu amb la baralla familiar\nDisseny: Laura Layland\n \nKatie per Agnieszka Czajkowska\n \nKonqi per Stefan Spatz\n +Comment[cs]=Moderní Konqi - hrajte rodinnou hru\nNávrh: Laura Laylanda\n \nKatie vytvoÅ™ila Agnieszka Czajkowska\n \nKonqiho vytvoÅ™il Stefan Spatz\n +Comment[cy]=Set cerdiau cyfoes Konqi - chwarae yn erbyn y teulu\nDylunio:Laura Layland\n \nKatie gan Agnieszka Czajkowska\n \nKonqi gan Stefan Spatz\n +Comment[da]=Modern Konqi - spil familiekortspillet\nDesign: Laura Layland\n \nKatie by Agnieszka Czajkowska\n \nKonqi by Stefan Spatz\n +Comment[de]=Modernes Konqi - Spielen Sie das Familienspiel\nDesign: Laura Layland\n \nKatie von Agnieszka Czajkowska\n \nKonqi von Stefan Spatz\n +Comment[el]=ΜοντέÏνος Konqi - play the family θέμα καÏτών\nΣχεδίαση: Laura Layland\n \nKatie από Agnieszka Czajkowska\n \nKonqi από Stefan Spatz\n +Comment[eo]=Moderna Konĉjo - ludu per la familiokartaro\nDesegno: Laura Layland\n \nKonjo de Agnieszka Czajkowska\n \nKonĉjo de Stefan Spatz\n +Comment[es]=Konqi moderno - juegue con la baraja familiar\nDiseño: Laura Layland\n \nKatie por Agnieszka Czajkowska\n \nKonqi por Stefan Spatz\n +Comment[et]=Modern Konqi - play the family carddeck\nDesign: Laura Layland\n \nKatie: Agnieszka Czajkowska\n \nKonqi: Stefan Spatz\n +Comment[eu]=Konqi modernoa - kartetan jokatzeko\nDiseinua: Laura Layland\n \nKatie-ren egilea: Agnieszka Czajkowska\n \nKonqi-ren egilea Stefan Spatz\n +Comment[fa]=Konqi مدرن - بازی خانوادگی carddeck\nطرح: لورا \n لایلند \nKatie توسط آگنیسکا زاکووسکا\n \nKonqi توسط استÙان اسپاتز\n +Comment[fi]=Moderni Konqi - perheen korttipakka\nSuunnittelu: Laura Layland\n a\nKatie Agnieszka Czajkowskaa\n a\nKonqi Stefan Spatza\n +Comment[fr]=Konqi moderne - pour jouer aux cartes\nConception : Laura Layland\n \nKatie par Agnieszka Czajkowska\n \nKonqi par Stefan Spatz\n +Comment[hr]=Suvremeni Konqi - obiteljska igra s kartama\nDizajn: Laura Layland\n \nKatie: Agnieszka Czajkowska\n \nKonqi: Stefan Spatz\n +Comment[hu]=Modern Konqi - családi kártyacsomag\nTervezte: Laura Layland\n \nKatie: Agnieszka Czajkowska\n \nKonqi: Stefan Spatz\n +Comment[is]=Nútíma Konqi - spilastokkur fjölskyldunnar\nHönnun: Laura Layland\n \nKatie e. Agnieszka Czajkowska\n \nKonqi e. Stefan Spatz\n +Comment[it]=Konqui moderno - carte familiari\nDesign: Laura Layland\n\nKatie di Agnieszka Czajkowska\n\nKonqi di Stefan Spatz\n +Comment[ja]=モダン Konqi - ファミリå‘ã‘カードデッキ\nデザイン: Laura Layland\n \nKatie ã®ä½œè€…: Agnieszka Czajkowska\n \nKonqi ã®ä½œè€…: Stefan Spatz \n +Comment[lt]=Modern Konqi - žaisktie Å¡eimos kortų žaidimÄ…\nDizainas: Laura Layland\n \nKatie by Agnieszka Czajkowska\n \nKonqi by Stefan Spatz\n +Comment[lv]=Modernais Konkvi - spÄ“lÄ“t pie Ä£imenes kÄrÅ¡u galda\n Dizains: Laura Layland\n \n Katie no Agnieszka Czajkowska\n \n Konkvi no Stefan Spatz\n +Comment[mk]=Модерен Konqi - играјте Ñо Ñемејниот шпил карти\nДизајн: Laura Layland\n\nKatie од Agnieszka Czajkowska\n \nKonqi од Stefan Spatz\n +Comment[nb]=Moderne Konqi – familiekortstokken\nUtforming: Laura Layland\n \nKatie av Agnieszka Czajkowska\n \nKonqi av Stefan Spatz\n +Comment[nds]=Modern Konqi - Speel mit de Familienkoorten\nDesign: Laura Layland\n \nKatie vun Agnieszka Czajkowska\n \nKonqi vun Stefan Spatz\n +Comment[ne]=आधà¥à¤¨à¤¿à¤• कोनà¥à¤•à¥à¤•à¥€ - परिवारिक कारà¥à¤¡à¤¡à¥‡à¤• \nडिजाइन पà¥à¤²à¥‡: लाउरा लेलà¥à¤¯à¤¾à¤¨à¥à¤¡\n \nKatie, अगà¥à¤¨à¤¿à¤œà¥à¤•à¤¾ जजà¥à¤•à¥‹à¤¸à¥à¤•à¤¾à¤¦à¥à¤µà¤¾à¤°à¤¾\n \nKonqi, सà¥à¤Ÿà¥‡à¤«à¤¾à¤¨ सà¥à¤ªà¤¾à¤°à¥à¤Ÿà¤œà¤¦à¥à¤µà¤¾à¤°à¤¾ \n +Comment[nl]=Modern Konqi - speel met de familie-kaartdek\nDesign: Laura Layland\n \nKatie door Agnieszka Czajkowska\n \nKonqi door Stefan Spatz\n +Comment[nn]=Moderne Konqi – familiekortstokken\nUtforming: Laura Layland\n \nKatie av Agnieszka Czajkowska\n \nKonqi av Stefan Spatz\n +Comment[pl]=Nowoczesny Konqi - zagraj w grÄ™ rodzinnÄ…\nProjekt: Laura Laylanda\n a\nKatie: Agnieszka Czajkowska\n a\nKonqi: Stefan Spatza\n +Comment[pt]=Konqi Moderno - o baralho de cartas familiar\nConcepção: Laura Layland\n \nKatie por Agnieszka Czajkowska\n \nKonqi por Stefan Spatz\n +Comment[pt_BR]=Konqi Moderno - jogue com o baralho da família\nDesign: Laura Layland\n \nKatie por Agnieszka Czajkowska\n \nKonqi por Stefan Spatz\n +Comment[ru]=Ð¡Ð¾Ð²Ñ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ ÐºÐ¾Ð»Ð¾Ð´Ð° Ñ ÑемейÑтвом Конки\nДизайн: Лаура ЛейлÑнд (Laura Layland) \nКÑйт нариÑована Ðгниежкой ЗайковÑкой (Agnieszka Czajkowska) \nКонки нариÑован Штефаном Спартцом (Stefan Spatz) +Comment[sk]=Moderný Konqi - hrajte rodinné kartové hry\nDesign: Laura Laylanda\n a\nKatie od Agnieszky Czajkowskeja\n a\nKonqi od Stefana Spatza\n +Comment[sl]=Moderni Konqi - igrajte z družinskim kupom kart\nOblikovanje: Laura Laylanda\n a\nKatie od Agnieszke Czajkowske\n a\nKonqi od Stefana Spatza\n +Comment[sr]=Модеран Конки - играјте Ñа породичним шпилом\nДизајн: Лора Лејленд (Laura Layland)\n \nКети: Ðгњешка ЧајковÑка (Agnieszka Czajkowska)\n \nКонки: Штефан Шпац (Stefan Spatz)\n +Comment[sr@Latn]=Moderan Konki - igrajte sa porodiÄnim Å¡pilom\nDizajn: Lora Lejlend (Laura Layland)\n \nKeti: AgnjeÅ¡ka ÄŒajkovska (Agnieszka Czajkowska)\n \nKonki: Å tefan Å pac (Stefan Spatz)\n +Comment[sv]=Modern Konqi - spela familjens kortlek\nDesign: Laura Layland\n \nKatie av Agnieszka Czajkowska\n \nKonqi av Stefan Spatza\n +Comment[ta]=மாரà¯à®Ÿà®©à¯ கானà¯à®•à®¿ - பà¯à®¤à®¿à®¯ கà¯à®Ÿà¯à®®à¯à®ªà®šà¯ சீடà¯à®Ÿà¯à®¤à¯ தளதà¯à®¤à¯ˆ விளையாடà¯\nவடிவமைபà¯à®ªà¯: லெளரா லேலேணà¯à®Ÿà¯\n \n Katie by Agnieszka Czajkowska\n \nKonqi by Stefan Spatz\n +Comment[uk]=СучаÑний Конкі - зіграйте у Ñімейні карти\nРозробка: Laura Laylanda\n \nKatie від Agnieszka Czajkowska\n \nKonqi від Stefan Spatza\n +Comment[wa]=Modiene Konqi - cwÃ¥rdjeus des familes\nDessins: Laura Layland\n \nKatie pa Agnieszka Czajkowska\n \nKonqi pa Stefan Spatz\n +Comment[zh_TW]=ç¾ä»£ Konqi - 玩家庭牌局\n設計︰Laura Layland...\nKatie by Agnieszka Czajkowska...\nKonqi by Stefan Spatz... diff --git a/libkdegames/carddecks/cards-penguins/1.png b/libkdegames/carddecks/cards-penguins/1.png new file mode 100644 index 00000000..b59022e8 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/1.png differ diff --git a/libkdegames/carddecks/cards-penguins/10.png b/libkdegames/carddecks/cards-penguins/10.png new file mode 100644 index 00000000..d54669fe Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/10.png differ diff --git a/libkdegames/carddecks/cards-penguins/11.png b/libkdegames/carddecks/cards-penguins/11.png new file mode 100644 index 00000000..02c2464c Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/11.png differ diff --git a/libkdegames/carddecks/cards-penguins/12.png b/libkdegames/carddecks/cards-penguins/12.png new file mode 100644 index 00000000..d179eb99 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/12.png differ diff --git a/libkdegames/carddecks/cards-penguins/13.png b/libkdegames/carddecks/cards-penguins/13.png new file mode 100644 index 00000000..135af493 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/13.png differ diff --git a/libkdegames/carddecks/cards-penguins/14.png b/libkdegames/carddecks/cards-penguins/14.png new file mode 100644 index 00000000..4a179917 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/14.png differ diff --git a/libkdegames/carddecks/cards-penguins/15.png b/libkdegames/carddecks/cards-penguins/15.png new file mode 100644 index 00000000..b5238d00 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/15.png differ diff --git a/libkdegames/carddecks/cards-penguins/16.png b/libkdegames/carddecks/cards-penguins/16.png new file mode 100644 index 00000000..33fe6b8e Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/16.png differ diff --git a/libkdegames/carddecks/cards-penguins/17.png b/libkdegames/carddecks/cards-penguins/17.png new file mode 100644 index 00000000..99b77ce8 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/17.png differ diff --git a/libkdegames/carddecks/cards-penguins/18.png b/libkdegames/carddecks/cards-penguins/18.png new file mode 100644 index 00000000..b88ae958 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/18.png differ diff --git a/libkdegames/carddecks/cards-penguins/19.png b/libkdegames/carddecks/cards-penguins/19.png new file mode 100644 index 00000000..532899aa Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/19.png differ diff --git a/libkdegames/carddecks/cards-penguins/2.png b/libkdegames/carddecks/cards-penguins/2.png new file mode 100644 index 00000000..090cfd02 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/2.png differ diff --git a/libkdegames/carddecks/cards-penguins/20.png b/libkdegames/carddecks/cards-penguins/20.png new file mode 100644 index 00000000..ed129a32 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/20.png differ diff --git a/libkdegames/carddecks/cards-penguins/21.png b/libkdegames/carddecks/cards-penguins/21.png new file mode 100644 index 00000000..5bf532f7 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/21.png differ diff --git a/libkdegames/carddecks/cards-penguins/22.png b/libkdegames/carddecks/cards-penguins/22.png new file mode 100644 index 00000000..07c61ced Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/22.png differ diff --git a/libkdegames/carddecks/cards-penguins/23.png b/libkdegames/carddecks/cards-penguins/23.png new file mode 100644 index 00000000..ecce302b Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/23.png differ diff --git a/libkdegames/carddecks/cards-penguins/24.png b/libkdegames/carddecks/cards-penguins/24.png new file mode 100644 index 00000000..feee0a9e Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/24.png differ diff --git a/libkdegames/carddecks/cards-penguins/25.png b/libkdegames/carddecks/cards-penguins/25.png new file mode 100644 index 00000000..d42f2b2b Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/25.png differ diff --git a/libkdegames/carddecks/cards-penguins/26.png b/libkdegames/carddecks/cards-penguins/26.png new file mode 100644 index 00000000..cc5e930f Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/26.png differ diff --git a/libkdegames/carddecks/cards-penguins/27.png b/libkdegames/carddecks/cards-penguins/27.png new file mode 100644 index 00000000..4ae7702a Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/27.png differ diff --git a/libkdegames/carddecks/cards-penguins/28.png b/libkdegames/carddecks/cards-penguins/28.png new file mode 100644 index 00000000..ee9b40ea Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/28.png differ diff --git a/libkdegames/carddecks/cards-penguins/29.png b/libkdegames/carddecks/cards-penguins/29.png new file mode 100644 index 00000000..ba2dfe7d Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/29.png differ diff --git a/libkdegames/carddecks/cards-penguins/3.png b/libkdegames/carddecks/cards-penguins/3.png new file mode 100644 index 00000000..7f53daf9 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/3.png differ diff --git a/libkdegames/carddecks/cards-penguins/30.png b/libkdegames/carddecks/cards-penguins/30.png new file mode 100644 index 00000000..1486cf98 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/30.png differ diff --git a/libkdegames/carddecks/cards-penguins/31.png b/libkdegames/carddecks/cards-penguins/31.png new file mode 100644 index 00000000..805d39e9 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/31.png differ diff --git a/libkdegames/carddecks/cards-penguins/32.png b/libkdegames/carddecks/cards-penguins/32.png new file mode 100644 index 00000000..4a1b12f0 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/32.png differ diff --git a/libkdegames/carddecks/cards-penguins/33.png b/libkdegames/carddecks/cards-penguins/33.png new file mode 100644 index 00000000..b8ae8965 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/33.png differ diff --git a/libkdegames/carddecks/cards-penguins/34.png b/libkdegames/carddecks/cards-penguins/34.png new file mode 100644 index 00000000..e2ce160c Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/34.png differ diff --git a/libkdegames/carddecks/cards-penguins/35.png b/libkdegames/carddecks/cards-penguins/35.png new file mode 100644 index 00000000..580c4d12 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/35.png differ diff --git a/libkdegames/carddecks/cards-penguins/36.png b/libkdegames/carddecks/cards-penguins/36.png new file mode 100644 index 00000000..296d4c7c Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/36.png differ diff --git a/libkdegames/carddecks/cards-penguins/37.png b/libkdegames/carddecks/cards-penguins/37.png new file mode 100644 index 00000000..fbbefa84 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/37.png differ diff --git a/libkdegames/carddecks/cards-penguins/38.png b/libkdegames/carddecks/cards-penguins/38.png new file mode 100644 index 00000000..7d9a34bd Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/38.png differ diff --git a/libkdegames/carddecks/cards-penguins/39.png b/libkdegames/carddecks/cards-penguins/39.png new file mode 100644 index 00000000..23ffffaa Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/39.png differ diff --git a/libkdegames/carddecks/cards-penguins/4.png b/libkdegames/carddecks/cards-penguins/4.png new file mode 100644 index 00000000..79ced119 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/4.png differ diff --git a/libkdegames/carddecks/cards-penguins/40.png b/libkdegames/carddecks/cards-penguins/40.png new file mode 100644 index 00000000..b9bed320 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/40.png differ diff --git a/libkdegames/carddecks/cards-penguins/41.png b/libkdegames/carddecks/cards-penguins/41.png new file mode 100644 index 00000000..c5a65385 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/41.png differ diff --git a/libkdegames/carddecks/cards-penguins/42.png b/libkdegames/carddecks/cards-penguins/42.png new file mode 100644 index 00000000..aba32e3e Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/42.png differ diff --git a/libkdegames/carddecks/cards-penguins/43.png b/libkdegames/carddecks/cards-penguins/43.png new file mode 100644 index 00000000..04623c1e Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/43.png differ diff --git a/libkdegames/carddecks/cards-penguins/44.png b/libkdegames/carddecks/cards-penguins/44.png new file mode 100644 index 00000000..7e6069e6 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/44.png differ diff --git a/libkdegames/carddecks/cards-penguins/45.png b/libkdegames/carddecks/cards-penguins/45.png new file mode 100644 index 00000000..36b67487 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/45.png differ diff --git a/libkdegames/carddecks/cards-penguins/46.png b/libkdegames/carddecks/cards-penguins/46.png new file mode 100644 index 00000000..80216f18 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/46.png differ diff --git a/libkdegames/carddecks/cards-penguins/47.png b/libkdegames/carddecks/cards-penguins/47.png new file mode 100644 index 00000000..9fb16882 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/47.png differ diff --git a/libkdegames/carddecks/cards-penguins/48.png b/libkdegames/carddecks/cards-penguins/48.png new file mode 100644 index 00000000..12f928e6 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/48.png differ diff --git a/libkdegames/carddecks/cards-penguins/49.png b/libkdegames/carddecks/cards-penguins/49.png new file mode 100644 index 00000000..9119c351 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/49.png differ diff --git a/libkdegames/carddecks/cards-penguins/5.png b/libkdegames/carddecks/cards-penguins/5.png new file mode 100644 index 00000000..f18a3a57 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/5.png differ diff --git a/libkdegames/carddecks/cards-penguins/50.png b/libkdegames/carddecks/cards-penguins/50.png new file mode 100644 index 00000000..e3dacdba Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/50.png differ diff --git a/libkdegames/carddecks/cards-penguins/51.png b/libkdegames/carddecks/cards-penguins/51.png new file mode 100644 index 00000000..fa46ff72 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/51.png differ diff --git a/libkdegames/carddecks/cards-penguins/52.png b/libkdegames/carddecks/cards-penguins/52.png new file mode 100644 index 00000000..1522ed3f Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/52.png differ diff --git a/libkdegames/carddecks/cards-penguins/6.png b/libkdegames/carddecks/cards-penguins/6.png new file mode 100644 index 00000000..9d84eb8d Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/6.png differ diff --git a/libkdegames/carddecks/cards-penguins/7.png b/libkdegames/carddecks/cards-penguins/7.png new file mode 100644 index 00000000..2b8d0768 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/7.png differ diff --git a/libkdegames/carddecks/cards-penguins/8.png b/libkdegames/carddecks/cards-penguins/8.png new file mode 100644 index 00000000..933be6e7 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/8.png differ diff --git a/libkdegames/carddecks/cards-penguins/9.png b/libkdegames/carddecks/cards-penguins/9.png new file mode 100644 index 00000000..669d6e00 Binary files /dev/null and b/libkdegames/carddecks/cards-penguins/9.png differ diff --git a/libkdegames/carddecks/cards-penguins/COPYRIGHT b/libkdegames/carddecks/cards-penguins/COPYRIGHT new file mode 100644 index 00000000..cf87594e --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/COPYRIGHT @@ -0,0 +1,10 @@ +This PySol cardset was adapted from the Ace of Penguins 1.0. +http://www.delorie.com/store/ace/ + +Copyright (C) 1998 DJ Delorie +Copyright (C) 1998 Markus F.X.J. Oberhumer + +This cardset is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 of +the License, or (at your option) any later version. diff --git a/libkdegames/carddecks/cards-penguins/index.desktop b/libkdegames/carddecks/cards-penguins/index.desktop new file mode 100644 index 00000000..2fe12197 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/index.desktop @@ -0,0 +1,67 @@ +[KDE Backdeck] +Name=Penguins +Name[af]=Pikkewyne +Name[ar]=بطاريق +Name[az]=PinqvinlÉ™r +Name[be]=Пінгвіны +Name[bg]=Пингвин +Name[bn]=পেঙà§à¦—à§à¦‡à¦¨ +Name[br]=Pennoù-gwenn +Name[bs]=Pingvini +Name[ca]=Pingüins +Name[cs]=TuÄňáci +Name[da]=Pingviner +Name[de]=Pinguine +Name[el]=Πιγκουίνοι +Name[eo]=Pingvenoj +Name[es]=Pingüinos +Name[et]=Pingviinid +Name[eu]=Pinguinoak +Name[fa]=پنگوئنها +Name[fi]=Pingviinit +Name[fr]=Pingouins +Name[gl]=Pingüíns +Name[he]=×¤× ×’×•×™× ×™× +Name[hi]=पेंगà¥à¤µà¤¿à¤¨à¥à¤¸ +Name[hr]=Pingvini +Name[hu]=Pingvinek +Name[is]=Mörgæsir +Name[it]=Pinguini +Name[km]=áž—áŸáž“ឃ្វីន +Name[ko]=펭귄 +Name[lt]=Pingvinai +Name[lv]=PingvÄ«ni +Name[mk]=Пингвини +Name[mt]=Pingwini +Name[nb]=Pingviner +Name[nds]=Pinguins +Name[ne]=पेनà¥à¤—à¥à¤‡à¤¨ +Name[nl]=Pinguïns +Name[nn]=Pingvinar +Name[pa]=ਪੈਂਗੂਇਨ +Name[pl]=Pingwiny +Name[pt]=Pinguins +Name[pt_BR]=Pingüins +Name[ro]=Pinguini +Name[ru]=Пингвины +Name[se]=Pingviinnat +Name[sk]=TuÄniaci +Name[sl]=Pingvini +Name[sr]=Пингвини +Name[sr@Latn]=Pingvini +Name[sv]=Pingviner +Name[ta]=பெனà¯à®•à¯à®¯à®¿à®©à¯à®•à®³à¯ +Name[tg]=Пингвинҳо +Name[th]=เพนà¸à¸§à¸´à¸™ +Name[tr]=Penguenler +Name[uk]=Пінгвіни +Name[uz]=Pingvinlar +Name[uz@cyrillic]=Пингвинлар +Name[vi]=Chim cánh cụt +Name[wa]=Pingwins +Name[xh]=iintaka zaselwandle +Name[zh_CN]=ä¼é¹… +Name[zh_TW]=ä¼éµ +Name[zu]=Izinyoni zasemanzini +Preview=11.png +PySol=yes diff --git a/libkdegames/carddecks/cards-spaced/1.png b/libkdegames/carddecks/cards-spaced/1.png new file mode 100644 index 00000000..62220b38 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/1.png differ diff --git a/libkdegames/carddecks/cards-spaced/10.png b/libkdegames/carddecks/cards-spaced/10.png new file mode 100644 index 00000000..d0bcd828 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/10.png differ diff --git a/libkdegames/carddecks/cards-spaced/11.png b/libkdegames/carddecks/cards-spaced/11.png new file mode 100644 index 00000000..b4e6016e Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/11.png differ diff --git a/libkdegames/carddecks/cards-spaced/12.png b/libkdegames/carddecks/cards-spaced/12.png new file mode 100644 index 00000000..45ab5cf5 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/12.png differ diff --git a/libkdegames/carddecks/cards-spaced/13.png b/libkdegames/carddecks/cards-spaced/13.png new file mode 100644 index 00000000..37e7c2ce Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/13.png differ diff --git a/libkdegames/carddecks/cards-spaced/14.png b/libkdegames/carddecks/cards-spaced/14.png new file mode 100644 index 00000000..97af1f02 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/14.png differ diff --git a/libkdegames/carddecks/cards-spaced/15.png b/libkdegames/carddecks/cards-spaced/15.png new file mode 100644 index 00000000..fb2fcc76 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/15.png differ diff --git a/libkdegames/carddecks/cards-spaced/16.png b/libkdegames/carddecks/cards-spaced/16.png new file mode 100644 index 00000000..9042b161 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/16.png differ diff --git a/libkdegames/carddecks/cards-spaced/17.png b/libkdegames/carddecks/cards-spaced/17.png new file mode 100644 index 00000000..a67b2832 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/17.png differ diff --git a/libkdegames/carddecks/cards-spaced/18.png b/libkdegames/carddecks/cards-spaced/18.png new file mode 100644 index 00000000..f4c8a04b Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/18.png differ diff --git a/libkdegames/carddecks/cards-spaced/19.png b/libkdegames/carddecks/cards-spaced/19.png new file mode 100644 index 00000000..fac5b199 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/19.png differ diff --git a/libkdegames/carddecks/cards-spaced/2.png b/libkdegames/carddecks/cards-spaced/2.png new file mode 100644 index 00000000..af3c76fd Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/2.png differ diff --git a/libkdegames/carddecks/cards-spaced/20.png b/libkdegames/carddecks/cards-spaced/20.png new file mode 100644 index 00000000..12d43520 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/20.png differ diff --git a/libkdegames/carddecks/cards-spaced/21.png b/libkdegames/carddecks/cards-spaced/21.png new file mode 100644 index 00000000..ea715adc Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/21.png differ diff --git a/libkdegames/carddecks/cards-spaced/22.png b/libkdegames/carddecks/cards-spaced/22.png new file mode 100644 index 00000000..13e6d7ae Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/22.png differ diff --git a/libkdegames/carddecks/cards-spaced/23.png b/libkdegames/carddecks/cards-spaced/23.png new file mode 100644 index 00000000..f43a745f Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/23.png differ diff --git a/libkdegames/carddecks/cards-spaced/24.png b/libkdegames/carddecks/cards-spaced/24.png new file mode 100644 index 00000000..810f59ef Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/24.png differ diff --git a/libkdegames/carddecks/cards-spaced/25.png b/libkdegames/carddecks/cards-spaced/25.png new file mode 100644 index 00000000..836b9863 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/25.png differ diff --git a/libkdegames/carddecks/cards-spaced/26.png b/libkdegames/carddecks/cards-spaced/26.png new file mode 100644 index 00000000..27bc29bf Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/26.png differ diff --git a/libkdegames/carddecks/cards-spaced/27.png b/libkdegames/carddecks/cards-spaced/27.png new file mode 100644 index 00000000..a44baa51 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/27.png differ diff --git a/libkdegames/carddecks/cards-spaced/28.png b/libkdegames/carddecks/cards-spaced/28.png new file mode 100644 index 00000000..af480ddd Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/28.png differ diff --git a/libkdegames/carddecks/cards-spaced/29.png b/libkdegames/carddecks/cards-spaced/29.png new file mode 100644 index 00000000..622968f4 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/29.png differ diff --git a/libkdegames/carddecks/cards-spaced/3.png b/libkdegames/carddecks/cards-spaced/3.png new file mode 100644 index 00000000..5b31e13b Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/3.png differ diff --git a/libkdegames/carddecks/cards-spaced/30.png b/libkdegames/carddecks/cards-spaced/30.png new file mode 100644 index 00000000..e43e9bd0 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/30.png differ diff --git a/libkdegames/carddecks/cards-spaced/31.png b/libkdegames/carddecks/cards-spaced/31.png new file mode 100644 index 00000000..382d0c11 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/31.png differ diff --git a/libkdegames/carddecks/cards-spaced/32.png b/libkdegames/carddecks/cards-spaced/32.png new file mode 100644 index 00000000..969e921d Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/32.png differ diff --git a/libkdegames/carddecks/cards-spaced/33.png b/libkdegames/carddecks/cards-spaced/33.png new file mode 100644 index 00000000..e9dbd109 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/33.png differ diff --git a/libkdegames/carddecks/cards-spaced/34.png b/libkdegames/carddecks/cards-spaced/34.png new file mode 100644 index 00000000..49d26d66 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/34.png differ diff --git a/libkdegames/carddecks/cards-spaced/35.png b/libkdegames/carddecks/cards-spaced/35.png new file mode 100644 index 00000000..f60149e5 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/35.png differ diff --git a/libkdegames/carddecks/cards-spaced/36.png b/libkdegames/carddecks/cards-spaced/36.png new file mode 100644 index 00000000..49b7044a Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/36.png differ diff --git a/libkdegames/carddecks/cards-spaced/37.png b/libkdegames/carddecks/cards-spaced/37.png new file mode 100644 index 00000000..dafb3402 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/37.png differ diff --git a/libkdegames/carddecks/cards-spaced/38.png b/libkdegames/carddecks/cards-spaced/38.png new file mode 100644 index 00000000..f1d7e030 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/38.png differ diff --git a/libkdegames/carddecks/cards-spaced/39.png b/libkdegames/carddecks/cards-spaced/39.png new file mode 100644 index 00000000..8e045381 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/39.png differ diff --git a/libkdegames/carddecks/cards-spaced/4.png b/libkdegames/carddecks/cards-spaced/4.png new file mode 100644 index 00000000..e8ceab32 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/4.png differ diff --git a/libkdegames/carddecks/cards-spaced/40.png b/libkdegames/carddecks/cards-spaced/40.png new file mode 100644 index 00000000..de378577 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/40.png differ diff --git a/libkdegames/carddecks/cards-spaced/41.png b/libkdegames/carddecks/cards-spaced/41.png new file mode 100644 index 00000000..0e4f3382 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/41.png differ diff --git a/libkdegames/carddecks/cards-spaced/42.png b/libkdegames/carddecks/cards-spaced/42.png new file mode 100644 index 00000000..910524a2 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/42.png differ diff --git a/libkdegames/carddecks/cards-spaced/43.png b/libkdegames/carddecks/cards-spaced/43.png new file mode 100644 index 00000000..f9f218bf Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/43.png differ diff --git a/libkdegames/carddecks/cards-spaced/44.png b/libkdegames/carddecks/cards-spaced/44.png new file mode 100644 index 00000000..fe64a155 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/44.png differ diff --git a/libkdegames/carddecks/cards-spaced/45.png b/libkdegames/carddecks/cards-spaced/45.png new file mode 100644 index 00000000..d914f7b1 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/45.png differ diff --git a/libkdegames/carddecks/cards-spaced/46.png b/libkdegames/carddecks/cards-spaced/46.png new file mode 100644 index 00000000..f6953f9d Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/46.png differ diff --git a/libkdegames/carddecks/cards-spaced/47.png b/libkdegames/carddecks/cards-spaced/47.png new file mode 100644 index 00000000..bfaa56ff Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/47.png differ diff --git a/libkdegames/carddecks/cards-spaced/48.png b/libkdegames/carddecks/cards-spaced/48.png new file mode 100644 index 00000000..b5d46c34 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/48.png differ diff --git a/libkdegames/carddecks/cards-spaced/49.png b/libkdegames/carddecks/cards-spaced/49.png new file mode 100644 index 00000000..f890b5db Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/49.png differ diff --git a/libkdegames/carddecks/cards-spaced/5.png b/libkdegames/carddecks/cards-spaced/5.png new file mode 100644 index 00000000..e160f9b3 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/5.png differ diff --git a/libkdegames/carddecks/cards-spaced/50.png b/libkdegames/carddecks/cards-spaced/50.png new file mode 100644 index 00000000..488eb5fa Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/50.png differ diff --git a/libkdegames/carddecks/cards-spaced/51.png b/libkdegames/carddecks/cards-spaced/51.png new file mode 100644 index 00000000..4d1f41b0 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/51.png differ diff --git a/libkdegames/carddecks/cards-spaced/52.png b/libkdegames/carddecks/cards-spaced/52.png new file mode 100644 index 00000000..345579f4 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/52.png differ diff --git a/libkdegames/carddecks/cards-spaced/6.png b/libkdegames/carddecks/cards-spaced/6.png new file mode 100644 index 00000000..90e8e998 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/6.png differ diff --git a/libkdegames/carddecks/cards-spaced/7.png b/libkdegames/carddecks/cards-spaced/7.png new file mode 100644 index 00000000..374ee771 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/7.png differ diff --git a/libkdegames/carddecks/cards-spaced/8.png b/libkdegames/carddecks/cards-spaced/8.png new file mode 100644 index 00000000..987a24c6 Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/8.png differ diff --git a/libkdegames/carddecks/cards-spaced/9.png b/libkdegames/carddecks/cards-spaced/9.png new file mode 100644 index 00000000..f2855e9f Binary files /dev/null and b/libkdegames/carddecks/cards-spaced/9.png differ diff --git a/libkdegames/carddecks/cards-spaced/COPYRIGHT b/libkdegames/carddecks/cards-spaced/COPYRIGHT new file mode 100644 index 00000000..f54e3a4e --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/COPYRIGHT @@ -0,0 +1,16 @@ +The backs for these cards came from the U.S. National Aeronautics +and Space Administration. The original images can be found at: +http://antwrp.gsfc.nasa.gov/apod/archivepix.html +along with a lot more just like 'em. + +The penguins are by "The PAPA" +and can be found at: +http://biancaneve.ing.unifi.it/~papalini/ +and there's a lot more of those too. + +Copyright (C) 1999 T. Kirk + +This card set is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 of +the License, or (at your option) any later version. diff --git a/libkdegames/carddecks/cards-spaced/index.desktop b/libkdegames/carddecks/cards-spaced/index.desktop new file mode 100644 index 00000000..0e9eadf0 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/index.desktop @@ -0,0 +1,46 @@ +[KDE Backdeck] +Name=Spaced +Name[af]=Gespasieer +Name[az]=BoÅŸluqlu +Name[be]=КоÑÐ¼Ð°Ñ +Name[bg]=Звезден +Name[bn]=সà§à¦ªà§‡à¦¸à¦¯à§à¦•à§à¦¤ +Name[ca]=Espaiades +Name[cs]=Vesmír +Name[de]=Mit Abstand +Name[eo]=Komika +Name[es]=Espaciadas +Name[eu]=Tartea +Name[fr]=Spatial +Name[gl]=Espaciadas +Name[hi]=सà¥à¤ªà¥‡à¤¸à¥à¤¡ +Name[hu]=Å°r +Name[is]=Speisað +Name[it]=Spaziale +Name[ko]=우주 +Name[lv]=Kosmisks +Name[mk]=Раздалечени +Name[mt]=Spazzjat +Name[nb]=Rom-duell +Name[nds]=Afstand +Name[ne]=खाली सà¥à¤¥à¤¾à¤¨ +Name[nl]=Ruimtelijk +Name[nso]=Beetswe Sekgoba +Name[pl]=Przestrzenny +Name[pt]=Espacial +Name[pt_BR]=Espaçado +Name[ro]=SpaÅ£iat +Name[ru]=КоÑÐ¼Ð¾Ñ +Name[sl]=Vesoljski dvoboj +Name[sr]=Размакнут +Name[sr@Latn]=Razmaknut +Name[sv]=Spejsad +Name[ta]=இடம௠விடபà¯à®ªà®Ÿà¯à®Ÿ +Name[tg]=Кайҳонӣ +Name[tr]=BoÅŸluklu +Name[uk]=КоÑмічний +Name[ven]=Hu na zwikala +Name[xh]=ukugqagqeneyo +Name[zu]=Kunezikhala +Preview=11.png +PySol=yes diff --git a/libkdegames/carddecks/cards-warwick/0.png b/libkdegames/carddecks/cards-warwick/0.png new file mode 100644 index 00000000..e8fc5c76 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/0.png differ diff --git a/libkdegames/carddecks/cards-warwick/1.png b/libkdegames/carddecks/cards-warwick/1.png new file mode 100644 index 00000000..79fc960c Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/1.png differ diff --git a/libkdegames/carddecks/cards-warwick/10.png b/libkdegames/carddecks/cards-warwick/10.png new file mode 100644 index 00000000..9b459b5c Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/10.png differ diff --git a/libkdegames/carddecks/cards-warwick/105.png b/libkdegames/carddecks/cards-warwick/105.png new file mode 100644 index 00000000..0f49bdd9 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/105.png differ diff --git a/libkdegames/carddecks/cards-warwick/106.png b/libkdegames/carddecks/cards-warwick/106.png new file mode 100644 index 00000000..4e2371c1 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/106.png differ diff --git a/libkdegames/carddecks/cards-warwick/107.png b/libkdegames/carddecks/cards-warwick/107.png new file mode 100644 index 00000000..c7a19052 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/107.png differ diff --git a/libkdegames/carddecks/cards-warwick/108.png b/libkdegames/carddecks/cards-warwick/108.png new file mode 100644 index 00000000..09ee0a0a Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/108.png differ diff --git a/libkdegames/carddecks/cards-warwick/109.png b/libkdegames/carddecks/cards-warwick/109.png new file mode 100644 index 00000000..a48b134c Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/109.png differ diff --git a/libkdegames/carddecks/cards-warwick/11.png b/libkdegames/carddecks/cards-warwick/11.png new file mode 100644 index 00000000..a78f94b7 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/11.png differ diff --git a/libkdegames/carddecks/cards-warwick/110.png b/libkdegames/carddecks/cards-warwick/110.png new file mode 100644 index 00000000..2a3fccd9 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/110.png differ diff --git a/libkdegames/carddecks/cards-warwick/111.png b/libkdegames/carddecks/cards-warwick/111.png new file mode 100644 index 00000000..20c9f0f9 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/111.png differ diff --git a/libkdegames/carddecks/cards-warwick/112.png b/libkdegames/carddecks/cards-warwick/112.png new file mode 100644 index 00000000..90935873 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/112.png differ diff --git a/libkdegames/carddecks/cards-warwick/113.png b/libkdegames/carddecks/cards-warwick/113.png new file mode 100644 index 00000000..7df5d9b1 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/113.png differ diff --git a/libkdegames/carddecks/cards-warwick/114.png b/libkdegames/carddecks/cards-warwick/114.png new file mode 100644 index 00000000..062ae319 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/114.png differ diff --git a/libkdegames/carddecks/cards-warwick/115.png b/libkdegames/carddecks/cards-warwick/115.png new file mode 100644 index 00000000..fd4b83b4 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/115.png differ diff --git a/libkdegames/carddecks/cards-warwick/116.png b/libkdegames/carddecks/cards-warwick/116.png new file mode 100644 index 00000000..7f1c0dcd Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/116.png differ diff --git a/libkdegames/carddecks/cards-warwick/12.png b/libkdegames/carddecks/cards-warwick/12.png new file mode 100644 index 00000000..41d93ef2 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/12.png differ diff --git a/libkdegames/carddecks/cards-warwick/13.png b/libkdegames/carddecks/cards-warwick/13.png new file mode 100644 index 00000000..b990c512 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/13.png differ diff --git a/libkdegames/carddecks/cards-warwick/14.png b/libkdegames/carddecks/cards-warwick/14.png new file mode 100644 index 00000000..45b94a15 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/14.png differ diff --git a/libkdegames/carddecks/cards-warwick/15.png b/libkdegames/carddecks/cards-warwick/15.png new file mode 100644 index 00000000..46c1f3f6 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/15.png differ diff --git a/libkdegames/carddecks/cards-warwick/16.png b/libkdegames/carddecks/cards-warwick/16.png new file mode 100644 index 00000000..d8d118f9 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/16.png differ diff --git a/libkdegames/carddecks/cards-warwick/17.png b/libkdegames/carddecks/cards-warwick/17.png new file mode 100644 index 00000000..e6e0aa93 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/17.png differ diff --git a/libkdegames/carddecks/cards-warwick/18.png b/libkdegames/carddecks/cards-warwick/18.png new file mode 100644 index 00000000..eb84255b Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/18.png differ diff --git a/libkdegames/carddecks/cards-warwick/19.png b/libkdegames/carddecks/cards-warwick/19.png new file mode 100644 index 00000000..88d4d6d9 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/19.png differ diff --git a/libkdegames/carddecks/cards-warwick/2.png b/libkdegames/carddecks/cards-warwick/2.png new file mode 100644 index 00000000..98e798a1 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/2.png differ diff --git a/libkdegames/carddecks/cards-warwick/20.png b/libkdegames/carddecks/cards-warwick/20.png new file mode 100644 index 00000000..4f75de50 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/20.png differ diff --git a/libkdegames/carddecks/cards-warwick/21.png b/libkdegames/carddecks/cards-warwick/21.png new file mode 100644 index 00000000..39bba2f2 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/21.png differ diff --git a/libkdegames/carddecks/cards-warwick/22.png b/libkdegames/carddecks/cards-warwick/22.png new file mode 100644 index 00000000..733c396d Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/22.png differ diff --git a/libkdegames/carddecks/cards-warwick/23.png b/libkdegames/carddecks/cards-warwick/23.png new file mode 100644 index 00000000..ab00c341 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/23.png differ diff --git a/libkdegames/carddecks/cards-warwick/24.png b/libkdegames/carddecks/cards-warwick/24.png new file mode 100644 index 00000000..eeb73bdd Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/24.png differ diff --git a/libkdegames/carddecks/cards-warwick/25.png b/libkdegames/carddecks/cards-warwick/25.png new file mode 100644 index 00000000..25715b2f Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/25.png differ diff --git a/libkdegames/carddecks/cards-warwick/26.png b/libkdegames/carddecks/cards-warwick/26.png new file mode 100644 index 00000000..e248ded1 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/26.png differ diff --git a/libkdegames/carddecks/cards-warwick/27.png b/libkdegames/carddecks/cards-warwick/27.png new file mode 100644 index 00000000..1bb2ab90 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/27.png differ diff --git a/libkdegames/carddecks/cards-warwick/28.png b/libkdegames/carddecks/cards-warwick/28.png new file mode 100644 index 00000000..33085ffb Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/28.png differ diff --git a/libkdegames/carddecks/cards-warwick/29.png b/libkdegames/carddecks/cards-warwick/29.png new file mode 100644 index 00000000..3f15d8d9 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/29.png differ diff --git a/libkdegames/carddecks/cards-warwick/3.png b/libkdegames/carddecks/cards-warwick/3.png new file mode 100644 index 00000000..7f298774 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/3.png differ diff --git a/libkdegames/carddecks/cards-warwick/30.png b/libkdegames/carddecks/cards-warwick/30.png new file mode 100644 index 00000000..ce1f2dd9 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/30.png differ diff --git a/libkdegames/carddecks/cards-warwick/31.png b/libkdegames/carddecks/cards-warwick/31.png new file mode 100644 index 00000000..fadee1b1 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/31.png differ diff --git a/libkdegames/carddecks/cards-warwick/32.png b/libkdegames/carddecks/cards-warwick/32.png new file mode 100644 index 00000000..4d4391d2 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/32.png differ diff --git a/libkdegames/carddecks/cards-warwick/33.png b/libkdegames/carddecks/cards-warwick/33.png new file mode 100644 index 00000000..7d1a106f Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/33.png differ diff --git a/libkdegames/carddecks/cards-warwick/34.png b/libkdegames/carddecks/cards-warwick/34.png new file mode 100644 index 00000000..8d2197e7 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/34.png differ diff --git a/libkdegames/carddecks/cards-warwick/35.png b/libkdegames/carddecks/cards-warwick/35.png new file mode 100644 index 00000000..e8f5789d Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/35.png differ diff --git a/libkdegames/carddecks/cards-warwick/36.png b/libkdegames/carddecks/cards-warwick/36.png new file mode 100644 index 00000000..32bd75b8 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/36.png differ diff --git a/libkdegames/carddecks/cards-warwick/37.png b/libkdegames/carddecks/cards-warwick/37.png new file mode 100644 index 00000000..8a85b206 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/37.png differ diff --git a/libkdegames/carddecks/cards-warwick/38.png b/libkdegames/carddecks/cards-warwick/38.png new file mode 100644 index 00000000..edc98fa9 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/38.png differ diff --git a/libkdegames/carddecks/cards-warwick/39.png b/libkdegames/carddecks/cards-warwick/39.png new file mode 100644 index 00000000..6ac9b1ab Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/39.png differ diff --git a/libkdegames/carddecks/cards-warwick/4.png b/libkdegames/carddecks/cards-warwick/4.png new file mode 100644 index 00000000..ba94daeb Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/4.png differ diff --git a/libkdegames/carddecks/cards-warwick/40.png b/libkdegames/carddecks/cards-warwick/40.png new file mode 100644 index 00000000..046f281e Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/40.png differ diff --git a/libkdegames/carddecks/cards-warwick/41.png b/libkdegames/carddecks/cards-warwick/41.png new file mode 100644 index 00000000..278f7242 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/41.png differ diff --git a/libkdegames/carddecks/cards-warwick/42.png b/libkdegames/carddecks/cards-warwick/42.png new file mode 100644 index 00000000..e0c8857d Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/42.png differ diff --git a/libkdegames/carddecks/cards-warwick/43.png b/libkdegames/carddecks/cards-warwick/43.png new file mode 100644 index 00000000..126b5d8e Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/43.png differ diff --git a/libkdegames/carddecks/cards-warwick/44.png b/libkdegames/carddecks/cards-warwick/44.png new file mode 100644 index 00000000..220f6e45 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/44.png differ diff --git a/libkdegames/carddecks/cards-warwick/45.png b/libkdegames/carddecks/cards-warwick/45.png new file mode 100644 index 00000000..0a5742e1 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/45.png differ diff --git a/libkdegames/carddecks/cards-warwick/46.png b/libkdegames/carddecks/cards-warwick/46.png new file mode 100644 index 00000000..f4312a3b Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/46.png differ diff --git a/libkdegames/carddecks/cards-warwick/47.png b/libkdegames/carddecks/cards-warwick/47.png new file mode 100644 index 00000000..a4495a2e Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/47.png differ diff --git a/libkdegames/carddecks/cards-warwick/48.png b/libkdegames/carddecks/cards-warwick/48.png new file mode 100644 index 00000000..039755f8 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/48.png differ diff --git a/libkdegames/carddecks/cards-warwick/49.png b/libkdegames/carddecks/cards-warwick/49.png new file mode 100644 index 00000000..dd7b9779 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/49.png differ diff --git a/libkdegames/carddecks/cards-warwick/5.png b/libkdegames/carddecks/cards-warwick/5.png new file mode 100644 index 00000000..d881d1a8 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/5.png differ diff --git a/libkdegames/carddecks/cards-warwick/50.png b/libkdegames/carddecks/cards-warwick/50.png new file mode 100644 index 00000000..5840d1a7 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/50.png differ diff --git a/libkdegames/carddecks/cards-warwick/51.png b/libkdegames/carddecks/cards-warwick/51.png new file mode 100644 index 00000000..04628627 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/51.png differ diff --git a/libkdegames/carddecks/cards-warwick/52.png b/libkdegames/carddecks/cards-warwick/52.png new file mode 100644 index 00000000..07efb60a Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/52.png differ diff --git a/libkdegames/carddecks/cards-warwick/6.png b/libkdegames/carddecks/cards-warwick/6.png new file mode 100644 index 00000000..dbe64d0a Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/6.png differ diff --git a/libkdegames/carddecks/cards-warwick/7.png b/libkdegames/carddecks/cards-warwick/7.png new file mode 100644 index 00000000..16176ad2 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/7.png differ diff --git a/libkdegames/carddecks/cards-warwick/8.png b/libkdegames/carddecks/cards-warwick/8.png new file mode 100644 index 00000000..f62b3441 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/8.png differ diff --git a/libkdegames/carddecks/cards-warwick/9.png b/libkdegames/carddecks/cards-warwick/9.png new file mode 100644 index 00000000..a773d309 Binary files /dev/null and b/libkdegames/carddecks/cards-warwick/9.png differ diff --git a/libkdegames/carddecks/cards-warwick/index.desktop b/libkdegames/carddecks/cards-warwick/index.desktop new file mode 100644 index 00000000..042b6f1b --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/index.desktop @@ -0,0 +1,126 @@ +[KDE Backdeck] +Name=Blue Balloon +Name[af]=Blou Ballon +Name[ar]=بالون أزرق +Name[az]=Göy Balon +Name[be]=Паветраны шар +Name[bg]=Син балон +Name[bn]=নীল বেলà§à¦¨ +Name[br]=Boull glas +Name[bs]=Plavi balon +Name[ca]=Pilota blava +Name[cs]=Modrý balón +Name[cy]=Balwn Glas +Name[da]=BlÃ¥ ballon +Name[de]=Blauer Ballon +Name[el]=Μπλε μπαλόνι +Name[eo]=Blua balono +Name[es]=Globo azul +Name[et]=Sinine õhupall +Name[eu]=Globo urdina +Name[fi]=Sininen pallo +Name[fr]=Ballon bleu +Name[gl]=Globo azul +Name[he]=בלון כחול +Name[hi]=बà¥à¤²à¥‚ बलून +Name[hr]=Plavi balon +Name[hu]=Kék léggömb +Name[is]=Blá blaðra +Name[it]=Pallone blu +Name[ja]=é’風船 +Name[km]=បាល់​ážáŸ€ážœ +Name[ko]=파란 í’ì„  +Name[lt]=MÄ—lynas balionas +Name[lv]=Zili baloni +Name[mk]=Син балон +Name[mt]=Bużżieqa Blu +Name[nb]=BlÃ¥ ballong +Name[nds]=Blaag Ballon +Name[ne]=निलो बेलà¥à¤¨ +Name[nl]=Blauwe ballon +Name[nn]=BlÃ¥ ballong +Name[pa]=ਨੀਲਾ ਗà©à¨¬à¨°à¨¾ +Name[pl]=Niebieski balon +Name[pt]=Balão Azul +Name[pt_BR]=Balão Azul +Name[ro]=Balon albastru +Name[ru]=Воздушный шар +Name[se]=Alit balloÅ‹ga +Name[sk]=Modrý balón +Name[sl]=Modri balon +Name[sr]=Плави балон +Name[sr@Latn]=Plavi balon +Name[sv]=BlÃ¥ ballong +Name[ta]=நீல பலூன௠+Name[tg]=Курраҳои Ҳавоӣ +Name[th]=ลูà¸à¹‚ป่งสีฟ้า - K +Name[tr]=Mavi Balon +Name[uk]=Блакитна кулька +Name[ven]=Baloni la muvhala wa Lutombo +Name[vi]=Bóng bay xanh +Name[xh]=Ibhaluni eblowu +Name[zh_CN]=è“æ°”çƒ +Name[zh_TW]=è—è‰²æ°£çƒ +Name[zu]=Ibhelunde eliluhlaza +Preview=11.png +PySol=false +Comment=Card set supplied by Warwick Allison +Comment[af]=Kaart stel verskaf deur Warwick Allison +Comment[az]=Warwick Allison tÉ™rÉ™findÉ™n düzÉ™ldilÉ™n kart dÉ™stÉ™si +Comment[be]=Калода картаў ад Ворвіка ЭліÑана (Warwick Allison) +Comment[bg]=Колода карти от Warwick Allison +Comment[bn]=কারà§à¦¡à§‡à¦° সেট সরবরাহ করেছেন ওয়ারউইক à¦à¦²à§à¦²à¦¿à¦¸à¦¨ +Comment[bs]=Å pil je dostavio Warwick Allison +Comment[ca]=Joc de cartes aportat per Warwick Allison +Comment[cs]=Sada karet od Warwicka Allisona +Comment[cy]=Set cerdiau wedi ei ddarparu gan Warwick Allison +Comment[da]=Kortspil fra Warwick Allison +Comment[de]=Karten von Warwick Allison +Comment[el]=Σετ καÏτών από τον Warwick Allison +Comment[eo]=Kartaro donita de Warwick Allison +Comment[es]=Juego de cartas suministrado por Warwick Allison +Comment[et]=Warwick Allison'i poolt pakutud kaardipakk +Comment[eu]=Warwick Allison-ek emandako karta-sorta +Comment[fa]=مجموعه کارت توسط وارویک آلیسون تهیه شد +Comment[fi]=Warwick Allison toimittama korttipakka +Comment[fr]=Jeu de cartes fourni par Warwick Allison +Comment[gl]=Baralla proporcionada por Warwick Allison +Comment[he]=ערכת ×”×§×œ×¤×™× ×¡×•×¤×§×” על ידי וורויק ×ליסון +Comment[hi]=ताश की गडà¥à¤¡à¥€ वारविक à¤à¤²à¥€à¤¸à¤¨ दà¥à¤µà¤¾à¤°à¤¾ पà¥à¤°à¤¦à¤¤à¥à¤¤ किया गया +Comment[hr]=Set karata, poklonio Warwick Allison +Comment[hu]=Warwick Allison kártyacsomagja +Comment[is]=Spilastokkur eftir Warwick Allison +Comment[it]=Mazzo di carte fornito da Warwick Allison +Comment[ja]=Warwick Allison 作ã®ã‚«ãƒ¼ãƒ‰ã‚»ãƒƒãƒˆ +Comment[km]=បៀរ​ážáŸ’រូវ​កំណážáŸ‹â€‹áž•áŸ’ដល់​ដោយ Warwick Allison +Comment[ko]=Warwick Allisonì´ ë§Œë“  ì¹´ë“œ ì…‹ +Comment[lt]=Warwick Allison kortų rinkinys +Comment[lv]=KÄrÅ¡u komplekts no Warwick Allison +Comment[mk]=Комплетот карти е обезбеден од Ворвик ÐлиÑон (Warwick Allison) +Comment[mt]=Sett ta' karti mogħti minn Warwick Allison +Comment[nb]=Kortstokk levert av Warwick Allison +Comment[nds]=Koorten vun Warwick Allison +Comment[ne]=वारविक à¤à¤²à¤¿à¤¸à¥‹à¤¨à¤¦à¥à¤µà¤¾à¤°à¤¾ वितरण गरिà¤à¤•à¥‹ कारà¥à¤¡ सेट +Comment[nl]=Kaartset, geleverd door Warwick Allison +Comment[nn]=Kortstokk frÃ¥ Warwick Allison +Comment[pl]=Zestaw kart dostarczony przez Warwicka Allisona +Comment[pt]=Baralho de cartas fornecido por Warwick Allison +Comment[pt_BR]=Jogo de cartas fornecido por Warwick Allison +Comment[ro]=Set de cărÅ£i de joc de Warwick Allison +Comment[ru]=Колода карт от Warwick Allison +Comment[sk]=BalíÄek kariet od Warwicka Allisona +Comment[sl]=Nabor kart od Warwicka Allisona +Comment[sr]=Шпил карата кога је обезбедио Варвик ÐлиÑон (Warwick Allison) +Comment[sr@Latn]=Å pil karata koga je obezbedio Varvik Alison (Warwick Allison) +Comment[sv]=Kortuppsättning tillhandahÃ¥llen av Warwick Allison +Comment[ta]=சீடà¯à®Ÿà¯à®•à¯à®•à®Ÿà¯à®Ÿà¯ விலà¯à®²à®¿à®¯à®®à¯ எலிசனால௠அனà¯à®ªà¯à®ªà®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=Маҷмӯи Кортҳо, ки аз тарафи Warwick Allison пешниҳод шудаанд +Comment[th]=ชุดไพ่สนับสนุนโดย Warwick Allison +Comment[tr]=Kart kümeleri Warwick Allison tarafından saÄŸlanmıştır +Comment[uk]=Ðабір карт від Warwick Allison +Comment[ven]=Garata yo diswa nga Warwick Allison +Comment[vi]=Bá»Ethẻ được cung cấp bởi Warwick Allison +Comment[xh]=Imfano nye yamakhadi inikwe ngu Warwick Allison +Comment[zh_CN]=牌é¢ç”± Warwick Allison æä¾› +Comment[zh_TW]=牌局由 Warwick Allison æä¾› +Comment[zu]=Iqoqo lamakhadi linikelwe ngu-Warwick Allison diff --git a/libkdegames/carddecks/cards-xskat-french/1.png b/libkdegames/carddecks/cards-xskat-french/1.png new file mode 100644 index 00000000..fdc71a26 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/1.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/10.png b/libkdegames/carddecks/cards-xskat-french/10.png new file mode 100644 index 00000000..9dede1c7 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/10.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/11.png b/libkdegames/carddecks/cards-xskat-french/11.png new file mode 100644 index 00000000..4c1dbf2a Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/11.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/12.png b/libkdegames/carddecks/cards-xskat-french/12.png new file mode 100644 index 00000000..e34fc6bb Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/12.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/13.png b/libkdegames/carddecks/cards-xskat-french/13.png new file mode 100644 index 00000000..842935ca Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/13.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/14.png b/libkdegames/carddecks/cards-xskat-french/14.png new file mode 100644 index 00000000..e87706aa Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/14.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/15.png b/libkdegames/carddecks/cards-xskat-french/15.png new file mode 100644 index 00000000..28d82c71 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/15.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/16.png b/libkdegames/carddecks/cards-xskat-french/16.png new file mode 100644 index 00000000..4920f2f4 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/16.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/17.png b/libkdegames/carddecks/cards-xskat-french/17.png new file mode 100644 index 00000000..27658c7c Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/17.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/18.png b/libkdegames/carddecks/cards-xskat-french/18.png new file mode 100644 index 00000000..ea34c973 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/18.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/19.png b/libkdegames/carddecks/cards-xskat-french/19.png new file mode 100644 index 00000000..80a0c22b Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/19.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/2.png b/libkdegames/carddecks/cards-xskat-french/2.png new file mode 100644 index 00000000..de465ef6 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/2.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/20.png b/libkdegames/carddecks/cards-xskat-french/20.png new file mode 100644 index 00000000..4a0aca66 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/20.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/21.png b/libkdegames/carddecks/cards-xskat-french/21.png new file mode 100644 index 00000000..b7c6027f Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/21.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/22.png b/libkdegames/carddecks/cards-xskat-french/22.png new file mode 100644 index 00000000..ae77e460 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/22.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/23.png b/libkdegames/carddecks/cards-xskat-french/23.png new file mode 100644 index 00000000..967385fc Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/23.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/24.png b/libkdegames/carddecks/cards-xskat-french/24.png new file mode 100644 index 00000000..e8b84baf Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/24.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/25.png b/libkdegames/carddecks/cards-xskat-french/25.png new file mode 100644 index 00000000..f933ca25 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/25.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/26.png b/libkdegames/carddecks/cards-xskat-french/26.png new file mode 100644 index 00000000..ab981154 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/26.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/27.png b/libkdegames/carddecks/cards-xskat-french/27.png new file mode 100644 index 00000000..f34477ff Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/27.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/28.png b/libkdegames/carddecks/cards-xskat-french/28.png new file mode 100644 index 00000000..0d1d71ff Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/28.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/29.png b/libkdegames/carddecks/cards-xskat-french/29.png new file mode 100644 index 00000000..80e86965 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/29.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/3.png b/libkdegames/carddecks/cards-xskat-french/3.png new file mode 100644 index 00000000..e4cc9d63 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/3.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/30.png b/libkdegames/carddecks/cards-xskat-french/30.png new file mode 100644 index 00000000..a935d55e Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/30.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/31.png b/libkdegames/carddecks/cards-xskat-french/31.png new file mode 100644 index 00000000..6c8d2883 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/31.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/32.png b/libkdegames/carddecks/cards-xskat-french/32.png new file mode 100644 index 00000000..5af0268b Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/32.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/33.png b/libkdegames/carddecks/cards-xskat-french/33.png new file mode 100644 index 00000000..7bd0fbed Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/33.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/34.png b/libkdegames/carddecks/cards-xskat-french/34.png new file mode 100644 index 00000000..dc8bb29f Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/34.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/35.png b/libkdegames/carddecks/cards-xskat-french/35.png new file mode 100644 index 00000000..7eb3f06f Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/35.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/36.png b/libkdegames/carddecks/cards-xskat-french/36.png new file mode 100644 index 00000000..01902b33 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/36.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/37.png b/libkdegames/carddecks/cards-xskat-french/37.png new file mode 100644 index 00000000..80599469 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/37.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/38.png b/libkdegames/carddecks/cards-xskat-french/38.png new file mode 100644 index 00000000..fb4a37e4 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/38.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/39.png b/libkdegames/carddecks/cards-xskat-french/39.png new file mode 100644 index 00000000..01fe85ae Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/39.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/4.png b/libkdegames/carddecks/cards-xskat-french/4.png new file mode 100644 index 00000000..dff28353 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/4.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/40.png b/libkdegames/carddecks/cards-xskat-french/40.png new file mode 100644 index 00000000..a9e28170 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/40.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/41.png b/libkdegames/carddecks/cards-xskat-french/41.png new file mode 100644 index 00000000..66e69166 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/41.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/42.png b/libkdegames/carddecks/cards-xskat-french/42.png new file mode 100644 index 00000000..92e21830 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/42.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/43.png b/libkdegames/carddecks/cards-xskat-french/43.png new file mode 100644 index 00000000..b39a4afa Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/43.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/44.png b/libkdegames/carddecks/cards-xskat-french/44.png new file mode 100644 index 00000000..94bf0fc7 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/44.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/45.png b/libkdegames/carddecks/cards-xskat-french/45.png new file mode 100644 index 00000000..bbdb0ad6 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/45.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/46.png b/libkdegames/carddecks/cards-xskat-french/46.png new file mode 100644 index 00000000..ed610b6a Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/46.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/47.png b/libkdegames/carddecks/cards-xskat-french/47.png new file mode 100644 index 00000000..2688622a Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/47.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/48.png b/libkdegames/carddecks/cards-xskat-french/48.png new file mode 100644 index 00000000..0dc1d52e Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/48.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/49.png b/libkdegames/carddecks/cards-xskat-french/49.png new file mode 100644 index 00000000..4499c0fd Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/49.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/5.png b/libkdegames/carddecks/cards-xskat-french/5.png new file mode 100644 index 00000000..702b65bc Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/5.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/50.png b/libkdegames/carddecks/cards-xskat-french/50.png new file mode 100644 index 00000000..9b8b6619 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/50.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/51.png b/libkdegames/carddecks/cards-xskat-french/51.png new file mode 100644 index 00000000..1edf32d5 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/51.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/52.png b/libkdegames/carddecks/cards-xskat-french/52.png new file mode 100644 index 00000000..0fe25caa Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/52.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/6.png b/libkdegames/carddecks/cards-xskat-french/6.png new file mode 100644 index 00000000..642225b1 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/6.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/7.png b/libkdegames/carddecks/cards-xskat-french/7.png new file mode 100644 index 00000000..a16236b0 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/7.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/8.png b/libkdegames/carddecks/cards-xskat-french/8.png new file mode 100644 index 00000000..3cb261c4 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/8.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/9.png b/libkdegames/carddecks/cards-xskat-french/9.png new file mode 100644 index 00000000..2b1d9c43 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-french/9.png differ diff --git a/libkdegames/carddecks/cards-xskat-french/COPYRIGHT b/libkdegames/carddecks/cards-xskat-french/COPYRIGHT new file mode 100644 index 00000000..dc217dc3 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/COPYRIGHT @@ -0,0 +1,8 @@ +This PySol cardset was adapted from the game XSkat 4.0 + +Copyright (C) 2004 Gunter Gerhardt (http://www.xskat.de) + +This cardset is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 of +the License, or (at your option) any later version. diff --git a/libkdegames/carddecks/cards-xskat-french/index.desktop b/libkdegames/carddecks/cards-xskat-french/index.desktop new file mode 100644 index 00000000..3dec4a0c --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/index.desktop @@ -0,0 +1,55 @@ +[KDE Backdeck] +Name=XSkat French +Name[be]=ФранцузÑкі XSkat +Name[bg]=ФренÑки модел +Name[bn]=ফরাসি à¦à¦•à§à¦¸-সà§à¦•à¦¾à¦Ÿ +Name[br]=XSkat gallek +Name[bs]=XSkat Francuski +Name[ca]=XSkat francès +Name[cs]=Francouzský XSkat +Name[cy]=XSkat Ffrangeg +Name[da]=XSkat-fransk +Name[de]=XSkat Französisch +Name[el]=XSkat Γαλλικό +Name[eo]=XSkat Franca +Name[es]=XSkat francés +Name[et]=XSkat (Prantsuse) +Name[eu]=XSkat Frantsesa +Name[fa]=XSkat Ùرانسوی +Name[fi]=XSkat Ranska +Name[fr]=XSkat français +Name[he]=XSkat צרפתי +Name[hr]=Francuski XSkat +Name[hu]=FRancia XSkat +Name[is]=XSkat á frönsku +Name[it]=XSkat francese +Name[ja]=XSkat フランス語 +Name[km]=XSkat បារាំង +Name[ko]=프랑스 XSkat +Name[lt]=XSkat PrancÅ«ziÅ¡kai +Name[lv]=XSkat FranÄu +Name[mk]=ФранцуÑки XSkat +Name[nb]=Fransk XSkat +Name[nds]=XSkat (Franzöösch) +Name[ne]=à¤à¤•à¥à¤¸ सà¥à¤•à¥à¤¯à¤¾à¤Ÿ फà¥à¤°à¥‡à¤¨à¥à¤š +Name[nl]=XSkat Frans +Name[nn]=Fransk XSkat +Name[pa]=XSkat ਫਰੈਂਚ +Name[pl]=Francuski skat +Name[pt]=XSkat Francês +Name[pt_BR]=XSkat Francês +Name[ru]=ФранцузÑкий XSkat +Name[se]=FránskkalaÅ¡ XSkat +Name[sk]=XSkat francúzsky +Name[sl]=Francoski XSkat +Name[sr]=XSkat француÑки +Name[sr@Latn]=XSkat francuski +Name[sv]=Fransk X-skat +Name[ta]=XSkat ஃபிரஞà¯à®šà¯ +Name[tg]=XSkat ФаронÑавӣ +Name[tr]=XSkat Fransızca +Name[uk]=Французький XSkat +Name[zh_CN]=XSkat 法语 +Name[zh_TW]=XSkat 法語 +Preview=11.png +PySol=yes diff --git a/libkdegames/carddecks/cards-xskat-german/1.png b/libkdegames/carddecks/cards-xskat-german/1.png new file mode 100644 index 00000000..e679884b Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/1.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/10.png b/libkdegames/carddecks/cards-xskat-german/10.png new file mode 100644 index 00000000..c9590dbb Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/10.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/11.png b/libkdegames/carddecks/cards-xskat-german/11.png new file mode 100644 index 00000000..d1c6f500 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/11.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/12.png b/libkdegames/carddecks/cards-xskat-german/12.png new file mode 100644 index 00000000..5c92d83a Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/12.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/13.png b/libkdegames/carddecks/cards-xskat-german/13.png new file mode 100644 index 00000000..d68f0c00 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/13.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/14.png b/libkdegames/carddecks/cards-xskat-german/14.png new file mode 100644 index 00000000..324e3756 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/14.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/15.png b/libkdegames/carddecks/cards-xskat-german/15.png new file mode 100644 index 00000000..9a516e30 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/15.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/16.png b/libkdegames/carddecks/cards-xskat-german/16.png new file mode 100644 index 00000000..9d9dec03 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/16.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/17.png b/libkdegames/carddecks/cards-xskat-german/17.png new file mode 100644 index 00000000..5dc10fa2 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/17.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/18.png b/libkdegames/carddecks/cards-xskat-german/18.png new file mode 100644 index 00000000..90562629 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/18.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/19.png b/libkdegames/carddecks/cards-xskat-german/19.png new file mode 100644 index 00000000..7e4552ab Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/19.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/2.png b/libkdegames/carddecks/cards-xskat-german/2.png new file mode 100644 index 00000000..1c1f8118 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/2.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/20.png b/libkdegames/carddecks/cards-xskat-german/20.png new file mode 100644 index 00000000..a15534b6 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/20.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/21.png b/libkdegames/carddecks/cards-xskat-german/21.png new file mode 100644 index 00000000..7aca79e7 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/21.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/22.png b/libkdegames/carddecks/cards-xskat-german/22.png new file mode 100644 index 00000000..b487a1c8 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/22.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/23.png b/libkdegames/carddecks/cards-xskat-german/23.png new file mode 100644 index 00000000..761d3bfe Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/23.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/24.png b/libkdegames/carddecks/cards-xskat-german/24.png new file mode 100644 index 00000000..537e590c Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/24.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/25.png b/libkdegames/carddecks/cards-xskat-german/25.png new file mode 100644 index 00000000..09567171 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/25.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/26.png b/libkdegames/carddecks/cards-xskat-german/26.png new file mode 100644 index 00000000..761fc42d Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/26.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/27.png b/libkdegames/carddecks/cards-xskat-german/27.png new file mode 100644 index 00000000..05083aa2 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/27.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/28.png b/libkdegames/carddecks/cards-xskat-german/28.png new file mode 100644 index 00000000..e885a908 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/28.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/29.png b/libkdegames/carddecks/cards-xskat-german/29.png new file mode 100644 index 00000000..5b7244a0 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/29.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/3.png b/libkdegames/carddecks/cards-xskat-german/3.png new file mode 100644 index 00000000..978e63c0 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/3.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/30.png b/libkdegames/carddecks/cards-xskat-german/30.png new file mode 100644 index 00000000..3f8b03ef Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/30.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/31.png b/libkdegames/carddecks/cards-xskat-german/31.png new file mode 100644 index 00000000..9ab830ce Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/31.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/32.png b/libkdegames/carddecks/cards-xskat-german/32.png new file mode 100644 index 00000000..b87d68d0 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/32.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/33.png b/libkdegames/carddecks/cards-xskat-german/33.png new file mode 100644 index 00000000..078abf39 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/33.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/34.png b/libkdegames/carddecks/cards-xskat-german/34.png new file mode 100644 index 00000000..94c5297f Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/34.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/35.png b/libkdegames/carddecks/cards-xskat-german/35.png new file mode 100644 index 00000000..241ba5dc Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/35.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/36.png b/libkdegames/carddecks/cards-xskat-german/36.png new file mode 100644 index 00000000..339a155a Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/36.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/37.png b/libkdegames/carddecks/cards-xskat-german/37.png new file mode 100644 index 00000000..106023f4 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/37.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/38.png b/libkdegames/carddecks/cards-xskat-german/38.png new file mode 100644 index 00000000..848f9696 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/38.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/39.png b/libkdegames/carddecks/cards-xskat-german/39.png new file mode 100644 index 00000000..43eaba16 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/39.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/4.png b/libkdegames/carddecks/cards-xskat-german/4.png new file mode 100644 index 00000000..d69848a0 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/4.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/40.png b/libkdegames/carddecks/cards-xskat-german/40.png new file mode 100644 index 00000000..87619dce Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/40.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/41.png b/libkdegames/carddecks/cards-xskat-german/41.png new file mode 100644 index 00000000..6b09c88c Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/41.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/42.png b/libkdegames/carddecks/cards-xskat-german/42.png new file mode 100644 index 00000000..04a1993b Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/42.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/43.png b/libkdegames/carddecks/cards-xskat-german/43.png new file mode 100644 index 00000000..d5b92e29 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/43.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/44.png b/libkdegames/carddecks/cards-xskat-german/44.png new file mode 100644 index 00000000..6cf2e09b Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/44.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/45.png b/libkdegames/carddecks/cards-xskat-german/45.png new file mode 100644 index 00000000..b6a6a2a7 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/45.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/46.png b/libkdegames/carddecks/cards-xskat-german/46.png new file mode 100644 index 00000000..05f686ab Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/46.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/47.png b/libkdegames/carddecks/cards-xskat-german/47.png new file mode 100644 index 00000000..f31c89f3 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/47.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/48.png b/libkdegames/carddecks/cards-xskat-german/48.png new file mode 100644 index 00000000..a8e1c51e Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/48.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/49.png b/libkdegames/carddecks/cards-xskat-german/49.png new file mode 100644 index 00000000..2a473ac3 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/49.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/5.png b/libkdegames/carddecks/cards-xskat-german/5.png new file mode 100644 index 00000000..66a4b568 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/5.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/50.png b/libkdegames/carddecks/cards-xskat-german/50.png new file mode 100644 index 00000000..3a7d6ac0 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/50.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/51.png b/libkdegames/carddecks/cards-xskat-german/51.png new file mode 100644 index 00000000..09e25a6c Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/51.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/52.png b/libkdegames/carddecks/cards-xskat-german/52.png new file mode 100644 index 00000000..e7d74ae4 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/52.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/6.png b/libkdegames/carddecks/cards-xskat-german/6.png new file mode 100644 index 00000000..e4b1d7de Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/6.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/7.png b/libkdegames/carddecks/cards-xskat-german/7.png new file mode 100644 index 00000000..09e99d70 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/7.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/8.png b/libkdegames/carddecks/cards-xskat-german/8.png new file mode 100644 index 00000000..58fdc56e Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/8.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/9.png b/libkdegames/carddecks/cards-xskat-german/9.png new file mode 100644 index 00000000..b55ee847 Binary files /dev/null and b/libkdegames/carddecks/cards-xskat-german/9.png differ diff --git a/libkdegames/carddecks/cards-xskat-german/COPYRIGHT b/libkdegames/carddecks/cards-xskat-german/COPYRIGHT new file mode 100644 index 00000000..dc217dc3 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/COPYRIGHT @@ -0,0 +1,8 @@ +This PySol cardset was adapted from the game XSkat 4.0 + +Copyright (C) 2004 Gunter Gerhardt (http://www.xskat.de) + +This cardset is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 of +the License, or (at your option) any later version. diff --git a/libkdegames/carddecks/cards-xskat-german/index.desktop b/libkdegames/carddecks/cards-xskat-german/index.desktop new file mode 100644 index 00000000..3f19d526 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/index.desktop @@ -0,0 +1,55 @@ +[KDE Backdeck] +Name=XSkat German +Name[be]=ÐÑмецкі XSkat +Name[bg]=ÐемÑки модел +Name[bn]=জরà§à¦®à¦¨ à¦à¦•à§à¦¸-সà§à¦•à¦¾à¦Ÿ +Name[br]=XSkat alamanek +Name[bs]=XSkat NjemaÄki +Name[ca]=XSkat alemany +Name[cs]=NÄ›mecký XSkat +Name[cy]=XSkat Almaeneg +Name[da]=XSkat-tysk +Name[de]=XSkat Deutsch +Name[el]=XSkat ΓεÏμανικό +Name[eo]=XSkat Germana +Name[es]=XSkat alemán +Name[et]=XSkat (Saksa) +Name[eu]=XSkat alemaniera +Name[fa]=آلمانی XSkat +Name[fi]=XSkat Saksa +Name[fr]=XSkat allemand +Name[he]=XSkat גרמני +Name[hr]=NjemaÄki XSkat +Name[hu]=Német XSkat +Name[is]=XSkat á þýsku +Name[it]=XSkat tedesco +Name[ja]=XSkat ドイツ語 +Name[km]=XSkat អាល្លឺម៉ង់ +Name[ko]=ë…ì¼ XSkat +Name[lt]=XSkat VokiÅ¡kai +Name[lv]=XSkat VÄcu +Name[mk]=ГерманÑки XSkat +Name[nb]=Tysk XSkat +Name[nds]=XSkat (Düütsch) +Name[ne]=à¤à¤•à¥à¤¸ सà¥à¤•à¥à¤¯à¤¾à¤Ÿ जरà¥à¤®à¤¨ +Name[nl]=XSkat Duits +Name[nn]=Tysk XSkat +Name[pa]=XSkat ਜਰਮਨ +Name[pl]=Niemiecki skat +Name[pt]=XSkat Alemã +Name[pt_BR]=XSkat Alemão +Name[ru]=Ðемецкий XSkat +Name[se]=DuiskkalaÅ¡ XSkat +Name[sk]=XScat nemecký +Name[sl]=NemÅ¡ki XSkat +Name[sr]=XSkat немачки +Name[sr@Latn]=XSkat nemaÄki +Name[sv]=Tysk X-skat +Name[ta]=XSkat ஜெரà¯à®®à®©à¯ +Name[tg]=XSkat Олмонӣ +Name[tr]=XSkat Almanca +Name[uk]=Ðімецький XSkat +Name[zh_CN]=XSkat 德语 +Name[zh_TW]=XSkat 德語 +Preview=11.png +PySol=yes diff --git a/libkdegames/carddecks/convertpysols b/libkdegames/carddecks/convertpysols new file mode 100755 index 00000000..5ed10b25 --- /dev/null +++ b/libkdegames/carddecks/convertpysols @@ -0,0 +1,69 @@ +#! /bin/sh + +cd $1 + +convert -format png -geometry "72x96" 01c.gif 1.png +convert -format png -geometry "72x96" 01s.gif 2.png +convert -format png -geometry "72x96" 01h.gif 3.png +convert -format png -geometry "72x96" 01d.gif 4.png + +convert -format png -geometry "72x96" 13c.gif 5.png +convert -format png -geometry "72x96" 13s.gif 6.png +convert -format png -geometry "72x96" 13h.gif 7.png +convert -format png -geometry "72x96" 13d.gif 8.png + +convert -format png -geometry "72x96" 12c.gif 9.png +convert -format png -geometry "72x96" 12s.gif 10.png +convert -format png -geometry "72x96" 12h.gif 11.png +convert -format png -geometry "72x96" 12d.gif 12.png + +convert -format png -geometry "72x96" 11c.gif 13.png +convert -format png -geometry "72x96" 11s.gif 14.png +convert -format png -geometry "72x96" 11h.gif 15.png +convert -format png -geometry "72x96" 11d.gif 16.png + +convert -format png -geometry "72x96" 10c.gif 17.png +convert -format png -geometry "72x96" 10s.gif 18.png +convert -format png -geometry "72x96" 10h.gif 19.png +convert -format png -geometry "72x96" 10d.gif 20.png + +convert -format png -geometry "72x96" 09c.gif 21.png +convert -format png -geometry "72x96" 09s.gif 22.png +convert -format png -geometry "72x96" 09h.gif 23.png +convert -format png -geometry "72x96" 09d.gif 24.png + +convert -format png -geometry "72x96" 08c.gif 25.png +convert -format png -geometry "72x96" 08s.gif 26.png +convert -format png -geometry "72x96" 08h.gif 27.png +convert -format png -geometry "72x96" 08d.gif 28.png + +convert -format png -geometry "72x96" 07c.gif 29.png +convert -format png -geometry "72x96" 07s.gif 30.png +convert -format png -geometry "72x96" 07h.gif 31.png +convert -format png -geometry "72x96" 07d.gif 32.png + +convert -format png -geometry "72x96" 06c.gif 33.png +convert -format png -geometry "72x96" 06s.gif 34.png +convert -format png -geometry "72x96" 06h.gif 35.png +convert -format png -geometry "72x96" 06d.gif 36.png + +convert -format png -geometry "72x96" 05c.gif 37.png +convert -format png -geometry "72x96" 05s.gif 38.png +convert -format png -geometry "72x96" 05h.gif 39.png +convert -format png -geometry "72x96" 05d.gif 40.png + +convert -format png -geometry "72x96" 04c.gif 41.png +convert -format png -geometry "72x96" 04s.gif 42.png +convert -format png -geometry "72x96" 04h.gif 43.png +convert -format png -geometry "72x96" 04d.gif 44.png + +convert -format png -geometry "72x96" 03c.gif 45.png +convert -format png -geometry "72x96" 03s.gif 46.png +convert -format png -geometry "72x96" 03h.gif 47.png +convert -format png -geometry "72x96" 03d.gif 48.png + +convert -format png -geometry "72x96" 02c.gif 49.png +convert -format png -geometry "72x96" 02s.gif 50.png +convert -format png -geometry "72x96" 02h.gif 51.png +convert -format png -geometry "72x96" 02d.gif 52.png + diff --git a/libkdegames/carddecks/decks/deck0.desktop b/libkdegames/carddecks/decks/deck0.desktop new file mode 100644 index 00000000..0388ea4b --- /dev/null +++ b/libkdegames/carddecks/decks/deck0.desktop @@ -0,0 +1,113 @@ +[KDE Cards] +Name=Technics +Name[az]=Texnik +Name[be]=ТÑхніка +Name[bg]=Техника +Name[bn]=টেকনিকà§à¦¸ +Name[bs]=TehniÄki +Name[ca]=Tècniques +Name[cs]=Technika +Name[eo]=Tekniko +Name[es]=Técnicas +Name[et]=Tehnika +Name[eu]=Teknikoa +Name[fi]=Tekniikka +Name[fr]=Technique +Name[gl]=Técnicas +Name[hi]=टेकà¥à¤¨à¤¿à¤•à¥à¤¸ +Name[hr]=Tehnika +Name[hu]=Technikai +Name[id]=Teknik +Name[is]=Tækni +Name[it]=Tecnico +Name[km]=វិធីសាស្ážáŸ’ážš +Name[lt]=Technika +Name[lv]=Tehnika +Name[mk]=Техника +Name[mt]=TekniÄ‹i +Name[nb]=Teknisk +Name[nds]=Technik +Name[ne]=उपाय +Name[nl]=Technisch +Name[nn]=Teknisk +Name[pt]=Técnico +Name[pt_BR]=Técnicas +Name[ro]=Tehnic +Name[ru]=ШеÑтерёнки +Name[se]=TeknihkalaÅ¡ +Name[sk]=Technické +Name[sl]=Tehnika +Name[sr]=Ð¢ÐµÑ…Ð½Ð¸ÐºÑ +Name[sr@Latn]=Tehniks +Name[sv]=Teknik +Name[ta]=நà¯à®£à¯à®•à¯à®•à®™à¯à®•à®³à¯ +Name[tg]=Шомгонаҳо +Name[th]=เทคนิค +Name[tr]=Teknik +Name[uk]=ШеÑтерні +Name[ven]=Madaela +Name[vi]=KÄ© thuật +Name[xh]=iindlela zokudlala +Name[zh_CN]=工艺 +Name[zh_TW]=å·¥è— +Comment=Standard KDE card deck +Comment[af]=Standaard Kde kaart pak +Comment[az]=Standart KDE kart dÉ™stÉ™si +Comment[be]=Ð¡Ñ‚Ð°Ð½Ð´Ð°Ñ€Ñ‚Ð½Ð°Ñ ÐºÐ°Ð»Ð¾Ð´Ð° картаў KDE +Comment[bg]=Стандартна колода карти за KDE +Comment[bn]=কে.ডি.ই.-র সাধারণ তাস সেট +Comment[bs]=Standardni KDE Å¡pil karata +Comment[ca]=Joc de cartes estàndard del KDE +Comment[cs]=Standardní sada karet KDE +Comment[cy]=Set cerdiau safonol KDE +Comment[da]=Standard KDE-kortspil +Comment[de]=Standardmäßige KDE-Karten +Comment[el]=ΠÏοκαθοÏισμένο σÏνολο Ï„Ïάπουλας του KDE +Comment[eo]=Normala KDEa kartaro +Comment[es]=Baraja de cartas estándar de KDE +Comment[et]=Standardne KDE kaardipakk +Comment[eu]=KDE-ren karta-sorta +Comment[fa]=دسته کارت استاندارد KDE +Comment[fi]=Normaali KDE:n korttipakka +Comment[fr]=Jeu de cartes standard de KDE +Comment[gl]=Baralla de cartas estándar de KDE +Comment[he]=חפיסת ×”×§×œ×¤×™× ×”×¡×˜× ×“×¨×˜×™×ª של KDE +Comment[hi]=मानक केडीई ताश गडà¥à¤¡à¥€ +Comment[hr]=Standardni KDE komplet karata +Comment[hu]=Standard KDE kártyacsomag +Comment[is]=Venjulegi KDE spilastokkurinn +Comment[it]=Mazzo di carte standard di KDE +Comment[ja]=KDE 標準カードデッキ +Comment[km]=ហូ​បៀ KDE ážáŸ’នាážâ€‹áž‚ំរូ +Comment[ko]=표준 KDE ì¹´ë“œ ëª¨ìŒ +Comment[lt]=StandartinÄ— KDE kortų kaladÄ— +Comment[lv]=Standarta KDE kÄrÅ¡u galds +Comment[mk]=Стандарден шпил карти на KDE +Comment[mt]=Mazz karti standard tal-KDE +Comment[nb]=Standard KDE-kortstokk +Comment[nds]=KDE-Standardkoorten +Comment[ne]=मानक केडीई कारà¥à¤¡ डेक +Comment[nl]=Standaard KDE-kaartrug +Comment[nn]=Standard KDE-kortstokk +Comment[pa]=ਮਿਆਰੀ KDE ਤਾਸ਼ ਪੱਤੇ +Comment[pl]=Standardowy zestaw kart KDE +Comment[pt]=Baralho de cartas por omissão do KDE +Comment[pt_BR]=Baralho padrão do KDE +Comment[ro]=Set de cărÅ£i de joc KDE standard +Comment[ru]=Ð¡Ñ‚Ð°Ð½Ð´Ð°Ñ€Ñ‚Ð½Ð°Ñ ÐºÐ¾Ð»Ð¾Ð´Ð° карт KDE +Comment[sk]=Å tandardný balíÄek kariet KDE +Comment[sl]=Standardni komplet kart za KDE +Comment[sr]=Стандардни KDE-ов шпил карата +Comment[sr@Latn]=Standardni KDE-ov Å¡pil karata +Comment[sv]=Förvald KDE-kortlek +Comment[ta]=நிலையான கேடிஇ சீடà¯à®Ÿà®¿à®©à¯ மேல௠தளம௠+Comment[tg]=Маҷмӯи кортҳои муқаррарии KDE +Comment[th]=ชุดไพ่มาตรà¸à¸²à¸™à¸‚อง KDE +Comment[tr]=Standart KDE kart destesi +Comment[uk]=Типовий малюнок карт KDE +Comment[ven]=Tshidzhumba tsha magarata tsha murole wa KDE +Comment[vi]=KDE card deck chuẩn +Comment[xh]=Umgangatho womphezulu wamakhadi eKDE +Comment[zh_CN]=标准 KDE 牌垛 +Comment[zh_TW]=標準 KDE 紙牌花色 +Comment[zu]=Isigaxa samakhadi esilinganisiwe we-KDE diff --git a/libkdegames/carddecks/decks/deck0.png b/libkdegames/carddecks/decks/deck0.png new file mode 100644 index 00000000..0a877a51 Binary files /dev/null and b/libkdegames/carddecks/decks/deck0.png differ diff --git a/libkdegames/carddecks/decks/deck1.desktop b/libkdegames/carddecks/decks/deck1.desktop new file mode 100644 index 00000000..0a8c2cf7 --- /dev/null +++ b/libkdegames/carddecks/decks/deck1.desktop @@ -0,0 +1,56 @@ +[KDE Cards] +Name=Fairy +Name[af]=Fee +Name[az]=PÉ™ri +Name[be]=Ð¤ÐµÑ +Name[bg]=Ð¤ÐµÑ +Name[bn]=পরী +Name[ca]=Fada +Name[cs]=Víla +Name[cy]=Tylwyth Teg +Name[da]=Fe +Name[de]=Elfe +Name[el]=ÎεÏάιδα +Name[eo]=Elfo +Name[es]=Hada +Name[et]=Haldjas +Name[eu]=Maitagarria +Name[fa]=جن Ùˆ پری +Name[fi]=Keijukainen +Name[fr]=Féérie +Name[gl]=Fada +Name[hi]=परी +Name[hr]=Vila +Name[hu]=Tündéres +Name[is]=Ãlfur +Name[it]=Fata +Name[km]=áž‘áŸážœážáž¶ +Name[ko]=요정 +Name[lt]=FÄ—ja +Name[lv]=Pasaku +Name[mk]=Самовила +Name[mt]=Għafrid +Name[nb]=Fe +Name[nds]=Fee +Name[ne]=अपà¥à¤¸à¤°à¤¾ +Name[nl]=Fee +Name[nn]=Fe +Name[pl]=Wróżkarski +Name[pt]=Fada +Name[pt_BR]=Mágico +Name[ro]=Basm +Name[ru]=Ð¤ÐµÑ +Name[sr]=Вила +Name[sr@Latn]=Vila +Name[sv]=Älva +Name[ta]=தேவதை +Name[tg]=Парӣ +Name[tr]=Peri +Name[uk]=Чарівний +Name[uz]=Pari +Name[uz@cyrillic]=Пари +Name[ven]=Zwi a pfesesea +Name[vi]=Xinh đẹp +Name[zh_CN]=仙女 +Name[zh_TW]=仙女 +Name[zu]=Okunomlingo diff --git a/libkdegames/carddecks/decks/deck1.png b/libkdegames/carddecks/decks/deck1.png new file mode 100644 index 00000000..247875a2 Binary files /dev/null and b/libkdegames/carddecks/decks/deck1.png differ diff --git a/libkdegames/carddecks/decks/deck10.desktop b/libkdegames/carddecks/decks/deck10.desktop new file mode 100644 index 00000000..5e871ede --- /dev/null +++ b/libkdegames/carddecks/decks/deck10.desktop @@ -0,0 +1,66 @@ +[KDE Cards] +Name=Classic Blue +Name[af]=Klasieke Blou +Name[ar]=أزرق كلاسيكي +Name[az]=Klassik Göy +Name[be]=КлаÑічны Ñіні +Name[bg]=КлаÑичеÑко Ñиньо +Name[bn]=কà§à¦²à¦¾à¦¸à¦¿à¦• নীল +Name[bs]=KlasiÄni plavi +Name[ca]=Blau clàssic +Name[cs]=Klasická modrá +Name[cy]=Glas Clasurol +Name[da]=Klassisk blÃ¥ +Name[de]=Klassisches Blau +Name[el]=Κλασσικό μπλε +Name[eo]=Klasika bluo +Name[es]=Azul clásico +Name[et]=Klassikaline sinine +Name[eu]=Urdin klasikoa +Name[fa]=آبی کلاسیک‌ +Name[fi]=Klassinen sininen +Name[fr]=Bleu classique +Name[gl]=Azul clásico +Name[he]=כחול קלסי +Name[hi]=आदरà¥à¤¶ नीला +Name[hr]=KlasiÄno plava +Name[hu]=Klasszikus kék +Name[id]=Biru klasik +Name[is]=Klassískur blár +Name[it]=Blu classico +Name[ja]=クラシックブルー +Name[km]=ážáŸ€ážœâ€‹áž€áŸ’លាស៊ិក +Name[ko]=ê³ ì „ 파란색 +Name[lt]=KlasikinÄ— mÄ—lyna +Name[lv]=Klasiski zils +Name[mk]=КлаÑично Ñино +Name[mt]=Blu Klassiku +Name[nb]=Klassisk BlÃ¥ +Name[nds]=Klass'sch Blaag +Name[ne]=उतà¥à¤•à¥ƒà¤·à¥à¤Ÿ निलो +Name[nl]=Klassiek blauw +Name[nn]=Klassisk blÃ¥ +Name[pa]=ਟਕਸਾਲੀ ਨੀਲੇ +Name[pl]=Klasyczny niebieski +Name[pt]=Azul Clássico +Name[pt_BR]=Azul Clássico +Name[ro]=Albastru clasic +Name[ru]=КлаÑÑичеÑкий Ñиний +Name[se]=KlassihkalaÅ¡ alit +Name[sk]=Klasické modré +Name[sl]=KlasiÄna modra +Name[sr]=КлаÑични плави +Name[sr@Latn]=KlasiÄni plavi +Name[sv]=Klassisk blÃ¥ +Name[ta]=சிறநà¯à®¤ நீலம௠+Name[tg]=Кабуди КлаÑÑикӣ +Name[th]=ฟ้าคลาสสิค +Name[tr]=Klasik Mavi +Name[uk]=Блакитний +Name[ven]=Muvhala wa Lutombo wa Maimo +Name[vi]=Màu xanh cá»EÄ‘iển +Name[wa]=Bleu classike +Name[xh]=Umbala oblowu omdala +Name[zh_CN]=ç»å…¸è“ +Name[zh_TW]=å¤å…¸è— +Name[zu]=Ubuluhlaza obuhle diff --git a/libkdegames/carddecks/decks/deck10.png b/libkdegames/carddecks/decks/deck10.png new file mode 100644 index 00000000..c84dcf73 Binary files /dev/null and b/libkdegames/carddecks/decks/deck10.png differ diff --git a/libkdegames/carddecks/decks/deck11.desktop b/libkdegames/carddecks/decks/deck11.desktop new file mode 100644 index 00000000..8d0fbb44 --- /dev/null +++ b/libkdegames/carddecks/decks/deck11.desktop @@ -0,0 +1,66 @@ +[KDE Cards] +Name=Classic Red +Name[af]=Klasieke Rooi +Name[ar]=أحمر كلاسيكي +Name[az]=Klassik Qırmızı +Name[be]=КлаÑічны чырвоны +Name[bg]=КлаÑичеÑко червено +Name[bn]=কà§à¦²à¦¾à¦¸à¦¿à¦• লাল +Name[bs]=KlasiÄni crveni +Name[ca]=Vermell clàssic +Name[cs]=Klasická Äervená +Name[cy]=Coch Clasurol +Name[da]=Klassisk Rød +Name[de]=Klassisches Rot +Name[el]=Κλασσικό κόκκινο +Name[eo]=Klasika ruÄo +Name[es]=Rojo clásico +Name[et]=Klassikaline punane +Name[eu]=Gorri klasikoa +Name[fa]=قرمز کلاسیک +Name[fi]=Klassinen punainen +Name[fr]=Rouge classique +Name[gl]=Vermello clásico +Name[he]=××“×•× ×§×œ×¡×™ +Name[hi]=आदरà¥à¤¶ लाल +Name[hr]=KlasiÄno crvena +Name[hu]=klasszikus vörös +Name[id]=Merah klasik +Name[is]=Klassískur rauður +Name[it]=Rosso classico +Name[ja]=クラシックレッド +Name[km]=ក្រហម​ក្លាស៊ិក +Name[ko]=ê³ ì „ 빨간색 +Name[lt]=KlasikinÄ— raudona +Name[lv]=Klasiski sarkans +Name[mk]=КлаÑично црвено +Name[mt]=Aħmar Klassiku +Name[nb]=Klassisk Rød +Name[nds]=Klass'sch Root +Name[ne]=उतà¥à¤•à¥ƒà¤·à¥à¤Ÿ रातो +Name[nl]=Klassiek rood +Name[nn]=Klassisk raud +Name[pa]=ਟਕਸਾਲੀ ਲਾਲ +Name[pl]=Klasyczny czerwony +Name[pt]=Vermelho Clássico +Name[pt_BR]=Vermelho Clássico +Name[ro]=RoÅŸu clasic +Name[ru]=КлаÑÑичеÑкий краÑный +Name[se]=KlassihkalaÅ¡ ruoksat +Name[sk]=Klasické Äervené +Name[sl]=KlasiÄna rdeÄa +Name[sr]=КлаÑични црвени +Name[sr@Latn]=KlasiÄni crveni +Name[sv]=Klassisk röd +Name[ta]= சிறநà¯à®¤ சிகபà¯à®ªà¯ +Name[tg]=Сурхи КлаÑÑикӣ +Name[th]=à¹à¸”งคลาสสิค +Name[tr]=Klasik Kırmızı +Name[uk]=Червоний +Name[ven]=Muvhala Mutshuku wa Maimo +Name[vi]=Äá»Ecá»EÄ‘iển +Name[wa]=Rodje classike +Name[xh]=Umbala obomvu wakudala +Name[zh_CN]=ç»å…¸çº¢ +Name[zh_TW]=å¤å…¸ç´… +Name[zu]=Ububomvu obuhle diff --git a/libkdegames/carddecks/decks/deck11.png b/libkdegames/carddecks/decks/deck11.png new file mode 100644 index 00000000..0474a551 Binary files /dev/null and b/libkdegames/carddecks/decks/deck11.png differ diff --git a/libkdegames/carddecks/decks/deck12.desktop b/libkdegames/carddecks/decks/deck12.desktop new file mode 100644 index 00000000..aa0918bd --- /dev/null +++ b/libkdegames/carddecks/decks/deck12.desktop @@ -0,0 +1,41 @@ +[KDE Cards] +Name=Chin +Name[af]=Ken +Name[az]=ÇənÉ™ +Name[be]=Памада +Name[bg]=Чин +Name[bn]=চিবà§à¦• +Name[cs]=Brada +Name[cy]=Gên +Name[eo]=VizaÄo +Name[eu]=Kokotsa +Name[fr]=Visage +Name[hi]=ठोढ़ी +Name[hr]=Brada +Name[is]=Haka +Name[it]=Mento +Name[ja]=中国 +Name[km]=ចង្កា +Name[lt]=Smakras +Name[lv]=Seja +Name[mk]=Брада +Name[nds]=Kinn +Name[ne]=चिन +Name[nl]=Kin +Name[pl]=Podbródek +Name[ro]=Bărbie +Name[ru]=Помада +Name[sl]=Džin +Name[sr]=Брада +Name[sr@Latn]=Brada +Name[sv]=Pingla +Name[ta]=தாடை +Name[tg]=Манаҳ +Name[tr]=Çene +Name[uk]=КраÑÑƒÐ½Ñ +Name[uz@cyrillic]=Чин +Name[ven]=Tshitefu +Name[xh]=Isilevu +Name[zh_CN]=å¤´åƒ +Name[zh_TW]=中國 +Name[zu]=Isilevu diff --git a/libkdegames/carddecks/decks/deck12.png b/libkdegames/carddecks/decks/deck12.png new file mode 100644 index 00000000..df9f6b71 Binary files /dev/null and b/libkdegames/carddecks/decks/deck12.png differ diff --git a/libkdegames/carddecks/decks/deck13.desktop b/libkdegames/carddecks/decks/deck13.desktop new file mode 100644 index 00000000..37180379 --- /dev/null +++ b/libkdegames/carddecks/decks/deck13.desktop @@ -0,0 +1,63 @@ +[KDE Cards] +Name=Copy +Name[af]=Kopie +Name[ar]=إنسخ +Name[az]=Köçür +Name[be]=СкапіÑваць +Name[bg]=Ръка +Name[bn]=কপি +Name[br]=Eilañ +Name[bs]=Kopiraj +Name[ca]=Còpia +Name[cs]=Kopie +Name[cy]=Copio +Name[da]=Kopi +Name[de]=Kopie +Name[eo]=Mano +Name[es]=Copia +Name[et]=Koopia +Name[eu]=Kopiatu +Name[fi]=Kopio +Name[fo]=Avrit +Name[fr]=Copie +Name[ga]=Cóipeáil +Name[gl]=Copia +Name[hi]=नक़ल +Name[hr]=Kopija +Name[hu]=Másolás +Name[id]=Salin +Name[is]=Afrit +Name[it]=Impronta +Name[ja]=コピー +Name[km]=ចម្លង +Name[lt]=Kopija +Name[lv]=Kopija +Name[mk]=Копија +Name[mt]=Kopja +Name[nb]=Kopier +Name[nds]=Kopie +Name[ne]=पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¤¿ +Name[nl]=Hand +Name[nn]=Kopi +Name[pl]=Kopia +Name[pt]=Cópia +Name[pt_BR]=Cópia +Name[ro]=Copie +Name[ru]=Отпечаток +Name[se]=Máŋgus +Name[sl]=Prepis +Name[sr]=Копија +Name[sr@Latn]=Kopija +Name[sv]=Kopia +Name[ta]=படியெட௠+Name[tg]=Ðақш +Name[tr]=Kopyala +Name[uk]=ÐšÐ¾Ð¿Ñ–Ñ +Name[uz]=Nusxa +Name[uz@cyrillic]=ÐуÑха +Name[ven]=Tshikopololwa +Name[wa]=Copyî +Name[xh]=Ukukopa +Name[zh_CN]=å¤åˆ¶ +Name[zh_TW]=å¾©åˆ¶å“ +Name[zu]=Khiphela diff --git a/libkdegames/carddecks/decks/deck13.png b/libkdegames/carddecks/decks/deck13.png new file mode 100644 index 00000000..377ec7a1 Binary files /dev/null and b/libkdegames/carddecks/decks/deck13.png differ diff --git a/libkdegames/carddecks/decks/deck14.desktop b/libkdegames/carddecks/decks/deck14.desktop new file mode 100644 index 00000000..fd5df9f8 --- /dev/null +++ b/libkdegames/carddecks/decks/deck14.desktop @@ -0,0 +1,66 @@ +[KDE Cards] +Name=Penguin +Name[af]=Pikkewyn +Name[ar]=بطريق +Name[az]=Pinqvin +Name[be]=Пінгвін +Name[bg]=Пингвин +Name[bn]=পেঙà§à¦—à§à¦‡à¦¨ +Name[br]=Penn-gwenn +Name[bs]=Pingvin +Name[ca]=Pingüí +Name[cs]=TuÄňák +Name[cy]=Pengwin +Name[da]=Pingvin +Name[de]=Pinguin +Name[el]=Πιγκουίνος +Name[eo]=Pingveno +Name[es]=Pingüino +Name[et]=Pingviin +Name[eu]=Pinguinoa +Name[fa]=پنگوئن +Name[fi]=Pingviini +Name[fr]=Pingouin +Name[gl]=Pingüín +Name[he]=פינגווין +Name[hi]=पेंगà¥à¤µà¤¿à¤¨ +Name[hr]=Pingvin +Name[hu]=Pingvin +Name[is]=Mörgæs +Name[it]=Pinguino +Name[km]=áž•áŸáž“ឃ្វីន +Name[ko]=펭귄 +Name[lt]=Pingvinas +Name[lv]=PingvÄ«ns +Name[mk]=Пингвин +Name[mt]=Pingwin +Name[nb]=Pingvin +Name[nds]=Pinguin +Name[ne]=पेनà¥à¤—à¥à¤‡à¤¨ +Name[nl]=Pinguïn +Name[nn]=Pingvin +Name[pa]=ਪੈਂਗੂਇਨ +Name[pl]=Pingwin +Name[pt]=Pinguim +Name[pt_BR]=Pingüim +Name[ro]=Pinguin +Name[ru]=Пингвин +Name[se]=PiÅ‹viidna +Name[sk]=TuÄniak +Name[sl]=Pingvin +Name[sr]=Пингвин +Name[sr@Latn]=Pingvin +Name[sv]=Pingvin +Name[ta]=பெனà¯à®•à¯à®¯à®¿à®©à¯ +Name[tg]=Пингвин +Name[th]=เพนà¸à¸§à¸´à¸™ +Name[tr]=Penguen +Name[uk]=Пінгвін +Name[uz]=Pingvin +Name[uz@cyrillic]=Пингвин +Name[vi]=Chim cánh cụt +Name[wa]=Pingwin +Name[xh]=Ipenguin +Name[zh_CN]=ä¼é¹… +Name[zh_TW]=ä¼éµ +Name[zu]=Inyoni yasemanzini diff --git a/libkdegames/carddecks/decks/deck14.png b/libkdegames/carddecks/decks/deck14.png new file mode 100644 index 00000000..694632bd Binary files /dev/null and b/libkdegames/carddecks/decks/deck14.png differ diff --git a/libkdegames/carddecks/decks/deck15.desktop b/libkdegames/carddecks/decks/deck15.desktop new file mode 100644 index 00000000..92015b81 --- /dev/null +++ b/libkdegames/carddecks/decks/deck15.desktop @@ -0,0 +1,19 @@ +[KDE Cards] +Name=Tristan +Name[be]=ТрыÑтан +Name[bg]=ТриÑтан +Name[bn]=তà§à¦°à¦¿à¦¸à§à¦¤à¦¾à¦¨ +Name[eo]=Ornamo +Name[hi]=टà¥à¤°à¤¾à¤ˆà¤¸à¥à¤Ÿà¤¾à¤¨ +Name[hu]=Trisztán +Name[lv]=Tristans +Name[mk]=ТриÑтан +Name[ne]=टà¥à¤°à¤¿à¤¸à¥à¤Ÿà¤¾à¤¨ +Name[pt]=Tristão +Name[ru]=ТриÑтан +Name[sr]=ТриÑтан +Name[ta]=டà¯à®°à®¿à®¸à¯à®Ÿà®©à¯ +Name[tg]=ТриÑтан +Name[uk]=ТріÑтан +Name[uz@cyrillic]=ТриÑтан +Name[zu]=I-Tristan diff --git a/libkdegames/carddecks/decks/deck15.png b/libkdegames/carddecks/decks/deck15.png new file mode 100644 index 00000000..73c3b92c Binary files /dev/null and b/libkdegames/carddecks/decks/deck15.png differ diff --git a/libkdegames/carddecks/decks/deck16.desktop b/libkdegames/carddecks/decks/deck16.desktop new file mode 100644 index 00000000..11ec9761 --- /dev/null +++ b/libkdegames/carddecks/decks/deck16.desktop @@ -0,0 +1,60 @@ +[KDE Cards] +Name=Grandma +Name[af]=Ouma +Name[ar]=جدَة +Name[az]=NÉ™nÉ™ +Name[be]=Ð‘Ð°Ð±ÑƒÐ»Ñ +Name[bg]=Стар Ñтил +Name[bn]=ঠাকà§à¦°à¦®à¦¾, দাদী, নানী ইতà§à¦¯à¦¾à¦¦à¦¿ +Name[br]=Mamm-goz +Name[ca]=Àvia +Name[cs]=BabiÄka +Name[cy]=Nain +Name[da]=Bedstemor +Name[de]=Großmutter +Name[el]=Γιαγιά +Name[eo]=Avinjo +Name[es]=Abuela +Name[et]=Vanaema +Name[eu]=Amona +Name[fi]=Isoäiti +Name[fo]=Omma +Name[fr]=Grand-mère +Name[gl]=Aboa +Name[he]=×¡×‘×ª× +Name[hi]=दादी अमà¥à¤®à¤¾ +Name[hr]=Baka +Name[hu]=Nagyi +Name[is]=Amma +Name[it]=Nonna +Name[km]=យាយ +Name[ko]=할머니 +Name[lv]=VecmÄmiņa +Name[mk]=СтаровремÑко +Name[mt]=Nanna +Name[nb]=Bestemor +Name[nds]=Oma +Name[ne]=हजà¥à¤°à¤†à¤®à¤¾ +Name[nl]=Grootmoeder +Name[nn]=Bestemor +Name[pl]=Babcia +Name[pt]=Avó +Name[pt_BR]=Vovó +Name[ro]=Bunica +Name[ru]=Ð‘Ð°Ð±ÑƒÐ»Ñ +Name[se]=Ãhkku +Name[sk]=Stará mama +Name[sl]=Babica +Name[sr]=Бака +Name[sr@Latn]=Baka +Name[sv]=Farmor +Name[ta]=பாடà¯à®Ÿà®¿ +Name[tg]=Модаркалон +Name[tr]=Büyük anne +Name[uk]=БабуÑÑ +Name[ven]=Makhulu vha mukegulu +Name[wa]=Grand-mere +Name[xh]=Umakhulu +Name[zh_CN]=ç¥–æ¯ +Name[zh_TW]=ç¥–æ¯ +Name[zu]=Ugogo diff --git a/libkdegames/carddecks/decks/deck16.png b/libkdegames/carddecks/decks/deck16.png new file mode 100644 index 00000000..8310ecbe Binary files /dev/null and b/libkdegames/carddecks/decks/deck16.png differ diff --git a/libkdegames/carddecks/decks/deck17.desktop b/libkdegames/carddecks/decks/deck17.desktop new file mode 100644 index 00000000..1537939f --- /dev/null +++ b/libkdegames/carddecks/decks/deck17.desktop @@ -0,0 +1,65 @@ +[KDE Cards] +Name=Modern Red +Name[af]=Moderne Rooi +Name[ar]=أحمر حديث +Name[az]=ÇaÄŸdaÅŸ Qırmızı +Name[be]=СучаÑны чырвоны +Name[bg]=Модерно червено +Name[bn]=আধà§à¦¨à¦¿à¦• লাল +Name[bs]=Moderna crvena +Name[ca]=Vermell modern +Name[cs]=Moderní Äervená +Name[cy]=Coch Cyfoes +Name[da]=Moderne rød +Name[de]=Modernes Rot +Name[el]=ΜοντέÏνο κόκκινο +Name[eo]=Moderna ruÄo +Name[es]=Rojo moderno +Name[et]=Modernpunane +Name[eu]=Gorri modernoa +Name[fa]=قرمز مدرن‌ +Name[fi]=Moderni punainen +Name[fr]=Rouge moderne +Name[gl]=Vermello moderno +Name[he]=××“×•× ×ž×•×“×¨× ×™ +Name[hi]=आधà¥à¤¨à¤¿à¤• लाल +Name[hr]=Suvremeno crvena +Name[hu]=Modern vörös +Name[is]=Nútíma rauður +Name[it]=Rosso moderno +Name[ja]=モダンレッド +Name[km]=ពណ៌​ក្រហម​ទំនើប +Name[ko]=í˜„ëŒ€ì  ë¹¨ê°„ìƒ‰ +Name[lt]=Moderni raudona +Name[lv]=Moderni sarkans +Name[mk]=Модерно црвено +Name[mt]=Aħmar modern +Name[nb]=Moderne Rød +Name[nds]=Modern Root +Name[ne]=उनà¥à¤¨à¤¤ रातो +Name[nl]=Modern rood +Name[nn]=Moderne raud +Name[pa]=ਨਵਾਂ ਲਾਲ +Name[pl]=Nowoczesny czerwony +Name[pt]=Vermelho Moderno +Name[pt_BR]=Vermelho Moderno +Name[ro]=RoÅŸu modern +Name[ru]=Современный краÑный +Name[se]=OÄ‘Ä‘aáigásaÅ¡ ruoksat +Name[sk]=Moderné Äervené +Name[sl]=Moderna rdeÄa +Name[sr]=Модерни црвени +Name[sr@Latn]=Moderni crveni +Name[sv]=Modern röd +Name[ta]=நவீன சிகபà¯à®ªà¯ +Name[tg]=Сурхи Замонавӣ +Name[th]=à¹à¸”งทันสมัย +Name[tr]=Modern Kırmızı +Name[uk]=Плетінь +Name[ven]=Muvhala Mutshuku wa Tshizwino +Name[vi]=Màu Ä‘á»Ehiện đại +Name[wa]=Rodje modiene +Name[xh]=Umbala omtsha obomvu +Name[zh_CN]=现代红 +Name[zh_TW]=ç¾ä»£ç´… +Name[zu]=Ububomvu besimanje diff --git a/libkdegames/carddecks/decks/deck17.png b/libkdegames/carddecks/decks/deck17.png new file mode 100644 index 00000000..71054897 Binary files /dev/null and b/libkdegames/carddecks/decks/deck17.png differ diff --git a/libkdegames/carddecks/decks/deck18.desktop b/libkdegames/carddecks/decks/deck18.desktop new file mode 100644 index 00000000..c3266a98 --- /dev/null +++ b/libkdegames/carddecks/decks/deck18.desktop @@ -0,0 +1,19 @@ +[KDE Cards] +Name=Holstentor +Name[be]=Палац +Name[bg]=Замък +Name[bn]=হোলসà§à¦Ÿà§‡à¦¨à¦Ÿà¦° +Name[eo]=Holsten-pordego +Name[hi]=हॉलà¥à¤¸à¥à¤Ÿà¥‡à¤‚टर +Name[lv]=HolÅ¡teina +Name[mk]=ХолÑтентор +Name[nds]=Holstendoor +Name[ne]=होलà¥à¤¸à¤Ÿà¥‡à¤¨à¥à¤Ÿà¤° +Name[ru]=Замок +Name[sr]=ХолÑтентор +Name[ta]=ஹாலà¯à®¸à¯à®Ÿà¯†à®©à¯à®Ÿà®°à¯ +Name[tg]=Қулф +Name[uk]=Замок +Name[ven]=Vhushavhelo +Name[zh_CN]=豪斯顿门 +Name[zu]=I-Holstentor diff --git a/libkdegames/carddecks/decks/deck18.png b/libkdegames/carddecks/decks/deck18.png new file mode 100644 index 00000000..fc93b02f Binary files /dev/null and b/libkdegames/carddecks/decks/deck18.png differ diff --git a/libkdegames/carddecks/decks/deck19.desktop b/libkdegames/carddecks/decks/deck19.desktop new file mode 100644 index 00000000..d77c2c98 --- /dev/null +++ b/libkdegames/carddecks/decks/deck19.desktop @@ -0,0 +1,63 @@ +[KDE Cards] +Name=Horizon +Name[af]=Horison +Name[ar]=الأÙÙ‚ +Name[az]=Ãœfüq +Name[be]=Гарызонт +Name[bg]=Хоризонт +Name[bn]=দিগনà§à¦¤ +Name[br]=Dremmwel +Name[bs]=Horizont +Name[ca]=Horitzó +Name[cs]=Horizont +Name[cy]=Gorwel +Name[da]=Horisont +Name[de]=Horizont +Name[el]=ΟÏίζοντας +Name[eo]=Horizonto +Name[es]=Horizonte +Name[et]=Horisont +Name[eu]=Zeruertza +Name[fa]=اÙÙ‚ +Name[fi]=Horisontti +Name[ga]=Léaslíne +Name[gl]=Horizonte +Name[he]=×ופק +Name[hi]=कà¥à¤·à¤¿à¤¤à¤¿à¤œ +Name[hr]=Horizont +Name[hu]=Horizont +Name[is]=Sjóndeildarhringur +Name[it]=Orizzonte +Name[km]=ជើង​មáŸážƒ +Name[ko]=수í‰ì„  +Name[lv]=Horizonts +Name[mk]=Хоризонт +Name[mt]=Orizzont +Name[nb]=Horisont +Name[nds]=Kimm +Name[ne]=कà¥à¤·à¤¿à¤¤à¤¿à¤œ +Name[nn]=Horisont +Name[pa]=ਖੇਤਰ +Name[pl]=Horyzont +Name[pt]=Horizonte +Name[pt_BR]=Horizonte +Name[ro]=Orizont +Name[ru]=Горизонт +Name[sk]=Horizont +Name[sl]=Obzorje +Name[sr]=Хоризонт +Name[sr@Latn]=Horizont +Name[sv]=Horisont +Name[ta]=ஹாரிஸான௠+Name[tg]=Уфуқ +Name[tr]=Ufuk +Name[uk]=Обрій +Name[uz]=Ufq +Name[uz@cyrillic]=Уфқ +Name[ven]=Magumoni a shango +Name[vi]=Chân trá»i +Name[wa]=Roye di cir +Name[xh]=Ulundi +Name[zh_CN]=地平线 +Name[zh_TW]=地平線 +Name[zu]=Okusesibhakabhakeni diff --git a/libkdegames/carddecks/decks/deck19.png b/libkdegames/carddecks/decks/deck19.png new file mode 100644 index 00000000..1526e0b3 Binary files /dev/null and b/libkdegames/carddecks/decks/deck19.png differ diff --git a/libkdegames/carddecks/decks/deck2.desktop b/libkdegames/carddecks/decks/deck2.desktop new file mode 100644 index 00000000..4e3906b2 --- /dev/null +++ b/libkdegames/carddecks/decks/deck2.desktop @@ -0,0 +1,49 @@ +[KDE Cards] +Name=Starrise +Name[ar]=ظهور النجوم +Name[be]=Узыход зоркі +Name[bg]=Звезди +Name[bn]=তারার উদয় +Name[ca]=Estrellat +Name[cs]=Úsvit +Name[cy]=Codiad y sêr +Name[de]=Sternenaufgang +Name[eo]=Stellumo +Name[et]=Tähetõus +Name[eu]=Izarrak +Name[fa]=پدیدار شدن ستاره +Name[fi]=Tähdennousu +Name[fr]=Étoiles +Name[he]=צ×ת ×”×›×•×›×‘×™× +Name[hi]=सà¥à¤Ÿà¤¾à¤°-राइज़ +Name[hr]=Uspon zvijezda +Name[hu]=Csillagok +Name[is]=Ris stjarna +Name[it]=Alba spaziale +Name[ko]=별무리 +Name[lv]=Zvaigžņu lÄ“kts +Name[mk]=Изгрев +Name[mt]=SemaStilel +Name[nb]=Stjerner +Name[nds]=Steernopgang +Name[ne]=सà¥à¤Ÿà¤¾à¤°à¤°à¤¾à¤‡à¤œ +Name[nl]=Sterrijzenis +Name[nn]=Stjerner +Name[pa]=ਚੜਦਾ ਤਾਰਾ +Name[pl]=Wschód gwiazdy +Name[ro]=Stelar +Name[ru]=ВоÑход звезды +Name[se]=Násttit +Name[sk]=Východ hviezd +Name[sl]=Vzhod zvezde +Name[sr]=Сазвежђе +Name[sr@Latn]=Sazvežđe +Name[sv]=StjärnuppgÃ¥ng +Name[ta]=நடà¯à®šà®¤à¯à®¤à®¿à®° உதயம௠+Name[tg]=Пайдоиши Ñитора +Name[uk]=Схід зірки +Name[ven]=Vhubva naledzi +Name[xh]=Inkwenkwezi ephumayo +Name[zh_CN]=星空 +Name[zh_TW]=星空 +Name[zu]=Ukuvuka kwezinkanyezi diff --git a/libkdegames/carddecks/decks/deck2.png b/libkdegames/carddecks/decks/deck2.png new file mode 100644 index 00000000..52f1b67a Binary files /dev/null and b/libkdegames/carddecks/decks/deck2.png differ diff --git a/libkdegames/carddecks/decks/deck20.desktop b/libkdegames/carddecks/decks/deck20.desktop new file mode 100644 index 00000000..3403b23f --- /dev/null +++ b/libkdegames/carddecks/decks/deck20.desktop @@ -0,0 +1,71 @@ +[KDE Cards] +Name=Flowers +Name[af]=Blomme +Name[ar]=أزهار +Name[az]=ÇiçəklÉ™r +Name[be]=Кветкі +Name[bg]=Ð¦Ð²ÐµÑ‚Ñ +Name[bn]=ফà§à¦² +Name[br]=Bleunioù +Name[bs]=Cvijeće +Name[ca]=Flors +Name[cs]=KvÄ›tiny +Name[cy]=Blodau +Name[da]=Blomster +Name[de]=Blumen +Name[el]=ΛουλοÏδια +Name[eo]=Floroj +Name[es]=Flores +Name[et]=Lilled +Name[eu]=Loreak +Name[fa]=گلها +Name[fi]=Kukat +Name[fo]=Blómur +Name[fr]=Fleurs +Name[ga]=Bláthanna +Name[gl]=Flores +Name[he]=×¤×¨×—×™× +Name[hi]=पà¥à¤·à¥à¤ª +Name[hr]=Cvjetovi +Name[hu]=Virágok +Name[is]=Blóm +Name[it]=Fiori +Name[ja]=花 +Name[km]=ផ្កា +Name[ko]=꽃 +Name[lt]=GÄ—lÄ—s +Name[lv]=PuÄ·es +Name[mk]=Цвеќиња +Name[mt]=Fjuri +Name[nb]=Blomster +Name[nds]=Blomen +Name[ne]=फूल +Name[nl]=Bloemen +Name[nn]=Blomar +Name[nso]=Maloba +Name[pa]=ਫà©à©±à¨² +Name[pl]=Kwiaty +Name[pt]=Flores +Name[pt_BR]=Flores +Name[ro]=Flori +Name[ru]=Цветы +Name[se]=LieÄ‘it +Name[sk]=Kvety +Name[sl]=Rože +Name[sr]=Цвеће +Name[sr@Latn]=Cveće +Name[sv]=Blommor +Name[ta]=மலரà¯à®•à®³à¯ +Name[tg]=Гулҳо +Name[th]=ดอà¸à¹„ม้ +Name[tr]=Çiçekler +Name[uk]=Квіти +Name[uz]=Gullar +Name[uz@cyrillic]=Гуллар +Name[ven]=Maluvha +Name[vi]=Hoa +Name[wa]=Fleurs +Name[xh]=Iintyatyambo +Name[zh_CN]=鲜花 +Name[zh_TW]=鮮花 +Name[zu]=Izimbali diff --git a/libkdegames/carddecks/decks/deck20.png b/libkdegames/carddecks/decks/deck20.png new file mode 100644 index 00000000..73d76887 Binary files /dev/null and b/libkdegames/carddecks/decks/deck20.png differ diff --git a/libkdegames/carddecks/decks/deck21.desktop b/libkdegames/carddecks/decks/deck21.desktop new file mode 100644 index 00000000..b170ee53 --- /dev/null +++ b/libkdegames/carddecks/decks/deck21.desktop @@ -0,0 +1,65 @@ +[KDE Cards] +Name=Carpet +Name[af]=Tapyt +Name[ar]=سجادة +Name[az]=Xalı +Name[be]=Дыван +Name[bg]=Килим +Name[bn]=গালিচা +Name[bs]=Ćilim +Name[ca]=Catifa +Name[cs]=Koberec +Name[cy]=Carped +Name[da]=Tæppe +Name[de]=Teppich +Name[el]=Μοκέτα +Name[eo]=TapiÅo +Name[es]=Alfombra +Name[et]=Vaip +Name[eu]=Tapiza +Name[fa]=Ùرش +Name[fi]=Matto +Name[fr]=Tapis +Name[gl]=Alfombra +Name[he]=שטיח +Name[hi]=कालीन +Name[hr]=Tepih +Name[hu]=SzÅ‘nyeg +Name[is]=Teppi +Name[it]=Tappeto +Name[km]=កម្រាល​ព្រំ +Name[ko]=ì–‘íƒ„ìž +Name[lt]=Kilimas +Name[lv]=PaklÄjs +Name[mk]=Килим +Name[mt]=Tapit +Name[nb]=Teppe +Name[nds]=Teppich +Name[ne]=कारà¥à¤ªà¥‡à¤Ÿ +Name[nl]=Tapijt +Name[nn]=Teppe +Name[pa]=ਦਰੀ +Name[pl]=Dywan +Name[pt]=Carpete +Name[pt_BR]=Carpete +Name[ro]=Carpetă +Name[ru]=Ковёр +Name[se]=Máhttá +Name[sl]=Preproga +Name[sr]=Тепих +Name[sr@Latn]=Tepih +Name[sv]=Matta +Name[ta]=கமà¯à®ªà®³à®®à¯ +Name[tg]=Гилем +Name[th]=พรม +Name[tr]=Halı +Name[uk]=Килим +Name[uz]=Gilam +Name[uz@cyrillic]=Гилам +Name[ven]=Dzithovho +Name[vi]=Thảm +Name[wa]=Carpete +Name[xh]=Ikhaphethi +Name[zh_CN]=地毯 +Name[zh_TW]=地毯 +Name[zu]=UKhaphethi diff --git a/libkdegames/carddecks/decks/deck21.png b/libkdegames/carddecks/decks/deck21.png new file mode 100644 index 00000000..204156ba Binary files /dev/null and b/libkdegames/carddecks/decks/deck21.png differ diff --git a/libkdegames/carddecks/decks/deck22.desktop b/libkdegames/carddecks/decks/deck22.desktop new file mode 100644 index 00000000..940b4f53 --- /dev/null +++ b/libkdegames/carddecks/decks/deck22.desktop @@ -0,0 +1,58 @@ +[KDE Cards] +Name=Bathing +Name[af]=Swem +Name[ar]=يتحمم +Name[az]=ÇimmÉ™ +Name[be]=Купанне +Name[bg]=Море +Name[bn]=গোসল, সà§à¦¨à¦¾à¦¨ +Name[bs]=Kupanje +Name[ca]=Balneari +Name[cs]=Koupel +Name[cy]=Ymolchi +Name[da]=Badning +Name[de]=Baden +Name[eo]=Banloko +Name[es]=Balneario +Name[et]=Suplus +Name[eu]=Bainua +Name[fi]=Kylpy +Name[fr]=Baignade +Name[gl]=Baño +Name[he]=מקלחת +Name[hi]=बाथिंग +Name[hr]=Kupanje +Name[hu]=FürdÅ‘ +Name[is]=à baði +Name[it]=Balneare +Name[km]=កំពុង​ងូážâ€‹áž‘ឹក +Name[ko]=목욕 +Name[lt]=MaudynÄ—s +Name[lv]=PeldÄ“Å¡anÄs +Name[mk]=Капење +Name[mt]=Għawm +Name[nb]=Bading +Name[nds]=Baden +Name[ne]=नà¥à¤¹à¤¾à¤à¤•à¥‹ +Name[nl]=Baden +Name[nn]=Bad +Name[pl]=KÄ…piel +Name[pt]=Banho +Name[pt_BR]=Banho +Name[ro]=ÃŽmbăiere +Name[ru]=Купание +Name[se]=Lávgudeapmi +Name[sl]=Kopel +Name[sr]=Купање +Name[sr@Latn]=Kupanje +Name[sv]=Bad +Name[ta]=கà¯à®³à®¿à®¯à®²à¯ +Name[tg]=Оббозӣ +Name[tr]=Yüzme +Name[uk]=ÐšÑƒÐ¿Ð°Ð½Ð½Ñ +Name[ven]=Hu khou tambiwa +Name[vi]=Tắm +Name[xh]=Ukuhlamba +Name[zh_CN]=游泳 +Name[zh_TW]=游泳 +Name[zu]=Ukugeza diff --git a/libkdegames/carddecks/decks/deck22.png b/libkdegames/carddecks/decks/deck22.png new file mode 100644 index 00000000..ff3f9834 Binary files /dev/null and b/libkdegames/carddecks/decks/deck22.png differ diff --git a/libkdegames/carddecks/decks/deck23.desktop b/libkdegames/carddecks/decks/deck23.desktop new file mode 100644 index 00000000..ef096f15 --- /dev/null +++ b/libkdegames/carddecks/decks/deck23.desktop @@ -0,0 +1,55 @@ +[KDE Cards] +Name=Oasis +Name[af]=Oase +Name[ar]=واحة +Name[az]=Oazis +Name[be]=ÐÐ°Ð·Ñ–Ñ +Name[bg]=ÐžÐ°Ð·Ð¸Ñ +Name[bn]=মরà§à¦¦à§à¦¯à¦¾à¦¨ +Name[br]=Oazis +Name[bs]=Oaza +Name[cs]=Oáza +Name[da]=Oase +Name[de]=Oase +Name[el]=Όαση +Name[eo]=Oazo +Name[et]=Oaas +Name[eu]=Oasia +Name[fa]=آبادی +Name[fo]=Oasa +Name[hi]=ओà¤à¤¸à¤¿à¤¸ +Name[hr]=Oaza +Name[hu]=Oázis +Name[is]=Vin +Name[it]=Oasi +Name[ko]=오아시스 +Name[lt]=OazÄ— +Name[lv]=OÄze +Name[mk]=Оаза +Name[mt]=Oażi +Name[nb]=Oase +Name[nds]=Oaas +Name[ne]=ओसिस +Name[nl]=Oase +Name[nn]=Oase +Name[pl]=Oaza +Name[pt]=Oásis +Name[pt_BR]=Oásis +Name[ru]=ÐžÐ°Ð·Ð¸Ñ +Name[sk]=Oáza +Name[sl]=Oaza +Name[sr]=Оаза +Name[sr@Latn]=Oaza +Name[sv]=Oas +Name[ta]=ஒயாஸிஸ௠+Name[tg]=ÐžÐ°Ð·Ð¸Ñ +Name[th]=โอเอซิส +Name[tr]=Vaha +Name[uk]=ÐžÐ°Ð·Ð¸Ñ +Name[uz]=Voha +Name[uz@cyrillic]=Воҳа +Name[vi]=Ô'c đảo +Name[xh]=Indawo esentlango enamanzi +Name[zh_CN]=绿洲 +Name[zh_TW]=綠洲 +Name[zu]=Indawo evundileyo diff --git a/libkdegames/carddecks/decks/deck23.png b/libkdegames/carddecks/decks/deck23.png new file mode 100644 index 00000000..a3025212 Binary files /dev/null and b/libkdegames/carddecks/decks/deck23.png differ diff --git a/libkdegames/carddecks/decks/deck24.desktop b/libkdegames/carddecks/decks/deck24.desktop new file mode 100644 index 00000000..002af784 --- /dev/null +++ b/libkdegames/carddecks/decks/deck24.desktop @@ -0,0 +1,60 @@ +[KDE Cards] +Name=Konqi +Name[be]=Конкі +Name[bg]=Конки +Name[bn]=কনকি +Name[cs]=Konqui +Name[eo]=Konĉja +Name[hi]=के-ऑनà¥à¤—ी +Name[it]=Konqui +Name[lv]=Konvi +Name[ne]=कोनà¥à¤•à¥à¤µà¥€ +Name[ru]=Конки +Name[sr]=Конки +Name[sr@Latn]=Konki +Name[ta]=கானà¯à®•à®¿ +Name[tg]=Конки +Name[uk]=Конкі +Name[zu]=I-Konqi +Comment=Modern Konqi - play the family carddeck\nDesign: Laura Layland\n \nKonqi by Stefan Spatz\n +Comment[bn]=আধà§à¦¨à¦¿à¦• কনকি - à¦à¦•à¦Ÿà¦¿ পারিবারিক তাস খেলা\nডিজাইন: লরা লেলà§à¦¯à¦¾à¦¨à§à¦¡\n \nকনকি: সà§à¦Ÿà¦¿à¦«à§‡à¦¨ সà§à¦ªà¦¾à¦œ\n +Comment[bs]=Modern Konqi - igrajte sa porodiÄnim Å¡pilom\nDesign: Laura Layland\n \nKonqi by Stefan Spatz\n +Comment[ca]=Konqi modern - jugueu amb la baralla familiar\nDisseny: Laura Layland\n \nKonqi per Stefan Spatz\n +Comment[cs]=Moderní Konqi - hrajte rodinnou hru\nNávrh: Laura Laylanda\n \nKonqiho vytvoÅ™il Stefan Spatz\n +Comment[cy]=Set cerdiau cyfoes Konqi - chwarae yn erbyn y teulu\nDylunio:Laura Layland\n \nKonqi gan Stefan Spatz\n +Comment[da]=Modern Konqi - spil familiekortspillet\nDesign: Laura Layland\n \nKonqi by Stefan Spatz\n +Comment[de]=Modernes Konqi - Spielen Sie das Familienspiel\nDesign: Laura Laylanda\n \nKonqi von Stefan Spatz\n +Comment[el]=ΜοντέÏνος Konqi - play the family θέμα καÏτών\nΣχεδίαση: Laura Layland\n \nKonqi από Stefan Spatz\n +Comment[eo]=Moderna Konĉjo - ludu per la familiokartaro\nDesegno: Laura Layland\n \nKonĉjo de Stefan Spatz\n +Comment[es]=Konqi moderno - juegue con la baraja familiar\nDiseño: Laura Layland\n \nKonqi por Stefan Spatz\n +Comment[et]=Modern Konqi - play the family carddeck\nDesign: Laura Layland\n \nKonqi: Stefan Spatz\n +Comment[eu]=Konqi modernoa - kartetan jokatzeko\nDiseinua: Laura Layland\n \nKonqi-ren egilea Stefan Spatz\n +Comment[fa]=Konqi مدرن - بازی خانوادگی carddeck\nطرح: لورا \n لایلند \nKonqi توسط استÙان اسپاتز\n +Comment[fi]=Moderni Konqi - perheen korttipakka\nSuunnittelu: Laura Layland\n \nKonqi Stefan Spatza\n +Comment[fr]=Konqi moderne - pour jouer aux cartes\nConception : Laura Layland\n \nKonqi par Stefan Spatz\n +Comment[he]=קונקי מודרני - חפיסת ×§×œ×™× ×œ×›×œ השפחה\nעיצוב: Laura Layland\n \nKonqi by Stefan Spatz\n +Comment[hr]=Suvremeni Konqi - obiteljska igra s kartama\nDizajn: Laura Layland\n \nKonqi: Stefan Spatz\n +Comment[hu]=Modern Konqi - családi kártyacsomag\nTervezte: Laura Layland\n \nKonqi: Stefan Spatz\n +Comment[it]=Konqui Moderno - carte familiari\nDesign: Laura Layland\n\nKonqui di Stefan Spatz\n +Comment[ja]=モダン Konqi - ファミリå‘ã‘カードデッキ\nデザイン: Laura Layland\n \nKonqi ã®ä½œè€…: Stefan Spatz\n +Comment[lt]=Modern Konqi - žaisktie Å¡eimos kortų žaidimÄ…\nDizainas: Laura Layland\n \nKatie by Agnieszka Czajkowska\n \n +Comment[lv]=Modernais Konkvi - spÄ“lÄ“t pie Ä£imenes kÄrÅ¡u galda\n Dizains: Laura Layland\n \n Konkvi no Stefan Spatza\n +Comment[mk]=Модерен Konqi - играјте Ñо Ñемејниот шпил карти\nДизајн: Laura Layland\n\nKatie од Agnieszka Czajkowska\n +Comment[nb]=Moderne Konqi – familiekortstokken\nUtforming: Laura Layland\n \nKonqi av Stefan Spatz\n +Comment[nds]=Modern Konqi - Speel mit de Familienkoorten\nDesign: Laura Layland\n \nKonqi vun Stefan Spatz\n +Comment[ne]=आधà¥à¤¨à¤¿à¤• कà¥à¤µà¥‹à¤¨à¥à¤•à¥€ - पारिवारिक कारà¥à¤¡à¤¡à¥‡à¤•\nडिजाइन पà¥à¤²à¥‡: लाउरा लेलà¥à¤¯à¤¾à¤¨à¥à¤¡\n \nKonqi, सà¥à¤Ÿà¥‡à¤«à¤¨ सà¥à¤ªà¤¾à¤°à¥à¤Ÿà¤œà¤¦à¥à¤µà¤¾à¤°à¤¾\n +Comment[nl]=Modern Konqi - speel met de familie-kaartdek\nDesign: Laura Layland\n \nKonqi door Stefan Spatz\n +Comment[nn]=Moderne Konqi – familiekortstokken\nUtforming: Laura Layland\n \nKonqi av Stefan Spatz\n +Comment[pl]=Nowoczesny Konqi - zagraj w grÄ™ rodzinnÄ…\nProjekt: Laura Laylanda\n a\nKonqi: Stefan Spatza\n +Comment[pt]=Konqi Moderno - o baralho de cartas familiar\nConcepção: Laura Layland\n \nKonqi por Stefan Spatza\n +Comment[pt_BR]=Konqi Moderno - jogue com o baralho da família\nDesign: Laura Layland\n \nKonqi por Stefan Spatz\n +Comment[ru]=Колода Ñ ÑемейÑтвом Конки\nДизайн: Лаура ЛейлÑнд (Laura Layland) \nКонки нариÑован Штефаном Спартцом (Stefan Spatz) +Comment[sk]=Moderný Konqi - hrajte rodinné kartové hry\nDesign: Laura Laylanda\n a\nKonqi od Stefana Spatza\n +Comment[sl]=Moderni Konqi - igrajte z družinskim kupom kart\nOblikovanje: Laura Layland\n \nKonqi od Stefana Spatza\n +Comment[sr]=Модеран Конки - играјте Ñа породичним шпилом\nДизајн: Лора Лејленд (Laura Layland)\n \nКонки: Штефан Шпац (Stefan Spatz)\n +Comment[sr@Latn]=Moderan Konki - igrajte sa porodiÄnim Å¡pilom\nDizajn: Lora Lejlend (Laura Layland)\n \nKonki: Å tefan Å pac (Stefan Spatz)\n +Comment[sv]=Modern Konqi - spela familjens kortlek\nDesign: Laura Layland\n \nKonqi av Stefan Spatza\n +Comment[ta]=மாரà¯à®Ÿà®©à¯ கானà¯à®•à®¿ - கà¯à®Ÿà¯à®®à¯à®ªà®šà¯ சீடà¯à®Ÿà¯à®¤à¯ தளதà¯à®¤à¯ˆ விளையாடà¯\nவடிவமைபà¯à®ªà¯: லௌரா லேலாணà¯à®Ÿà¯\n \nKonqi by Stefan Spatz\n +Comment[uk]=СучаÑний Конкі - зіграйте у Ñімейні карти\nРозробка: Laura Layland\n \nKatie від Agnieszka Czajkowska\n +Comment[wa]=Modiene Konqi - cwÃ¥rdjeus des familes\nDessins: Laura Layland\n \nKonqi pa Stefan Spatz\n +Comment[zh_TW]=ç¾ä»£ Konqi - 玩家庭牌局\n設計︰Laura Layland...\nKonqi by Stefan Spatz... diff --git a/libkdegames/carddecks/decks/deck24.png b/libkdegames/carddecks/decks/deck24.png new file mode 100644 index 00000000..0596e919 Binary files /dev/null and b/libkdegames/carddecks/decks/deck24.png differ diff --git a/libkdegames/carddecks/decks/deck3.desktop b/libkdegames/carddecks/decks/deck3.desktop new file mode 100644 index 00000000..a383495e --- /dev/null +++ b/libkdegames/carddecks/decks/deck3.desktop @@ -0,0 +1,60 @@ +[KDE Cards] +Name=Romantic +Name[af]=Romantiese +Name[ar]=رومانسي +Name[az]=Romantik +Name[be]=Рамантыка +Name[bg]=Романтика +Name[bn]=রোমানà§à¦Ÿà¦¿à¦• +Name[bs]=Romantika +Name[ca]=Romàntic +Name[cs]=Romantika +Name[cy]=Rhamantus +Name[da]=Romantisk +Name[de]=Romantisch +Name[el]=Ρομαντικό +Name[eo]=Romantiko +Name[es]=Romántico +Name[et]=Romantika +Name[eu]=Erromantikoa +Name[fa]=خیال‌انگیز +Name[fi]=Romanttinen +Name[fr]=Romantique +Name[gl]=Romántica +Name[he]=רומנטי +Name[hi]=रूमानी +Name[hr]=Romantika +Name[hu]=Romantikus +Name[is]=Rómantískt +Name[it]=Romantico +Name[km]=មនោសញ្ចáŸážáž“ា +Name[ko]=로맨틱 +Name[lt]=Romantika +Name[lv]=Romantika +Name[mk]=Романтика +Name[mt]=Romantiku +Name[nb]=Romantisk +Name[nds]=Romantsch +Name[ne]=रोमानà¥à¤Ÿà¤¿à¤• +Name[nl]=Romantisch +Name[nn]=Romantisk +Name[pa]=ਰà©à¨®à¨¾à¨‚ਸਵਾਦੀ +Name[pl]=Romantyczny +Name[pt]=Romântico +Name[pt_BR]=Romântico +Name[ru]=Романтика +Name[se]=RomántalaÅ¡ +Name[sk]=Romantické +Name[sl]=RomantiÄno +Name[sr]=Романтичан +Name[sr@Latn]=RomantiÄan +Name[sv]=Romantisk +Name[ta]=ரொமானà¯à®Ÿà®¿à®•à¯ +Name[tg]=ХаёлпараÑÑ‚Ó£ +Name[th]=โรà¹à¸¡à¸™à¸•à¸´à¸ +Name[uk]=Романтичний +Name[vi]=Lãng mạn +Name[wa]=Romantike +Name[zh_CN]=浪漫 +Name[zh_TW]=浪漫的 +Name[zu]=I-Romantic diff --git a/libkdegames/carddecks/decks/deck3.png b/libkdegames/carddecks/decks/deck3.png new file mode 100644 index 00000000..dced5af7 Binary files /dev/null and b/libkdegames/carddecks/decks/deck3.png differ diff --git a/libkdegames/carddecks/decks/deck4.desktop b/libkdegames/carddecks/decks/deck4.desktop new file mode 100644 index 00000000..c23f4963 --- /dev/null +++ b/libkdegames/carddecks/decks/deck4.desktop @@ -0,0 +1,93 @@ +[KDE Cards] +Name=Panda +Name[ar]=باندا +Name[be]=Панда +Name[bg]=Панда +Name[bn]=পানà§à¦¡à¦¾ +Name[el]=Πάντα +Name[eo]=Pando +Name[fa]=پاندا +Name[he]=פנדה +Name[hi]=पाणà¥à¤¡à¤¾ +Name[is]=Pandabjörn +Name[ko]=íŒ¬ë” +Name[mk]=Панда +Name[ne]=पानà¥à¤¡à¤¾ +Name[pa]=ਪਾਂਡਾ +Name[ru]=Панда +Name[sr]=Панда +Name[ta]=பாணà¯à®Ÿà®¾ +Name[tg]=Панда +Name[th]=à¹à¸žà¸™à¸”้า +Name[uk]=Панда +Name[uz@cyrillic]=Панда +Name[ven]=Tshivhingwi +Name[vi]=Gấu mèo +Name[xh]=Ibhere +Name[zh_CN]=熊猫 +Name[zh_TW]=熊貓 +Name[zu]=Ibhele +Comment=Dedicated to WWF +Comment[af]=Opgedra na Wwf +Comment[ar]=إهداء إلى المؤسسة العالمية للحÙاظ على البيئة (WWF) +Comment[az]=WWFyÉ™ hasr edilib +Comment[be]=ПрыÑвÑчаецца WWF +Comment[bg]=ПоÑвещава Ñе на WWF +Comment[bn]=WWF-à¦à¦° পà§à¦°à¦¤à¦¿ উতà§â€à¦¸à¦°à§à¦—ীকৃত +Comment[bs]=Posvećen WWFu +Comment[ca]=Dedicat a WWF +Comment[cs]=VÄ›nované WWF +Comment[cy]=Cyflwynedig i\'r WWF +Comment[da]=Dedikeret til WWF +Comment[de]=Dem WWF gewidmet +Comment[el]=ΑφιεÏωμένο στο WWF +Comment[eo]=Dediĉita al WWF +Comment[es]=Dedicado a WWF +Comment[et]=Pühendatud organisatsioonile WWF +Comment[eu]=WWF-ren ohorean +Comment[fa]=مختص WWF +Comment[fi]=Omistettu WWF:lle +Comment[fr]=Dédicacé au WWF +Comment[gl]=Adicado a WWF +Comment[he]=מוקדש לקרן העולמית +Comment[hi]=डबà¥à¤²à¤¯à¥‚डबà¥à¤²à¤¯à¥‚à¤à¤« को समरà¥à¤ªà¤¿à¤¤ +Comment[hr]=Posvećeno WWF-u +Comment[hu]=A WWF-nek dedikálva +Comment[is]=Tileinkað WWF +Comment[it]=Dedicato al WWF +Comment[ja]=WWF ã«æ§ã +Comment[km]=ឧទ្ទិស​ជូន WWF +Comment[ko]=WWFì— ë°”ì¹¨ +Comment[lt]=Skirta WWF +Comment[lv]=VeltÄ«ts WWF +Comment[mk]=ПоÑветено на WWF (ÑветÑка фондација за дивиот Ñвет) +Comment[mt]=Dedikat lill-WWF +Comment[nb]=Dedikert til WWF +Comment[nds]=Den WWF toeegt +Comment[ne]=डबà¥à¤²à¥‚डबà¥à¤²à¥‚à¤à¤« पà¥à¤°à¤¤à¤¿ समरà¥à¤ªà¤¿à¤¤ +Comment[nl]=Opgedragen aan WWF (WereldNatuurFonds) +Comment[nn]=Tileigna WWF +Comment[pa]=WWF ਨੂੰ ਸਮਰਪਤ +Comment[pl]=Przeznaczony do WWF +Comment[pt]=Dedicado ao WWF +Comment[pt_BR]=Dedicado ao WWF +Comment[ro]=Dedicat lui WWF +Comment[ru]=ПоÑвÑщаетÑÑ WWF +Comment[sk]=Venované WWF +Comment[sl]=PosveÄeno WWF +Comment[sr]=ПоÑвећено WWF-у +Comment[sr@Latn]=Posvećeno WWF-u +Comment[sv]=Tillägnad WWF +Comment[ta]= WWF-கà¯à®•à¯ à®…à®°à¯à®ªà¯à®ªà®£à®¿à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=Бахшида шудааÑÑ‚ ба WWF +Comment[tr]=WWF'e adanmış +Comment[uk]=ПриÑвÑчено WWF +Comment[uz]=WWF tashkilotiga bagÊ»ishlangan +Comment[uz@cyrillic]=WWF ташкилотига бағишланган +Comment[ven]=Yo livhiswa kha WWF +Comment[vi]=Dâng tặng WWF +Comment[wa]=Dicåçté Ã¥ WWF +Comment[xh]=Yenzelwe ku WWF +Comment[zh_CN]=献给 WWF +Comment[zh_TW]=ç»çµ¦ WWF +Comment[zu]=Inikelwe kwi-WWF diff --git a/libkdegames/carddecks/decks/deck4.png b/libkdegames/carddecks/decks/deck4.png new file mode 100644 index 00000000..23fa4010 Binary files /dev/null and b/libkdegames/carddecks/decks/deck4.png differ diff --git a/libkdegames/carddecks/decks/deck5.desktop b/libkdegames/carddecks/decks/deck5.desktop new file mode 100644 index 00000000..5cbe2d6f --- /dev/null +++ b/libkdegames/carddecks/decks/deck5.desktop @@ -0,0 +1,67 @@ +[KDE Cards] +Name=Water +Name[ar]=ماء +Name[az]=Su +Name[be]=Вада +Name[bg]=Вода +Name[bn]=পানি +Name[br]=Dou +Name[bs]=Voda +Name[ca]=Aigua +Name[cs]=Voda +Name[cy]=Dŵr +Name[da]=Vand +Name[de]=Wasser +Name[el]=ÎεÏÏŒ +Name[eo]=Akvo +Name[es]=Agua +Name[et]=Vesi +Name[eu]=Ura +Name[fa]=آب +Name[fi]=Vesi +Name[fo]=Vatn +Name[fr]=Eau +Name[ga]=Uisce +Name[gl]=Auga +Name[he]=×ž×™× +Name[hi]=पानी +Name[hr]=Voda +Name[hu]=Víz +Name[is]=Vatn +Name[it]=Acqua +Name[ja]=æ°´ +Name[km]=ទឹក +Name[ko]=물 +Name[lt]=Vanduo +Name[lv]=Ūdens +Name[mk]=Вода +Name[mt]=Ilma +Name[nb]=Vann +Name[ne]=पानी +Name[nn]=Vatn +Name[nso]=Meetse +Name[pa]=ਪਾਣੀ +Name[pl]=Woda +Name[pt]=Ãgua +Name[pt_BR]=Ãgua +Name[ro]=Apă +Name[ru]=Вода +Name[se]=Čáhci +Name[sk]=Voda +Name[sl]=Voda +Name[sr]=Вода +Name[sr@Latn]=Voda +Name[sv]=Vatten +Name[ta]=தணà¯à®£à¯€à®°à¯ +Name[tg]=Об +Name[th]=น้ำ +Name[uk]=Вода +Name[uz]=Suv +Name[uz@cyrillic]=Сув +Name[ven]=Madi +Name[vi]=NÆ°á»›c +Name[wa]=Aiwe +Name[xh]=Amanzi +Name[zh_CN]=æ°´ +Name[zh_TW]=æ°´ +Name[zu]=Amanzi diff --git a/libkdegames/carddecks/decks/deck5.png b/libkdegames/carddecks/decks/deck5.png new file mode 100644 index 00000000..63cb6762 Binary files /dev/null and b/libkdegames/carddecks/decks/deck5.png differ diff --git a/libkdegames/carddecks/decks/deck6.desktop b/libkdegames/carddecks/decks/deck6.desktop new file mode 100644 index 00000000..6d84a8ad --- /dev/null +++ b/libkdegames/carddecks/decks/deck6.desktop @@ -0,0 +1,66 @@ +[KDE Cards] +Name=Beach +Name[af]=Strand +Name[ar]=شاطئ +Name[az]=ÇimÉ™rlik +Name[be]=ПлÑж +Name[bg]=Плаж +Name[bn]=সৈকত +Name[br]=Traezhenn +Name[bs]=Plaža +Name[ca]=Platja +Name[cs]=Pláž +Name[cy]=Traeth +Name[da]=Strand +Name[de]=Strand +Name[el]=ΠαÏαλία +Name[eo]=PlaÄo +Name[es]=Playa +Name[et]=Rand +Name[eu]=Hondartza +Name[fa]=ساحل +Name[fi]=Ranta +Name[fr]=Plage +Name[gl]=Praia +Name[he]=חוף +Name[hi]=समà¥à¤¦à¥à¤° तट +Name[hr]=Plaža +Name[hu]=Tengerpart +Name[is]=Strönd +Name[it]=Spiaggia +Name[km]=ឆ្នáŸážšâ€‹ážáŸ’សាច់ +Name[ko]=바닷가 +Name[lt]=PaplÅ«dimys +Name[lv]=Pludmale +Name[mk]=Плажа +Name[mt]=Ramla +Name[nb]=Strand +Name[nds]=Strand +Name[ne]=किनारा +Name[nl]=Strand +Name[nn]=Strand +Name[nso]=Lewatle +Name[pa]=ਬੀਚ +Name[pl]=Plaża +Name[pt]=Praia +Name[pt_BR]=Praia +Name[ro]=Plajă +Name[ru]=ПлÑж +Name[se]=Fiervá +Name[sk]=Pláž +Name[sl]=Obala +Name[sr]=Плажа +Name[sr@Latn]=Plaža +Name[sv]=Strand +Name[ta]=கடல௠+Name[tg]=ПлÑж +Name[th]=อ่าว +Name[tr]=Sahil +Name[uk]=ПлÑж +Name[ven]=Bitshini +Name[vi]=Bãi biển +Name[wa]=Pladje +Name[xh]=Ulwande +Name[zh_CN]=海滩 +Name[zh_TW]=æµ·ç˜ +Name[zu]=Ulwandle diff --git a/libkdegames/carddecks/decks/deck6.png b/libkdegames/carddecks/decks/deck6.png new file mode 100644 index 00000000..1708d784 Binary files /dev/null and b/libkdegames/carddecks/decks/deck6.png differ diff --git a/libkdegames/carddecks/decks/deck7.desktop b/libkdegames/carddecks/decks/deck7.desktop new file mode 100644 index 00000000..3590a15e --- /dev/null +++ b/libkdegames/carddecks/decks/deck7.desktop @@ -0,0 +1,67 @@ +[KDE Cards] +Name=Sunset +Name[af]=Sonsondergang +Name[ar]=غروب الشمس +Name[az]=Gün doÄŸuÅŸu +Name[be]=Заход +Name[bg]=Залез +Name[bn]=সূরà§à¦¯à¦¾à¦¸à§à¦¤ +Name[br]=Kuzh-heol +Name[bs]=Zalazak +Name[ca]=Posta de sol +Name[cs]=Západ slunce +Name[cy]=Machlud +Name[da]=Solnedgang +Name[de]=Sonnenuntergang +Name[el]=Ηλιοβασίλεμα +Name[eo]=Sunsubiro +Name[es]=Puesta de sol +Name[et]=Loojang +Name[eu]=Eguzki-sartzea +Name[fa]=غروب +Name[fi]=Auringonlasku +Name[fo]=Sólsetur +Name[fr]=Soleil +Name[gl]=Entre lusco e fusco +Name[he]=שקיעה +Name[hi]=सूरà¥à¤¯à¤¾à¤¸à¥à¤¤ +Name[hr]=Zalazak sunca +Name[hu]=Naplemente +Name[is]=Sólsetur +Name[it]=Tramonto +Name[km]=សុរិយា​អស្ដង្គហ+Name[ko]=ì €ë…놀 +Name[lt]=SaulÄ—lydis +Name[lv]=SaullÄ“kts +Name[mk]=ЗајдиÑонце +Name[mt]=Għabex +Name[nb]=Solnedgang +Name[nds]=Sünnünnergang +Name[ne]=सà¥à¤°à¥à¤¯à¤¾à¤¸à¥à¤¤ +Name[nl]=Zonsondergang +Name[nn]=Solrenning +Name[pa]=ਡà©à©±à¨¬à¨¦à¨¾ ਸੂਰਜ +Name[pl]=Zachód sÅ‚oÅ„ca +Name[pt]=Pôr-do-Sol +Name[pt_BR]=Pôr-do-sol +Name[ro]=AsfinÅ£it +Name[ru]=Закат +Name[sk]=Západ slnka +Name[sl]=SonÄni zahod +Name[sr]=Залазак Ñунца +Name[sr@Latn]=Zalazak sunca +Name[sv]=SolnedgÃ¥ng +Name[ta]=சூரிய அஸà¯à®¤à®®à®®à¯ +Name[tg]=Ғуруби Офтоб +Name[th]=พระอาทิตย์ตภ+Name[tr]=GündoÄŸumu +Name[uk]=Захід ÑÐ¾Ð½Ñ†Ñ +Name[uz]=Oqshom +Name[uz@cyrillic]=Оқшом +Name[ven]=Vhukovhela +Name[vi]=Hoàng hôn +Name[wa]=Solea djus +Name[xh]=Ukutshona kwelanga +Name[zh_CN]=æ—¥è½ +Name[zh_TW]=æ—¥è½ +Name[zu]=Ukushona kwelanga diff --git a/libkdegames/carddecks/decks/deck7.png b/libkdegames/carddecks/decks/deck7.png new file mode 100644 index 00000000..c7f043db Binary files /dev/null and b/libkdegames/carddecks/decks/deck7.png differ diff --git a/libkdegames/carddecks/decks/deck8.desktop b/libkdegames/carddecks/decks/deck8.desktop new file mode 100644 index 00000000..0c68bc9f --- /dev/null +++ b/libkdegames/carddecks/decks/deck8.desktop @@ -0,0 +1,67 @@ +[KDE Cards] +Name=Road +Name[af]=Pad +Name[ar]=طريق +Name[az]=Yol +Name[be]=Дарога +Name[bg]=Път +Name[bn]=পথ +Name[bs]=Put +Name[ca]=Carretera +Name[cs]=Cesta +Name[cy]=Ffordd +Name[da]=Vej +Name[de]=Straße +Name[el]=ΔÏόμος +Name[eo]=Strato +Name[es]=Carretera +Name[et]=Maantee +Name[eu]=Errepidea +Name[fa]=جاده +Name[fi]=Tie +Name[fo]=Vegur +Name[fr]=Route +Name[gl]=Estrada +Name[he]=×‘×“×¨×›×™× +Name[hi]=रासà¥à¤¤à¤¾ +Name[hr]=Cesta +Name[hu]=Országút +Name[is]=Vegur +Name[it]=Strada +Name[km]=ផ្លូវ +Name[ko]=길 +Name[lt]=Kelias +Name[lv]=Ceļš +Name[mk]=Пат +Name[mt]=Triq +Name[nb]=Vei +Name[nds]=Straat +Name[ne]=बाटो +Name[nl]=Weg +Name[nn]=Veg +Name[nso]=Tsela +Name[pa]=ਰਾਹ +Name[pl]=Droga +Name[pt]=Estrada +Name[pt_BR]=Estrada +Name[ro]=Åžosea +Name[ru]=Дорога +Name[se]=Geaidnu +Name[sk]=Cesta +Name[sl]=Cesta +Name[sr]=Пут +Name[sr@Latn]=Put +Name[sv]=Väg +Name[ta]=சாலை +Name[tg]=Роҳ +Name[th]=ถนน +Name[tr]=Yol +Name[uk]=Дорога +Name[uz]=YoÊ»l +Name[uz@cyrillic]=Йўл +Name[ven]=Bada +Name[vi]=Con Ä‘Æ°á»ng +Name[xh]=Indlela +Name[zh_CN]=è·¯ +Name[zh_TW]=é“è·¯ +Name[zu]=Umgwaqo diff --git a/libkdegames/carddecks/decks/deck8.png b/libkdegames/carddecks/decks/deck8.png new file mode 100644 index 00000000..4fed86e6 Binary files /dev/null and b/libkdegames/carddecks/decks/deck8.png differ diff --git a/libkdegames/carddecks/decks/deck9.desktop b/libkdegames/carddecks/decks/deck9.desktop new file mode 100644 index 00000000..2541e22a --- /dev/null +++ b/libkdegames/carddecks/decks/deck9.desktop @@ -0,0 +1,7 @@ +[KDE Cards] +Name=KDE +Name[af]=Kde +Name[bn]=কে.ডি.ই. +Name[hi]=केडीई +Name[ne]=केडीई +Name[ta]=கேடிஇ diff --git a/libkdegames/carddecks/decks/deck9.png b/libkdegames/carddecks/decks/deck9.png new file mode 100644 index 00000000..35abd0a8 Binary files /dev/null and b/libkdegames/carddecks/decks/deck9.png differ diff --git a/libkdegames/configure.in.in b/libkdegames/configure.in.in new file mode 100644 index 00000000..ffcaf9c9 --- /dev/null +++ b/libkdegames/configure.in.in @@ -0,0 +1,49 @@ +dnl AB: checking for a system-wide highscore file. If "no" then the default +dnl (just kapp->config()) is used. See KHighscore for details. + +AC_MSG_CHECKING(whether to use system-wide highscores) +AC_ARG_ENABLE(highscore-dir, +AC_HELP_STRING([--enable-highscore-dir=DIR], [system-wide highscore table @<:@default=no@:>@]), [use_highscore_dir=yes], [use_highscore_dir=no]) + +if test "$use_highscore_dir" = "no"; then + AC_MSG_RESULT(no) + AC_SUBST(HIGHSCORE_DIRECTORY, "") +else + case "${enableval}" in + yes) highscore_dir='${localstatedir}/games' ;; + no) ;; + *) highscore_dir=${enableval} ;; + esac + AC_DEFINE_UNQUOTED(HIGHSCORE_DIRECTORY, "$highscore_dir", [The system-wide highscore directory]) + AC_SUBST(HIGHSCORE_DIRECTORY, $highscore_dir) + AC_MSG_RESULT($use_highscore_dir) +fi + +AC_MSG_CHECKING(whether to setgid binaries) +AC_ARG_ENABLE(setgid, + [ --enable-setgid Enable the use of setgid binaries], + [case "${enableval}" in + yes) + case "$use_highscore_dir" in + yes) setgid=true;; + no) setgid=false;; + esac ;; + no) setgid=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-setgid) ;; + esac],[setgid=false]) +AC_SUBST(setgid) +AC_MSG_RESULT($setgid) + +AC_MSG_CHECKING(what group to use for the highscore tables and binaries) +AC_ARG_WITH(highscore-group, +[ --with-highscore-group=group Group for the highscore tables and binaries], +highscore_group="$withval",highscore_group="games") +AC_SUBST(highscore_group) +AC_MSG_RESULT($highscore_group) + +AC_MSG_CHECKING(what user to use for the highscore tables and binaries) +AC_ARG_WITH(highscore-user, +[ --with-highscore-user=user User for the highscore tables], +highscore_user="$withval",highscore_user="games") +AC_SUBST(highscore_user) +AC_MSG_RESULT($highscore_user) diff --git a/libkdegames/highscore/INSTALL b/libkdegames/highscore/INSTALL new file mode 100644 index 00000000..a16fa57d --- /dev/null +++ b/libkdegames/highscore/INSTALL @@ -0,0 +1,12 @@ +Installation notes for the highscore files ; this is only relevant if you +configured libkdegames with option --enable-highscore-dir=DIR (usually DIR is +/var/games) for using system-wide highscore files. + +For each game using the highscore system : + +- the game executable "mygame" should be installed sgid "games" + +- an empty file "mygame.scores" should be created in the directory pointed by +the configuration option. It should be owned by group "games" with read and +write permissions and should -not- be world readable (since it can contains +possibly sensitive information associating username with game usage). diff --git a/libkdegames/highscore/Makefile.am b/libkdegames/highscore/Makefile.am new file mode 100644 index 00000000..6fa18cc0 --- /dev/null +++ b/libkdegames/highscore/Makefile.am @@ -0,0 +1,19 @@ +noinst_LTLIBRARIES = libkhighscore.la + +INCLUDES = $(all_includes) + +libkhighscore_la_SOURCES = kconfigrawbackend.cpp \ + kfilelock.cpp khighscore.cpp kscoredialog.cpp \ + kexthighscore_item.cpp kexthighscore_internal.cpp \ + kexthighscore_tab.cpp kexthighscore_gui.cpp \ + kexthighscore.cpp + +include_HEADERS = khighscore.h kscoredialog.h \ + kexthighscore_item.h kexthighscore.h + +noinst_HEADERS = kconfigrawbackend.h \ + kfilelock.h kexthighscore_internal.h kexthighscore_tab.h \ + kexthighscore_gui.h + +METASOURCES = kconfigrawbackend.moc khighscore.moc kscoredialog.moc \ + kexthighscore_tab.moc kexthighscore_gui.moc diff --git a/libkdegames/highscore/kconfigrawbackend.cpp b/libkdegames/highscore/kconfigrawbackend.cpp new file mode 100644 index 00000000..a379ba23 --- /dev/null +++ b/libkdegames/highscore/kconfigrawbackend.cpp @@ -0,0 +1,62 @@ +/* + This file is part of the KDE games library + Copyright (C) 2003 Nicolas Hadacek + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kconfigrawbackend.h" +#include "kconfigrawbackend.moc" + +#include +#include + + +KConfigRawBackEnd::KConfigRawBackEnd(KConfigBase *_config, int fd) + : KConfigINIBackEnd(_config, QString::null, "config", false), + _fd(fd), _stream(0) +{ + _file.open(IO_ReadOnly, _fd); +} + +KConfigRawBackEnd::~KConfigRawBackEnd() +{ + if (_stream) fclose(_stream); +} + +bool KConfigRawBackEnd::parseConfigFiles() +{ + _file.reset(); + parseSingleConfigFile(_file); + return true; +} + +void KConfigRawBackEnd::sync(bool bMerge) +{ + // write-sync is only necessary if there are dirty entries + if ( !pConfig->isDirty() || pConfig->isReadOnly() ) return; + + _file.reset(); + KEntryMap aTempMap; + getEntryMap(aTempMap, false, bMerge ? &_file : 0); + + if ( _stream==0 ) { + _stream = fdopen(_fd, "w"); + if ( _stream==0 ) return; + } + ftruncate(_fd, 0); + writeEntries(_stream, aTempMap); + fflush(_stream); +} diff --git a/libkdegames/highscore/kconfigrawbackend.h b/libkdegames/highscore/kconfigrawbackend.h new file mode 100644 index 00000000..4b780320 --- /dev/null +++ b/libkdegames/highscore/kconfigrawbackend.h @@ -0,0 +1,57 @@ +/* + This file is part of the KDE games library + Copyright (C) 2003 Nicolas Hadacek + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _KCONFIGRAWBACKEND_H +#define _KCONFIGRAWBACKEND_H + +#include + +#include +#include + + +class KConfigRawBackEnd : public KConfigINIBackEnd +{ +public: + KConfigRawBackEnd(KConfigBase *_config, int fd); + ~KConfigRawBackEnd(); + + bool parseConfigFiles(); + + void sync(bool bMerge = true); + +private: + int _fd; + FILE *_stream; + QFile _file; + + class KConfigRawBackEndPrivate; + KConfigRawBackEndPrivate *d; +}; + +class KRawConfig : public KSimpleConfig +{ + Q_OBJECT +public: + KRawConfig(int fd, bool readOnly) + : KSimpleConfig(new KConfigRawBackEnd(this, fd), readOnly) {} +}; + + +#endif diff --git a/libkdegames/highscore/kexthighscore.cpp b/libkdegames/highscore/kexthighscore.cpp new file mode 100644 index 00000000..0ad9b3af --- /dev/null +++ b/libkdegames/highscore/kexthighscore.cpp @@ -0,0 +1,289 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001-2004 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kexthighscore.h" + +#include + +#include + +#include "kexthighscore_internal.h" +#include "kexthighscore_gui.h" + + +namespace KExtHighscore +{ + +//----------------------------------------------------------------------------- +ManagerPrivate *internal = 0; + +uint gameType() +{ + internal->checkFirst(); + return internal->gameType(); +} + +void setGameType(uint type) +{ + internal->setGameType(type); +} + +bool configure(QWidget *parent) +{ + internal->checkFirst(); + ConfigDialog *cd = new ConfigDialog(parent); + cd->exec(); + bool saved = cd->hasBeenSaved(); + delete cd; + return saved; +} + +void show(QWidget *parent, int rank) +{ + HighscoresDialog *hd = new HighscoresDialog(rank, parent); + hd->exec(); + delete hd; +} + +void submitScore(const Score &score, QWidget *widget) +{ + int rank = internal->submitScore(score, widget, + internal->showMode!=Manager::NeverShow); + + switch (internal->showMode) { + case Manager::AlwaysShow: + show(widget, -1); + break; + case Manager::ShowForHigherScore: + if ( rank!=-1) show(widget, rank); + break; + case Manager::ShowForHighestScore: + if ( rank==0 ) show(widget, rank); + break; + case Manager::NeverShow: + break; + } +} + +void show(QWidget *widget) +{ + internal->checkFirst(); + show(widget, -1); +} + +Score lastScore() +{ + internal->checkFirst(); + internal->hsConfig().readCurrentConfig(); + uint nb = internal->scoreInfos().maxNbEntries(); + return internal->readScore(nb-1); +} + +Score firstScore() +{ + internal->checkFirst(); + internal->hsConfig().readCurrentConfig(); + return internal->readScore(0); +} + + +//----------------------------------------------------------------------------- +Manager::Manager(uint nbGameTypes, uint maxNbEntries) +{ + Q_ASSERT(nbGameTypes); + Q_ASSERT(maxNbEntries); + if (internal) + kdFatal(11002) << "A highscore object already exists" << endl; + internal = new ManagerPrivate(nbGameTypes, *this); + internal->init(maxNbEntries); +} + +Manager::~Manager() +{ + delete internal; + internal = 0; +} + +void Manager::setTrackLostGames(bool track) +{ + internal->trackLostGames = track; +} + +void Manager::setTrackDrawGames(bool track) +{ + internal->trackDrawGames = track; +} + +void Manager::setShowStatistics(bool show) +{ + internal->showStatistics = show; +} + +void Manager::showStatistics(bool show) +{ + internal->showStatistics = show; +} + +void Manager::setShowDrawGamesStatistic(bool show) +{ + internal->showDrawGames = show; +} + +void Manager::setWWHighscores(const KURL &url, const QString &version) +{ + Q_ASSERT( url.isValid() ); + internal->serverURL = url; + const char *HS_WW_URL = "ww hs url"; + ConfigGroup cg; + if ( cg.config()->hasKey(HS_WW_URL) ) + internal->serverURL = cg.config()->readEntry(HS_WW_URL); + else cg.config()->writeEntry(HS_WW_URL, url.url()); + internal->version = version; +} + +void Manager::setScoreHistogram(const QMemArray &scores, + ScoreTypeBound type) +{ + Q_ASSERT( scores.size()>=2 ); + for (uint i=0; iplayerInfos().createHistoItems(scores, type==ScoreBound); +} + +void Manager::setShowMode(ShowMode mode) +{ + internal->showMode = mode; +} + +void Manager::setScoreType(ScoreType type) +{ + switch (type) { + case Normal: + return; + case MinuteTime: { + Item *item = createItem(ScoreDefault); + item->setPrettyFormat(Item::MinuteTime); + setScoreItem(0, item); + + item = createItem(MeanScoreDefault); + item->setPrettyFormat(Item::MinuteTime); + setPlayerItem(MeanScore, item); + + item = createItem(BestScoreDefault); + item->setPrettyFormat(Item::MinuteTime); + setPlayerItem(BestScore, item); + return; + } + } +} + +void Manager::submitLegacyScore(const Score &score) const +{ + internal->submitLocal(score); +} + +bool Manager::isStrictlyLess(const Score &s1, const Score &s2) const +{ + return s1.score()setPrettyFormat(Item::OneDecimal); + item->setPrettySpecial(Item::DefaultNotDefined); + break; + case BestScoreDefault: + item = new Item((uint)0, i18n("Best Score"), Qt::AlignRight); + item->setPrettySpecial(Item::DefaultNotDefined); + break; + case ElapsedTime: + item = new Item((uint)0, i18n("Elapsed Time"), Qt::AlignRight); + item->setPrettyFormat(Item::MinuteTime); + item->setPrettySpecial(Item::ZeroNotDefined); + break; + } + return item; +} + +void Manager::setScoreItem(uint worstScore, Item *item) +{ + item->setDefaultValue(worstScore); + internal->scoreInfos().setItem("score", item); + internal->playerInfos().item("mean score") + ->item()->setDefaultValue(double(worstScore)); + internal->playerInfos().item("best score") + ->item()->setDefaultValue(worstScore); +} + +void Manager::addScoreItem(const QString &name, Item *item) +{ + internal->scoreInfos().addItem(name, item, true); +} + +void Manager::setPlayerItem(PlayerItemType type, Item *item) +{ + const Item *scoreItem = internal->scoreInfos().item("score")->item(); + uint def = scoreItem->defaultValue().toUInt(); + QString name; + switch (type) { + case MeanScore: + name = "mean score"; + item->setDefaultValue(double(def)); + break; + case BestScore: + name = "best score"; + item->setDefaultValue(def); + break; + } + internal->playerInfos().setItem(name, item); +} + +QString Manager::gameTypeLabel(uint gameType, LabelType type) const +{ + if ( gameType!=0 ) + kdFatal(11002) << "You need to reimplement KExtHighscore::Manager for " + << "multiple game types" << endl; + switch (type) { + case Icon: + case Standard: + case I18N: break; + case WW: return "normal"; + } + return QString::null; +} + +void Manager::addToQueryURL(KURL &url, const QString &item, + const QString &content) +{ + Q_ASSERT( !item.isEmpty() && url.queryItem(item).isNull() ); + + QString query = url.query(); + if ( !query.isEmpty() ) query += '&'; + query += item + '=' + KURL::encode_string(content); + url.setQuery(query); +} + +} // namescape diff --git a/libkdegames/highscore/kexthighscore.h b/libkdegames/highscore/kexthighscore.h new file mode 100644 index 00000000..2484f97b --- /dev/null +++ b/libkdegames/highscore/kexthighscore.h @@ -0,0 +1,367 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001-2004 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXTHIGHSCORE_H +#define KEXTHIGHSCORE_H + +#include "kexthighscore_item.h" + +#include +#include + +class QTabWidget; + + +namespace KExtHighscore +{ + +class Score; +class Item; + +class ManagerPrivate; +extern ManagerPrivate *internal; + +/** + * Get the current game type. + */ +KDE_EXPORT uint gameType(); + +/** + * Set the current game type. + */ +KDE_EXPORT void setGameType(uint gameType); + +/** + * Configure the highscores. + * @return true if the configuration has been modified and saved + */ +KDE_EXPORT bool configure(QWidget *parent); + +/** + * Show the highscores lists. + */ +KDE_EXPORT void show(QWidget *parent); + +/** + * Submit a score. See @ref Manager for usage example. + * + * @param widget a widget used as parent for error message box. + */ +KDE_EXPORT void submitScore(const Score &score, QWidget *widget); + +/** + * @return the last score in the local list of highscores. The worst possible + * score if there are less items than the maximum number. + */ +KDE_EXPORT Score lastScore(); + +/** + * @return the first score in the local list of highscores (the worst possible + * score if there is no entry). + */ +KDE_EXPORT Score firstScore(); + +/** + * This class manages highscores and players entries (several players can + * share the same highscores list if the libkdegame library is built to + * support a common highscores file; NOTE that to correctly implement such + * feature we probably need a locking mechanism in @ref KHighscore). + * + * You need one instance of this class during the application lifetime ; in + * main() just insert + * \code + * KExtHighscore::Manager highscoresManager; + * \endcode + * with the needed arguments. Use the derived class if you need to + * reimplement some of the default methods. + * + * This class has three functions : + *
    + *
  • Update the highscores list when new entries are submitted
  • + *
  • Display the highscores list and the players list
  • + *
  • Send query to an optionnal web server to support world-wide + * highscores
  • + *
+ * + * The highscores and the players lists contain several items described by + * the @ref Item class. + * + * The highscores list contains by default : + *
    + *
  • the player name (automatically set from the config value)
  • + *
  • the score value
  • + *
  • the time and date of the highscore (automatically set)
  • + *
+ * You can replace the score item (for e.g. displaying it differently) with + * setScoreItem or add an item with addScoreItem. + * + * The players list contains : + *
    + *
  • the player name (as defined by the user in the configuration + * dialog)
  • + *
  • the number of games played
  • + *
  • the mean score
  • + *
  • the best score
  • + *
  • the best score time and date
  • + *
  • the player comment (as defined by the user in the + * configuration dialog)
  • + *
+ * You can replace the best score and the mean score items + * by calling setPlayerItem. + * + * To submit a new score at game end, just construct a Score, set the + * score data and then call submitScore(). + * \code + * KExtHighscore::Score score(KExtHighscore::Won); + * score.setScore(myScore); + * KExtHighscore::submitScore(score, widget); + * \endcode + * You only need to set the score value with Score::setScore() + * and the value of the items that you have optionnally added + * with Score::setData() ; player name and date are set automatically. + */ +class KDE_EXPORT Manager +{ + public: + /** + * Constructor + * + * @param nbGameTypes the number of different game types (usually one). + * For example KMines has easy, normal and expert levels. + * @param maxNbEntries the maximum numbers of highscores entries (by game + * types) + */ + Manager(uint nbGameTypes = 1, uint maxNbEntries = 10); + virtual ~Manager(); + + /** + * Set the world-wide highscores. + * By default there is no world-wide highscores. + * + * Note: should be called at construction time. + * + * @param url the web server url + * @param version the game version which is sent to the web server (it can + * be useful for backward compatibility on the server side). + */ + void setWWHighscores(const KURL &url, const QString &version); + + /** + * Set if the number of lost games should be track for the world-wide + * highscores statistics. By default, there is no tracking. + * False by default. + * + * Note: should be called at construction time. + */ + void setTrackLostGames(bool track); + + /** + * @since 3.3 + * Set if the number of "draw" games should be track for the world-wide + * highscores statistics. By default, there is no tracking. + * False by default. + * + * Note: should be called at construction time. + */ + void setTrackDrawGames(bool track); + + /** + * @since 3.3 + * Set if the statistics tab should be shown in the highscores dialog. + * You only want to show this tab if it makes sense to lose or to win the + * game (for e.g. it makes no sense for a tetris game but it does for a + * minesweeper game). + * False by default. + * + * Note: should be called at construction time. + */ + void setShowStatistics(bool show); + + /** @obsolete */ + // KDE4 remove this + void showStatistics(bool show) KDE_DEPRECATED; + + /** + * @since 3.3 + * Set if draw games statistics should be shown (enable this if + * draws are possible in your game). + * False by default. + */ + void setShowDrawGamesStatistic(bool show); + + enum ScoreTypeBound { ScoreNotBound, ScoreBound }; + /** + * Set the ranges for the score histogram. + * + * Note: should be called at construction time. + */ + void setScoreHistogram(const QMemArray &scores, ScoreTypeBound type); + + /** + * Enumerate different conditions under which to show the + * high score dialog. + */ + enum ShowMode { AlwaysShow, ///< Always show the dialog + NeverShow, ///< Never show the dialog + ShowForHigherScore, ///< Show if score has improved + ShowForHighestScore ///< Only for the top spot + }; + /** + * Set how the highscores dialog is shown at game end. + * By default, the mode is ShowForHigherScore. + * + * Note: should be called at construction time. + */ + void setShowMode(ShowMode mode); + + /** + * Score type (@see setScoreType). + * @p Normal default score (unsigned integer without upper bound) + * @p MinuteTime score by time bound at 3599 seconds (for e.g. kmines) + */ + enum ScoreType { Normal, MinuteTime }; + /** + * Set score type. Helper method to quickly set the type of score. + * By default the type is Normal. + * + * Note: should be called at construction time. + */ + void setScoreType(ScoreType type); + + /** + * Some predefined item types. + * @p ScoreDefault default item for the score in the highscores list. + * @p MeanScoreDefault default item for the mean score (only show one decimal and + * 0 is shown as "--". + * @p BestScoreDefault default item for the best score (0 is shown as "--"). + * @p ElapsedTime optionnal item for elapsed time (maximum value is 3599 seconds). + */ + enum ItemType { ScoreDefault, MeanScoreDefault, BestScoreDefault, + ElapsedTime }; + /** + * Create a predefined item. + */ + static Item *createItem(ItemType type); + + /** + * Replace the default score item in the highscores list by the given one. + * @p worstScore is the worst possible score. By default it is 0. + * + * Note : This method should be called at construction time. + */ + void setScoreItem(uint worstScore, Item *item); + + /** + * Add an item in the highscores list (it will add a column to this list). + * + * Note : This method should be called at construction time. + */ + void addScoreItem(const QString &name, Item *item); + + enum PlayerItemType { MeanScore, BestScore }; + /** + * Replace an item in the players list. + * + * Note : This method should be called at construction time. + */ + void setPlayerItem(PlayerItemType type, Item *item); + + /** + * @return true if the first score is strictly worse than the second one. + * By default return
s1.score(). You can reimplement
+     * this method if additional items added to @ref Score can further
+     * differentiate the scores (for e.g. the time spent).
+     *
+     * Note that you do not need to use directly this method, simply write
+     * 
s1 since the operator calls this method.
+     */
+    virtual bool isStrictlyLess(const Score &s1, const Score &s2) const;
+
+    /**
+     * Possible type of label (@see gameTypeLabel).
+     * @p Standard label used in config file.
+     * @p I18N label used to display the game type.
+     * @p WW label used when contacting the world-wide highscores server.
+     * @p Icon label used to load the icon corresponding to the game type.
+     */
+    enum LabelType { Standard, I18N, WW, Icon };
+
+    /**
+     * @return the label corresponding to the game type. The default
+     * implementation works only for one game type : you need to reimplement
+     * this method if the number of game types is more than one.
+     */
+    virtual QString gameTypeLabel(uint gameType, LabelType type) const;
+
+ protected:
+    /**
+     * This method is called once for each player (ie for each user). You
+     * can reimplement it to convert old style highscores to the new mechanism
+     * (@see submitLegacyScore). By default this method does nothing.
+     *
+     * @param gameType the game type
+     */
+    virtual void convertLegacy(uint gameType) { Q_UNUSED(gameType); }
+
+    /**
+     * This method should be called from @ref convertLegacy. It is used
+     * to submit an old highscore (it will not be send over the network).
+     * For each score do something like:
+     * \code
+     * Score score(Won);
+     * score.setScore(oldScore);
+     * score.setData("name", name);
+     * submitLegacyScore(score);
+     * \endcode
+     * Note that here you can set the player "name" and the highscore "date"
+     * if they are known.
+     */
+    void submitLegacyScore(const Score &score) const;
+
+    /**
+     * This method is called before submitting a score to the world-wide
+     * highscores server. You can reimplement this method to add an entry
+     * with @ref addToQueryURL. By default this method does nothing.
+     *
+     * @param url the URL to query
+     * @param score the score to be submitted.
+     */
+    virtual void additionalQueryItems(KURL &url, const Score &score) const
+        { Q_UNUSED(url); Q_UNUSED(score); }
+
+    /**
+     * Add an entry to the url to be submitted (@see additionalQueryItems).
+     *
+     * @param url the URL to query
+     * @param item the item name
+     * @param content the item content
+     */
+    static void addToQueryURL(KURL &url, const QString &item,
+                              const QString &content);
+
+    friend class ManagerPrivate;
+
+ private:
+    Manager(const Manager &);
+    Manager &operator =(const Manager &);
+};
+
+} // namespace
+
+#endif
diff --git a/libkdegames/highscore/kexthighscore_gui.cpp b/libkdegames/highscore/kexthighscore_gui.cpp
new file mode 100644
index 00000000..547a885c
--- /dev/null
+++ b/libkdegames/highscore/kexthighscore_gui.cpp
@@ -0,0 +1,552 @@
+/*
+    This file is part of the KDE games library
+    Copyright (C) 2001-2003 Nicolas Hadacek (hadacek@kde.org)
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License version 2 as published by the Free Software Foundation.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "kexthighscore_gui.h"
+#include "kexthighscore_gui.moc"
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "kexthighscore_internal.h"
+#include "kexthighscore.h"
+#include "kexthighscore_tab.h"
+
+
+namespace KExtHighscore
+{
+
+//-----------------------------------------------------------------------------
+ShowItem::ShowItem(QListView *list, bool highlight)
+    : KListViewItem(list), _highlight(highlight)
+{}
+
+void ShowItem::paintCell(QPainter *p, const QColorGroup &cg,
+                         int column, int width, int align)
+{
+    QColorGroup cgrp(cg);
+    if (_highlight) cgrp.setColor(QColorGroup::Text, red);
+    KListViewItem::paintCell(p, cgrp, column, width, align);
+}
+
+//-----------------------------------------------------------------------------
+ScoresList::ScoresList(QWidget *parent)
+    : KListView(parent)
+{
+    setSelectionMode(QListView::NoSelection);
+    setItemMargin(3);
+    setAllColumnsShowFocus(true);
+    setSorting(-1);
+    header()->setClickEnabled(false);
+    header()->setMovingEnabled(false);
+}
+
+void ScoresList::addHeader(const ItemArray &items)
+{
+    addLineItem(items, 0, 0);
+}
+
+QListViewItem *ScoresList::addLine(const ItemArray &items,
+                                   uint index, bool highlight)
+{
+    QListViewItem *item = new ShowItem(this, highlight);
+    addLineItem(items, index, item);
+    return item;
+}
+
+void ScoresList::addLineItem(const ItemArray &items,
+                             uint index, QListViewItem *line)
+{
+    uint k = 0;
+    for (uint i=0; iisVisible() ) continue;
+        if (line) line->setText(k, itemText(container, index));
+        else {
+            addColumn( container.item()->label() );
+            setColumnAlignment(k, container.item()->alignment());
+        }
+        k++;
+    }
+}
+
+//-----------------------------------------------------------------------------
+HighscoresList::HighscoresList(QWidget *parent)
+    : ScoresList(parent)
+{}
+
+QString HighscoresList::itemText(const ItemContainer &item, uint row) const
+{
+    return item.pretty(row);
+}
+
+void HighscoresList::load(const ItemArray &items, int highlight)
+{
+    clear();
+    QListViewItem *line = 0;
+    for (int j=items.nbEntries()-1; j>=0; j--) {
+        QListViewItem *item = addLine(items, j, j==highlight);
+        if ( j==highlight ) line = item;
+    }
+    if (line) ensureItemVisible(line);
+}
+
+//-----------------------------------------------------------------------------
+HighscoresWidget::HighscoresWidget(QWidget *parent)
+    : QWidget(parent, "show_highscores_widget"),
+      _scoresUrl(0), _playersUrl(0), _statsTab(0), _histoTab(0)
+{
+    const ScoreInfos &s = internal->scoreInfos();
+    const PlayerInfos &p = internal->playerInfos();
+
+    QVBoxLayout *vbox = new QVBoxLayout(this, KDialogBase::spacingHint());
+
+    _tw = new QTabWidget(this);
+    connect(_tw, SIGNAL(currentChanged(QWidget *)), SLOT(tabChanged()));
+    vbox->addWidget(_tw);
+
+    // scores tab
+    _scoresList = new HighscoresList(_tw);
+    _scoresList->addHeader(s);
+    _tw->addTab(_scoresList, i18n("Best &Scores"));
+
+    // players tab
+    _playersList = new HighscoresList(_tw);
+    _playersList->addHeader(p);
+    _tw->addTab(_playersList, i18n("&Players"));
+
+    // statistics tab
+    if ( internal->showStatistics ) {
+        _statsTab = new StatisticsTab(_tw);
+        _tw->addTab(_statsTab, i18n("Statistics"));
+    }
+
+    // histogram tab
+    if ( p.histogram().size()!=0 ) {
+        _histoTab = new HistogramTab(_tw);
+        _tw->addTab(_histoTab, i18n("Histogram"));
+    }
+
+    // url labels
+    if ( internal->isWWHSAvailable() ) {
+        KURL url = internal->queryURL(ManagerPrivate::Scores);
+        _scoresUrl = new KURLLabel(url.url(),
+                                   i18n("View world-wide highscores"), this);
+        connect(_scoresUrl, SIGNAL(leftClickedURL(const QString &)),
+                SLOT(showURL(const QString &)));
+        vbox->addWidget(_scoresUrl);
+
+        url = internal->queryURL(ManagerPrivate::Players);
+        _playersUrl = new KURLLabel(url.url(),
+                                    i18n("View world-wide players"), this);
+        connect(_playersUrl, SIGNAL(leftClickedURL(const QString &)),
+                SLOT(showURL(const QString &)));
+        vbox->addWidget(_playersUrl);
+    }
+}
+
+void HighscoresWidget::changeTab(int i)
+{
+    if ( i!=_tw->currentPageIndex() )
+        _tw->setCurrentPage(i);
+}
+
+void HighscoresWidget::showURL(const QString &url) const
+{
+    (void)new KRun(KURL(url));
+}
+
+void HighscoresWidget::load(int rank)
+{
+    _scoresList->load(internal->scoreInfos(), rank);
+    _playersList->load(internal->playerInfos(), internal->playerInfos().id());
+    if (_scoresUrl)
+        _scoresUrl->setURL(internal->queryURL(ManagerPrivate::Scores).url());
+    if (_playersUrl)
+        _playersUrl->setURL(internal->queryURL(ManagerPrivate::Players).url());
+    if (_statsTab) _statsTab->load();
+    if (_histoTab) _histoTab->load();
+}
+
+//-----------------------------------------------------------------------------
+HighscoresDialog::HighscoresDialog(int rank, QWidget *parent)
+    : KDialogBase(internal->nbGameTypes()>1 ? TreeList : Plain,
+                  i18n("Highscores"), Close|User1|User2, Close,
+                  parent, "show_highscores", true, true,
+                  KGuiItem(i18n("Configure..."), "configure"),
+                  KGuiItem(i18n("Export..."))), _rank(rank), _tab(0)
+{
+    _widgets.resize(internal->nbGameTypes(), 0);
+
+    if ( internal->nbGameTypes()>1 ) {
+        for (uint i=0; inbGameTypes(); i++) {
+            QString title = internal->manager.gameTypeLabel(i, Manager::I18N);
+            QString icon = internal->manager.gameTypeLabel(i, Manager::Icon);
+            QWidget *w = addVBoxPage(title, QString::null,
+                                     BarIcon(icon, KIcon::SizeLarge));
+            if ( i==internal->gameType() ) createPage(w);
+        }
+
+        connect(this, SIGNAL(aboutToShowPage(QWidget *)),
+                SLOT(createPage(QWidget *)));
+        showPage(internal->gameType());
+    } else {
+        QVBoxLayout *vbox = new QVBoxLayout(plainPage());
+        createPage(plainPage());
+        vbox->addWidget(_widgets[0]);
+        setMainWidget(_widgets[0]);
+    }
+}
+
+void HighscoresDialog::createPage(QWidget *page)
+{
+    internal->hsConfig().readCurrentConfig();
+    _current = page;
+    bool several = ( internal->nbGameTypes()>1 );
+    int i = (several ? pageIndex(page) : 0);
+    if ( _widgets[i]==0 ) {
+        _widgets[i] = new HighscoresWidget(page);
+        connect(_widgets[i], SIGNAL(tabChanged(int)), SLOT(tabChanged(int)));
+    }
+    uint type = internal->gameType();
+    if (several) internal->setGameType(i);
+    _widgets[i]->load(uint(i)==type ? _rank : -1);
+    if (several) setGameType(type);
+    _widgets[i]->changeTab(_tab);
+}
+
+void HighscoresDialog::slotUser1()
+{
+    if ( KExtHighscore::configure(this) )
+        createPage(_current);
+}
+
+void HighscoresDialog::slotUser2()
+{
+    KURL url = KFileDialog::getSaveURL(QString::null, QString::null, this);
+    if ( url.isEmpty() ) return;
+    if ( KIO::NetAccess::exists(url, true, this) ) {
+        KGuiItem gi = KStdGuiItem::save();
+        gi.setText(i18n("Overwrite"));
+        int res = KMessageBox::warningContinueCancel(this,
+                                 i18n("The file already exists. Overwrite?"),
+                                 i18n("Export"), gi);
+        if ( res==KMessageBox::Cancel ) return;
+    }
+    KTempFile tmp;
+    internal->exportHighscores(*tmp.textStream());
+    tmp.close();
+    KIO::NetAccess::upload(tmp.name(), url, this);
+    tmp.unlink();
+}
+
+//-----------------------------------------------------------------------------
+LastMultipleScoresList::LastMultipleScoresList(
+                            const QValueVector &scores, QWidget *parent)
+    : ScoresList(parent), _scores(scores)
+{
+    const ScoreInfos &s = internal->scoreInfos();
+    addHeader(s);
+    for (uint i=0; isetText(i, itemText(*container, index));
+        else {
+            addColumn(  container->item()->label() );
+            setColumnAlignment(i, container->item()->alignment());
+        }
+    }
+}
+
+QString LastMultipleScoresList::itemText(const ItemContainer &item,
+                                         uint row) const
+{
+    QString name = item.name();
+    if ( name=="rank" )
+        return (_scores[row].type()==Won ? i18n("Winner") : QString::null);
+    QVariant v = _scores[row].data(name);
+    if ( name=="name" ) return v.toString();
+    return item.item()->pretty(row, v);
+}
+
+//-----------------------------------------------------------------------------
+TotalMultipleScoresList::TotalMultipleScoresList(
+                            const QValueVector &scores, QWidget *parent)
+    : ScoresList(parent), _scores(scores)
+{
+    const ScoreInfos &s = internal->scoreInfos();
+    addHeader(s);
+    for (uint i=0; iplayerInfos();
+    uint k = 1; // skip "id"
+    for (uint i=0; i<4; i++) { // skip additional fields
+        const ItemContainer *container;
+        if ( i==2 ) container = pi.item("nb games");
+        else if ( i==3 ) container = pi.item("mean score");
+        else {
+            container = si[k];
+            k++;
+        }
+        if (line) line->setText(i, itemText(*container, index));
+        else {
+            QString label =
+                (i==2 ? i18n("Won Games") : container->item()->label());
+            addColumn(label);
+            setColumnAlignment(i, container->item()->alignment());
+        }
+    }
+}
+
+QString TotalMultipleScoresList::itemText(const ItemContainer &item,
+                                          uint row) const
+{
+    QString name = item.name();
+    if ( name=="rank" ) return QString::number(_scores.size()-row);
+    if ( name=="nb games" )
+        return QString::number( _scores[row].data("nb won games").toUInt() );
+    QVariant v = _scores[row].data(name);
+    if ( name=="name" ) return v.toString();
+    return item.item()->pretty(row, v);
+}
+
+
+//-----------------------------------------------------------------------------
+ConfigDialog::ConfigDialog(QWidget *parent)
+    : KDialogBase(Swallow, i18n("Configure Highscores"),
+                  Ok|Apply|Cancel, Cancel,
+                  parent, "configure_highscores", true, true),
+      _saved(false), _WWHEnabled(0)
+{
+    QWidget *page = 0;
+    QTabWidget *tab = 0;
+    if ( internal->isWWHSAvailable() ) {
+        tab = new QTabWidget(this);
+        setMainWidget(tab);
+        page = new QWidget(tab);
+        tab->addTab(page, i18n("Main"));
+    } else {
+        page = new QWidget(this);
+        setMainWidget(page);
+    }
+
+    QGridLayout *pageTop =
+        new QGridLayout(page, 2, 2, spacingHint(), spacingHint());
+
+    QLabel *label = new QLabel(i18n("Nickname:"), page);
+    pageTop->addWidget(label, 0, 0);
+    _nickname = new QLineEdit(page);
+    connect(_nickname, SIGNAL(textChanged(const QString &)),
+            SLOT(modifiedSlot()));
+    connect(_nickname, SIGNAL(textChanged(const QString &)),
+            SLOT(nickNameChanged(const QString &)));
+
+    _nickname->setMaxLength(16);
+    pageTop->addWidget(_nickname, 0, 1);
+
+    label = new QLabel(i18n("Comment:"), page);
+    pageTop->addWidget(label, 1, 0);
+    _comment = new QLineEdit(page);
+    connect(_comment, SIGNAL(textChanged(const QString &)),
+            SLOT(modifiedSlot()));
+    _comment->setMaxLength(50);
+    pageTop->addWidget(_comment, 1, 1);
+
+    if (tab) {
+        _WWHEnabled
+            = new QCheckBox(i18n("World-wide highscores enabled"), page);
+        connect(_WWHEnabled, SIGNAL(toggled(bool)),
+                SLOT(modifiedSlot()));
+        pageTop->addMultiCellWidget(_WWHEnabled, 2, 2, 0, 1);
+
+        // advanced tab
+        QWidget *page = new QWidget(tab);
+        tab->addTab(page, i18n("Advanced"));
+        QVBoxLayout *pageTop =
+            new QVBoxLayout(page, spacingHint(), spacingHint());
+
+        QVGroupBox *group = new QVGroupBox(i18n("Registration Data"), page);
+        pageTop->addWidget(group);
+        QGrid *grid = new QGrid(2, group);
+        grid->setSpacing(spacingHint());
+
+        label = new QLabel(i18n("Nickname:"), grid);
+        _registeredName = new KLineEdit(grid);
+        _registeredName->setReadOnly(true);
+
+        label = new QLabel(i18n("Key:"), grid);
+        _key = new KLineEdit(grid);
+        _key->setReadOnly(true);
+
+        KGuiItem gi = KStdGuiItem::clear();
+        gi.setText(i18n("Remove"));
+        _removeButton = new KPushButton(gi, grid);
+        connect(_removeButton, SIGNAL(clicked()), SLOT(removeSlot()));
+    }
+
+    load();
+    enableButtonOK( !_nickname->text().isEmpty() );
+    enableButtonApply(false);
+}
+
+void ConfigDialog::nickNameChanged(const QString &text)
+{
+    enableButtonOK( !text.isEmpty() );
+}
+
+
+void ConfigDialog::modifiedSlot()
+{
+    enableButtonApply(true && !_nickname->text().isEmpty() );
+}
+
+void ConfigDialog::accept()
+{
+    if ( save() ) {
+        KDialogBase::accept();
+        kapp->config()->sync(); // safer
+    }
+}
+
+void ConfigDialog::removeSlot()
+{
+    KGuiItem gi = KStdGuiItem::clear();
+    gi.setText(i18n("Remove"));
+    int res = KMessageBox::warningContinueCancel(this,
+                               i18n("This will permanently remove your "
+                               "registration key. You will not be able to use "
+                               "the currently registered nickname anymore."),
+                               QString::null, gi);
+    if ( res==KMessageBox::Continue ) {
+        internal->playerInfos().removeKey();
+        _registeredName->clear();
+        _key->clear();
+        _removeButton->setEnabled(false);
+        _WWHEnabled->setChecked(false);
+        modifiedSlot();
+    }
+}
+
+void ConfigDialog::load()
+{
+    internal->hsConfig().readCurrentConfig();
+    const PlayerInfos &infos = internal->playerInfos();
+    _nickname->setText(infos.isAnonymous() ? QString::null : infos.name());
+    _comment->setText(infos.comment());
+    if (_WWHEnabled) {
+        _WWHEnabled->setChecked(infos.isWWEnabled());
+        if ( !infos.key().isEmpty() ) {
+            _registeredName->setText(infos.registeredName());
+            _registeredName->home(false);
+            _key->setText(infos.key());
+            _key->home(false);
+        }
+        _removeButton->setEnabled(!infos.key().isEmpty());
+    }
+}
+
+bool ConfigDialog::save()
+{
+    bool enabled = (_WWHEnabled ? _WWHEnabled->isChecked() : false);
+
+    // do not bother the user with "nickname empty" if he has not
+    // messed with nickname settings ...
+    QString newName = _nickname->text();
+    if ( newName.isEmpty() && !internal->playerInfos().isAnonymous()
+         && !enabled ) return true;
+
+    if ( newName.isEmpty() ) {
+        KMessageBox::sorry(this, i18n("Please choose a non empty nickname."));
+        return false;
+    }
+    if ( internal->playerInfos().isNameUsed(newName) ) {
+        KMessageBox::sorry(this, i18n("Nickname already in use. Please "
+                                      "choose another one"));
+        return false;
+    }
+
+    int res =
+        internal->modifySettings(newName, _comment->text(), enabled, this);
+    if (res) {
+        load(); // needed to update view when "apply" is clicked
+        enableButtonApply(false);
+    }
+    _saved = true;
+    return res;
+}
+
+//-----------------------------------------------------------------------------
+AskNameDialog::AskNameDialog(QWidget *parent)
+    : KDialogBase(Plain, i18n("Enter Your Nickname"), Ok | Cancel, Ok,
+                  parent, "ask_name_dialog")
+{
+    internal->hsConfig().readCurrentConfig();
+
+    QVBoxLayout *top =
+        new QVBoxLayout(plainPage(), marginHint(), spacingHint());
+    QLabel *label =
+        new QLabel(i18n("Congratulations, you have won!"), plainPage());
+    top->addWidget(label);
+
+    QHBoxLayout *hbox = new QHBoxLayout(top);
+    label = new QLabel(i18n("Enter your nickname:"), plainPage());
+    hbox->addWidget(label);
+    _edit = new QLineEdit(plainPage());
+    _edit->setFocus();
+    connect(_edit, SIGNAL(textChanged(const QString &)), SLOT(nameChanged()));
+    hbox->addWidget(_edit);
+
+    top->addSpacing(spacingHint());
+    _checkbox = new QCheckBox(i18n("Do not ask again."),  plainPage());
+    top->addWidget(_checkbox);
+
+    nameChanged();
+}
+
+void AskNameDialog::nameChanged()
+{
+    enableButtonOK( !name().isEmpty()
+                    && !internal->playerInfos().isNameUsed(name()) );
+}
+
+} // namespace
diff --git a/libkdegames/highscore/kexthighscore_gui.h b/libkdegames/highscore/kexthighscore_gui.h
new file mode 100644
index 00000000..e721299a
--- /dev/null
+++ b/libkdegames/highscore/kexthighscore_gui.h
@@ -0,0 +1,207 @@
+/*
+    This file is part of the KDE games library
+    Copyright (C) 2001-02 Nicolas Hadacek (hadacek@kde.org)
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License version 2 as published by the Free Software Foundation.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXTHIGHSCORE_GUI_H
+#define KEXTHIGHSCORE_GUI_H
+
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+
+#include "kexthighscore.h"
+
+
+namespace KExtHighscore
+{
+
+class ItemContainer;
+class ItemArray;
+class Score;
+class AdditionalTab;
+
+//-----------------------------------------------------------------------------
+class ShowItem : public KListViewItem
+{
+ public:
+    ShowItem(QListView *, bool highlight);
+
+ protected:
+    virtual void paintCell(QPainter *, const QColorGroup &, int column,
+						   int width, int align);
+
+ private:
+    bool _highlight;
+};
+
+class ScoresList : public KListView
+{
+ Q_OBJECT
+ public:
+    ScoresList(QWidget *parent);
+
+    void addHeader(const ItemArray &);
+
+ protected:
+    QListViewItem *addLine(const ItemArray &, uint index, bool highlight);
+    virtual QString itemText(const ItemContainer &, uint row) const = 0;
+
+ private:
+    virtual void addLineItem(const ItemArray &, uint index,
+                             QListViewItem *item);
+};
+
+//-----------------------------------------------------------------------------
+class HighscoresList : public ScoresList
+{
+ Q_OBJECT
+ public:
+    HighscoresList(QWidget *parent);
+
+    void load(const ItemArray &, int highlight);
+
+ protected:
+    QString itemText(const ItemContainer &, uint row) const;
+};
+
+class HighscoresWidget : public QWidget
+{
+ Q_OBJECT
+ public:
+    HighscoresWidget(QWidget *parent);
+
+    void load(int rank);
+
+ signals:
+    void tabChanged(int i);
+
+ public slots:
+    void changeTab(int i);
+
+ private slots:
+    void showURL(const QString &) const;
+    void tabChanged() { emit tabChanged(_tw->currentPageIndex()); }
+
+ private:
+    QTabWidget     *_tw;
+    HighscoresList *_scoresList, *_playersList;
+    KURLLabel      *_scoresUrl, *_playersUrl;
+    AdditionalTab  *_statsTab, *_histoTab;
+};
+
+class HighscoresDialog : public KDialogBase
+{
+ Q_OBJECT
+ public:
+    HighscoresDialog(int rank, QWidget *parent);
+
+ private slots:
+    void slotUser1();
+    void slotUser2();
+    void tabChanged(int i) { _tab = i; }
+    void createPage(QWidget *);
+
+ private:
+    int _rank, _tab;
+    QWidget *_current;
+    QValueVector _widgets;
+};
+
+//-----------------------------------------------------------------------------
+class LastMultipleScoresList : public ScoresList
+{
+    Q_OBJECT
+public:
+    LastMultipleScoresList(const QValueVector &, QWidget *parent);
+
+private:
+    void addLineItem(const ItemArray &, uint index, QListViewItem *line);
+    QString itemText(const ItemContainer &, uint row) const;
+
+private:
+    const QValueVector &_scores;
+};
+
+class TotalMultipleScoresList : public ScoresList
+{
+    Q_OBJECT
+public:
+    TotalMultipleScoresList(const QValueVector &, QWidget *parent);
+
+private:
+    void addLineItem(const ItemArray &, uint index, QListViewItem *line);
+    QString itemText(const ItemContainer &, uint row) const;
+
+private:
+    const QValueVector &_scores;
+};
+
+//-----------------------------------------------------------------------------
+class ConfigDialog : public KDialogBase
+{
+ Q_OBJECT
+ public:
+    ConfigDialog(QWidget *parent);
+
+    bool hasBeenSaved() const { return _saved; }
+
+ private slots:
+    void modifiedSlot();
+    void removeSlot();
+    void accept();
+    void slotApply() { save(); }
+    void nickNameChanged(const QString &);
+
+ private:
+    bool         _saved;
+    QCheckBox   *_WWHEnabled;
+    QLineEdit   *_nickname, *_comment;
+    KLineEdit   *_key, *_registeredName;
+    KPushButton *_removeButton;
+
+    void load();
+    bool save();
+};
+
+//-----------------------------------------------------------------------------
+class AskNameDialog : public KDialogBase
+{
+ Q_OBJECT
+ public:
+    AskNameDialog(QWidget *parent);
+
+    QString name() const { return _edit->text(); }
+    bool dontAskAgain() const { return _checkbox->isChecked(); }
+
+ private slots:
+    void nameChanged();
+
+ private:
+    QLineEdit *_edit;
+    QCheckBox *_checkbox;
+};
+
+} // namespace
+
+#endif
diff --git a/libkdegames/highscore/kexthighscore_internal.cpp b/libkdegames/highscore/kexthighscore_internal.cpp
new file mode 100644
index 00000000..a8395753
--- /dev/null
+++ b/libkdegames/highscore/kexthighscore_internal.cpp
@@ -0,0 +1,868 @@
+/*
+    This file is part of the KDE games library
+    Copyright (C) 2001-2004 Nicolas Hadacek (hadacek@kde.org)
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License version 2 as published by the Free Software Foundation.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "kexthighscore_internal.h"
+
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "config.h"
+#include "kexthighscore.h"
+#include "kexthighscore_gui.h"
+#include "kemailsettings.h"
+
+
+namespace KExtHighscore
+{
+
+//-----------------------------------------------------------------------------
+const char ItemContainer::ANONYMOUS[] = "_";
+const char ItemContainer::ANONYMOUS_LABEL[] = I18N_NOOP("anonymous");
+
+ItemContainer::ItemContainer()
+    : _item(0)
+{}
+
+ItemContainer::~ItemContainer()
+{
+    delete _item;
+}
+
+void ItemContainer::setItem(Item *item)
+{
+    delete _item;
+    _item = item;
+}
+
+QString ItemContainer::entryName() const
+{
+    if ( _subGroup.isEmpty() ) return _name;
+    return _name + "_" + _subGroup;
+}
+
+QVariant ItemContainer::read(uint i) const
+{
+    Q_ASSERT(_item);
+
+    QVariant v = _item->defaultValue();
+    if ( isStored() ) {
+        internal->hsConfig().setHighscoreGroup(_group);
+        v = internal->hsConfig().readPropertyEntry(i+1, entryName(), v);
+    }
+    return _item->read(i, v);
+}
+
+QString ItemContainer::pretty(uint i) const
+{
+    Q_ASSERT(_item);
+    return _item->pretty(i, read(i));
+}
+
+void ItemContainer::write(uint i, const QVariant &value) const
+{
+    Q_ASSERT( isStored() );
+    Q_ASSERT( internal->hsConfig().isLocked() );
+    internal->hsConfig().setHighscoreGroup(_group);
+    internal->hsConfig().writeEntry(i+1, entryName(), value);
+}
+
+uint ItemContainer::increment(uint i) const
+{
+    uint v = read(i).toUInt() + 1;
+    write(i, v);
+    return v;
+}
+
+//-----------------------------------------------------------------------------
+ItemArray::ItemArray()
+    : _group(""), _subGroup("") // no null groups
+{}
+
+ItemArray::~ItemArray()
+{
+    for (uint i=0; iname()==name ) return i;
+    return -1;
+}
+
+const ItemContainer *ItemArray::item(const QString &name) const
+{
+    int i = findIndex(name);
+    if ( i==-1 ) kdError(11002) << k_funcinfo << "no item named \"" << name
+                                << "\"" << endl;
+    return at(i);
+}
+
+ItemContainer *ItemArray::item(const QString &name)
+{
+    int i = findIndex(name);
+    if ( i==-1 ) kdError(11002) << k_funcinfo << "no item named \"" << name
+                                << "\"" << endl;
+    return at(i);
+}
+
+void ItemArray::setItem(const QString &name, Item *item)
+{
+    int i = findIndex(name);
+    if ( i==-1 ) kdError(11002) << k_funcinfo << "no item named \"" << name
+                                << "\"" << endl;
+    bool stored = at(i)->isStored();
+    bool canHaveSubGroup = at(i)->canHaveSubGroup();
+    _setItem(i, name, item, stored, canHaveSubGroup);
+}
+
+void ItemArray::addItem(const QString &name, Item *item,
+                        bool stored, bool canHaveSubGroup)
+{
+    if ( findIndex(name)!=-1 )
+        kdError(11002) << "item already exists \"" << name << "\"" << endl;
+    uint i = size();
+    resize(i+1);
+    at(i) = new ItemContainer;
+    _setItem(i, name, item, stored, canHaveSubGroup);
+}
+
+void ItemArray::_setItem(uint i, const QString &name, Item *item,
+                         bool stored, bool canHaveSubGroup)
+{
+    at(i)->setItem(item);
+    at(i)->setName(name);
+    at(i)->setGroup(stored ? _group : QString::null);
+    at(i)->setSubGroup(canHaveSubGroup ? _subGroup : QString::null);
+}
+
+void ItemArray::setGroup(const QString &group)
+{
+    Q_ASSERT( !group.isNull() );
+    _group = group;
+    for (uint i=0; iisStored() ) at(i)->setGroup(group);
+}
+
+void ItemArray::setSubGroup(const QString &subGroup)
+{
+    Q_ASSERT( !subGroup.isNull() );
+    _subGroup = subGroup;
+    for (uint i=0; icanHaveSubGroup() ) at(i)->setSubGroup(subGroup);
+}
+
+void ItemArray::read(uint k, Score &data) const
+{
+    for (uint i=0; iisStored() ) continue;
+        data.setData(at(i)->name(), at(i)->read(k));
+    }
+}
+
+void ItemArray::write(uint k, const Score &data, uint nb) const
+{
+    for (uint i=0; iisStored() ) continue;
+        for (uint j=nb-1; j>k; j--)  at(i)->write(j, at(i)->read(j-1));
+        at(i)->write(k, data.data(at(i)->name()));
+    }
+}
+
+void ItemArray::exportToText(QTextStream &s) const
+{
+    for (uint k=0; kitem();
+            if ( item->isVisible() ) {
+                if ( i!=0 ) s << '\t';
+                if ( k==0 ) s << item->label();
+                else s << at(i)->pretty(k-1);
+            }
+        }
+        s << endl;
+    }
+}
+
+//-----------------------------------------------------------------------------
+class ScoreNameItem : public NameItem
+{
+ public:
+    ScoreNameItem(const ScoreInfos &score, const PlayerInfos &infos)
+        : _score(score), _infos(infos) {}
+
+    QString pretty(uint i, const QVariant &v) const {
+        uint id = _score.item("id")->read(i).toUInt();
+        if ( id==0 ) return NameItem::pretty(i, v);
+        return _infos.prettyName(id-1);
+    }
+
+ private:
+    const ScoreInfos  &_score;
+    const PlayerInfos &_infos;
+};
+
+//-----------------------------------------------------------------------------
+ScoreInfos::ScoreInfos(uint maxNbEntries, const PlayerInfos &infos)
+    : _maxNbEntries(maxNbEntries)
+{
+    addItem("id", new Item((uint)0));
+    addItem("rank", new RankItem, false);
+    addItem("name", new ScoreNameItem(*this, infos));
+    addItem("score", Manager::createItem(Manager::ScoreDefault));
+    addItem("date", new DateItem);
+}
+
+uint ScoreInfos::nbEntries() const
+{
+    uint i = 0;
+    for (; i<_maxNbEntries; i++)
+        if ( item("score")->read(i)==item("score")->item()->defaultValue() )
+            break;
+    return i;
+}
+
+//-----------------------------------------------------------------------------
+const char *HS_ID              = "player id";
+const char *HS_REGISTERED_NAME = "registered name";
+const char *HS_KEY             = "player key";
+const char *HS_WW_ENABLED      = "ww hs enabled";
+
+PlayerInfos::PlayerInfos()
+{
+    setGroup("players");
+
+    // standard items
+    addItem("name", new NameItem);
+    Item *it = new Item((uint)0, i18n("Games Count"),Qt::AlignRight);
+    addItem("nb games", it, true, true);
+    it = Manager::createItem(Manager::MeanScoreDefault);
+    addItem("mean score", it, true, true);
+    it = Manager::createItem(Manager::BestScoreDefault);
+    addItem("best score", it, true, true);
+    addItem("date", new DateItem, true, true);
+    it = new Item(QString::null, i18n("Comment"), Qt::AlignLeft);
+    addItem("comment", it);
+
+    // statistics items
+    addItem("nb black marks", new Item((uint)0), true, true); // legacy
+    addItem("nb lost games", new Item((uint)0), true, true);
+    addItem("nb draw games", new Item((uint)0), true, true);
+    addItem("current trend", new Item((int)0), true, true);
+    addItem("max lost trend", new Item((uint)0), true, true);
+    addItem("max won trend", new Item((uint)0), true, true);
+
+    struct passwd *pwd = getpwuid(getuid());
+    QString username = pwd->pw_name;
+#ifdef HIGHSCORE_DIRECTORY
+    internal->hsConfig().setHighscoreGroup("players");
+    for (uint i=0; ;i++) {
+        if ( !internal->hsConfig().hasEntry(i+1, "username") ) {
+            _newPlayer = true;
+            _id = i;
+            break;
+        }
+        if ( internal->hsConfig().readEntry(i+1, "username")==username ) {
+            _newPlayer = false;
+            _id = i;
+            return;
+        }
+    }
+#endif
+    internal->hsConfig().lockForWriting();
+	KEMailSettings emailConfig;
+	emailConfig.setProfile(emailConfig.defaultProfileName());
+	QString name = emailConfig.getSetting(KEMailSettings::RealName);
+	if ( name.isEmpty() || isNameUsed(name) ) name = username;
+	if ( isNameUsed(name) ) name= QString(ItemContainer::ANONYMOUS);
+#ifdef HIGHSCORE_DIRECTORY
+    internal->hsConfig().writeEntry(_id+1, "username", username);
+    item("name")->write(_id, name);
+#endif
+
+    ConfigGroup cg;
+    _oldLocalPlayer = cg.config()->hasKey(HS_ID);
+    _oldLocalId = cg.config()->readUnsignedNumEntry(HS_ID);
+#ifdef HIGHSCORE_DIRECTORY
+    if (_oldLocalPlayer) { // player already exists in local config file
+        // copy player data
+        QString prefix = QString("%1_").arg(_oldLocalId+1);
+        QMap entries =
+            cg.config()->entryMap("KHighscore_players");
+        QMap::const_iterator it;
+        for (it=entries.begin(); it!=entries.end(); ++it) {
+            QString key = it.key();
+            if ( key.find(prefix)==0 ) {
+                QString name = key.right(key.length()-prefix.length());
+                if ( name!="name" || !isNameUsed(it.data()) )
+                    internal->hsConfig().writeEntry(_id+1, name, it.data());
+            }
+        }
+    }
+#else
+    _newPlayer = !_oldLocalPlayer;
+    if (_oldLocalPlayer) _id = _oldLocalId;
+    else {
+        _id = nbEntries();
+        cg.config()->writeEntry(HS_ID, _id);
+        item("name")->write(_id, name);
+    }
+#endif
+    _bound = true;
+    internal->hsConfig().writeAndUnlock();
+}
+
+void PlayerInfos::createHistoItems(const QMemArray &scores, bool bound)
+{
+    Q_ASSERT( _histogram.size()==0 );
+    _bound = bound;
+    _histogram = scores;
+    for (uint i=1; ihsConfig().setHighscoreGroup("players");
+    QStringList list = internal->hsConfig().readList("name", -1);
+    return list.count();
+}
+
+QString PlayerInfos::key() const
+{
+    ConfigGroup cg;
+    return cg.config()->readEntry(HS_KEY, QString::null);
+}
+
+bool PlayerInfos::isWWEnabled() const
+{
+    ConfigGroup cg;
+    return cg.config()->readBoolEntry(HS_WW_ENABLED, false);
+}
+
+QString PlayerInfos::histoName(uint i) const
+{
+    const QMemArray &sh = _histogram;
+    Q_ASSERT( iincrement(_id);
+    switch (score.type()) {
+    case Lost:
+        item("nb lost games")->increment(_id);
+        break;
+    case Won: break;
+    case Draw:
+        item("nb draw games")->increment(_id);
+        break;
+    };
+
+    // update mean
+    if ( score.type()==Won ) {
+        uint nbWonGames = nbGames - item("nb lost games")->read(_id).toUInt()
+                        - item("nb draw games")->read(_id).toUInt()
+                        - item("nb black marks")->read(_id).toUInt(); // legacy
+        double mean = (nbWonGames==1 ? 0.0
+                       : item("mean score")->read(_id).toDouble());
+        mean += (double(score.score()) - mean) / nbWonGames;
+        item("mean score")->write(_id, mean);
+    }
+
+    // update best score
+    Score best = score; // copy optionnal fields (there are not taken into account here)
+    best.setScore( item("best score")->read(_id).toUInt() );
+    if ( bestwrite(_id, score.score());
+        item("date")->write(_id, score.data("date").toDateTime());
+    }
+
+    // update trends
+    int current = item("current trend")->read(_id).toInt();
+    switch (score.type()) {
+    case Won: {
+        if ( current<0 ) current = 0;
+        current++;
+        uint won = item("max won trend")->read(_id).toUInt();
+        if ( (uint)current>won ) item("max won trend")->write(_id, current);
+        break;
+    }
+    case Lost: {
+        if ( current>0 ) current = 0;
+        current--;
+        uint lost = item("max lost trend")->read(_id).toUInt();
+        uint clost = -current;
+        if ( clost>lost ) item("max lost trend")->write(_id, clost);
+        break;
+    }
+    case Draw:
+        current = 0;
+        break;
+    }
+    item("current trend")->write(_id, current);
+
+    // update histogram
+    if ( score.type()==Won ) {
+        const QMemArray &sh = _histogram;
+        for (uint i=1; iincrement(_id);
+                break;
+            }
+    }
+}
+
+bool PlayerInfos::isNameUsed(const QString &newName) const
+{
+    if ( newName==name() ) return false; // own name...
+    for (uint i=0; iread(i).toString().lower() ) return true;
+    if ( newName==i18n(ItemContainer::ANONYMOUS_LABEL) ) return true;
+    return false;
+}
+
+void PlayerInfos::modifyName(const QString &newName) const
+{
+    item("name")->write(_id, newName);
+}
+
+void PlayerInfos::modifySettings(const QString &newName,
+                                 const QString &comment, bool WWEnabled,
+                                 const QString &newKey) const
+{
+    modifyName(newName);
+    item("comment")->write(_id, comment);
+    ConfigGroup cg;
+    cg.config()->writeEntry(HS_WW_ENABLED, WWEnabled);
+    if ( !newKey.isEmpty() ) cg.config()->writeEntry(HS_KEY, newKey);
+    if (WWEnabled) cg.config()->writeEntry(HS_REGISTERED_NAME, newName);
+}
+
+QString PlayerInfos::registeredName() const
+{
+    ConfigGroup cg;
+    return cg.config()->readEntry(HS_REGISTERED_NAME, QString::null);
+}
+
+void PlayerInfos::removeKey()
+{
+    ConfigGroup cg;
+
+    // save old key/nickname
+    uint i = 0;
+    QString str = "%1 old #%2";
+    QString sk;
+    do {
+        i++;
+        sk = str.arg(HS_KEY).arg(i);
+    } while ( !cg.config()->readEntry(sk, QString::null).isEmpty() );
+    cg.config()->writeEntry(sk, key());
+    cg.config()->writeEntry(str.arg(HS_REGISTERED_NAME).arg(i),
+                            registeredName());
+
+    // clear current key/nickname
+    cg.config()->deleteEntry(HS_KEY);
+    cg.config()->deleteEntry(HS_REGISTERED_NAME);
+    cg.config()->writeEntry(HS_WW_ENABLED, false);
+}
+
+//-----------------------------------------------------------------------------
+ManagerPrivate::ManagerPrivate(uint nbGameTypes, Manager &m)
+    : manager(m), showStatistics(false), showDrawGames(false),
+      trackLostGames(false), trackDrawGames(false), 
+      showMode(Manager::ShowForHigherScore),
+      _first(true), _nbGameTypes(nbGameTypes), _gameType(0)
+{}
+
+void ManagerPrivate::init(uint maxNbEntries)
+{
+    _hsConfig = new KHighscore(false, 0);
+    _playerInfos = new PlayerInfos;
+    _scoreInfos = new ScoreInfos(maxNbEntries, *_playerInfos);
+}
+
+ManagerPrivate::~ManagerPrivate()
+{
+    delete _scoreInfos;
+    delete _playerInfos;
+    delete _hsConfig;
+}
+
+KURL ManagerPrivate::queryURL(QueryType type, const QString &newName) const
+{
+    KURL url = serverURL;
+    QString nameItem = "nickname";
+    QString name = _playerInfos->registeredName();
+    bool withVersion = true;
+    bool key = false;
+    bool level = false;
+
+	switch (type) {
+        case Submit:
+            url.addPath("submit.php");
+            level = true;
+            key = true;
+            break;
+        case Register:
+            url.addPath("register.php");
+            name = newName;
+            break;
+        case Change:
+            url.addPath("change.php");
+            key = true;
+            if ( newName!=name )
+                Manager::addToQueryURL(url, "new_nickname", newName);
+            break;
+        case Players:
+            url.addPath("players.php");
+            nameItem = "highlight";
+            withVersion = false;
+            break;
+        case Scores:
+            url.addPath("highscores.php");
+            withVersion = false;
+            if ( _nbGameTypes>1 ) level = true;
+            break;
+	}
+
+    if (withVersion) Manager::addToQueryURL(url, "version", version);
+    if ( !name.isEmpty() ) Manager::addToQueryURL(url, nameItem, name);
+    if (key) Manager::addToQueryURL(url, "key", _playerInfos->key());
+    if (level) {
+        QString label = manager.gameTypeLabel(_gameType, Manager::WW);
+        if ( !label.isEmpty() ) Manager::addToQueryURL(url, "level", label);
+    }
+
+    return url;
+}
+
+// strings that needs to be translated (coming from the highscores server)
+const char *DUMMY_STRINGS[] = {
+    I18N_NOOP("Undefined error."),
+    I18N_NOOP("Missing argument(s)."),
+    I18N_NOOP("Invalid argument(s)."),
+
+    I18N_NOOP("Unable to connect to MySQL server."),
+    I18N_NOOP("Unable to select database."),
+    I18N_NOOP("Error on database query."),
+    I18N_NOOP("Error on database insert."),
+
+    I18N_NOOP("Nickname already registered."),
+    I18N_NOOP("Nickname not registered."),
+    I18N_NOOP("Invalid key."),
+    I18N_NOOP("Invalid submit key."),
+
+    I18N_NOOP("Invalid level."),
+    I18N_NOOP("Invalid score.")
+};
+
+const char *UNABLE_TO_CONTACT =
+    I18N_NOOP("Unable to contact world-wide highscore server");
+
+bool ManagerPrivate::doQuery(const KURL &url, QWidget *parent,
+                                QDomNamedNodeMap *map)
+{
+    KIO::http_update_cache(url, true, 0); // remove cache !
+
+    QString tmpFile;
+    if ( !KIO::NetAccess::download(url, tmpFile, parent) ) {
+        QString details = i18n("Server URL: %1").arg(url.host());
+        KMessageBox::detailedSorry(parent, i18n(UNABLE_TO_CONTACT), details);
+        return false;
+    }
+
+	QFile file(tmpFile);
+	if ( !file.open(IO_ReadOnly) ) {
+        KIO::NetAccess::removeTempFile(tmpFile);
+        QString details = i18n("Unable to open temporary file.");
+        KMessageBox::detailedSorry(parent, i18n(UNABLE_TO_CONTACT), details);
+        return false;
+    }
+
+	QTextStream t(&file);
+	QString content = t.read().stripWhiteSpace();
+	file.close();
+    KIO::NetAccess::removeTempFile(tmpFile);
+
+	QDomDocument doc;
+    if ( doc.setContent(content) ) {
+        QDomElement root = doc.documentElement();
+        QDomElement element = root.firstChild().toElement();
+        if ( element.tagName()=="success" ) {
+            if (map) *map = element.attributes();
+            return true;
+        }
+        if ( element.tagName()=="error" ) {
+            QDomAttr attr = element.attributes().namedItem("label").toAttr();
+            if ( !attr.isNull() ) {
+                QString msg = i18n(attr.value().latin1());
+                QString caption = i18n("Message from world-wide highscores "
+                                       "server");
+                KMessageBox::sorry(parent, msg, caption);
+                return false;
+            }
+        }
+    }
+    QString msg = i18n("Invalid answer from world-wide highscores server.");
+    QString details = i18n("Raw message: %1").arg(content);
+    KMessageBox::detailedSorry(parent, msg, details);
+    return false;
+}
+
+bool ManagerPrivate::getFromQuery(const QDomNamedNodeMap &map,
+                                  const QString &name, QString &value,
+                                  QWidget *parent)
+{
+    QDomAttr attr = map.namedItem(name).toAttr();
+    if ( attr.isNull() ) {
+	    KMessageBox::sorry(parent,
+               i18n("Invalid answer from world-wide "
+                    "highscores server (missing item: %1).").arg(name));
+		return false;
+    }
+    value = attr.value();
+    return true;
+}
+
+Score ManagerPrivate::readScore(uint i) const
+{
+    Score score(Won);
+    _scoreInfos->read(i, score);
+    return score;
+}
+
+int ManagerPrivate::rank(const Score &score) const
+{
+    uint nb = _scoreInfos->nbEntries();
+    uint i = 0;
+	for (; imaxNbEntries() ? (int)i : -1);
+}
+
+bool ManagerPrivate::modifySettings(const QString &newName,
+                                    const QString &comment, bool WWEnabled,
+                                    QWidget *widget)
+{
+    QString newKey;
+    bool newPlayer = false;
+
+    if (WWEnabled) {
+        newPlayer = _playerInfos->key().isEmpty()
+                    || _playerInfos->registeredName().isEmpty();
+        KURL url = queryURL((newPlayer ? Register : Change), newName);
+        Manager::addToQueryURL(url, "comment", comment);
+
+        QDomNamedNodeMap map;
+        bool ok = doQuery(url, widget, &map);
+        if ( !ok || (newPlayer && !getFromQuery(map, "key", newKey, widget)) )
+            return false;
+    }
+
+    bool ok = _hsConfig->lockForWriting(widget); // no GUI when locking
+    if (ok) {
+        // check again name in case the config file has been changed...
+        // if it has, it is unfortunate because the WWW name is already
+        // committed but should be very rare and not really problematic
+        ok = ( !_playerInfos->isNameUsed(newName) );
+        if (ok)
+            _playerInfos->modifySettings(newName, comment, WWEnabled, newKey);
+        _hsConfig->writeAndUnlock();
+    }
+    return ok;
+}
+
+void ManagerPrivate::convertToGlobal()
+{
+    // read old highscores
+    KHighscore *tmp = _hsConfig;
+    _hsConfig = new KHighscore(true, 0);
+    QValueVector scores(_scoreInfos->nbEntries());
+    for (uint i=0; ilockForWriting();
+    for (uint i=0; ioldLocalId()+1 )
+            submitLocal(scores[i]);
+    _hsConfig->writeAndUnlock();
+}
+
+void ManagerPrivate::setGameType(uint type)
+{
+    if (_first) {
+        _first = false;
+        if ( _playerInfos->isNewPlayer() ) {
+            // convert legacy highscores
+            for (uint i=0; i<_nbGameTypes; i++) {
+                setGameType(i);
+                manager.convertLegacy(i);
+            }
+
+#ifdef HIGHSCORE_DIRECTORY
+            if ( _playerInfos->isOldLocalPlayer() ) {
+                // convert local to global highscores
+                for (uint i=0; i<_nbGameTypes; i++) {
+                    setGameType(i);
+                    convertToGlobal();
+                }
+            }
+#endif
+        }
+    }
+
+    Q_ASSERT( type<_nbGameTypes );
+    _gameType = kMin(type, _nbGameTypes-1);
+    QString str = "scores";
+    QString lab = manager.gameTypeLabel(_gameType, Manager::Standard);
+    if ( !lab.isEmpty() ) {
+        _playerInfos->setSubGroup(lab);
+        str += "_" + lab;
+    }
+    _scoreInfos->setGroup(str);
+}
+
+void ManagerPrivate::checkFirst()
+{
+    if (_first) setGameType(0);
+}
+
+int ManagerPrivate::submitScore(const Score &ascore,
+                                QWidget *widget, bool askIfAnonymous)
+{
+    checkFirst();
+
+    Score score = ascore;
+    score.setData("id", _playerInfos->id() + 1);
+    score.setData("date", QDateTime::currentDateTime());
+
+    // ask new name if anonymous and winner
+    const char *dontAskAgainName = "highscore_ask_name_dialog";
+    QString newName;
+    KMessageBox::ButtonCode dummy;
+    if ( score.type()==Won && askIfAnonymous && _playerInfos->isAnonymous()
+     && KMessageBox::shouldBeShownYesNo(dontAskAgainName, dummy) ) {
+         AskNameDialog d(widget);
+         if ( d.exec()==QDialog::Accepted ) newName = d.name();
+         if ( d.dontAskAgain() )
+             KMessageBox::saveDontShowAgainYesNo(dontAskAgainName,
+                                                 KMessageBox::No);
+    }
+
+    int rank = -1;
+    if ( _hsConfig->lockForWriting(widget) ) { // no GUI when locking
+        // check again new name in case the config file has been changed...
+        if ( !newName.isEmpty() && !_playerInfos->isNameUsed(newName) )
+             _playerInfos->modifyName(newName);
+
+        // commit locally
+        _playerInfos->submitScore(score);
+        if ( score.type()==Won ) rank = submitLocal(score);
+        _hsConfig->writeAndUnlock();
+    }
+
+    if ( _playerInfos->isWWEnabled() )
+        submitWorldWide(score, widget);
+
+    return rank;
+}
+
+int ManagerPrivate::submitLocal(const Score &score)
+{
+    int r = rank(score);
+    if ( r!=-1 ) {
+        uint nb = _scoreInfos->nbEntries();
+        if ( nb<_scoreInfos->maxNbEntries() ) nb++;
+        _scoreInfos->write(r, score, nb);
+    }
+    return r;
+}
+
+bool ManagerPrivate::submitWorldWide(const Score &score,
+                                     QWidget *widget) const
+{
+    if ( score.type()==Lost && !trackLostGames ) return true;
+    if ( score.type()==Draw && !trackDrawGames ) return true;
+
+    KURL url = queryURL(Submit);
+    manager.additionalQueryItems(url, score);
+    int s = (score.type()==Won ? score.score() : (int)score.type());
+    QString str =  QString::number(s);
+    Manager::addToQueryURL(url, "score", str);
+    KMD5 context(QString(_playerInfos->registeredName() + str).latin1());
+    Manager::addToQueryURL(url, "check", context.hexDigest());
+
+    return doQuery(url, widget);
+}
+
+void ManagerPrivate::exportHighscores(QTextStream &s)
+{
+    uint tmp = _gameType;
+
+    for (uint i=0; i<_nbGameTypes; i++) {
+        setGameType(i);
+        if ( _nbGameTypes>1 ) {
+            if ( i!=0 ) s << endl;
+            s << "--------------------------------" << endl;
+            s << "Game type: "
+              << manager.gameTypeLabel(_gameType, Manager::I18N)
+              << endl;
+            s << endl;
+        }
+        s << "Players list:" << endl;
+        _playerInfos->exportToText(s);
+        s << endl;
+        s << "Highscores list:" << endl;
+        _scoreInfos->exportToText(s);
+    }
+
+    setGameType(tmp);
+}
+
+} // namespace
diff --git a/libkdegames/highscore/kexthighscore_internal.h b/libkdegames/highscore/kexthighscore_internal.h
new file mode 100644
index 00000000..3b206877
--- /dev/null
+++ b/libkdegames/highscore/kexthighscore_internal.h
@@ -0,0 +1,277 @@
+/*
+    This file is part of the KDE games library
+    Copyright (C) 2001-2004 Nicolas Hadacek (hadacek@kde.org)
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License version 2 as published by the Free Software Foundation.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXTHIGHSCORE_INTERNAL_H
+#define KEXTHIGHSCORE_INTERNAL_H
+
+#include 
+#include 
+#include 
+#include 
+
+#include "khighscore.h"
+#include "kexthighscore.h"
+
+class QTextStream;
+class QTabWidget;
+class QDomNamedNodeMap;
+
+
+namespace KExtHighscore
+{
+
+class PlayerInfos;
+class Score;
+class Manager;
+
+
+//-----------------------------------------------------------------------------
+class RankItem : public Item
+{
+ public:
+    RankItem()
+        : Item((uint)0, i18n("Rank"), Qt::AlignRight) {}
+
+    QVariant read(uint rank, const QVariant &) const  { return rank; }
+    QString pretty(uint rank, const QVariant &) const
+        { return QString::number(rank+1); }
+};
+
+class NameItem : public Item
+{
+ public:
+    NameItem()
+        : Item(QString::null, i18n("Name"), Qt::AlignLeft) {
+            setPrettySpecial(Anonymous);
+    }
+};
+
+class DateItem : public Item
+{
+ public:
+    DateItem()
+        : Item(QDateTime(), i18n("Date"), Qt::AlignRight) {
+            setPrettyFormat(DateTime);
+    }
+};
+
+class SuccessPercentageItem : public Item
+{
+ public:
+    SuccessPercentageItem()
+        : Item((double)-1, i18n("Success"), Qt::AlignRight) {
+            setPrettyFormat(Percentage);
+            setPrettySpecial(NegativeNotDefined);
+    }
+};
+
+//-----------------------------------------------------------------------------
+class ItemContainer
+{
+ public:
+    ItemContainer();
+    ~ItemContainer();
+
+    void setItem(Item *item);
+    const Item *item() const { return _item; }
+    Item *item() { return _item; }
+
+    void setName(const QString &name) { _name = name; }
+    QString name() const { return _name; }
+
+    void setGroup(const QString &group) { _group = group; }
+    bool isStored() const { return !_group.isNull(); }
+
+    void setSubGroup(const QString &subGroup) { _subGroup = subGroup; }
+    bool canHaveSubGroup() const { return !_subGroup.isNull(); }
+
+    static const char ANONYMOUS[]; // name assigned to anonymous players
+    static const char ANONYMOUS_LABEL[];
+
+    QVariant read(uint i) const;
+    QString pretty(uint i) const;
+    void write(uint i, const QVariant &value) const;
+    // for UInt QVariant (return new value)
+    uint increment(uint i) const;
+
+ private:
+    Item    *_item;
+    QString  _name, _group, _subGroup;
+
+    QString entryName() const;
+
+    ItemContainer(const ItemContainer &);
+    ItemContainer &operator =(const ItemContainer &);
+};
+
+//-----------------------------------------------------------------------------
+/**
+ * Manage a bunch of @ref Item which are saved under the same group
+ * in KHighscores config file.
+ */
+class ItemArray : public QMemArray
+{
+ public:
+    ItemArray();
+    virtual ~ItemArray();
+
+    virtual uint nbEntries() const = 0;
+
+    const ItemContainer *item(const QString &name) const;
+    ItemContainer *item(const QString &name);
+
+    void addItem(const QString &name, Item *, bool stored = true,
+                 bool canHaveSubGroup = false);
+    void setItem(const QString &name, Item *);
+    int findIndex(const QString &name) const;
+
+    void setGroup(const QString &group);
+    void setSubGroup(const QString &subGroup);
+
+    void read(uint k, Score &data) const;
+    void write(uint k, const Score &data, uint maxNbLines) const;
+
+    void exportToText(QTextStream &) const;
+
+ private:
+    QString _group, _subGroup;
+
+    void _setItem(uint i, const QString &name, Item *, bool stored,
+                  bool canHaveSubGroup);
+
+    ItemArray(const ItemArray &);
+    ItemArray &operator =(const ItemArray &);
+};
+
+//-----------------------------------------------------------------------------
+class ScoreInfos : public ItemArray
+{
+ public:
+    ScoreInfos(uint maxNbEntries, const PlayerInfos &infos);
+
+    uint nbEntries() const;
+    uint maxNbEntries() const { return _maxNbEntries; }
+
+ private:
+    uint _maxNbEntries;
+};
+
+//-----------------------------------------------------------------------------
+class ConfigGroup : public KConfigGroupSaver
+{
+ public:
+    ConfigGroup(const QString &group = QString::null)
+        : KConfigGroupSaver(kapp->config(), group) {}
+};
+
+//-----------------------------------------------------------------------------
+class PlayerInfos : public ItemArray
+{
+ public:
+    PlayerInfos();
+
+    bool isNewPlayer() const { return _newPlayer; }
+    bool isOldLocalPlayer() const { return _oldLocalPlayer; }
+    uint nbEntries() const;
+    QString name() const { return item("name")->read(_id).toString(); }
+    bool isAnonymous() const;
+    QString prettyName() const { return prettyName(_id); }
+    QString prettyName(uint id) const { return item("name")->pretty(id); }
+    QString registeredName() const;
+    QString comment() const { return item("comment")->pretty(_id); }
+    bool isWWEnabled() const;
+    QString key() const;
+    uint id() const { return _id; }
+    uint oldLocalId() const { return _oldLocalId; }
+
+    void createHistoItems(const QMemArray &scores, bool bound);
+    QString histoName(uint i) const;
+    uint histoSize() const;
+    const QMemArray &histogram() const { return _histogram; }
+
+    void submitScore(const Score &) const;
+    // return true if the nickname is already used locally
+    bool isNameUsed(const QString &name) const;
+    void modifyName(const QString &newName) const;
+    void modifySettings(const QString &newName, const QString &comment,
+                        bool WWEnabled, const QString &newKey) const;
+    void removeKey();
+
+ private:
+    bool _newPlayer, _bound, _oldLocalPlayer;
+    uint _id, _oldLocalId;
+    QMemArray _histogram;
+};
+
+//-----------------------------------------------------------------------------
+class ManagerPrivate
+{
+ public:
+    ManagerPrivate(uint nbGameTypes, Manager &manager);
+    void init(uint maxNbentries);
+    ~ManagerPrivate();
+
+    bool modifySettings(const QString &newName, const QString &comment,
+                        bool WWEnabled, QWidget *widget);
+
+    void setGameType(uint type);
+    void checkFirst();
+    int submitLocal(const Score &score);
+    int submitScore(const Score &score, QWidget *widget, bool askIfAnonymous);
+    Score readScore(uint i) const;
+
+    uint gameType() const        { return _gameType; }
+    uint nbGameTypes() const     { return _nbGameTypes; }
+    bool isWWHSAvailable() const { return !serverURL.isEmpty(); }
+    ScoreInfos &scoreInfos()     { return *_scoreInfos; }
+    PlayerInfos &playerInfos()   { return *_playerInfos; }
+    KHighscore &hsConfig()       { return *_hsConfig; }
+    enum QueryType { Submit, Register, Change, Players, Scores };
+    KURL queryURL(QueryType type, const QString &newName=QString::null) const;
+
+    void exportHighscores(QTextStream &);
+
+    Manager &manager;
+    KURL     serverURL;
+    QString  version;
+    bool     showStatistics, showDrawGames, trackLostGames, trackDrawGames;
+    Manager::ShowMode showMode;
+
+ private:
+    KHighscore   *_hsConfig;
+    PlayerInfos  *_playerInfos;
+    ScoreInfos   *_scoreInfos;
+    bool          _first;
+    const uint    _nbGameTypes;
+    uint          _gameType;
+
+    // return -1 if not a local best score
+    int rank(const Score &score) const;
+
+    bool submitWorldWide(const Score &score, QWidget *parent) const;
+    static bool doQuery(const KURL &url, QWidget *parent,
+                        QDomNamedNodeMap *map = 0);
+    static bool getFromQuery(const QDomNamedNodeMap &map, const QString &name,
+                             QString &value, QWidget *parent);
+    void convertToGlobal();
+};
+
+} // namespace
+
+#endif
diff --git a/libkdegames/highscore/kexthighscore_item.cpp b/libkdegames/highscore/kexthighscore_item.cpp
new file mode 100644
index 00000000..48556e02
--- /dev/null
+++ b/libkdegames/highscore/kexthighscore_item.cpp
@@ -0,0 +1,312 @@
+/*
+    This file is part of the KDE games library
+    Copyright (C) 2001-2003 Nicolas Hadacek (hadacek@kde.org)
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License version 2 as published by the Free Software Foundation.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "kexthighscore_item.h"
+
+#include 
+#include 
+#include 
+#include 
+
+#include "khighscore.h"
+#include "kexthighscore_internal.h"
+#include "kexthighscore_gui.h"
+
+
+namespace KExtHighscore
+{
+
+//-----------------------------------------------------------------------------
+Item::Item(const QVariant &def, const QString &label, int alignment)
+    : _default(def), _label(label), _alignment(alignment),
+      _format(NoFormat), _special(NoSpecial)
+{}
+
+Item::~Item()
+{}
+
+QVariant Item::read(uint, const QVariant &value) const
+{
+    return value;
+}
+
+void Item::setPrettyFormat(Format format)
+{
+    bool buint = ( _default.type()==QVariant::UInt );
+    bool bdouble = ( _default.type()==QVariant::Double );
+    bool bnum = ( buint || bdouble || _default.type()==QVariant::Int );
+
+    switch (format) {
+    case OneDecimal:
+    case Percentage:
+        Q_ASSERT(bdouble);
+        break;
+    case MinuteTime:
+        Q_ASSERT(bnum);
+        break;
+    case DateTime:
+    	Q_ASSERT( _default.type()==QVariant::DateTime );
+	break;
+    case NoFormat:
+        break;
+    }
+
+    _format = format;
+}
+
+void Item::setPrettySpecial(Special special)
+{
+    bool buint = ( _default.type()==QVariant::UInt );
+    bool bnum = ( buint || _default.type()==QVariant::Double
+                  || _default.type()==QVariant::Int );
+
+    switch (special) {
+    case ZeroNotDefined:
+        Q_ASSERT(bnum);
+        break;
+    case NegativeNotDefined:
+        Q_ASSERT(bnum && !buint);
+        break;
+    case DefaultNotDefined:
+        break;
+    case Anonymous:
+        Q_ASSERT( _default.type()==QVariant::String );
+        break;
+    case NoSpecial:
+        break;
+    }
+
+     _special = special;
+}
+
+QString Item::timeFormat(uint n)
+{
+    Q_ASSERT( n<=3600 && n!=0 );
+    n = 3600 - n;
+    return QString::number(n / 60).rightJustify(2, '0') + ':'
+        + QString::number(n % 60).rightJustify(2, '0');
+}
+
+QString Item::pretty(uint, const QVariant &value) const
+{
+    switch (_special) {
+    case ZeroNotDefined:
+        if ( value.toUInt()==0 ) return "--";
+        break;
+    case NegativeNotDefined:
+        if ( value.toInt()<0 ) return "--";
+        break;
+    case DefaultNotDefined:
+        if ( value==_default ) return "--";
+        break;
+    case Anonymous:
+        if ( value.toString()==ItemContainer::ANONYMOUS )
+            return i18n(ItemContainer::ANONYMOUS_LABEL);
+        break;
+    case NoFormat:
+        break;
+    }
+
+    switch (_format) {
+    case OneDecimal:
+        return QString::number(value.toDouble(), 'f', 1);
+    case Percentage:
+        return QString::number(value.toDouble(), 'f', 1) + "%";
+    case MinuteTime:
+        return timeFormat(value.toUInt());
+    case DateTime:
+        if ( value.toDateTime().isNull() ) return "--";
+        return KGlobal::locale()->formatDateTime(value.toDateTime());
+    case NoSpecial:
+        break;
+    }
+
+    return value.toString();
+}
+
+//-----------------------------------------------------------------------------
+Score::Score(ScoreType type)
+    : _type(type)
+{
+    const ItemArray &items = internal->scoreInfos();
+    for (uint i=0; iname()] = items[i]->item()->defaultValue();
+}
+
+Score::~Score()
+{}
+
+const QVariant &Score::data(const QString &name) const
+{
+    Q_ASSERT( _data.contains(name) );
+    return _data[name];
+}
+
+void Score::setData(const QString &name, const QVariant &value)
+{
+    Q_ASSERT( _data.contains(name) );
+    Q_ASSERT( _data[name].type()==value.type() );
+    _data[name] = value;
+}
+
+bool Score::isTheWorst() const
+{
+    Score s;
+    return score()==s.score();
+}
+
+bool Score::operator <(const Score &score)
+{
+    return internal->manager.isStrictlyLess(*this, score);
+}
+
+QDataStream &operator <<(QDataStream &s, const Score &score)
+{
+    s << (Q_UINT8)score.type();
+    s << score._data;
+    return s;
+}
+
+QDataStream &operator >>(QDataStream &s, Score &score)
+{
+    Q_UINT8 type;
+    s >> type;
+    score._type = (ScoreType)type;
+    s >> score._data;
+    return s;
+}
+
+//-----------------------------------------------------------------------------
+MultiplayerScores::MultiplayerScores()
+{}
+
+MultiplayerScores::~MultiplayerScores()
+{}
+
+void MultiplayerScores::clear()
+{
+    Score score;
+    for (uint i=0; i<_scores.size(); i++) {
+        _nbGames[i] = 0;
+        QVariant name = _scores[i].data("name");
+        _scores[i] = score;
+        _scores[i].setData("name", name);
+        _scores[i]._data["mean score"] = double(0);
+        _scores[i]._data["nb won games"] = uint(0);
+    }
+}
+
+void MultiplayerScores::setPlayerCount(uint nb)
+{
+    _nbGames.resize(nb);
+    _scores.resize(nb);
+    clear();
+}
+
+void MultiplayerScores::setName(uint i, const QString &name)
+{
+    _scores[i].setData("name", name);
+}
+
+void MultiplayerScores::addScore(uint i, const Score &score)
+{
+    QVariant name = _scores[i].data("name");
+    double mean = _scores[i].data("mean score").toDouble();
+    uint won = _scores[i].data("nb won games").toUInt();
+    _scores[i] = score;
+    _scores[i].setData("name", name);
+    _nbGames[i]++;
+    mean += (double(score.score()) - mean) / _nbGames[i];
+    _scores[i]._data["mean score"] = mean;
+    if ( score.type()==Won ) won++;
+    _scores[i]._data["nb won games"] = won;
+}
+
+void MultiplayerScores::show(QWidget *parent)
+{
+    // check consistency
+    if ( _nbGames.size()<2 ) kdWarning(11002) << "less than 2 players" << endl;
+    else {
+        bool ok = true;
+        uint nb = _nbGames[0];
+        for (uint i=1; i<_nbGames.size(); i++)
+            if ( _nbGames[i]!=nb ) ok = false;
+        if (!ok)
+           kdWarning(11002) << "players have not same number of games" << endl;
+    }
+
+    // order the players according to the number of won games
+    QValueVector ordered;
+    for (uint i=0; i<_scores.size(); i++) {
+        uint won = _scores[i].data("nb won games").toUInt();
+        double mean = _scores[i].data("mean score").toDouble();
+        QValueVector::iterator it;
+        for(it = ordered.begin(); it!=ordered.end(); ++it) {
+            uint cwon = (*it).data("nb won games").toUInt();
+            double cmean = (*it).data("mean score").toDouble();
+            if ( wonaddWidget(vbox);
+    if ( _nbGames[0]==0 ) (void)new QLabel(i18n("No game played."), vbox);
+    else {
+        (void)new QLabel(i18n("Scores for last game:"), vbox);
+        (void)new LastMultipleScoresList(ordered, vbox);
+    }
+
+    if ( _nbGames[0]>1 ) {
+        vbox = new QVBox(dialog.plainPage());
+        hbox->addWidget(vbox);
+        (void)new QLabel(i18n("Scores for the last %1 games:")
+                         .arg(_nbGames[0]), vbox);
+        (void)new TotalMultipleScoresList(ordered, vbox);
+    }
+
+    dialog.enableButtonSeparator(false);
+    dialog.exec();
+}
+
+QDataStream &operator <<(QDataStream &s, const MultiplayerScores &score)
+{
+    s << score._scores;
+    s << score._nbGames;
+    return s;
+}
+
+QDataStream &operator >>(QDataStream &s, MultiplayerScores &score)
+{
+    s >> score._scores;
+    s >> score._nbGames;
+    return s;
+}
+
+} // namespace
diff --git a/libkdegames/highscore/kexthighscore_item.h b/libkdegames/highscore/kexthighscore_item.h
new file mode 100644
index 00000000..0200fabd
--- /dev/null
+++ b/libkdegames/highscore/kexthighscore_item.h
@@ -0,0 +1,317 @@
+/*
+    This file is part of the KDE games library
+    Copyright (C) 2001-2003 Nicolas Hadacek (hadacek@kde.org)
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License version 2 as published by the Free Software Foundation.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXTHIGHSCORE_ITEM_H
+#define KEXTHIGHSCORE_ITEM_H
+
+#include 
+#include 
+#include 
+#include 
+#include 
+class QWidget;
+
+
+namespace KExtHighscore
+{
+
+//-----------------------------------------------------------------------------
+/**
+ * This class defines how to convert and how to display
+ * a highscore element (such as the score, the date, ...) or a player
+ * info (such as the player name, the best score, ...).
+ */
+class KDE_EXPORT Item
+{
+ public:
+    /**
+     * Possible display format.
+     * 
    + *
  • @p NoFormat : no formatting (default)
  • + *
  • @p OneDecimal : with one decimal (only for Double)
  • + *
  • @p Percentage : with one decimal + % (only for Double)
  • + *
  • @p MinuteTime : MM:SS ie 3600 is 00:00, 1 is 59:59 and 0 is + * undefined (only for UInt, Int and Double)
  • + *
  • @p DateTime : date and time according to locale (only for + * DateTime)
  • + *
+ */ + enum Format { NoFormat, OneDecimal, Percentage, MinuteTime, + DateTime }; + + /** + * Possible special value for display format. + *
    + *
  • @p NoSpecial : no special value ; a null DateTime is replaced by + * "--" (default)
  • + *
  • ZeroNotDefined : 0 is replaced by "--" (only for UInt, Int and + * Double)
  • + *
  • @p NegativeNotDefined : negative values are replaced by "--" (only + * for Int and Double)
  • + *
  • @p DefaultNotDefined : default value is replaced by "--"
  • + *
  • @p Anonymous : replace the special value ItemBase::ANONYMOUS + * by i18n("anonymous") (only for String)
  • + *
+ */ + enum Special { NoSpecial, ZeroNotDefined, NegativeNotDefined, + DefaultNotDefined, Anonymous }; + + /** + * Constructor. + * + * @param def default value ; the QVariant also gives the type of data. + * Be sure to cast the value to the required type (for e.g. with uint). + * @param label the label corresponding to the item. If empty, the item + * is not shown. + * @param alignment the alignment of the item. + */ + Item(const QVariant &def = QVariant::Invalid, + const QString &label = QString::null, int alignment = Qt::AlignRight); + + virtual ~Item(); + + /** + * Set the display format. + * @see Format + */ + void setPrettyFormat(Format format); + + /** + * Set the special value for display. + * @see Special + */ + void setPrettySpecial(Special special); + + /** + * @return if the item is shown. + */ + bool isVisible() const { return !_label.isEmpty(); } + + /** + * Set the label. + */ + void setLabel(const QString &label) { _label = label; } + + /** + * @return the label. + */ + QString label() const { return _label; } + + /** + * @return the alignment. + */ + int alignment() const { return _alignment; } + + /** + * Set default value. + */ + void setDefaultValue(const QVariant &value) { _default = value; } + + /** + * @return the default value. + */ + const QVariant &defaultValue() const { return _default; } + + /** + * @return the converted value (by default the value is left + * unchanged). Most of the time you don't need to reimplement this method. + * + * @param i the element index ("rank" for score / "id" for player) + * @param value the value to convert + */ + virtual QVariant read(uint i, const QVariant &value) const; + + /** + * @return the string to be displayed. You may need to reimplement this + * method for special formatting (different from the standard ones). + * + * @param i the element index ("rank" for score / "id" for player) + * @param value the value to convert + */ + virtual QString pretty(uint i, const QVariant &value) const; + + private: + QVariant _default; + QString _label; + int _alignment; + Format _format; + Special _special; + + class ItemPrivate; + ItemPrivate *d; + + static QString timeFormat(uint); +}; + +//----------------------------------------------------------------------------- +/** + * Possible score type. + * @p Won the game has been won. + * @p Lost the game has been lost or has been aborted. + * @p Draw the game is a draw. + */ +enum ScoreType { Won = 0, Lost = -1, Draw = -2 }; + +/** + * This class contains data for a score. You should not inherit from + * this class but reimplement the methods in Highscores. + */ +class KDE_EXPORT Score +{ + public: + Score(ScoreType type = Won); + + ~Score(); + + /** + * @return the game type. + */ + ScoreType type() const { return _type; } + + /** + * Set the game type. + */ + void setType(ScoreType type) { _type = type; } + + /** + * @return the data associated with the named Item. + */ + const QVariant &data(const QString &name) const; + + /** + * Set the data associated with the named Item. Note that the + * value should have the type of the default value of the + * Item. + */ + void setData(const QString &name, const QVariant &value); + + /** + * @return the score value. + * + * Equivalent to
data("score").toUInt()
. + */ + uint score() const { return data("score").toUInt(); } + + /** + * Set the score value. + * + * Equivalent to
setData("score", score)
. + */ + void setScore(uint score) { setData("score", score); } + + /** + * @return true if this is the worst possible score (ie the default + * argument of ScoreItem). + */ + bool isTheWorst() const; + + /** + * Comparison operator. + * + * @see Manager::isStrictlyLess + */ + bool operator <(const Score &score); + + private: + ScoreType _type; + QMap _data; + + class ScorePrivate; + ScorePrivate *d; + + friend class MultiplayerScores; + + friend QDataStream &operator <<(QDataStream &stream, const Score &score); + friend QDataStream &operator >>(QDataStream &stream, Score &score); +}; + +KDE_EXPORT QDataStream &operator <<(QDataStream &stream, const Score &score); +KDE_EXPORT QDataStream &operator >>(QDataStream &stream, Score &score); + +/** + * This class is used to store and show scores for multiplayer games. + * + * Example of use: + * Initialize the class: + *
+ * KExtHighscore::MultiScore ms(2);
+ * ms.setPlayerName(0, "player 1");
+ * ms.setPlayerName(1, "player 2");
+ * 
+ * At the end of each game, add the score of each players: + *
+ * KExtHighscore::Score score(KExtHighscore::Won);
+ * score.setScore(100);
+ * ms.addScore(0, score);
+ * score.setType(KExtHighscore::Lost);
+ * score.setScore(20);
+ * ms.addScore(1, score);
+ * 
+ */ +class KDE_EXPORT MultiplayerScores +{ + public: + MultiplayerScores(); + + ~MultiplayerScores(); + + /** + * Set the number of players and clear the scores. + */ + void setPlayerCount(uint nb); + + /** + * Set the name of player. + */ + void setName(uint player, const QString &name); + + /** + * Add the score of player. + */ + void addScore(uint player, const Score &score); + + /** + * Clear all scores. + */ + void clear(); + + /** + * Show scores. + */ + void show(QWidget *parent); + + private: + QValueVector _nbGames; + QValueVector _scores; + + class MultiplayerScoresPrivate; + MultiplayerScoresPrivate *d; + + friend QDataStream &operator <<(QDataStream &stream, + const MultiplayerScores &score); + friend QDataStream &operator >>(QDataStream &stream, + MultiplayerScores &score); +}; + +KDE_EXPORT QDataStream &operator <<(QDataStream &stream, const MultiplayerScores &score); +KDE_EXPORT QDataStream &operator >>(QDataStream &stream, MultiplayerScores &score); + +} // namespace + +#endif diff --git a/libkdegames/highscore/kexthighscore_tab.cpp b/libkdegames/highscore/kexthighscore_tab.cpp new file mode 100644 index 00000000..3e9cbe8a --- /dev/null +++ b/libkdegames/highscore/kexthighscore_tab.cpp @@ -0,0 +1,281 @@ +/* + This file is part of the KDE games library + Copyright (C) 2002 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kexthighscore_tab.h" +#include "kexthighscore_tab.moc" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "kexthighscore.h" +#include "kexthighscore_internal.h" + + +namespace KExtHighscore +{ + +//----------------------------------------------------------------------------- +PlayersCombo::PlayersCombo(QWidget *parent, const char *name) + : QComboBox(parent, name) +{ + const PlayerInfos &p = internal->playerInfos(); + for (uint i = 0; i'); + connect(this, SIGNAL(activated(int)), SLOT(activatedSlot(int))); +} + +void PlayersCombo::activatedSlot(int i) +{ + const PlayerInfos &p = internal->playerInfos(); + if ( i==(int)p.nbEntries() ) emit allSelected(); + else if ( i==(int)p.nbEntries()+1 ) emit noneSelected(); + else emit playerSelected(i); +} + +void PlayersCombo::load() +{ + const PlayerInfos &p = internal->playerInfos(); + for (uint i = 0; iaddWidget(label); + _combo = new PlayersCombo(this); + connect(_combo, SIGNAL(playerSelected(uint)), + SLOT(playerSelected(uint))); + connect(_combo, SIGNAL(allSelected()), SLOT(allSelected())); + hbox->addWidget(_combo); + hbox->addStretch(1); +} + +void AdditionalTab::init() +{ + uint id = internal->playerInfos().id(); + _combo->setCurrentItem(id); + playerSelected(id); +} + +void AdditionalTab::allSelected() +{ + display(internal->playerInfos().nbEntries()); +} + +QString AdditionalTab::percent(uint n, uint total, bool withBraces) +{ + if ( n==0 || total==0 ) return QString::null; + QString s = QString("%1%").arg(100.0 * n / total, 0, 'f', 1); + return (withBraces ? QString("(") + s + ")" : s); +} + +void AdditionalTab::load() +{ + _combo->load(); +} + + +//----------------------------------------------------------------------------- +const char *StatisticsTab::COUNT_LABELS[Nb_Counts] = { + I18N_NOOP("Total:"), I18N_NOOP("Won:"), I18N_NOOP("Lost:"), + I18N_NOOP("Draw:") +}; +const char *StatisticsTab::TREND_LABELS[Nb_Trends] = { + I18N_NOOP("Current:"), I18N_NOOP("Max won:"), I18N_NOOP("Max lost:") +}; + +StatisticsTab::StatisticsTab(QWidget *parent) + : AdditionalTab(parent, "statistics_tab") +{ + // construct GUI + QVBoxLayout *top = static_cast(layout()); + + QHBoxLayout *hbox = new QHBoxLayout(top); + QVBoxLayout *vbox = new QVBoxLayout(hbox); + QVGroupBox *group = new QVGroupBox(i18n("Game Counts"), this); + vbox->addWidget(group); + QGrid *grid = new QGrid(3, group); + grid->setSpacing(KDialogBase::spacingHint()); + for (uint k=0; kshowDrawGames ) continue; + (void)new QLabel(i18n(COUNT_LABELS[k]), grid); + _nbs[k] = new QLabel(grid); + _percents[k] = new QLabel(grid); + } + + group = new QVGroupBox(i18n("Trends"), this); + vbox->addWidget(group); + grid = new QGrid(2, group); + grid->setSpacing(KDialogBase::spacingHint()); + for (uint k=0; kaddStretch(1); + top->addStretch(1); +} + +void StatisticsTab::load() +{ + AdditionalTab::load(); + const PlayerInfos &pi = internal->playerInfos(); + uint nb = pi.nbEntries(); + _data.resize(nb+1); + for (uint i=0; i<_data.size()-1; i++) { + _data[i].count[Total] = pi.item("nb games")->read(i).toUInt(); + _data[i].count[Lost] = pi.item("nb lost games")->read(i).toUInt() + + pi.item("nb black marks")->read(i).toUInt(); // legacy + _data[i].count[Draw] = pi.item("nb draw games")->read(i).toUInt(); + _data[i].count[Won] = _data[i].count[Total] - _data[i].count[Lost] + - _data[i].count[Draw]; + _data[i].trend[CurrentTrend] = + pi.item("current trend")->read(i).toInt(); + _data[i].trend[WonTrend] = pi.item("max won trend")->read(i).toUInt(); + _data[i].trend[LostTrend] = + -(int)pi.item("max lost trend")->read(i).toUInt(); + } + + for (uint k=0; kshowDrawGames ) continue; + _nbs[k]->setText(QString::number(d.count[k])); + _percents[k]->setText(percent(d, Count(k))); + } + for (uint k=0; k0 ) s = '+'; + int prec = (i==internal->playerInfos().nbEntries() ? 1 : 0); + _trends[k]->setText(s + QString::number(d.trend[k], 'f', prec)); + } +} + +//----------------------------------------------------------------------------- +HistogramTab::HistogramTab(QWidget *parent) + : AdditionalTab(parent, "histogram_tab") +{ + // construct GUI + QVBoxLayout *top = static_cast(layout()); + + _list = new KListView(this); + _list->setSelectionMode(QListView::NoSelection); + _list->setItemMargin(3); + _list->setAllColumnsShowFocus(true); + _list->setSorting(-1); + _list->header()->setClickEnabled(false); + _list->header()->setMovingEnabled(false); + top->addWidget(_list); + + _list->addColumn(i18n("From")); + _list->addColumn(i18n("To")); + _list->addColumn(i18n("Count")); + _list->addColumn(i18n("Percent")); + for (uint i=0; i<4; i++) _list->setColumnAlignment(i, AlignRight); + _list->addColumn(QString::null); + + const Item *sitem = internal->scoreInfos().item("score")->item(); + const PlayerInfos &pi = internal->playerInfos(); + const QMemArray &sh = pi.histogram(); + for (uint k=1; kpretty(0, sh[k-1]); + QString s2; + if ( k==sh.size() ) s2 = "..."; + else if ( sh[k]!=sh[k-1]+1 ) s2 = sitem->pretty(0, sh[k]); + (void)new KListViewItem(_list, s1, s2); + } +} + +void HistogramTab::load() +{ + AdditionalTab::load(); + const PlayerInfos &pi = internal->playerInfos(); + uint n = pi.nbEntries(); + uint s = pi.histoSize() - 1; + _counts.resize((n+1) * s); + _data.fill(0, n+1); + for (uint k=0; kread(i).toUInt(); + _counts[i*s + k] = nb; + _counts[n*s + k] += nb; + _data[i] += nb; + _data[n] += nb; + } + } + + init(); +} + +void HistogramTab::display(uint i) +{ + const PlayerInfos &pi = internal->playerInfos(); + QListViewItem *item = _list->firstChild(); + uint s = pi.histoSize() - 1; + for (int k=s-1; k>=0; k--) { + uint nb = _counts[i*s + k]; + item->setText(2, QString::number(nb)); + item->setText(3, percent(nb, _data[i])); + uint width = (_data[i]==0 ? 0 : qRound(150.0 * nb / _data[i])); + QPixmap pixmap(width, 10); + pixmap.fill(blue); + item->setPixmap(4, pixmap); + item = item->nextSibling(); + } +} + +} // namespace diff --git a/libkdegames/highscore/kexthighscore_tab.h b/libkdegames/highscore/kexthighscore_tab.h new file mode 100644 index 00000000..ce47a75f --- /dev/null +++ b/libkdegames/highscore/kexthighscore_tab.h @@ -0,0 +1,117 @@ +/* + This file is part of the KDE games library + Copyright (C) 2002 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXTHIGHSCORE_TAB_H +#define KEXTHIGHSCORE_TAB_H + +#include +#include + +class QLabel; +class KListView; + + +namespace KExtHighscore +{ + +//----------------------------------------------------------------------------- +class PlayersCombo : public QComboBox +{ + Q_OBJECT + public: + PlayersCombo(QWidget *parent = 0, const char *name = 0); + + void load(); + + signals: + void playerSelected(uint i); + void allSelected(); + void noneSelected(); + + private slots: + void activatedSlot(int i); +}; + +//----------------------------------------------------------------------------- +class AdditionalTab : public QWidget +{ + Q_OBJECT + public: + AdditionalTab(QWidget *parent, const char *name); + + virtual void load(); + + private slots: + void playerSelected(uint i) { display(i) ; } + void allSelected(); + + protected: + void init(); + static QString percent(uint n, uint total, bool withBraces = false); + virtual void display(uint i) = 0; + + private: + PlayersCombo *_combo; +}; + +//----------------------------------------------------------------------------- +class StatisticsTab : public AdditionalTab +{ + Q_OBJECT + public: + StatisticsTab(QWidget *parent); + + void load(); + + private: + enum Count { Total = 0, Won, Lost, Draw, Nb_Counts }; + static const char *COUNT_LABELS[Nb_Counts]; + enum Trend { CurrentTrend = 0, WonTrend, LostTrend, Nb_Trends }; + static const char *TREND_LABELS[Nb_Trends]; + struct Data { + uint count[Nb_Counts]; + double trend[Nb_Trends]; + }; + QMemArray _data; + QLabel *_nbs[Nb_Counts], *_percents[Nb_Counts], *_trends[Nb_Trends]; + + QString percent(const Data &, Count) const; + void display(uint i); +}; + +//----------------------------------------------------------------------------- +class HistogramTab : public AdditionalTab +{ + Q_OBJECT + public: + HistogramTab(QWidget *parent); + + void load(); + + private: + QMemArray _counts; + QMemArray _data; + KListView *_list; + + void display(uint i); +}; + +} // namespace + +#endif diff --git a/libkdegames/highscore/kfilelock.cpp b/libkdegames/highscore/kfilelock.cpp new file mode 100644 index 00000000..7dc83b96 --- /dev/null +++ b/libkdegames/highscore/kfilelock.cpp @@ -0,0 +1,88 @@ +/* + This file is part of the KDE games library + Copyright (C) 2003 Nicolas Hadacek + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kfilelock.h" + +#include +#include +#include + +#include + + +KFileLock::KFileLock(int fd) + : _fd(fd), _locked(false) +{} + +int KFileLock::lock() +{ + kdDebug(11002) << "lock fd=" << _fd << endl; +#ifdef F_SETLK +# ifndef SEEK_SET +# define SEEK_SET 0 +# endif + struct flock lock_data; + lock_data.l_type = F_WRLCK; + lock_data.l_whence = SEEK_SET; + lock_data.l_start = lock_data.l_len = 0; + if ( fcntl(_fd, F_SETLK, &lock_data)==-1 ) { + if ( errno==EAGAIN ) return -2; + return -1; + } +#else +# ifdef LOCK_EX + if ( flock (_fd, LOCK_EX|LOCK_NB)==-1 ) { + if ( errno==EWOULDBLOCK ) return -2; + return -1; + } +# else + if ( lockf(_fd, F_TLOCK, 0)==-1 ) { + if ( errno==EACCES ) return -2; + return -1; + } +# endif +#endif + _locked = true; + return 0; +} + +KFileLock::~KFileLock() +{ + unlock(); +} + +void KFileLock::unlock() +{ + if ( !_locked ) return; + kdDebug(11002) << "unlock" << endl; +# ifdef F_SETLK + struct flock lock_data; + lock_data.l_type = F_UNLCK; + lock_data.l_whence = SEEK_SET; + lock_data.l_start = lock_data.l_len = 0; + (void)fcntl(_fd, F_SETLK, &lock_data); +# else +# ifdef F_ULOCK + lockf(_fd, F_ULOCK, 0); +# else + flock(_fd, LOCK_UN); +# endif +# endif + _locked = false; +} diff --git a/libkdegames/highscore/kfilelock.h b/libkdegames/highscore/kfilelock.h new file mode 100644 index 00000000..2e1841ba --- /dev/null +++ b/libkdegames/highscore/kfilelock.h @@ -0,0 +1,53 @@ +/* + This file is part of the KDE games library + Copyright (C) 2003 Nicolas Hadacek + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef KFILELOCK_H +#define KFILELOCK_H + + +class KFileLock +{ +public: + KFileLock(int fd); + + /** Call unlock(). */ + ~KFileLock(); + + /** @return the file descriptor. */ + int fd() const { return _fd; } + + /* + * Lock the file. + * @return 0 on success, -1 on failure (no permission) and -2 if another + * process is currently locking the file. + */ + int lock(); + + /** Unlock the file. */ + void unlock(); + + /** @return true if we currently lock the file. */ + bool isLocked() const { return _locked; } + +private: + int _fd; + bool _locked; +}; + + +#endif diff --git a/libkdegames/highscore/khighscore.cpp b/libkdegames/highscore/khighscore.cpp new file mode 100644 index 00000000..4e1f68e5 --- /dev/null +++ b/libkdegames/highscore/khighscore.cpp @@ -0,0 +1,262 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2003 Nicolas Hadacek + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "khighscore.h" +#include "kconfigrawbackend.h" +#include "kfilelock.h" + +#define GROUP "KHighscore" + +class KHighscorePrivate +{ +public: + KHighscorePrivate() {} + + QString group; + bool global; +}; + +KFileLock *KHighscore::_lock = 0; +KRawConfig *KHighscore::_config = 0; +static KStaticDeleter lockSD; +static KStaticDeleter configSD; + + +KHighscore::KHighscore(QObject* parent) + : QObject(parent) +{ + init(true); +} + +KHighscore::KHighscore(bool forceLocal, QObject* parent) + : QObject(parent) +{ + init(forceLocal); +} + +void KHighscore::init(bool forceLocal) +{ + d = new KHighscorePrivate; +#ifdef HIGHSCORE_DIRECTORY + d->global = !forceLocal; + if ( d->global && _lock==0 ) + kdFatal(11002) << "KHighscore::init should be called before!!" << endl; +#else + d->global = false; + Q_UNUSED(forceLocal); +#endif + readCurrentConfig(); +} + +bool KHighscore::isLocked() const +{ + return (d->global ? _lock->isLocked() : true); +} + +void KHighscore::readCurrentConfig() +{ + if ( d->global ) _config->reparseConfiguration(); +} + +void KHighscore::init(const char *appname) +{ +#ifdef HIGHSCORE_DIRECTORY + const QString filename = QString::fromLocal8Bit("%1/%2.scores") + .arg(HIGHSCORE_DIRECTORY).arg(appname); + int fd = open(filename.local8Bit(), O_RDWR); + if ( fd<0 ) kdFatal(11002) << "cannot open global highscore file \"" + << filename << "\"" << endl; + lockSD.setObject(_lock, new KFileLock(fd)); + configSD.setObject(_config, new KRawConfig(fd, true)); // read-only + + // drop the effective gid + int gid = getgid(); + setregid(gid, gid); +#else + Q_UNUSED(appname); +#endif +} + +bool KHighscore::lockForWriting(QWidget *widget) +{ + if ( isLocked() ) return true; + + bool first = true; + for (;;) { + kdDebug(11002) << "try locking" << endl; + // lock the highscore file (it should exist) + int result = _lock->lock(); + bool ok = ( result==0 ); + kdDebug(11002) << "locking system-wide highscore file res=" + << result << " (ok=" << ok << ")" << endl; + if (ok) { + readCurrentConfig(); + _config->setReadOnly(false); + return true; + } + + if ( !first ) { + KGuiItem item = KStdGuiItem::cont(); + item.setText(i18n("Retry")); + int res = KMessageBox::warningContinueCancel(widget, i18n("Cannot access the highscore file. Another user is probably currently writing to it."), QString::null, item, "ask_lock_global_highscore_file"); + if ( res==KMessageBox::Cancel ) break; + } else sleep(1); + first = false; + } + return false; +} + +void KHighscore::writeAndUnlock() +{ + if ( !d->global ) { + kapp->config()->sync(); + return; + } + if ( !isLocked() ) return; + + kdDebug(11002) << "unlocking" << endl; + _config->sync(); // write config + _lock->unlock(); + _config->setReadOnly(true); +} + +KHighscore::~KHighscore() +{ + writeAndUnlock(); + delete d; +} + +KConfig* KHighscore::config() const +{ + return (d->global ? _config : kapp->config()); +} + +void KHighscore::writeEntry(int entry, const QString& key, const QVariant& value) +{ + Q_ASSERT( isLocked() ); + KConfigGroupSaver cg(config(), group()); + QString confKey = QString("%1_%2").arg(entry).arg(key); + cg.config()->writeEntry(confKey, value); +} + +void KHighscore::writeEntry(int entry, const QString& key, int value) +{ + Q_ASSERT( isLocked() ); + KConfigGroupSaver cg(config(), group()); + QString confKey = QString("%1_%2").arg(entry).arg(key); + cg.config()->writeEntry(confKey, value); +} + +void KHighscore::writeEntry(int entry, const QString& key, const QString &value) +{ + Q_ASSERT (isLocked() ); + KConfigGroupSaver cg(config(), group()); + QString confKey = QString("%1_%2").arg(entry).arg(key); + cg.config()->writeEntry(confKey, value); +} + +QVariant KHighscore::readPropertyEntry(int entry, const QString& key, const QVariant& pDefault) const +{ + KConfigGroupSaver cg(config(), group()); + QString confKey = QString("%1_%2").arg(entry).arg(key); + return cg.config()->readPropertyEntry(confKey, pDefault); +} + +QString KHighscore::readEntry(int entry, const QString& key, const QString& pDefault) const +{ + KConfigGroupSaver cg(config(), group()); + QString confKey = QString("%1_%2").arg(entry).arg(key); + return cg.config()->readEntry(confKey, pDefault); +} + +int KHighscore::readNumEntry(int entry, const QString& key, int pDefault) const +{ + KConfigGroupSaver cg(config(), group()); + QString confKey = QString("%1_%2").arg(entry).arg(key); + return cg.config()->readNumEntry(confKey, pDefault); +} + +bool KHighscore::hasEntry(int entry, const QString& key) const +{ + KConfigGroupSaver cg(config(), group()); + QString confKey = QString("%1_%2").arg(entry).arg(key); + return cg.config()->hasKey(confKey); +} + +QStringList KHighscore::readList(const QString& key, int lastEntry) const +{ + QStringList list; + for (int i = 1; hasEntry(i, key) && ((lastEntry > 0) ? (i <= lastEntry) : true); i++) { + list.append(readEntry(i, key)); + } + return list; +} + +void KHighscore::writeList(const QString& key, const QStringList& list) +{ + for (int unsigned i = 1; i <= list.count(); i++) { + writeEntry(i, key, list[i - 1]); + } +} + +void KHighscore::setHighscoreGroup(const QString& group) +{ + d->group = group; +} + +const QString& KHighscore::highscoreGroup() const +{ + return d->group; +} + +QString KHighscore::group() const +{ + if ( highscoreGroup().isNull() ) + return (d->global ? QString::null : GROUP); + return (d->global ? highscoreGroup() + : QString("%1_%2").arg(GROUP).arg(highscoreGroup())); +} + +bool KHighscore::hasTable() const +{ return config()->hasGroup(group()); } + +void KHighscore::sync() +{ + writeAndUnlock(); +} + +#include "khighscore.moc" diff --git a/libkdegames/highscore/khighscore.h b/libkdegames/highscore/khighscore.h new file mode 100644 index 00000000..b1e3d25f --- /dev/null +++ b/libkdegames/highscore/khighscore.h @@ -0,0 +1,311 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2003 Nicolas Hadacek + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ +#ifndef __KHIGHSCORE_H__ +#define __KHIGHSCORE_H__ + +#include +#include +#include +class KConfig; +class KFileLock; +class KRawConfig; +class KHighscorePrivate; + +/** + * @short Class for managing highscore tables + * + * This is the KDE class for saving and reading highscore tables. It offers the + * possibility for system-wide highscore tables (configure with e.g. + * --enable-highscore-dir=/var/games) and a theoretically unlimited number of + * entries. + * + * You can specify different "keys" for an entry - just like the KConfig + * keys. But it will be prefixed with the number of the entry. For example you + * will probably use something like this to save the name of the player on the + * top of the list (ie the winner): + * \code + * highscore->writeEntry(1, "name", myPlayer->name()); + * \endcode + * Note that it doesn't really matter if you use "0" or "1" as the first entry + * of the list as long as your program always uses the same for the first + * entry. I recommend to use "1", as several convenience methods use this. + * + * You can also specify different groups using setHighscoreGroup. Just + * like the keys mentioned above the groups behave like groups in KConfig + * but are prefixed with "KHighscore_". The default group is just "KHighscore". + * You might use this e.g. to create different highscore tables like + * \code + * table->setHighscoreGroup("Easy"); + * // write the highscores for level "easy" to the table + * writeEasyHighscores(table); + * + * table->setHighscore("Player_1"); + * // write player specific highscores to the table + * writePlayerHighscores(table); + * \endcode + * As you can see above you can also use this to write the highscores of a + * single player, so the "best times" of a player. To write highscores for a + * specific player in a specific level you will have to use a more complex way: + * \code + * QString group = QString("%1_%2").arg(player).arg(level); + * table->setGroup(group); + * writeHighscore(table, player, level); + * \endcode + * + * Also note that you MUST NOT mark the key or the group for translation! I.e. + * don't use i18n() for the keys or groups! Here is the code to read the above + * written entry: + * \code + * QString firstName = highscore->readEntry(0, "name"); + * \endcode + * Easy, what? + * @author Andreas Beckermann + **/ +class KDE_EXPORT KHighscore : public QObject +{ + Q_OBJECT +public: + /** @obsolete + * Constructor. The highscore file is forced to be local to support + * games using the old behaviour. + */ + KHighscore(QObject* parent = 0); + + /** + * Constructor. + * + * @param forceLocal if true, the local highscore file is used even + * when the configuration has been set to use a system-wide file. This + * is convenient for converting highscores from legacy applications. + * @param parent parent widget for this widget + * @since 3.2 + */ + KHighscore(bool forceLocal, QObject *parent); + + /** + * Read the current state of the highscore file. Remember that when + * it's not locked for writing, this file can change at any time. + * (This method is only useful for a system-wide highscore file). + * @since 3.2 + */ + void readCurrentConfig(); + + /** @since 3.2 + * This method open the system-wide highscore file using the effective + * group id of the game executable (which should be "games"). The + * effective group id is completely dropped afterwards. + * + * Note: this method should be called in main() before creating a + * KApplication and doing anything else (KApplication checks that the + * program is not suid/sgid and will exit the program for security + * reason if it is the case). + */ + static void init(const char *appname); + + /** @since 3.2 + * Lock the system-wide highscore file for writing (does nothing and + * return true if the local file is used). + * You should perform writing without GUI interaction to avoid + * blocking and don't forget to unlock the file as soon as possible + * with writeAndUnlock(). + * + * If the config file cannot be locked, + * the method waits for 1 second and, if it failed again, displays + * a message box asking for retry or cancel. + * @param widget used as the parent of the message box. + * + * @return false on error or if the config file is locked by another + * process. In such case, the config stays read-only. + */ + bool lockForWriting(QWidget *widget = 0); + + /** + * Effectively write and unlock the system-wide highscore file + * (@see lockForWriting). + * If using a local highscore file, it will sync the config. + * @since 3.2 + */ + void writeAndUnlock(); + + /** + * @return true if the highscore file is locked or if a local + * file is used. + * @since 3.2 + */ + bool isLocked() const; + + /** + * Destructor. + * If necessary, write and unlock the highscore file. + */ + ~KHighscore(); + + /** + * @param entry The number of the entry / the placing of the player + * @param key A key for this entry. E.g. "name" for the name of the + * player. Nearly the same as the usual keys in KConfig - but they + * are prefixed with the entry number + * @param value The value of this entry + **/ + void writeEntry(int entry, const QString& key, const QString& value); + + /** + * This is an overloaded member function, provided for convenience. + * It differs from the above function only in what argument(s) it accepts. + **/ + void writeEntry(int entry, const QString& key, int value); + + /** + * This is an overloaded member function, provided for convenience. + * It differs from the above function only in what argument(s) it accepts. + * See KConfigBase documentation for allowed QVariant::Type. + **/ + void writeEntry(int entry, const QString& key, const QVariant &value); + + /** + * Reads an entry from the highscore table. + * @param entry The number of the entry / the placing to be read + * @param key The key of the entry. E.g. "name" for the name of the + * player. Nearly the same as the usual keys in KConfig - but they + * are prefixed with the entry number + * @param pDefault This will be used as default value if the key+pair + * entry can't be found. + * @return The value of this entry+key pair or pDefault if the entry+key + * pair doesn't exist + **/ + QString readEntry(int entry, const QString& key, const QString& pDefault = QString::null) const; + + /** + * Read a numeric value. + * @param entry The number of the entry / the placing to be read + * @param key The key of the entry. E.g. "name" for the name of the + * player. Nearly the same as the usual keys in KConfig - but they + * are prefixed with the entry number + * @param pDefault This will be used as default value if the key+pair + * entry can't be found. + * @return The value of this entry+key pair or pDefault if the entry+key + * pair doesn't exist + **/ + int readNumEntry(int entry, const QString& key, int pDefault = -1) const; + + /** + * Read a QVariant entry. + * See KConfigBase documentation for allowed QVariant::Type. + * + * @return the value of this entry+key pair or pDefault if the entry+key + * pair doesn't exist or + */ + QVariant readPropertyEntry(int entry, const QString &key, const QVariant &pDefault) const; + + /** + * @return True if the highscore table conatins the entry/key pair, + * otherwise false + **/ + bool hasEntry(int entry, const QString& key) const; + + /** + * Reads a list of entries from the highscore table starting at 1 until + * lastEntry. If an entry between those numbers doesn't exist the + * function aborts reading even if after the missing entry is an + * existing one. The first entry of the list is the first placing, the + * last on is the last placing. + * @return A list of the entries of this key. You could also call + * readEntry(i, key) where i is from 1 to 20. Note that this function + * depends on "1" as the first entry! + * @param key The key of the entry. E.g. "name" for the name of the + * player. Nearly the same as the usual keys in KConfig - but they + * are prefixed with the entry number + * @param lastEntry the last entry which will be includes into the list. + * 1 will include a list with maximal 1 entry - 20 a list with maximal + * 20 entries. If lastEntry is <= 0 then rading is only stopped when when an + * entry does not exist. + **/ + QStringList readList(const QString& key, int lastEntry = 20) const; + + /** + * Writes a list of entries to the highscore table. + * + * The first entry is prefixed with "1". Using this method is a short + * way of calling writeEntry(i, key, list[i]) from i = 1 to + * list.count() + * @param key A key for the entry. E.g. "name" for the name of the + * player. Nearly the same as the usual keys in KConfig - but they + * are prefixed with the entry number + * @param list The list of values + **/ + void writeList(const QString& key, const QStringList& list); + + /** + * @return Whether a highscore table exists. You can use this + * function to indicate whether KHighscore created a highscore table + * before and - if not - read your old (non-KHighscore) table instead. + * This way you can safely read an old table and save it using + * KHighscore without losing any data + **/ + bool hasTable() const; + + /** @obsolete + * This does the same as writeAndUnlock(). + */ + void sync(); + + /** + * Set the new highscore group. The group is being prefixed with + * "KHighscore_" in the table. + * @param groupname The new groupname. E.g. use "easy" for the easy + * level of your game. If you use QString::null (the default) the + * default group is used. + **/ + void setHighscoreGroup(const QString& groupname = QString::null); + + /** + * @return The currently used group. This doesn't contain the prefix + * ("KHighscore_") but the same as setHighscoreGroup uses. The + * default is QString::null + **/ + const QString& highscoreGroup() const; + +protected: + /** + * @return A groupname to be used in KConfig. Used internally to + * prefix the value from highscoreGroup() with "KHighscore_" + **/ + QString group() const; + + /** + * @return A pointer to the KConfig object to be used. This is + * either kapp->config() (default) or a KSimpleConfig object for + * a system-wide highscore file. + **/ + KConfig* config() const; + + void init(bool forceLocal); + +private: + KHighscorePrivate* d; + + static KFileLock *_lock; // lock on system-wide highscore file + static KRawConfig *_config; // config for system-wide highscore file +}; + +#endif diff --git a/libkdegames/highscore/kscoredialog.cpp b/libkdegames/highscore/kscoredialog.cpp new file mode 100644 index 00000000..37155650 --- /dev/null +++ b/libkdegames/highscore/kscoredialog.cpp @@ -0,0 +1,411 @@ +/**************************************************************** +Copyright (c) 1998 Sandro Sigala . +Copyright (c) 2001 Waldo Bastian +All rights reserved. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of the author not be used in +advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +The author disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall the author be liable for any +special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "kscoredialog.h" + +class KScoreDialog::KScoreDialogPrivate +{ +public: + QPtrList scores; + QWidget *page; + QGridLayout *layout; + QLineEdit *edit; + QPtrVector stack; + QPtrVector labels; + QLabel *commentLabel; + QString comment; + int fields; + int newName; + int latest; + int nrCols; + bool loaded; + QString configGroup; + + QMap col; + QMap header; + QMap key; + QString player; +}; + + +KScoreDialog::KScoreDialog(int fields, QWidget *parent, const char *oname) + : KDialogBase(parent, oname, true, i18n("High Scores"), Ok, Ok, true) +{ + d = new KScoreDialogPrivate(); + d->edit = 0; + d->fields = fields; + d->newName = -1; + d->latest = -1; + d->loaded = false; + d->nrCols = 0; + d->configGroup = "High Score"; + + d->scores.setAutoDelete(true); + d->header[Name] = i18n("Name"); + d->key[Name] = "Name"; + + d->header[Date] = i18n("Date"); + d->key[Date] = "Date"; + + d->header[Level] = i18n("Level"); + d->key[Level] = "Level"; + + d->header[Score] = i18n("Score"); + d->key[Score] = "Score"; + d->page = makeMainWidget(); + + connect(this, SIGNAL(okClicked()), SLOT(slotGotName())); +} + +KScoreDialog::~KScoreDialog() +{ + delete d; +} + +void KScoreDialog::setConfigGroup(const QString &group) +{ + d->configGroup = group; + d->loaded = false; +} + +void KScoreDialog::setComment(const QString &comment) +{ + d->comment = comment; +} + +void KScoreDialog::addField(int field, const QString &header, const QString &key) +{ + d->fields |= field; + d->header[field] = header; + d->key[field] = key; +} + +void KScoreDialog::setupDialog() +{ + d->nrCols = 1; + + for(int field = 1; field < d->fields; field = field * 2) + { + if (d->fields & field) + d->col[field] = d->nrCols++; + } + + d->layout = new QGridLayout(d->page, 15, d->nrCols, marginHint() + 20, spacingHint()); + d->layout->addRowSpacing(4, 15); + + d->commentLabel = new QLabel(d->page); + d->commentLabel->setAlignment(AlignVCenter | AlignHCenter); + d->layout->addMultiCellWidget(d->commentLabel, 1, 1, 0, d->nrCols-1); + + QFont bold = font(); + bold.setBold(true); + + QLabel *label; + d->layout->addColSpacing(0, 50); + label = new QLabel(i18n("Rank"), d->page); + d->layout->addWidget(label, 3, 0); + label->setFont(bold); + + for(int field = 1; field < d->fields; field = field * 2) + { + if (d->fields & field) + { + d->layout->addColSpacing(d->col[field], 50); + + label = new QLabel(d->header[field], d->page); + d->layout->addWidget(label, 3, d->col[field], field <= Name ? AlignLeft : AlignRight); + label->setFont(bold); + } + } + + KSeparator *sep = new KSeparator(Horizontal, d->page); + d->layout->addMultiCellWidget(sep, 4, 4, 0, d->nrCols-1); + + d->labels.resize(d->nrCols * 10); + d->stack.resize(10); + + QString num; + for (int i = 1; i <= 10; ++i) { + QLabel *label; + num.setNum(i); + label = new QLabel(i18n("#%1").arg(num), d->page); + d->labels.insert((i-1)*d->nrCols + 0, label); + d->layout->addWidget(label, i+4, 0); + if (d->fields & Name) + { + QWidgetStack *stack = new QWidgetStack(d->page); + d->stack.insert(i-1, stack); + d->layout->addWidget(stack, i+4, d->col[Name]); + label = new QLabel(d->page); + d->labels.insert((i-1)*d->nrCols + d->col[Name], label); + stack->addWidget(label); + stack->raiseWidget(label); + } + for(int field = Name * 2; field < d->fields; field = field * 2) + { + if (d->fields & field) + { + label = new QLabel(d->page); + d->labels.insert((i-1)*d->nrCols + d->col[field], label); + d->layout->addWidget(label, i+4, d->col[field], AlignRight); + } + } + } +} + +void KScoreDialog::aboutToShow() +{ + if (!d->loaded) + loadScores(); + + if (!d->nrCols) + setupDialog(); + + d->commentLabel->setText(d->comment); + if (d->comment.isEmpty()) + { + d->commentLabel->setMinimumSize(QSize(1,1)); + d->commentLabel->hide(); + d->layout->addRowSpacing(0, -15); + d->layout->addRowSpacing(2, -15); + } + else + { + d->commentLabel->setMinimumSize(d->commentLabel->sizeHint()); + d->commentLabel->show(); + d->layout->addRowSpacing(0, -10); + d->layout->addRowSpacing(2, 10); + } + d->comment = QString::null; + + QFont normal = font(); + QFont bold = normal; + bold.setBold(true); + + QString num; + for (int i = 1; i <= 10; ++i) { + QLabel *label; + num.setNum(i); + FieldInfo *score = d->scores.at(i-1); + label = d->labels[(i-1)*d->nrCols + 0]; + if (i == d->latest) + label->setFont(bold); + else + label->setFont(normal); + + if (d->fields & Name) + { + if (d->newName == i) + { + QWidgetStack *stack = d->stack[i-1]; + d->edit = new QLineEdit(d->player, stack); + d->edit->setMinimumWidth(40); + stack->addWidget(d->edit); + stack->raiseWidget(d->edit); + d->edit->setFocus(); + connect(d->edit, SIGNAL(returnPressed()), + this, SLOT(slotGotReturn())); + } + else + { + label = d->labels[(i-1)*d->nrCols + d->col[Name]]; + if (i == d->latest) + label->setFont(bold); + else + label->setFont(normal); + label->setText((*score)[Name]); + } + + } + for(int field = Name * 2; field < d->fields; field = field * 2) + { + if (d->fields & field) + { + label = d->labels[(i-1)*d->nrCols + d->col[field]]; + if (i == d->latest) + label->setFont(bold); + else + label->setFont(normal); + label->setText((*score)[field]); + } + } + } + d->latest = -1; + setFixedSize(minimumSizeHint()); +} + +void KScoreDialog::loadScores() +{ + QString key, value; + d->loaded = true; + d->scores.clear(); + KConfigGroup config(kapp->config(), d->configGroup.utf8()); + + d->player = config.readEntry("LastPlayer"); + + QString num; + for (int i = 1; i <= 10; ++i) { + num.setNum(i); + FieldInfo *score = new FieldInfo(); + for(int field = 1; field < d->fields; field = field * 2) + { + if (d->fields & field) + { + key = "Pos" + num + d->key[field]; + (*score)[field] = config.readEntry(key, "-"); + } + } + d->scores.append(score); + } +} + +void KScoreDialog::saveScores() +{ + QString key, value; + KConfigGroup config(kapp->config(), d->configGroup.utf8()); + + config.writeEntry("LastPlayer", d->player); + + QString num; + for (int i = 1; i <= 10; ++i) { + num.setNum(i); + FieldInfo *score = d->scores.at(i-1); + for(int field = 1; field < d->fields; field = field * 2) + { + if (d->fields & field) + { + key = "Pos" + num + d->key[field]; + config.writeEntry(key, (*score)[field]); + } + } + } + kapp->config()->sync(); +} + +int KScoreDialog::addScore(int newScore, const FieldInfo &newInfo, bool askName) +{ + return addScore(newScore, newInfo, askName, false); +} + +int KScoreDialog::addScore(int newScore, const FieldInfo &newInfo, bool askName, bool lessIsMore) +{ + if (!d->loaded) + loadScores(); + FieldInfo *score = d->scores.first(); + int i = 1; + for(; score; score = d->scores.next(), i++) + { + bool ok; + int num_score = (*score)[Score].toLong(&ok); + if (lessIsMore && !ok) + num_score = 1 << 30; + if (((newScore > num_score) && !lessIsMore) || + ((newScore < num_score) && lessIsMore)) + { + score = new FieldInfo(newInfo); + (*score)[Score].setNum(newScore); + d->scores.insert(i-1, score); + d->scores.remove(10); + d->latest = i; + if (askName) + d->newName = i; + else + saveScores(); + if (i == 1) + d->comment = i18n("Excellent!\nYou have a new high score!"); + else + d->comment = i18n("Well done!\nYou made it to the high score list!"); + return i; + } + } + return 0; +} + +void KScoreDialog::show() +{ + aboutToShow(); + KDialogBase::show(); +} + +void KScoreDialog::slotGotReturn() +{ + QTimer::singleShot(0, this, SLOT(slotGotName())); +} + +void KScoreDialog::slotGotName() +{ + if (d->newName == -1) return; + + d->player = d->edit->text(); + + (*d->scores.at(d->newName-1))[Name] = d->player; + saveScores(); + + QFont bold = font(); + bold.setBold(true); + + QLabel *label = d->labels[(d->newName-1)*d->nrCols + d->col[Name]]; + label->setFont(bold); + label->setText(d->player); + d->stack[(d->newName-1)]->raiseWidget(label); + delete d->edit; + d->edit = 0; + d->newName = -1; +} + +int KScoreDialog::highScore() +{ + if (!d->loaded) + loadScores(); + + return (*d->scores.first())[Score].toInt(); +} + +void KScoreDialog::keyPressEvent( QKeyEvent *ev) +{ + if ((d->newName != -1) && (ev->key() == Key_Return)) + { + ev->ignore(); + return; + } + KDialogBase::keyPressEvent(ev); +} + + +#include "kscoredialog.moc" diff --git a/libkdegames/highscore/kscoredialog.h b/libkdegames/highscore/kscoredialog.h new file mode 100644 index 00000000..4d4a76db --- /dev/null +++ b/libkdegames/highscore/kscoredialog.h @@ -0,0 +1,125 @@ +/**************************************************************** +Copyright (c) 1998 Sandro Sigala . +Copyright (c) 2001 Waldo Bastian +All rights reserved. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of the author not be used in +advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +The author disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall the author be liable for any +special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#ifndef KSCOREDIALOG_H +#define KSCOREDIALOG_H + +#include +#include + +#include +#include +class QGridLayout; +class QLineEdit; +class QWidgetStack; + +/** + * A simple high score dialog. + */ +class KDE_EXPORT KScoreDialog : public KDialogBase { + Q_OBJECT + +public: + enum Fields { Name = 1 << 0, + Level = 1 << 1, + + Custom1 = 1 << 10, + Custom2 = 1 << 11, + Custom3 = 1 << 12, + + Date = 1 << 27, + Time = 1 << 28, + Score = 1 << 29 }; + + typedef QMap FieldInfo; + + /** + * @param fields Which fields should be listed. + * @param parent passed to parent QWidget constructor + * @param name passed to parent QWidget constructor + */ + KScoreDialog(int fields, QWidget *parent=0, const char *name=0); + + ~KScoreDialog(); + + /** + * @param group to use for reading/writing highscores from/to. By default + * the class will use "High Score" + */ + void setConfigGroup(const QString &group); + + /** + * @param comment to add when showing high-scores. + * The comment is only used once. + */ + void setComment(const QString &comment); + + /** + * Define an extra FieldInfo entry. + * @param field Id of this field + * @param header Header shown in the dialog for this field + * @param key used to store this field with. + */ + void addField(int field, const QString &header, const QString &key); + + /** + * Adds a new score to the list. + * + * @param newScore the score of this game. + * @param newInfo additional info about the score. + * @param askName Whether to prompt for the players name. + * @param lessIsMore If true, the lowest score is the best score. + * + * @returns The highscore position if the score was good enough to + * make it into the list (1 being topscore) or 0 otherwise. + */ + int addScore(int newScore, const FieldInfo &newInfo, bool askName, bool lessIsMore); + int addScore(int newScore, const FieldInfo &newInfo, bool askName=true); + + /** + * Returns the current best score. + */ + int highScore(); + + virtual void show(); + +private slots: + void slotGotReturn(); + void slotGotName(); + +private: + /* read scores */ + void loadScores(); + void saveScores(); + + void aboutToShow(); + void setupDialog(); + void keyPressEvent( QKeyEvent *ev); + +private: + class KScoreDialogPrivate; + KScoreDialogPrivate *d; +}; + +#endif // !KSCOREDIALOG_H diff --git a/libkdegames/kcanvasrootpixmap.cpp b/libkdegames/kcanvasrootpixmap.cpp new file mode 100644 index 00000000..14592c66 --- /dev/null +++ b/libkdegames/kcanvasrootpixmap.cpp @@ -0,0 +1,39 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001,2002,2003 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kcanvasrootpixmap.h" + +#include + + +KCanvasRootPixmap::KCanvasRootPixmap(QCanvasView *view, const char *name) + : KRootPixmap(view, name), _view(view) +{ + setCustomPainting(true); + connect(this, SIGNAL(backgroundUpdated(const QPixmap &)), + SLOT(backgroundUpdatedSlot(const QPixmap &))); +} + +void KCanvasRootPixmap::backgroundUpdatedSlot(const QPixmap &pixmap) +{ + if ( _view && _view->canvas() ) + _view->canvas()->setBackgroundPixmap(pixmap); +} + +#include "kcanvasrootpixmap.moc" diff --git a/libkdegames/kcanvasrootpixmap.h b/libkdegames/kcanvasrootpixmap.h new file mode 100644 index 00000000..3eedb7e1 --- /dev/null +++ b/libkdegames/kcanvasrootpixmap.h @@ -0,0 +1,61 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001,2002,2003 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KCANVASROOTPIXMAP_H +#define KCANVASROOTPIXMAP_H + +#include +#include + +class QCanvasView; + +/** + * Implement KRootPixmap for a QCanvasView. + * + * The pixmap will be set as the background of the + * QCanvas associated with the view : + *
    + *
  • for correct positioning of the background pixmap, the given + * QCanvasView should be positioned at the origin of the canvas.
  • + *
  • no other view of the same canvas should use KCanvasRootPixmap.
  • + *
  • other views of the canvas will have the same background pixmap.
  • + *
+ */ +class KDE_EXPORT KCanvasRootPixmap : public KRootPixmap +{ + Q_OBJECT + + public: + /** + * Constructor. + */ + KCanvasRootPixmap(QCanvasView *view, const char *name = 0); + + private slots: + void backgroundUpdatedSlot(const QPixmap &); + + private: + QCanvasView *_view; + + class KCanvasRootPixmapPrivate; + KCanvasRootPixmapPrivate *d; +}; + +#endif + diff --git a/libkdegames/kcarddialog.cpp b/libkdegames/kcarddialog.cpp new file mode 100644 index 00000000..a4d2ac20 --- /dev/null +++ b/libkdegames/kcarddialog.cpp @@ -0,0 +1,808 @@ +/* + This file is part of the KDE games library + Copyright (C) 2000 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "kcarddialog.h" +#include +#include + +#define KCARD_DEFAULTDECK QString::fromLatin1("deck0.png") +#define KCARD_DEFAULTCARD QString::fromLatin1("11.png") +#define KCARD_DEFAULTCARDDIR QString::fromLatin1("cards-default/") + +// values for the resize slider +#define SLIDER_MIN 400 +#define SLIDER_MAX 3000 + +// KConfig entries +#define CONF_GROUP "KCardDialog" +#define CONF_RANDOMDECK QString::fromLatin1("RandomDeck") +#define CONF_DECK QString::fromLatin1("Deck") +#define CONF_CARDDIR QString::fromLatin1("CardDir") +#define CONF_RANDOMCARDDIR QString::fromLatin1("RandomCardDir") +#define CONF_USEGLOBALDECK QString::fromLatin1("GlobalDeck") +#define CONF_USEGLOBALCARDDIR QString::fromLatin1("GlobalCardDir") +#define CONF_SCALE QString::fromLatin1("Scale") + +#define CONF_GLOBAL_GROUP QString::fromLatin1("KCardDialog Settings") +#define CONF_GLOBAL_DECK QString::fromLatin1("GlobalDeck") +#define CONF_GLOBAL_CARDDIR QString::fromLatin1("GlobalCardDir") +#define CONF_GLOBAL_RANDOMDECK QString::fromLatin1("GlobalRandomDeck") +#define CONF_GLOBAL_RANDOMCARDDIR QString::fromLatin1("GlobalRandomCardDir") + + +class KCardDialogPrivate +{ +public: + KCardDialogPrivate() + { + deckLabel = 0; + cardLabel = 0; + deckIconView = 0; + cardIconView = 0; + randomDeck = 0; + randomCardDir = 0; + cPreview = 0; + scaleSlider = 0; + globalDeck = 0; + globalCardDir = 0; + + cScale = 1; + } + + QLabel* deckLabel; + QLabel* cardLabel; + KIconView* deckIconView; + KIconView* cardIconView; + QCheckBox* randomDeck; + QCheckBox* randomCardDir; + QCheckBox* globalDeck; + QCheckBox* globalCardDir; + + QSlider* scaleSlider; + QPixmap cPreviewPix; + QLabel* cPreview; + + QMap deckMap; + QMap cardMap; + QMap helpMap; + + //set query variables + KCardDialog::CardFlags cFlags; + QString cDeck; + QString cCardDir; + double cScale; +}; + +int KCardDialog::getCardDeck(QString &pDeck, QString &pCardDir, QWidget *pParent, + CardFlags pFlags, bool* pRandomDeck, bool* pRandomCardDir, + double* pScale, KConfig* pConf) +{ + KCardDialog dlg(pParent, "dlg", pFlags); + + dlg.setDeck(pDeck); + dlg.setCardDir(pCardDir); + + dlg.setupDialog(pScale != 0); + dlg.loadConfig(pConf); + dlg.showRandomDeckBox(pRandomDeck != 0); + dlg.showRandomCardDirBox(pRandomCardDir != 0); + int result=dlg.exec(); + if (result==QDialog::Accepted) + { + // TODO check for global cards/decks!!!! + pDeck=dlg.deck(); + pCardDir=dlg.cardDir(); + if (!pCardDir.isNull() && pCardDir.right(1)!=QString::fromLatin1("/")) + { + pCardDir+=QString::fromLatin1("/"); + } + if (pRandomDeck) + { + *pRandomDeck = dlg.isRandomDeck(); + } + if (pRandomCardDir) + { + *pRandomCardDir = dlg.isRandomCardDir(); + } + if (pScale) + { + *pScale = dlg.cardScale(); + } + + if (dlg.isGlobalDeck()) + { + kdDebug(11000) << "use global deck" << endl; + bool random; + getGlobalDeck(pDeck, random); + kdDebug(11000) << "use: " << pDeck<< endl; + if (pRandomDeck) + { + *pRandomDeck=random; + if (random) + kdDebug(11000) << "use random deck" << endl; + } + } + if (dlg.isGlobalCardDir()) + { + kdDebug(11000) << "use global carddir" << endl; + bool random; + getGlobalCardDir(pCardDir, random); + kdDebug(11000) << "use: " << pCardDir << endl; + if (pRandomCardDir) + { + *pRandomCardDir=random; + if (random) + kdDebug(11000) << "use random carddir" << endl; + } + } + } + dlg.saveConfig(pConf); + return result; +} + +void KCardDialog::getConfigCardDeck(KConfig* conf, QString &pDeck, QString &pCardDir, double& pScale) +{ +// TODO check for global cards/decks!!!! + if (!conf) { + return; + } + QString origGroup = conf->group(); + + conf->setGroup(CONF_GROUP); + if (conf->readBoolEntry(CONF_RANDOMDECK) || !conf->hasKey(CONF_DECK)) { + pDeck = getRandomDeck(); + } else { + pDeck = conf->readEntry(CONF_DECK); + } + if (conf->readBoolEntry(CONF_RANDOMCARDDIR) || !conf->hasKey(CONF_CARDDIR)) { + pCardDir = getRandomCardDir(); + } else { + pCardDir = conf->readPathEntry(CONF_CARDDIR); + } + pScale = conf->readDoubleNumEntry(CONF_SCALE, 1.0); + + if (conf->readBoolEntry(CONF_USEGLOBALDECK, false)) { + bool random; + getGlobalDeck(pCardDir, random); + if (random || pDeck.isNull() ) { + pDeck = getRandomDeck(); + } + } + if (conf->readBoolEntry(CONF_USEGLOBALCARDDIR, false)) { + bool random; + getGlobalCardDir(pCardDir, random); + if (random || pCardDir.isNull() ) { + pCardDir = getRandomCardDir(); + } + } + + conf->setGroup(origGroup); +} + +QString KCardDialog::getDefaultDeck() +{ + KCardDialog::init(); + return locate("cards", QString::fromLatin1("decks/") + KCARD_DEFAULTDECK); +} + +QString KCardDialog::getDefaultCardDir() +{ + KCardDialog::init(); + + QString file = KCARD_DEFAULTCARDDIR + KCARD_DEFAULTCARD; + return KGlobal::dirs()->findResourceDir("cards",file) + KCARD_DEFAULTCARDDIR; +} + +QString KCardDialog::getCardPath(const QString &carddir, int index) +{ + KCardDialog::init(); + + QString entry = carddir + QString::number(index); + if (KStandardDirs::exists(entry + QString::fromLatin1(".png"))) + return entry + QString::fromLatin1(".png"); + + // rather theoretical + if (KStandardDirs::exists(entry + QString::fromLatin1(".xpm"))) + return entry + QString::fromLatin1(".xpm"); + + return QString::null; +} + +const QString& KCardDialog::deck() const { return d->cDeck; } +void KCardDialog::setDeck(const QString& file) { d->cDeck=file; } +const QString& KCardDialog::cardDir() const { return d->cCardDir; } +void KCardDialog::setCardDir(const QString& dir) { d->cCardDir=dir; } +KCardDialog::CardFlags KCardDialog::flags() const { return d->cFlags; } +double KCardDialog::cardScale() const { return d->cScale; } +bool KCardDialog::isRandomDeck() const +{ return (d->randomDeck ? d->randomDeck->isChecked() : false); } +bool KCardDialog::isRandomCardDir() const +{ return (d->randomCardDir ? d->randomCardDir->isChecked() : false); } +bool KCardDialog::isGlobalDeck() const +{ return (d->globalDeck ? d->globalDeck->isChecked() : false); } +bool KCardDialog::isGlobalCardDir() const +{ return (d->globalCardDir ? d->globalCardDir->isChecked() : false); } + +void KCardDialog::setupDialog(bool showResizeBox) +{ + QHBoxLayout* topLayout = new QHBoxLayout(plainPage(), spacingHint()); + QVBoxLayout* cardLayout = new QVBoxLayout(topLayout); + QString path, file; + QWMatrix m; + m.scale(0.8,0.8); + + setInitialSize(QSize(600,400)); + + if (! (flags() & NoDeck)) + { + QHBoxLayout* layout = new QHBoxLayout(cardLayout); + + // Deck iconview + QGroupBox* grp1 = new QGroupBox(1, Horizontal, i18n("Choose Backside"), plainPage()); + layout->addWidget(grp1); + + d->deckIconView = new KIconView(grp1,"decks"); + d->deckIconView->setSpacing(8); + /* + deckIconView->setGridX(-1); + deckIconView->setGridY(50); + */ + d->deckIconView->setGridX(82); + d->deckIconView->setGridY(106); + d->deckIconView->setSelectionMode(QIconView::Single); + d->deckIconView->setResizeMode(QIconView::Adjust); + d->deckIconView->setMinimumWidth(360); + d->deckIconView->setMinimumHeight(170); + d->deckIconView->setWordWrapIconText(false); + d->deckIconView->showToolTips(); + + // deck select + QVBoxLayout* l = new QVBoxLayout(layout); + QGroupBox* grp3 = new QGroupBox(i18n("Backside"), plainPage()); + grp3->setFixedSize(100, 130); + l->addWidget(grp3, 0, AlignTop|AlignHCenter); + d->deckLabel = new QLabel(grp3); + d->deckLabel->setText(i18n("empty")); + d->deckLabel->setAlignment(AlignHCenter|AlignVCenter); + d->deckLabel->setGeometry(10, 20, 80, 90); + + d->randomDeck = new QCheckBox(plainPage()); + d->randomDeck->setChecked(false); + connect(d->randomDeck, SIGNAL(toggled(bool)), this, + SLOT(slotRandomDeckToggled(bool))); + d->randomDeck->setText(i18n("Random backside")); + l->addWidget(d->randomDeck, 0, AlignTop|AlignHCenter); + + d->globalDeck = new QCheckBox(plainPage()); + d->globalDeck->setChecked(false); + d->globalDeck->setText(i18n("Use global backside")); + l->addWidget(d->globalDeck, 0, AlignTop|AlignHCenter); + + QPushButton* b = new QPushButton(i18n("Make Backside Global"), plainPage()); + connect(b, SIGNAL(pressed()), this, SLOT(slotSetGlobalDeck())); + l->addWidget(b, 0, AlignTop|AlignHCenter); + + connect(d->deckIconView,SIGNAL(clicked(QIconViewItem *)), + this,SLOT(slotDeckClicked(QIconViewItem *))); + } + + if (! (flags() & NoCards)) + { + // Cards iconview + QHBoxLayout* layout = new QHBoxLayout(cardLayout); + QGroupBox* grp2 = new QGroupBox(1, Horizontal, i18n("Choose Frontside"), plainPage()); + layout->addWidget(grp2); + + d->cardIconView =new KIconView(grp2,"cards"); + /* + cardIconView->setGridX(36); + cardIconView->setGridY(50); + */ + d->cardIconView->setGridX(82); + d->cardIconView->setGridY(106); + d->cardIconView->setResizeMode(QIconView::Adjust); + d->cardIconView->setMinimumWidth(360); + d->cardIconView->setMinimumHeight(170); + d->cardIconView->setWordWrapIconText(false); + d->cardIconView->showToolTips(); + + // Card select + QVBoxLayout* l = new QVBoxLayout(layout); + QGroupBox* grp4 = new QGroupBox(i18n("Frontside"), plainPage()); + grp4->setFixedSize(100, 130); + l->addWidget(grp4, 0, AlignTop|AlignHCenter); + d->cardLabel = new QLabel(grp4); + d->cardLabel->setText(i18n("empty")); + d->cardLabel->setAlignment(AlignHCenter|AlignVCenter); + d->cardLabel->setGeometry(10, 20, 80, 90 ); + + d->randomCardDir = new QCheckBox(plainPage()); + d->randomCardDir->setChecked(false); + connect(d->randomCardDir, SIGNAL(toggled(bool)), this, + SLOT(slotRandomCardDirToggled(bool))); + d->randomCardDir->setText(i18n("Random frontside")); + l->addWidget(d->randomCardDir, 0, AlignTop|AlignHCenter); + + d->globalCardDir = new QCheckBox(plainPage()); + d->globalCardDir->setChecked(false); + d->globalCardDir->setText(i18n("Use global frontside")); + l->addWidget(d->globalCardDir, 0, AlignTop|AlignHCenter); + + QPushButton* b = new QPushButton(i18n("Make Frontside Global"), plainPage()); + connect(b, SIGNAL(pressed()), this, SLOT(slotSetGlobalCardDir())); + l->addWidget(b, 0, AlignTop|AlignHCenter); + + connect(d->cardIconView,SIGNAL(clicked(QIconViewItem *)), + this,SLOT(slotCardClicked(QIconViewItem *))); + } + + // Insert deck icons + // First find the default or alternate path + if (! (flags() & NoDeck)) + { + insertDeckIcons(); + d->deckIconView->arrangeItemsInGrid(); + + // Set default icons if given + if (!deck().isNull()) + { + file=deck(); + QPixmap pixmap(file); + pixmap=pixmap.xForm(m); + d->deckLabel->setPixmap(pixmap); + QToolTip::add(d->deckLabel,d->helpMap[file]); + } + } + + // Insert card icons + if (! (flags() & NoCards)) + { + insertCardIcons(); + d->cardIconView->arrangeItemsInGrid(); + + // Set default icons if given + if (!cardDir().isNull()) + { + file = cardDir() + KCARD_DEFAULTCARD; + QPixmap pixmap(file); + pixmap = pixmap.xForm(m); + d->cardLabel->setPixmap(pixmap); + QToolTip::add(d->cardLabel,d->helpMap[cardDir()]); + } + } + + // insert resize box + if (showResizeBox) + { + // this part is a little bit...tricky. + // i'm sure there is a cleaner way but i cannot find it. + // whenever the pixmap is resized (aka scaled) the box is resized, too. This + // leads to an always resizing dialog which is *very* ugly. i worked around + // this by using a QWidget which is the only child widget of the group box. + // The other widget are managed inside this QWidget - a stretch area on the + // right ensures that the KIconViews are not resized... + + // note that the dialog is still resized if you you scale the pixmap very + // large. This is desired behaviour as i don't want to make the box even + // larger but i want the complete pixmap to be displayed. the dialog is not + // resized if you make the pixmap smaller again. + QVBoxLayout* layout = new QVBoxLayout(topLayout); + QGroupBox* grp = new QGroupBox(1, Horizontal, i18n("Resize Cards"), plainPage()); + layout->setResizeMode(QLayout::Fixed); + layout->addWidget(grp); + QWidget* box = new QWidget(grp); + QHBoxLayout* hbox = new QHBoxLayout(box, 0, spacingHint()); + QVBoxLayout* boxLayout = new QVBoxLayout(hbox); + hbox->addStretch(0); + + d->scaleSlider = new QSlider(1, SLIDER_MAX, 1, (-1000+SLIDER_MIN+SLIDER_MAX), Horizontal, box); + d->scaleSlider->setMinValue(SLIDER_MIN); + connect(d->scaleSlider, SIGNAL(valueChanged(int)), this, SLOT(slotCardResized(int))); + boxLayout->addWidget(d->scaleSlider, 0, AlignLeft); + + QPushButton* b = new QPushButton(i18n("Default Size"), box); + connect(b, SIGNAL(pressed()), this, SLOT(slotDefaultSize())); + boxLayout->addWidget(b, 0, AlignLeft); + + QLabel* l = new QLabel(i18n("Preview:"), box); + boxLayout->addWidget(l); + d->cPreviewPix.load(getDefaultDeck()); + d->cPreview = new QLabel(box); + boxLayout->addWidget(d->cPreview, 0, AlignCenter|AlignVCenter); + + slotCardResized(d->scaleSlider->value()); + } +} + +void KCardDialog::insertCardIcons() +{ + QStringList list = KGlobal::dirs()->findAllResources("cards", "card*/index.desktop", false, true); + // kdDebug(11000) << "insert " << list.count() << endl; + if (list.isEmpty()) + return; + + // We shrink the icons a little + // + QWMatrix m; + m.scale(0.8,0.8); + + for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) + { + KSimpleConfig cfg(*it); + cfg.setGroup(QString::fromLatin1("KDE Backdeck")); + QString path = (*it).left((*it).findRev('/') + 1); + assert(path[path.length() - 1] == '/'); + QPixmap pixmap(path + cfg.readEntry("Preview", "12c.png")); + + if (pixmap.isNull()) + continue; + + QString name=cfg.readEntry("Name", i18n("unnamed")); + QIconViewItem *item= new QIconViewItem(d->cardIconView, name, pixmap); + + item->setDragEnabled(false); + item->setDropEnabled(false); + item->setRenameEnabled(false); + item->setSelectable(true); + + d->cardMap[item] = path; + d->helpMap[path] = cfg.readEntry("Comment",name); + } +} + +void KCardDialog::insertDeckIcons() +{ + QStringList list = KGlobal::dirs()->findAllResources("cards", "decks/*.desktop", false, true); + if (list.isEmpty()) + return; + + QString label; + + // We shrink the icons a little + QWMatrix m; + m.scale(0.8,0.8); + + for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) + { + KSimpleConfig cfg(*it); + QPixmap pixmap(getDeckName(*it)); + if (pixmap.isNull()) + continue; + + // pixmap=pixmap.xForm(m); + + cfg.setGroup(QString::fromLatin1("KDE Cards")); + QString name=cfg.readEntry("Name", i18n("unnamed")); + QIconViewItem *item= new QIconViewItem(d->deckIconView,name, pixmap); + + item->setDragEnabled(false); + item->setDropEnabled(false); + item->setRenameEnabled(false); + + d->deckMap[item] = getDeckName(*it); + d->helpMap[d->deckMap[item]] = cfg.readEntry("Comment",name); + } +} + + +KCardDialog::~KCardDialog() +{ + delete d; +} + + +// Create the dialog +KCardDialog::KCardDialog( QWidget *parent, const char *name, CardFlags mFlags) + : KDialogBase( Plain, i18n("Carddeck Selection"), Ok|Cancel, Ok, parent, name, true, true) +{ + KCardDialog::init(); + + d = new KCardDialogPrivate; + d->cFlags = mFlags; +} + +void KCardDialog::slotDeckClicked(QIconViewItem *item) +{ + if (item && item->pixmap()) + { + d->deckLabel->setPixmap(* (item->pixmap())); + QToolTip::remove( d->deckLabel ); + QToolTip::add(d->deckLabel,d->helpMap[d->deckMap[item]]); + setDeck(d->deckMap[item]); + } +} +void KCardDialog::slotCardClicked(QIconViewItem *item) +{ + if (item && item->pixmap()) + { + d->cardLabel->setPixmap(* (item->pixmap())); + QString path = d->cardMap[item]; + QToolTip::remove( d->deckLabel ); + QToolTip::add(d->cardLabel,d->helpMap[path]); + setCardDir(path); + } +} + +QString KCardDialog::getDeckName(const QString &desktop) +{ + QString entry = desktop.left(desktop.length() - strlen(".desktop")); + if (KStandardDirs::exists(entry + QString::fromLatin1(".png"))) + return entry + QString::fromLatin1(".png"); + + // rather theoretical + if (KStandardDirs::exists(entry + QString::fromLatin1(".xpm"))) + return entry + QString::fromLatin1(".xpm"); + return QString::null; +} + +QString KCardDialog::getRandomDeck() +{ + KCardDialog::init(); + + QStringList list = KGlobal::dirs()->findAllResources("cards", "decks/*.desktop"); + if (list.isEmpty()) + return QString::null; + + int d = KApplication::random() % list.count(); + return getDeckName(*list.at(d)); +} + +QString KCardDialog::getRandomCardDir() +{ + KCardDialog::init(); + + QStringList list = KGlobal::dirs()->findAllResources("cards", "card*/index.desktop"); + if (list.isEmpty()) + return QString::null; + + int d = KApplication::random() % list.count(); + QString entry = *list.at(d); + return entry.left(entry.length() - strlen("index.desktop")); +} + +void KCardDialog::showRandomDeckBox(bool s) +{ + if (!d->randomDeck) + return; + + if (s) + d->randomDeck->show(); + else + d->randomDeck->hide(); +} + +void KCardDialog::showRandomCardDirBox(bool s) +{ + if (!d->randomCardDir) + return; + + if (s) + d->randomCardDir->show(); + else + d->randomCardDir->hide(); +} + +void KCardDialog::slotRandomDeckToggled(bool on) +{ + if (on) { + d->deckLabel->setText("random"); + setDeck(getRandomDeck()); + } else { + d->deckLabel->setText("empty"); + setDeck(0); + } +} + +void KCardDialog::slotRandomCardDirToggled(bool on) +{ + if (on) { + d->cardLabel->setText("random"); + setCardDir(getRandomCardDir()); + if (cardDir().length()>0 && cardDir().right(1)!=QString::fromLatin1("/")) { + setCardDir(cardDir() + QString::fromLatin1("/")); + } + } else { + d->cardLabel->setText("empty"); + setCardDir(0); + } +} + +void KCardDialog::loadConfig(KConfig* conf) +{ + if (!conf) { + return; + } + + QString origGroup = conf->group(); + + conf->setGroup(CONF_GROUP); + if (! (flags() & NoDeck)) { + if (conf->hasKey(CONF_DECK)) { + setDeck(conf->readEntry(CONF_DECK)); + } + + bool random = conf->readBoolEntry(CONF_RANDOMDECK, false); + d->randomDeck->setChecked(random); + slotRandomDeckToggled(random); + + if (conf->hasKey(CONF_USEGLOBALDECK) && conf->readBoolEntry(CONF_USEGLOBALDECK)) { + d->globalDeck->setChecked(true); + } else { + d->globalDeck->setChecked(false); + } + } + if (! (flags() & NoCards)) { + if (conf->hasKey(CONF_CARDDIR)) { + setCardDir(conf->readPathEntry(CONF_CARDDIR)); + } + + bool random = conf->readBoolEntry(CONF_RANDOMCARDDIR, false); + d->randomCardDir->setChecked(random); + slotRandomCardDirToggled(random); + + if (conf->hasKey(CONF_USEGLOBALCARDDIR) && conf->readBoolEntry(CONF_USEGLOBALCARDDIR)) { + d->globalCardDir->setChecked(true); + } else { + d->globalCardDir->setChecked(false); + } + } + + d->cScale = conf->readDoubleNumEntry(CONF_SCALE, 1.0); + + conf->setGroup(origGroup); +} + +void KCardDialog::slotCardResized(int s) +{ + if (!d->cPreview) { + return; + } + if (s < SLIDER_MIN || s > SLIDER_MAX) { + kdError(11000) << "invalid scaling value!" << endl; + return; + } + + s *= -1; + s += (SLIDER_MIN + SLIDER_MAX); + + QWMatrix m; + double scale = (double)1000/s; + m.scale(scale, scale); + QPixmap pix = d->cPreviewPix.xForm(m); + d->cPreview->setPixmap(pix); + d->cScale = scale; +} + +void KCardDialog::slotDefaultSize() +{ + if (!d->scaleSlider) { + return; + } + d->scaleSlider->setValue(-1000 + SLIDER_MIN + SLIDER_MAX); +} + +void KCardDialog::saveConfig(KConfig* conf) +{ + if (!conf) { + return; + } + QString origGroup = conf->group(); + + conf->setGroup(CONF_GROUP); + if (! (flags() & NoDeck)) { + conf->writeEntry(CONF_DECK, deck()); + conf->writeEntry(CONF_RANDOMDECK, isRandomDeck()); + conf->writeEntry(CONF_USEGLOBALDECK, d->globalDeck->isChecked()); + } + if (! (flags() & NoCards)) { + conf->writePathEntry(CONF_CARDDIR, cardDir()); + conf->writeEntry(CONF_RANDOMCARDDIR, isRandomCardDir()); + conf->writeEntry(CONF_USEGLOBALCARDDIR, d->globalCardDir->isChecked()); + } + conf->writeEntry(CONF_SCALE, d->cScale); + + conf->setGroup(origGroup); +} + +void KCardDialog::slotSetGlobalDeck() +{ + KSimpleConfig* conf = new KSimpleConfig(QString::fromLatin1("kdeglobals"), false); + conf->setGroup(CONF_GLOBAL_GROUP); + + conf->writeEntry(CONF_GLOBAL_DECK, deck()); + conf->writeEntry(CONF_GLOBAL_RANDOMDECK, isRandomDeck()); + + delete conf; +} + +void KCardDialog::slotSetGlobalCardDir() +{ + KSimpleConfig* conf = new KSimpleConfig(QString::fromLatin1("kdeglobals"), false); + conf->setGroup(CONF_GLOBAL_GROUP); + + conf->writePathEntry(CONF_GLOBAL_CARDDIR, cardDir()); + conf->writeEntry(CONF_GLOBAL_RANDOMCARDDIR, isRandomCardDir()); + + delete conf; +} + +void KCardDialog::getGlobalDeck(QString& deck, bool& random) +{ + KSimpleConfig* conf = new KSimpleConfig(QString::fromLatin1("kdeglobals"), true); + conf->setGroup(CONF_GLOBAL_GROUP); + + if (!conf->hasKey(CONF_GLOBAL_DECK) || conf->readBoolEntry(CONF_GLOBAL_RANDOMDECK, false)) { + deck = getRandomDeck(); + random = true; + } else { + deck = conf->readEntry(CONF_GLOBAL_DECK); + random = conf->readBoolEntry(CONF_GLOBAL_RANDOMDECK, false); + } + + delete conf; +} + +void KCardDialog::getGlobalCardDir(QString& dir, bool& random) +{ + KSimpleConfig* conf = new KSimpleConfig(QString::fromLatin1("kdeglobals"), true); + conf->setGroup(CONF_GLOBAL_GROUP); + + if (!conf->hasKey(CONF_GLOBAL_CARDDIR) || conf->readBoolEntry(CONF_GLOBAL_RANDOMCARDDIR, false)) { + dir = getRandomCardDir(); + random = true; + } else { + dir = conf->readPathEntry(CONF_GLOBAL_CARDDIR); + random = conf->readBoolEntry(CONF_GLOBAL_RANDOMCARDDIR, false); + } + + delete conf; +} + +void KCardDialog::init() +{ + static bool _inited = false; + if (_inited) + return; + KGlobal::dirs()->addResourceType("cards", KStandardDirs::kde_default("data") + QString::fromLatin1("carddecks/")); + + KGlobal::locale()->insertCatalogue("libkdegames"); + _inited = true; +} + +#include "kcarddialog.moc" diff --git a/libkdegames/kcarddialog.h b/libkdegames/kcarddialog.h new file mode 100644 index 00000000..b32fd636 --- /dev/null +++ b/libkdegames/kcarddialog.h @@ -0,0 +1,345 @@ +/* + This file is part of the KDE games library + Copyright (C) 2000 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef __KCARDDIALOG_H_ +#define __KCARDDIALOG_H_ + +#include +#include +#include // TODO: remove - it is in kcarddialog.cpp now; left here for source compatibility + +#include +class QIconViewItem; + +class KConfig; + +class KCardDialogPrivate; + +/** + * @short A carddeck selection dialog for card games. + * + * The KCardDialog provides a dialog for interactive carddeck selection. + * It gives cardgames an easy to use interface to select front and + * back of the card sets. As card sets the KDE default cardsets are + * offered as well as used specified ones. + * + * In most cases, the simplest + * use of this class is the static method KCardDialog::getCardDeck, + * which pops up the dialog, allows the user to select a carddeck, and + * returns when the dialog is closed. Only if you really need some specific + * behaviour or if you overwrite the dialog you need all the other access + * functions. + * + * Example: + * + * \code + * QString deck,card; + * int result = KCardDialog::getCardDeck(deck,card ); + * if ( result == KCardDialog::Accepted ) + * ... + * \endcode + * + * Here you can see a card dialog in action + * @image html "kcarddialog.png" KCarddialog + * + * KCardDialog::getCardDeck takes a lot of different parameters which are + * probably very useful. You can e.g. use the parameters randomDeck and + * randomCardDir to give the end-user the ability to choose a random + * deck/carddir. You have to save the value of those parameters in your config + * file - that's why the parameters are needed. + * + * You can also provide a KConfig pointer (usually kapp->config()). This + * pointer is used to store information about the dialog in an own group + * ("KCardDailog"). + * So you can just ignore the randomCardDir and randomDeck + * values and call KCardDialog::getConfigCardDeck instead. The only reson + * for this function is to read a previously written configuration and give you + * the information about it. This way you don't have to save any configuration + * on your own - KCardDialog does this for you. + * + * Another Parameter for KCardDialog::getCardDeck is scale. This pointer + * to a double variable contains the scaling factor the user has chosen in the + * dialog (the scale box won't be shown if you don't provide this parameter). + * You might want to check out QPixmap::xFrom which gives you access to + * scaling. You can e.g. use + * \code + * QWMatrix m; + * m.scale(s,s); + * pixmap.xForm(m); + * \endcode + * to scale your pixmap. + * + * @author Martin Heni + * @version $Id$ + */ +class KDE_EXPORT KCardDialog : public KDialogBase +{ + Q_OBJECT + +public: + + /** + * @li @p Both - both are shown + * @li @p NoDeck - The deck (back) selection is not shown + * @li @p NoCards - The cards (front) selection is not shown + */ + enum CardFlags { Both=0, NoDeck=0x01, NoCards=0x02 }; + + /** + * Constructs a card deck selection dialog. + * + * @param parent The parent widget of the dialog, if any. + * @param name The name of the dialog. + * @param flags Specifies whether the dialog is modal or not. + */ + KCardDialog (QWidget* parent = NULL,const char* name = NULL, + CardFlags flags = Both); + /** + * Destructs a card deck selection dialog. + */ + ~KCardDialog(); + + /** + * Creates a modal carddeck dialog, lets the user choose a deck, + * and returns when the dialog is closed. + * + * @param deck a reference to the filename used as backside of the + * cards. It is an absolute path and can directly be loaded as + * pixmap. + * + * @param carddir a reference to the directory name used as front of the + * cards. The directory contains the card images as 1.png to 52.png + * + * @param parent an optional pointer to the parent window of the dialog + * + * @param flags what to show + * + * @param randomDeck if this pointer is non-zero, *ok is set to TRUE if + * the user wants a random deck otherwise to FALSE. Use this in the + * config file of your game to load a random deck on startup. + * See @ref getRandomDeck() + * + * @param randomCardDir if this pointer is non-zero, *ok is set to TRUE if + * the user wants a random card otherwise to FALSE. + * Use this in the config file of your game to load a random card + * foregrounds on startup. + * See @ref getRandomCardDir() + * + * @param scale If non-zero a box is shown which provides the possibility to + * change the size of the cards. The desired scaling factor is returned to the + * game in this variable. + * + * @param conf If non-zero KCardDialog reads the initial settings for + * this dialog from the applications config file and stores them there + * when the dialog is closed. You can just use getConfigCardDeck + * to get the deck/carddir the user selected before. Note that the + * parameters randomDeck and randomCardDir overwrite the initial settings from the + * config file. + * + * @return QDialog::result(). + */ + static int getCardDeck(QString &deck,QString &carddir, QWidget *parent=0, + CardFlags flags=Both, bool* randomDeck=0, + bool* randomCardDir=0, double* scale=0, KConfig* conf=0); + + /** + * Read the configuration from the applications rc file and put the + * previously chosen deck/frontside in the parameter deck and carddir. + * + * You probably want to use this function on startup of your program so that + * the user gets exactly the card/frontside he/she chose before. Note that + * you don't have to care whether the user wants to get a random carddeck or + * not as this function takes care of this. + * @param conf The config file to read from + * @param deck This will contain the chosen deck from the config file (or a + * random deck if this is desired according to the config) + * @param cardDir This will contain the chosen cardDir from the config file (or a + * random cardDir if this is desired according to the config) + * @param scale The scaling factor (usually 1) + **/ + static void getConfigCardDeck(KConfig* conf, QString& deck, QString& cardDir, double& scale); + + /** + * Returns the default path to the card deck backsides. You want + * to use this usually before the user used the card dialog the first + * time to get a default deck. You can assume that + * \code + * getDefaultDeckPath() + * \endcode + * is a valid deck. + * + * @return The default path + */ + static QString getDefaultDeck(); + + /** + * Returns the default path to the card frontsides. You want + * to use this usually before the user used the card dialog the first + * time to get an default deck. You can assume that + * \code + * getCardPath(getDefaultCardPath(), *) + * \endcode + * are valid cards for * from 1 to 52. + * + * @return returns the path to the card directory + */ + static QString getDefaultCardDir(); + + /** + * Returns the path to the card frontside specified in dir carddir + * + * @param index the card to open + * @param carddir The carddir which's path shall be searched for + * @return returns the path to the card + */ + static QString getCardPath(const QString &carddir, int index); + + /** + * Returns a random deck in deckPath() + * @return A random deck + **/ + static QString getRandomDeck(); + + /** + * Returns a random directory of cards + * @return A random card dir + **/ + static QString getRandomCardDir(); + + /** + * Show or hides the "random backside" checkbox + * @param s Shows the checkbox if true otherwise hides it + **/ + void showRandomDeckBox(bool s); + + /** + * Show or hides the "random foreside" checkbox + * @param s Shows the checkbox if true otherwise hides it + **/ + void showRandomCardDirBox(bool s); + + /** + * Returns the chosen deck, which is a valid path to a imagefile. + * + * @return The deck + */ + const QString& deck() const; + + /** + * Sets the default deck. + * @param file The full path to an image file + */ + void setDeck(const QString& file); + + /** + * @return The chosen card directory + */ + const QString& cardDir() const; + + /** + * Sets the default card directory. + * @param dir The full path to an card directory + */ + void setCardDir(const QString& dir); + + /** + * @return the flags set to the dialog + */ + CardFlags flags() const; + + /** + * Creates the default widgets in the dialog. Must be called after + * all flags are set. This is only needed if you do NOT use the + * getCardDeck static function which provides all calls for you. + */ + void setupDialog(bool showResizeBox = false); + + /** + * @return TRUE if the selected deck is a random deck (i.e. the user checked + * the random checkbox) otherwise FALSE + **/ + bool isRandomDeck() const; + + /** + * @return TRUE if the selected carddir is a random dir (i.e. the user + * checked the random checkbox) otherwise FALSE + **/ + bool isRandomCardDir() const; + + /** + * @return TRUE if the global checkbox was selected + **/ + bool isGlobalDeck() const; + + /** + * @return TRUE if the global checkbox was selected + **/ + bool isGlobalCardDir() const; + + /** + * @return The scaling factor of the card pixmap + **/ + double cardScale() const; + + /** + * Load the default settings into the dialog (e.g. whether the "use random + * deck" checkbox is checked or not). + **/ + void loadConfig(KConfig* conf); + + /** + * Saves the KCardDialog config into a config file. This should be the + * applications config file - KCardDialog creates an own group + * ("KCardDialog"). These settings are used by @ref loadConfig and @ref + * getConfigCardDeck. + **/ + void saveConfig(KConfig* conf); + + +protected: + void insertCardIcons(); + void insertDeckIcons(); + + static void getGlobalDeck(QString& cardDir, bool& random); + static void getGlobalCardDir(QString& deck, bool& random); + + static QString getDeckName(const QString& desktop); + + /** + * @return the groupname used by functions like @ref saveConfig and @ref + * loadConfig. + **/ + static QString group(); + +protected slots: + void slotDeckClicked(QIconViewItem *); + void slotCardClicked(QIconViewItem *); + void slotRandomCardDirToggled(bool on); + void slotRandomDeckToggled(bool on); + void slotCardResized(int); + void slotDefaultSize(); + void slotSetGlobalDeck(); + void slotSetGlobalCardDir(); + +private: + static void init(); + + KCardDialogPrivate* d; +}; + +#endif diff --git a/libkdegames/kcarddialog.png b/libkdegames/kcarddialog.png new file mode 100644 index 00000000..3446c461 Binary files /dev/null and b/libkdegames/kcarddialog.png differ diff --git a/libkdegames/kchat.cpp b/libkdegames/kchat.cpp new file mode 100644 index 00000000..d4ffd7ec --- /dev/null +++ b/libkdegames/kchat.cpp @@ -0,0 +1,115 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include +#include + +#include "kchat.h" + +class KChatPrivate +{ +public: + KChatPrivate() + { + } + + bool mAutoAddMessages; + + QMap mPlayerMap; + int mPlayerId; + int mFromId; +}; + +KChat::KChat(QWidget* parent, bool twoPlayerGame) : KChatBase(parent, twoPlayerGame) +{ + init(); +} + +KChat::~KChat() +{ + kdDebug(11000) << "DESTRUCT KChat " << this << endl; + delete d; +} + +void KChat::init() +{ + kdDebug(11001) << "INIT KChat " << this << endl; + d = new KChatPrivate; + d->mAutoAddMessages = true; + d->mPlayerId = 1; + d->mFromId = 1; +} + +void KChat::setFromNickname(const QString& n) +{ d->mFromId = addPlayer(n); } +const QString& KChat::fromName() const +{ return player(fromId()); } +void KChat::setAutoAddMessages(bool add) +{ d->mAutoAddMessages = add; } +bool KChat::autoAddMessages() const +{ return d->mAutoAddMessages; } +int KChat::uniqueId() +{ return d->mPlayerId++; } +int KChat::fromId() const +{ return d->mFromId; } +const QString& KChat::player(int id) const +{ return d->mPlayerMap[id]; } + +void KChat::returnPressed(const QString& text) +{ + int id = fromId(); + if (id < 0) { + // don't return - just display "unknown" as name + kdWarning(11000) << "KChat: no fromNickname has been set!" << endl; + } + emit signalSendMessage(id, text); + if (autoAddMessages()) { + QString p = player(id); + if (p.isNull()) { + p = i18n("Unknown"); + } + kdDebug(11000) << "auto adding message from player " << p << " ;id=" << id << endl; + addMessage(p, text); + } +} + +int KChat::addPlayer(const QString& nickname) +{ + int id = uniqueId(); + d->mPlayerMap.insert(id, nickname); + return id; +} + +void KChat::removePlayer(int id) +{ + d->mPlayerMap.remove(id); +} + +void KChat::removePlayer(const QString& nickname) +{ + QMap::Iterator it; + for (it = d->mPlayerMap.begin(); it != d->mPlayerMap.end(); ++it) { + if (it.data() == nickname) { + d->mPlayerMap.remove(it); + } + } +} + + +#include "kchat.moc" diff --git a/libkdegames/kchat.h b/libkdegames/kchat.h new file mode 100644 index 00000000..db479bc0 --- /dev/null +++ b/libkdegames/kchat.h @@ -0,0 +1,147 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef __KCHAT_H__ +#define __KCHAT_H__ + +#include + +#include "kchatbase.h" +#include + +class KChatPrivate; + +/** + * @short A chat widget for non-KGame games + * + * Docu is TODO + * + * @author Andreas Beckermann + **/ +class KDE_EXPORT KChat : public KChatBase +{ + Q_OBJECT +public: + /** + * @param parent The parent widget for this widget. + * @param twoPlayerGame If true the combo box where the player can + * choose to send to a single player or to all players will not be added + * as you will hardly need it in 2-player games. + **/ + KChat(QWidget* parent, bool twoPlayerGame = false); + + virtual ~KChat(); + + /** + * Equivalent to player(fromId()) + * @return The name that will be shown for messages from this widget. + * That is the string from @ref setFromNickname + **/ + virtual const QString& fromName() const; + + /** + * This sets the name that will be shown on all chat widgets if this + * widget sends a message. See signalSendMessage + * @param name The name of the player owning this widget + **/ + void setFromNickname(const QString& name); + +// TODO: +// void setPlayerList(QIntDict);// use this for non-KGame use + + /** + * Adds a player nickname. + * @return The unique ID of the player + **/ + int addPlayer(const QString& nick); + + /** + * Removes all players with this nickname. Better don't use this as it + * will remove *all* players with this nickname. Save the id instead and + * call removePlayer(id) + * @param nick The nickname of the removed players + **/ + void removePlayer(const QString& nick); + + /** + * Removes the player with this id, as returned by @ref addPlayer + * @param id The id of the player to be removed + **/ + void removePlayer(int id); + + + /** + * @return true if the messages which will be sent from here will be + * added automatically using @ref KChatBase::addMessage. See also @ref + * setAutoAddMessages + **/ + bool autoAddMessages() const; + + /** + * Usually the messages which will be sent from here (see @ref + * signalSendMessage) are added autmatically to this widget. But under + * some circumstances that would be very unhandy. So you can deactivate + * this behaviour here and call @ref KChatBase::addMessage yourself + * @param add If true (default) messages sent from here will be added + * automatically. Otherwise you will have to add them yourself + **/ + void setAutoAddMessages(bool add); + + /** + * @return The nickname of the player which belongs to this id + **/ + const QString& player(int id) const; + + /** + * @return The ID that belongs to the local player. + * @see setFromNickname + **/ + int fromId() const; + + +signals: + /** + * This signal is emitted when the player wants to send a message. + * + * The message is added automatically using @ref KChatBase::addMessage if @ref + * autoAddMessages is enabled. + * @param id The id of the player who sends the message - see + * setFromNickname and player + * @param msg The message itself + **/ + void signalSendMessage(int id, const QString& msg); + +protected: + /** + * This emits @ref signalSendMessage and, if @ref autoAddMessages is + * true, calls @ref KChatBase::addMessage + **/ + virtual void returnPressed(const QString&); + + /** + * The Id of the next player. Incremented after every call. + **/ + int uniqueId(); + +private: + void init(); + + KChatPrivate* d; +}; + +#endif diff --git a/libkdegames/kchatbase.cpp b/libkdegames/kchatbase.cpp new file mode 100644 index 00000000..4ccd7b08 --- /dev/null +++ b/libkdegames/kchatbase.cpp @@ -0,0 +1,530 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kchatbase.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +class KChatBaseTextPrivate +{ +public: + KChatBaseTextPrivate() + { + mNameFont = 0; + mMessageFont = 0; + } + + QString mName; + QString mMessage; + + const QFont* mNameFont; + const QFont* mMessageFont; +}; + + +KChatBaseText::KChatBaseText(const QString& name, const QString& message) : QListBoxText() +{ + init(); + setName(name); + setMessage(message); +} + +KChatBaseText::KChatBaseText(const QString& message) : QListBoxText() +{ + init(); + setMessage(message); +} + +KChatBaseText::~KChatBaseText() +{ + delete d; +} + +void KChatBaseText::init() +{ + d = new KChatBaseTextPrivate; +} + +void KChatBaseText::setName(const QString& n) +{ +// d->mName = n; + d->mName = QString("%1: ").arg(n); + setText(QString("%1: %2").arg(name()).arg(message())); // esp. for sorting +} + +void KChatBaseText::setMessage(const QString& m) +{ + d->mMessage = m; + setText(QString("%1: %2").arg(name()).arg(message())); // esp. for sorting +} + +const QString& KChatBaseText::name() const +{ return d->mName; } + +const QString& KChatBaseText::message() const +{ return d->mMessage; } + +QFont KChatBaseText::nameFont() const +{ + if (d->mNameFont) { + return *d->mNameFont; + } else if (listBox()) { + return listBox()->font(); + } else { + return QFont(); + } +} + +QFont KChatBaseText::messageFont() const +{ + if (d->mMessageFont) { + return *d->mMessageFont; + } else if (listBox()) { + return listBox()->font(); + } else { + return QFont(); + } +} + +void KChatBaseText::setNameFont(const QFont* f) +{ d->mNameFont = f; } + +void KChatBaseText::setMessageFont(const QFont* f) +{ d->mMessageFont = f; } + +void KChatBaseText::paint(QPainter* painter) +{ + QFontMetrics fm = painter->fontMetrics(); + painter->setFont(nameFont()); + painter->drawText(3, fm.ascent() + fm.leading()/2, name()); + painter->setFont(messageFont()); + painter->drawText(3 + QFontMetrics(nameFont()).width(name()), fm.ascent() + fm.leading()/2, message()); +} + +int KChatBaseText::width(QListBox* lb) const +{ + int w = 0; + if (lb) { + w += 6; + w += QFontMetrics(nameFont()).width(name()); + w += QFontMetrics(messageFont()).width(message()); + } +// int w = lb ? lb->fontMetrics().width( text() ) + 6 : 0; // QT orig + return QMAX(w, QApplication::globalStrut().width()); +} + +int KChatBaseText::height(QListBox* lb) const +{ + int h = 0; + if (lb) { + h += 2; + // AB: is lineSpacing still correct? + if (QFontMetrics(nameFont()).lineSpacing() > QFontMetrics(messageFont()).lineSpacing()) { + h += QFontMetrics(nameFont()).lineSpacing(); + } else { + h += QFontMetrics(messageFont()).lineSpacing(); + } + } +// int h = lb ? lb->fontMetrics().lineSpacing() + 2 : 0; // QT orig + return QMAX(h, QApplication::globalStrut().height()); +} + + + +class KChatBasePrivate +{ +public: + KChatBasePrivate() + { + mBox = 0; + mEdit = 0; + mCombo = 0; + + mAcceptMessage = true; + mMaxItems = -1; + } + QListBox* mBox; + KLineEdit* mEdit; + QComboBox* mCombo; + bool mAcceptMessage; + int mMaxItems; + + QValueList mIndex2Id; + + QFont mNameFont; + QFont mMessageFont; + QFont mSystemNameFont; + QFont mSystemMessageFont; +}; + +KChatBase::KChatBase(QWidget* parent, bool noComboBox) : QFrame(parent) +{ + init(noComboBox); +} + +KChatBase::~KChatBase() +{ +// kdDebug(11000) << "KChatBase: DESTRUCT (" << this << ")" << endl; + saveConfig(); + delete d; +} + +void KChatBase::init(bool noComboBox) +{ +// kdDebug(11000) << "KChatBase: INIT (" << this << ")" << endl; + + d = new KChatBasePrivate; + + setMinimumWidth(100); + setMinimumHeight(150); + + QVBoxLayout* l = new QVBoxLayout(this); + + d->mBox = new QListBox(this); + connect(d->mBox, SIGNAL(rightButtonClicked(QListBoxItem*, const QPoint&)), + this, SIGNAL(rightButtonClicked(QListBoxItem*, const QPoint&))); + l->addWidget(d->mBox); + d->mBox->setVScrollBarMode(QScrollView::AlwaysOn); + d->mBox->setHScrollBarMode(QScrollView::AlwaysOff); + d->mBox->setFocusPolicy(QWidget::NoFocus); +// d->mBox->setSelectionMode(QListBox::NoSelection); + d->mBox->setSelectionMode(QListBox::Single); + + l->addSpacing(5); + + QHBoxLayout* h = new QHBoxLayout(l); + d->mEdit = new KLineEdit(this); + d->mEdit->setHandleSignals(false); + d->mEdit->setTrapReturnKey(true); + d->mEdit->completionObject(); // add the completion object + d->mEdit->setCompletionMode(KGlobalSettings::CompletionNone); + connect(d->mEdit, SIGNAL(returnPressed(const QString&)), this, SLOT(slotReturnPressed(const QString&))); + h->addWidget(d->mEdit); + + if (!noComboBox) { + d->mCombo = new QComboBox(this); + h->addWidget(d->mCombo); + addSendingEntry(i18n("Send to All Players"), SendToAll);//FIXME: where to put the id? + } + + d->mAcceptMessage = true; // by default + setMaxItems(-1); // unlimited + + if (kapp) { + // kapp might be NULL as well - in case we are in Qt designer. + readConfig(); + } +} + +bool KChatBase::acceptMessage() const +{ return d->mAcceptMessage; } + +void KChatBase::setAcceptMessage(bool a) +{ d->mAcceptMessage = a; } + +bool KChatBase::addSendingEntry(const QString& text, int id) +{ +//FIXME: is ID used correctly? +// do we need ID at all? +// what the hell should be here? +// d->mCombo->insertItem(i18n("Send to All Players"), SendToAll); + return insertSendingEntry(text, id); +} + +bool KChatBase::insertSendingEntry(const QString& text, int id, int index) +{ + if (!d->mCombo) { + kdWarning(11000) << "KChatBase: Cannot add an entry to the combo box" << endl; + return false; + } + if (d->mIndex2Id.findIndex(id) != -1) { + kdError(11000) << "KChatBase: Cannot add more than one entry with the same ID! " << endl; + kdError(11000) << "KChatBase: Text="<mCombo->insertItem(text, index); + if (index < 0) { + d->mIndex2Id.append(id); + } else { + d->mIndex2Id.insert(d->mIndex2Id.at(index), id); + } + if (d->mIndex2Id.count() != (uint)d->mCombo->count()) { + kdError(11000) << "KChatBase: internal ERROR - local IDs do not match combo box entries!" << endl; + } + return true; +} + +int KChatBase::sendingEntry() const +{ + if (!d->mCombo) { + kdWarning(11001) << "Cannot retrieve index from NULL combo box" << endl; + return -1; + } + int index = d->mCombo->currentItem(); + if (d->mIndex2Id.at(index) == d->mIndex2Id.end()) { + kdWarning(11000) << "could not find the selected sending entry!" << endl; + return -1; + } + return d->mIndex2Id[index]; +} + +void KChatBase::removeSendingEntry(int id) +{ + if (!d->mCombo) { + kdWarning(11000) << "KChatBase: Cannot remove an entry from the combo box" << endl; + return; + } + d->mCombo->removeItem(findIndex(id)); + d->mIndex2Id.remove(id); +} + +void KChatBase::changeSendingEntry(const QString& text, int id) +{ + if (!d->mCombo) { + kdWarning(11000) << "KChatBase: Cannot change an entry in the combo box" << endl; + return; + } + int index = findIndex(id); + d->mCombo->changeItem(text, index); +} + +void KChatBase::setSendingEntry(int id) +{ + if (!d->mCombo) { + kdWarning(11000) << "KChatBase: Cannot set an entry in the combo box" << endl; + return; + } + d->mCombo->setCurrentItem(findIndex(id)); +} + +int KChatBase::findIndex(int id) const +{ + return d->mIndex2Id.findIndex(id); +} + +int KChatBase::nextId() const +{ + int i = SendToAll + 1; + while (d->mIndex2Id.findIndex(i) != -1) { + i++; + } + return i; +} + +void KChatBase::addItem(const QListBoxItem* text) +{ + d->mBox->insertItem(text); + int index = d->mBox->count() -1; + d->mBox->setBottomItem(index);//FIXME: don't scroll to bottom if user scrolled down manually + if (maxItems() >= 0 && d->mBox->count() > (unsigned int)maxItems()) { + d->mBox->removeItem(0); + } +} + +void KChatBase::addMessage(const QString& fromName, const QString& text) +{ +//maybe "%1 says: %2" or so + addItem(layoutMessage(fromName, text)); +} + +void KChatBase::addSystemMessage(const QString& fromName, const QString& text) +{ + addItem(layoutSystemMessage(fromName, text)); +} + +QListBoxItem* KChatBase::layoutMessage(const QString& fromName, const QString& text) +{ + //TODO: KChatBaseConfigure? - e.g. color + QListBoxItem* message; + if (text.startsWith("/me ")) { + // replace "/me" by a nice star. leave one space after the star + QPixmap pix; + pix.load(locate("data", QString::fromLatin1("kdegames/pics/star.png"))); + + //TODO KChatBasePixmap? Should change the font here! + + message = (QListBoxItem*)new QListBoxPixmap(pix, i18n("%1 %2").arg(fromName).arg(text.mid(3))); + } else { + // the text is not edited in any way. just return an item + KChatBaseText* m = new KChatBaseText(fromName, text); + m->setNameFont(&d->mNameFont); + m->setMessageFont(&d->mMessageFont); + message = (QListBoxItem*)m; + } + return message; +} + +QListBoxItem* KChatBase::layoutSystemMessage(const QString& fromName, const QString& text) +{ + //TODO: KChatBaseConfigure? - e.g. color + + // no need to check for /me etc. + KChatBaseText* m = new KChatBaseText(i18n("--- %1").arg(fromName), text); + m->setNameFont(&d->mSystemNameFont); + m->setMessageFont(&d->mSystemMessageFont); + return (QListBoxItem*)m; +} + +void KChatBase::slotReturnPressed(const QString& text) +{ + if (text.length() <= 0) { + // no text entered - probably hit return by accident + return; + } else if (!acceptMessage()) { + return; + } + d->mEdit->completionObject()->addItem(text); +// connect(d->mEdit, SIGNAL(returnPressed(const QString&)), comp, SLOT(addItem(const QString&))); + d->mEdit->clear(); + returnPressed(text); +} + +QString KChatBase::comboBoxItem(const QString& name) const +{ // TODO: such a function for "send to all" and "send to my group" + return i18n("Send to %1").arg(name); +} + +void KChatBase::slotClear() +{ + d->mBox->clear(); +} + +void KChatBase::setCompletionMode(KGlobalSettings::Completion mode) +{ d->mEdit->setCompletionMode(mode); } + +void KChatBase::setNameFont(const QFont& font) +{ + d->mNameFont = font; + d->mBox->triggerUpdate(false); +} + +void KChatBase::setMessageFont(const QFont& font) +{ + d->mMessageFont = font; + d->mBox->triggerUpdate(false); +} + +void KChatBase::setBothFont(const QFont& font) +{ + setNameFont(font); + setMessageFont(font); +} + +const QFont& KChatBase::nameFont() const +{ return d->mNameFont; } + +const QFont& KChatBase::messageFont() const +{ return d->mMessageFont; } + +void KChatBase::setSystemNameFont(const QFont& font) +{ + d->mSystemNameFont = font; + d->mBox->triggerUpdate(false); +} + +void KChatBase::setSystemMessageFont(const QFont& font) +{ + d->mSystemMessageFont = font; + d->mBox->triggerUpdate(false); +} + +void KChatBase::setSystemBothFont(const QFont& font) +{ + setSystemNameFont(font); + setSystemMessageFont(font); +} + +const QFont& KChatBase::systemNameFont() const +{ return d->mSystemNameFont; } + +const QFont& KChatBase::systemMessageFont() const +{ return d->mSystemMessageFont; } + +void KChatBase::saveConfig(KConfig* conf) +{ + QString oldGroup; + if (!conf) { + conf = kapp->config(); + oldGroup = conf->group(); + conf->setGroup("KChatBase"); + } + + conf->writeEntry("NameFont", nameFont()); + conf->writeEntry("MessageFont", messageFont()); + conf->writeEntry("SystemNameFont", systemNameFont()); + conf->writeEntry("SystemMessageFont", systemMessageFont()); + conf->writeEntry("MaxMessages", maxItems()); + + if (!oldGroup.isNull()) { + conf->setGroup(oldGroup); + } +} + +void KChatBase::readConfig(KConfig* conf) +{ + QString oldGroup; + if (!conf) { + conf = kapp->config(); + oldGroup = conf->group(); + conf->setGroup("KChatBase"); + } + + setNameFont(conf->readFontEntry("NameFont")); + setMessageFont(conf->readFontEntry("MessageFont")); + setSystemNameFont(conf->readFontEntry("SystemNameFont")); + setSystemMessageFont(conf->readFontEntry("SystemMessageFont")); + setMaxItems(conf->readNumEntry("MaxMessages", -1)); + + if (!oldGroup.isNull()) { + conf->setGroup(oldGroup); + } +} + +void KChatBase::clear() +{ + d->mBox->clear(); +} + +void KChatBase::setMaxItems(int maxItems) +{ + d->mMaxItems = maxItems; + //TODO cut too many messages + if (maxItems == 0) { + clear(); + } else if (maxItems > 0) { + while (d->mBox->count() > (unsigned int)maxItems) { + d->mBox->removeItem(0); + } + } +} + +int KChatBase::maxItems() const +{ return d->mMaxItems; } + + +#include "kchatbase.moc" diff --git a/libkdegames/kchatbase.h b/libkdegames/kchatbase.h new file mode 100644 index 00000000..07f786f9 --- /dev/null +++ b/libkdegames/kchatbase.h @@ -0,0 +1,510 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef __KCHATBASE_H__ +#define __KCHATBASE_H__ + +#include +#include +#include + +#include +#include +class QListBoxItem; + +class KConfig; + + +class KChatBaseTextPrivate; + +/** + * A QListBoxText implementation for KChatBase. + * + * It supports different colors, text fonts, ... + * + * A KChatBaseText consists of two text items: first the player part then the + * text part. This honors KChatBase::addMessage which also uses both. + * You can leave the player part out if you don't need it - there won't be any + * difference. + * + * You can set different colors and fonts for both parts. In the future there + * will probably some kind of KChatBaseDialog which offers the user the ability + * to configure things like color and font on the fly. + **/ +class KChatBaseText : public QListBoxText +{ +public: + + /** + * Constructs a KChatBaseText object with the player and text part + **/ + KChatBaseText(const QString& player, const QString& text); + + /** + * Constructs a KChatBaseText object without player part + **/ + KChatBaseText(const QString& text); + + /** + * Destruct a KChatBaseText object. + **/ + virtual ~KChatBaseText(); + + /** + * Set the name part of a message. A message is usually shown like + * "name: text" and you can change both parts independently. + * + * @see setMessage + * @param name The name of the sender (e.g. the player) + **/ + void setName(const QString& name); + + /** + * Set the text part of a message. A message is usually shown like + * "name: message" and you can change both parts independently. + * + * See also setName + * @param message The message that has been sent + **/ + void setMessage(const QString& message); + + /** + * @return The name part of a message. + * @see setName + **/ + const QString& name() const; + + /** + * @return The message text. + * @see setMessage + **/ + const QString& message() const; + + /** + * You can set the font of the sender name independently of the message + * itself. This font is used as the "name: " part of the message. + * @return The font that is used for the name + **/ + QFont nameFont() const; + + /** + * You can set the font of the message independently of the sender name. + * This font is used as the text part of the message. + * @return The font thaz is used for message text + **/ + QFont messageFont() const; + + /** + * Set the font for the name. + * @see nameFont + * @param font A pointer to the name font. Only the pointer is stored so + * don't delete the object. This way there is only one object for a lot + * of messages in memory. + **/ + void setNameFont(const QFont* font); + + /** + * Set the font for the message text. + * @see messageFont + * @param font A pointer to the message font. Only the pointer is stored so + * don't delete the object! This way there is only one object for a lot + * of messages in memory. + **/ + void setMessageFont(const QFont* font); + + /** + **/ + virtual int width(QListBox* ) const; + + /** + **/ + virtual int height(QListBox* ) const; + +protected: + /** + **/ + virtual void paint(QPainter*); + +private: + void init(); + +private: + KChatBaseTextPrivate* d; +}; + + +class KChatBasePrivate; + +/** + * @short The base class for chat widgets + * + * This is the base class for both KChat and KGameChat. KGameChat is the class + * you want to use if you write a KGame based game as it will do most things for + * you. KChat is more or less the same but not KGame dependant + * + * KChatBase provides a complete chat widget, featuring different sending means + * (e.g. "send to all", "send to player1", "send to group2" and so on - see + * addSendingEntry). It also provides full auto-completion capabilities (see + * KCompletion and KLineEdit) which defaults to disabled. The user can + * change this by right-clicking on the KLineEdit widget and selecting the + * desired behaviour. You can also change this manually by calling + * setCompletionMode. + * + * To make KChatBase useful you have to overwrite at least returnPressed. + * Here you should send the message to all of your clients (or just some of + * them, depending on sendingEntry). + * + * To add a message just call addMessage with the nickname of the player + * who sent the message and the message itself. If you don't want to use + * layoutMessage by any reason you can also call addItem directly. But you + * should better replace layoutMessage instead. + * + * You probably don't want to use the abstract class KChatBase directly but use + * one of the derived classess KChat or KGameChat. The latter is the + * widget of choice if you develop a KGame application as you don't have to + * do anything but providing a KGame object. + * + * @author Andreas Beckermann + **/ +class KDE_EXPORT KChatBase : public QFrame +{ + Q_OBJECT +public: + /** + * @param parent The parent widget for this widget. + * @param noComboBox If true then the combo box where the player can + * choose where to send messages to (either globally or just to some + * players) will not be added. + **/ + KChatBase(QWidget* parent, bool noComboBox = false); + + /** + * Destruct the KChatBase object + * + * Also calls saveConfig + **/ + virtual ~KChatBase(); + + enum SendingIds { + SendToAll = 0 + }; + + /** + * @return The name that will be shown for messages from this widget. Either the + * string that was set by setFromName or the name of the player + * that was set by setFromPlayer + **/ + virtual const QString& fromName() const = 0; + + /** + * Adds a new entry in the combo box. The default is "send to all + * players" only. This function is provided for convenience. You can + * also call inserSendingEntry with index = -1. + * See also nextId! + * @param text The text of the new entry + * @param id An ID for this entry. This must be unique for this + * entry. It has nothing to do with the position of the entry in the + * combo box. See nextId + * @return True if successful, otherwise false (e.g. if the id is already used) + **/ + bool addSendingEntry(const QString& text, int id); + + /** + * Inserts a new entry in the combo box. + * @param text The entry + * @param id An ID for this entry. This must be unique for this + * entry. It has nothing to do with the position of the entry in the + * combo box! + * @see nextId + * @param index The position of the entry. If -1 the entry will be added + * at the bottom + * @return True if successful, otherwise false (e.g. if the id is already used) + **/ + bool insertSendingEntry(const QString& text, int id, int index = -1); + + /** + * This changes a combo box entry. + * @param text The new text of the entry + * @param id The ID of the item to be changed + **/ + void changeSendingEntry(const QString& text, int id); + + /** + * This selects a combo box entry. + * @param id The ID of the item to be selected + **/ + void setSendingEntry(int id); + + /** + * Removes the entry with the ID id from the combo box. Note that id is + * _not_ the index of the entry! + * @see addSendingEntry + * @param id The unique id of the entry + **/ + void removeSendingEntry(int id); + + /** + * @return The _unique ID_ of the sending entry that has been selected. + * @see addSendingEntry + * + * Note that the entry "send to all" _always_ uses + * KChatBase::SendToAll, i.e. 0 as id! + **/ + int sendingEntry() const; + + /** + * @return The index of the combo box entry with the given id + **/ + int findIndex(int id) const; + + /** + * @return An ID that has not yet been used in the combo box. + * @see addSendingEntry + **/ + int nextId() const; + + /** + * @return True if this widget is able to send messages (see + * returnPressed) and false if not. The default implementation returns + * the value which has been set by setAcceptMessage (true by + * default) + **/ + virtual bool acceptMessage() const; + + /** + * See KLineEdit::setCompletionMode + **/ + void setCompletionMode(KGlobalSettings::Completion mode); + + /** + * Set the font that used used for the name part of a message. See also + * nameFont and setBothFont + **/ + void setNameFont(const QFont& font); + + /** + * Set the font that used used for the message part of a message. + * @see messageFont, setBothFont + **/ + void setMessageFont(const QFont& font); + + /** + * This sets both - nameFont and messageFont to font. You + * probably want to use this if you don't wish to distinguish between + * these parts of a message. + * @param font A font used for both nameFont and messageFont + **/ + void setBothFont(const QFont& font); + + /** + * Same as setNameFont but applies only to system messages. + * @see layoutSystemMessage + **/ + void setSystemNameFont(const QFont& font); + + /** + * Same as setMessageFont but applies only to system messages. + * @see layoutSystemMessage + **/ + void setSystemMessageFont(const QFont& font); + + /** + * Same as setBothFont but applies only to system messages. + * @see layoutSystemMessage + **/ + void setSystemBothFont(const QFont& font); + + /** + * This font should be used for the name (the "from: " part) of a + * message. layoutMessage uses this to set the font using + * KChatBaseText::setNameFont but if you want to overwrite + * layoutMessage you should do this yourself. + * @return The font that is used for the name part of the message. + **/ + const QFont& nameFont() const; + + /** + * This font should be used for a message. layoutMessage sets the + * font of a message using KChatBaseText::setMessageFont but if ypu + * replace layoutMessage with your own function you should use + * messageFont() yourself. + * @return The font that is used for a message + **/ + const QFont& messageFont() const; + + /** + * Same as systemNameFont but applies only to system messages. + * @see layoutSystemMessage + **/ + const QFont& systemNameFont() const; + + /** + * Same as systemMessageFont but applies only to system messages. + * @see layoutSystemMessage + **/ + const QFont& systemMessageFont() const; + + /** + * Save the configuration of the dialog to a KConfig object. If + * the supplied KConfig pointer is NULL then kapp->config() is used + * instead (and the group is changed to "KChatBase") butr the current + * group is restored at the end. + * @param conf A pointer to the KConfig object to save the config + * to. If you use 0 then kapp->config() is used and the group is changed + * to "KChatBase" (the current group is restored at the end). + **/ + virtual void saveConfig(KConfig* conf = 0); + + /** + * Read the configuration from a KConfig object. If the pointer is + * NULL kapp->config() is used and the group is changed to "KChatBase". + * The current KConfig::group is restored after this call. + **/ + virtual void readConfig(KConfig* conf = 0); + + /** + * Set the maximum number of items in the list. If the number of item + * exceeds the maximum as many items are deleted (oldest first) as + * necessary. The number of items will never exceed this value. + * @param maxItems the maximum number of items. -1 (default) for + * unlimited. + **/ + void setMaxItems(int maxItems); + + /** + * Clear all messages in the list. + **/ + void clear(); + + /** + * @return The maximum number of messages in the list. -1 is unlimited. See also + * setMaxItems + **/ + int maxItems() const; + + +public slots: + /** + * Add a text in the listbox. See also signalSendMessage() + * + * Maybe you want to replace this with a function that creates a nicer text + * than "fromName: text" + * + * Update: the function layoutMessage is called by this now. This + * means that you will get user defined outlook on the messages :-) + * @param fromName The player who sent this message + * @param text The text to be added + **/ + virtual void addMessage(const QString& fromName, const QString& text); + + /** + * This works just like addMessage but adds a system message. + * layoutSystemMessage is used to generate the displayed item. System + * messages will have a different look than player messages. + * + * You may wish to use this to display status information from your game. + **/ + virtual void addSystemMessage(const QString& fromName, const QString& text); + + /** + * This member function is mainly internally used to add a message. It + * is called by addMessage which creates a single text from a + * player name and a text. You will hardly ever use this - but if you + * need it it will be here ;-) + * + * But you may want to replace this in a derived class to create a + * non-default (maybe nicer ;-) ) behaviour + * @param item The QListBoxItem that is being added + **/ + virtual void addItem(const QListBoxItem* item); + + + /** + * This clears all messages in the view. Note that only the messages are + * cleared, not the sender names in the combo box! + **/ + void slotClear(); + + /** + * @param a If false this widget cannot send a message until + * setAcceptMessage(true) is called + **/ + void setAcceptMessage(bool a); + +signals: + /** + * Emitted when the user right-clicks on a list item. + * @see QListBox::rightButtonClicked + **/ + void rightButtonClicked(QListBoxItem*, const QPoint&); + +protected: + /** + * This is called whenever the user pushed return ie wants to send a + * message. + * + * Note that you MUST add the message to the widget when this function + * is called as it has already been added to the KCompletion object + * of the KLineEdit widget! + * + * Must be implemented in derived classes + * @param text The message to be sent + **/ + virtual void returnPressed(const QString& text) = 0; + + /** + * Replace to customise the combo box. + * + * Default: i18n("Send to %1).arg(name) + * @param name The name of the player + * @return The string as it will be shown in the combo box + **/ + virtual QString comboBoxItem(const QString& name) const; + + /** + * Create a QListBoxItem for this message. This function is not yet + * written usefully - currently just a QListBoxTex object is + * created which shows the message in this format: "fromName: text". + * This should fit most peoples needs but needs further improvements. + **/ + virtual QListBoxItem* layoutMessage(const QString& fromName, const QString& text); + + /** + * Create a QListBoxItem for this message. This does the same as + * layoutMessage but generates a system message. You might want to + * use such a message to display e.g. status information from your game. + * + * The default implementation just prepends "--- ". + **/ + virtual QListBoxItem* layoutSystemMessage(const QString& fromName, const QString& text); + +private slots: + /** + * Check if a text was entered and if acceptMessage returns true. + * Then add the message to the KCompletion object of the KLineEdit + * widget and call returnPressed + **/ + void slotReturnPressed(const QString&); + +private: + void init(bool noComboBox); + + KChatBasePrivate* d; +}; + +#endif diff --git a/libkdegames/kchatdialog.cpp b/libkdegames/kchatdialog.cpp new file mode 100644 index 00000000..bd196e7c --- /dev/null +++ b/libkdegames/kchatdialog.cpp @@ -0,0 +1,253 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kchatdialog.h" + +#include "kchatbase.h" + +#include +#include + +#include +#include +#include + +class KChatDialogPrivate +{ + public: + KChatDialogPrivate() + { + mTextPage = 0; + + mNamePreview = 0; + mTextPreview = 0; + mSystemNamePreview = 0; + mSystemTextPreview = 0; + + mChat = 0; + } + + QFrame* mTextPage; + + QLabel* mNamePreview; + QLabel* mTextPreview; + QLabel* mSystemNamePreview; + QLabel* mSystemTextPreview; + + QLineEdit* mMaxMessages; + + KChatBase* mChat; +}; + +KChatDialog::KChatDialog(KChatBase* chat, QWidget* parent, bool modal) +// : KDialogBase(Tabbed, i18n("Configure Chat"), Ok|Default|Apply|Cancel, Ok, parent, 0, modal, true) + : KDialogBase(Plain, i18n("Configure Chat"), Ok|Default|Apply|Cancel, Ok, parent, 0, modal, true) +{ + init(); + plugChatWidget(chat); +} + +KChatDialog::KChatDialog(QWidget* parent, bool modal) +// : KDialogBase(Tabbed, i18n("Configure Chat"), Ok|Default|Apply|Cancel, Ok, parent, 0, modal, true) + : KDialogBase(Plain, i18n("Configure Chat"), Ok|Default|Apply|Cancel, Ok, parent, 0, modal, true) +{ + init(); +} + +KChatDialog::~KChatDialog() +{ + delete d; +} + +void KChatDialog::init() +{ + d = new KChatDialogPrivate; +// d->mTextPage = addPage(i18n("&Messages"));// not a good name - game Messages? + d->mTextPage = plainPage(); + QGridLayout* layout = new QGridLayout(d->mTextPage, 7, 2, KDialog::marginHint(), KDialog::spacingHint()); + +// General fonts + QPushButton* nameFont = new QPushButton(i18n("Name Font..."), d->mTextPage); + connect(nameFont, SIGNAL(pressed()), this, SLOT(slotGetNameFont())); + layout->addWidget(nameFont, 0, 0); + QPushButton* textFont = new QPushButton(i18n("Text Font..."), d->mTextPage); + connect(textFont, SIGNAL(pressed()), this, SLOT(slotGetTextFont())); + layout->addWidget(textFont, 0, 1); + + QFrame* messagePreview = new QFrame(d->mTextPage); + messagePreview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + QHBoxLayout* messageLayout = new QHBoxLayout(messagePreview); + layout->addMultiCellWidget(messagePreview, 1, 1, 0, 1); + + d->mNamePreview = new QLabel(i18n("Player: "), messagePreview); + messageLayout->addWidget(d->mNamePreview, 0); + d->mTextPreview = new QLabel(i18n("This is a player message"), messagePreview); + messageLayout->addWidget(d->mTextPreview, 1); + + layout->addRowSpacing(2, 10); + +// System Message fonts + QLabel* systemMessages = new QLabel(i18n("System Messages - Messages directly sent from the game"), d->mTextPage); + layout->addMultiCellWidget(systemMessages, 3, 3, 0, 1); + QPushButton* systemNameFont = new QPushButton(i18n("Name Font..."), d->mTextPage); + connect(systemNameFont, SIGNAL(pressed()), this, SLOT(slotGetSystemNameFont())); + layout->addWidget(systemNameFont, 4, 0); + QPushButton* systemTextFont = new QPushButton(i18n("Text Font..."), d->mTextPage); + connect(systemTextFont, SIGNAL(pressed()), this, SLOT(slotGetSystemTextFont())); + layout->addWidget(systemTextFont, 4, 1); + + QFrame* systemMessagePreview = new QFrame(d->mTextPage); + systemMessagePreview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + QHBoxLayout* systemMessageLayout = new QHBoxLayout(systemMessagePreview); + layout->addMultiCellWidget(systemMessagePreview, 5, 5, 0, 1); + + d->mSystemNamePreview = new QLabel(i18n("--- Game: "), systemMessagePreview); + systemMessageLayout->addWidget(d->mSystemNamePreview, 0); + d->mSystemTextPreview = new QLabel(i18n("This is a system message"), systemMessagePreview); + systemMessageLayout->addWidget(d->mSystemTextPreview, 1); + +// message count + QLabel* maxMessages = new QLabel(i18n("Maximal number of messages (-1 = unlimited):"), d->mTextPage); + layout->addWidget(maxMessages, 6, 0); + d->mMaxMessages = new QLineEdit(d->mTextPage); + d->mMaxMessages->setText(QString::number(-1)); + layout->addWidget(d->mMaxMessages, 6, 1); +} + +void KChatDialog::slotGetNameFont() +{ + QFont font = nameFont(); + KFontDialog::getFont(font); + setNameFont(font); +} + +void KChatDialog::slotGetTextFont() +{ + QFont font = textFont(); + KFontDialog::getFont(font); + setTextFont(font); +} + +void KChatDialog::slotGetSystemNameFont() +{ + QFont font = systemNameFont(); + KFontDialog::getFont(font); + setSystemNameFont(font); +} + +void KChatDialog::slotGetSystemTextFont() +{ + QFont font = systemTextFont(); + KFontDialog::getFont(font); + setSystemTextFont(font); +} + +QFont KChatDialog::nameFont() const +{ + return d->mNamePreview->font(); +} + +QFont KChatDialog::textFont() const +{ + return d->mTextPreview->font(); +} + +QFont KChatDialog::systemNameFont() const +{ + return d->mSystemNamePreview->font(); +} + +QFont KChatDialog::systemTextFont() const +{ + return d->mSystemTextPreview->font(); +} + +void KChatDialog::plugChatWidget(KChatBase* widget, bool applyFonts) +{ + d->mChat = widget; + if (applyFonts && d->mChat) { + setNameFont(d->mChat->nameFont()); + setTextFont(d->mChat->messageFont()); + setSystemNameFont(d->mChat->systemNameFont()); + setSystemTextFont(d->mChat->systemMessageFont()); + setMaxMessages(d->mChat->maxItems()); + } +} + +void KChatDialog::configureChatWidget(KChatBase* widget) +{ + if (!widget) { + return; + } + widget->setNameFont(nameFont()); + widget->setMessageFont(textFont()); + + widget->setSystemNameFont(systemNameFont()); + widget->setSystemMessageFont(systemTextFont()); + + widget->setMaxItems(maxMessages()); +} + +void KChatDialog::slotOk() +{ + slotApply(); + KDialogBase::slotOk(); +} + +void KChatDialog::slotApply() +{ + configureChatWidget(d->mChat); +} + +void KChatDialog::setNameFont(QFont f) +{ + d->mNamePreview->setFont(f); +} + +void KChatDialog::setTextFont(QFont f) +{ + d->mTextPreview->setFont(f); +} + +void KChatDialog::setSystemNameFont(QFont f) +{ + d->mSystemNamePreview->setFont(f); +} + +void KChatDialog::setSystemTextFont(QFont f) +{ + d->mSystemTextPreview->setFont(f); +} + +void KChatDialog::setMaxMessages(int max) +{ + d->mMaxMessages->setText(QString::number(max)); +} + +int KChatDialog::maxMessages() const +{ + bool ok; + int max = d->mMaxMessages->text().toInt(&ok); + if (!ok) { + return -1; // unlimited is default + } + return max; +} + +#include "kchatdialog.moc" diff --git a/libkdegames/kchatdialog.h b/libkdegames/kchatdialog.h new file mode 100644 index 00000000..96b3eef2 --- /dev/null +++ b/libkdegames/kchatdialog.h @@ -0,0 +1,119 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KCHATDIALOG_H__ +#define __KCHATDIALOG_H__ + +#include +#include + +class KChatBase; + +class KChatDialogPrivate; + +class KDE_EXPORT KChatDialog : public KDialogBase +{ + Q_OBJECT +public: + /** + * Construct a KChatDialog widget + **/ + KChatDialog(QWidget* parent, bool modal = false); + + /** + * Construct a KChatDialog widget which automatically configures the + * @ref KChatBase widget. You probably want to use this as you don't + * have to care about the configuration stuff yourself. + **/ + KChatDialog(KChatBase* chatWidget, QWidget* parent, bool modal = false); + + /** + * Destruct the dialog + **/ + ~KChatDialog(); + + /** + * @return The font that shall be used as the "name: " part of a normal + * message. + **/ + QFont nameFont() const; + + /** + * @return The font that shall be used for normal messages. + **/ + QFont textFont() const; + + /** + * @return The font that shall be used as the "name: " part of a system + * (game) message. + **/ + QFont systemNameFont() const; + + /** + * @return The font that shall be used for a system (game) message. + **/ + QFont systemTextFont() const; + + /** + * Set the widget that will be configured by the dialog. Use this if you + * don't want to configure the widget yourself. + * @param widget The chat widget that shall be configured + * @param Whether you want to have the current @ref KChatBase fonts as + * defaults in the dialog + **/ + void plugChatWidget(KChatBase* widget, bool applyFonts = true); + + /** + * Used to configure the chat widget according to the user settings. + * This is called automatically if @ref plugChatWidget was called + * before. + * @param widget The chat widget that shall be configured + **/ + void configureChatWidget(KChatBase* widget); + + /** + * @return The maximal allowed messages in the chat widget. -1 is + * unlimited + **/ + int maxMessages() const; + +protected slots: + void slotGetNameFont(); + void slotGetTextFont(); + void slotGetSystemNameFont(); + void slotGetSystemTextFont(); + + virtual void slotApply(); + virtual void slotOk(); + +private: + void setNameFont(QFont); + void setTextFont(QFont); + void setSystemNameFont(QFont); + void setSystemTextFont(QFont); + void setMaxMessages(int max); + +private: + void init(); + +private: + KChatDialogPrivate* d; +}; + +#endif diff --git a/libkdegames/kgame/COMPAT b/libkdegames/kgame/COMPAT new file mode 100644 index 00000000..146d3a88 --- /dev/null +++ b/libkdegames/kgame/COMPAT @@ -0,0 +1,55 @@ +06.09.2001: replace the signal signalCreatePlayer by the virtual function + createPlayer. It has the same arguments but the return value + is the new player +06.09.2001: the KGameConfig dialog changes the parameter initConfigs from bool + to long. Use the ConfigOptions to specify what options you want + to have enabled. Default is all +06.09.2001: some int->Q_UINT32 in sender, receiver and player parameters. maybe + more will follow. +06.09.2001: KGameIO::signalPrepareMove(..., bool&) -> + KGameIO::signalPrepareMove(..., bool*): don't know why this was + necessary but it didn't work anymore... +16.09.2001: KGamePropertyHandler uses bool* for the sent parameter now. This is + because QT3 obviously doesn't honor referneces in signals/slots. + This might even be a QT bug. Bad situation - we use references + everywhere in KGame... hope nothing else is affecterd by this + problem (signalPrepareMove was fixed already by me) +18.09.2001: bool* for Key/Mouseevents and IOAdded in kgameio.h too +19.09.2001: Kgame:nextPlayer retunrs the KPlayer *nextplayer instead of bool +19.09.2001: gameOver() renamed to checkGameOver() !!!!! +18.09.2001: Question: Should the signal signalPlayerInput(QDataStream &,KPlayer *)) + be made a virtual function? + MH: This is done now. As this is a central function your programs will + not run anymore. Fix: rename your slot which is connected to the above + signal to playerInput() and return TRUE in it. This will make it 100% + compatible to the old version. I think this chagne is necessary especially + as a signal is of no use here as you cannot read twice from the same stream. + Therefore there can be only one function processing the input. If you really + need a signal, you can of course simply emit it in the overwritten playerInput + function +20.09.2001 playerInputFinished(void->KPlayer *) +--------------------- KGAME_ALPHA_1 --------------------- +06.10.2001 adding KGameNetwork::signalAdminStatusChanged - needed for + KGameDialog +06.10.2001 KGame::loadGame() doesn't call setPolicy() anymore! +08.10.2001 KGamePropertyList now honor policies! Use setPolicy(PolicyDirty) to + get the old behavior! + The behavior of KGamePropertyArray may have changed in this turn, + too! + The API stays the same. +11.10.2001 KGameDialogGeneralConfig now doesn't provide setMin/maxPlayers() + anymore. The game should manage this internally. layout() is + obsolete as well +18.10.2001 KPlayer::signalNetworkData contained QDataStream& instead of const + QByteArray& parameter (oops!). This is fixed now. All apps which + used this signal must be changed. +18.10.2001 KGame::sendProperty(), KGame::sendPlayerProperty(), + KPlayer::sendProperty() and related functions contain a "int msgid" + parameter. This is the id() of the property handler. This parameter + enables us to easily add any number of property handler to a game + just by connecting it to existing send slots and call + processMessage() in slotNetworkData() +03.11.2001 KPlayer::signalNetworkData now emits msgid-KGameMessage::IdUser just + like KGame::signalNetworkData does +06.11.2001 KGameDialog has some small improvements - easier and IMHO better + constructor code. Most code should be compatible :-) diff --git a/libkdegames/kgame/DESIGN b/libkdegames/kgame/DESIGN new file mode 100644 index 00000000..b1c48146 --- /dev/null +++ b/libkdegames/kgame/DESIGN @@ -0,0 +1,407 @@ +This document tries to describe the design of KGame - the KDE multiplayer +library. +This document has been written by: + Andreas Beckermann + M. Heni + Burkhard Lehner + +This document is published under the terms of the GNU FDL + +!!! +Note that this is the initial version of this document and has not yet been +aproved by all core developers (and is far from being complete) +AB: please remove this comments as soon as all KGame hackers have read the +document +!!! + +Please refer the API documentation of every KGame class if you want up tp date +information. + + +0. Contents +----------- + +1. DEFINITIONS +1.1 Message Server +1.2 Client or Message Client +1.3 Master +1.4 Admin +1.5 Server +1.6 Player + +2. Game Negotiation (M.Heni 20.05.2001) + +AB: 3.x is obsolete! +3. Game Properties (Andreas Beckermann 28.07.2001) ( not yet completed ) +3.1 Using KGameProperty +3.2 Custom Classes +3.3 Concepts + +4. KGameIO (Andreas Beckermann 10.08.2001) + +5. Debugging (Andreas Beckermann 06.10.2001) TODO! +5.1 KGameDebugDialog +5.1.1 Debug KGame +5.1.3 Debug Messages + +--------------------------------------------------------------------- +1. DEFINITIONS +-------------- + +First we have to clear some words. The main expressions used in KGame which +need a definition are + +1.1 Message Server +1.2 Client or Message Client +1.3 Master +1.4 Admin +1.5 Server +1.6 Player + +The most important and confusing ones are Master, Admin and Server. We make +quite big differerences between those inside KGame. + +1.1 Message Server: +------------------- +A game has always exactly one object of this class, for local games as well as +for network games. For network games, this object can be on one of the users +processes (usually inside KGame), or it can also be on an independant computer, +that has no idea about what game is played on it. + +A KMessageClient object can connect to it. It's main purpose is transmitting +messages between KMessageClient objects. + +The Message Server is the main communication object. It is represented by the +class KMessageServer. Note that there is also a "Master" and a "Server" which +both differ heavily from the Message Server! + +1.2 Client, Message Client: +--------------------------- +Each process that wants to take part in the game must have a +KMessageClient object, that is connected to the Message Server. KGame creates +this object and connects it to the Messager Server, so that you usually don't +need to create these of your own. Even in a local game (no network) there +must be a message server and one message client connected to it. This is usually +done by the KGame object itself. + +Each message client has a unique ID number (a positive integer value, not zero). +The KMessageClient object, which does the communication with the Message Server +is called "Message Client" and to simplify the use we call any KGame object (or +even the game process) that is connected to a game (i.e. even the Master) just +"Client". + +The main purpose of a Client is to connect to a Master (i.e. to a game) and to +communicate with it. A client has always a KGame object. + +1.3 Master: +----------- +The process that contains the Message Server is called "Master". In any local +game this is the game process. The Message Server is started by KGame using +KGame::setMaster(true) which is automatically done on startup. The Message +Server is deleted automatically as soon as you connect to another Master. +So in most cases there is exactly one KGame object / Client which is Master. But +in one case there can be no KGame object / Client that is Master - if the +Message Server is started as an own process. This "Message-Server-only" process +is called "Master" then, although there is no KGame object which is Master. See +also the definition of Admin! + +1.4 Admin: +---------- +One (and only one) of the Clients is the Admin. He can configure the Message +Server and the game in general in several ways. He can limit the maximum number +of connected message clients and can drop the connection to some other clients, +as well as he can configure game specific ssettings (like max/min players, start +money, ...). The Admin also initializes newly connected Clients. If the Admin +himself disconnects, another Client becomes Admin (The Admin can himself elect +some other Client to become Admin. He himself loses that Admin status then). +An Admin is *alway* a KGame object. The Admin is usually the same as the Master, +but if the Master is an own process (i.e. the Message Server has been started +outside KGame) then Master and Admin differ. An Admin *must* be a KGame object +while the Master doesn't have to be. + +1.5 Server: +----------- +The definition of Server differs quite much from the definition of Master. +A Master just accepts connections and forwards messages. The Server on the other +side checks these messages, calculates results and sends the results to the +Clients. That means the Server does all game calculations and doesn't directly +forward the messages from one Clients to all other Clients. +KGamer makes it possible to write multiplayer games even without a Server. All +Clients just send their moves to the Master which forwards them to all Clients. +Now all Clients calculate the result. +E.g. in a poker game a player selects two of five cards to be exchanges and +clicks on "draw" then the client sends the message "Exchange Card-1 and Card-2" +to the Master. A no-Server solution forwards this to all Clients, and these +Clients exchange the cards of the player. Note that in a no-Server solution +(you can also see it as a "every-Client-is-a-Server solution") all Clients must +have the same random seed and must be of the same version, i.e. the result must +be the same on all Clients. +In a Server-Solution on the other hand the Master forwards the Message +("Exchange Card-1 and Card-2") to the Server only. This Server now calculates +the result, and sends the new cards back to the Client. +Both concepts have advantages and disadvantages. It is on you - the game +developer - to decide which way is better for you. +E.g. the Server-Solution makes it easier for you to write games. The version +must not necessarily be the same, you have one central computer which does the +calcultations. The No-Server-Solution on the other hand decreases network +traffik as the Clients just send their moves and all Clients can calculate the +reactions. I'm sure there are a lot of advantages/disadvantages more for both +concepts. + +1.6 Player: +----------- +A KPlayer object is always connected to a KGame object and represents a +player that participates the game. In a network game, every KPlayer object is +duplicated on every other KGame object connected to the message server with +virtual KPlayer objects. So at every time in the game, every KGame object has +the same number of KPlayer objects. + + +2. Game negotiation +------------------- +Upon connection of a client the admin and the client try to negotiate +the game setup. Basically this means the game of the admin is transferred +(saved) on the client. However, the client's players are added to the game +as far as possible. If the addition of the client's players would add more +players than allowed some players are inactivated. Which players are +inactivated depends on their networkPriority(). This procedure allows +easy replacement of players in a constant number game (e.g. chess). If +this feature is of no interest simply keep the priorities equal (all 0) +and the client will only add only players if the number of players is +less or equal the maximum player number. + +The following is the negotiation procedure as started by the connection +of a client. It is initiated in the negotiateNetworkGame() virtual function +of KGame: + +admin: client: +------------ ------------ +IdSetupGame + QINT16 Library + Version + QINT32 Application + cookie + IdSetupGameContinue; + QValueList player id's + QValueList network priority's + +IdGameLoad + all game data + +IdGameReactivate + QValueList id's + +IdSyncRandom + int randomseed + + +3. Game Properties +------------------ +A very hard task in a network game is consistency. You have to achieve that all +properties of the game and of all players have the same value on all clients +every time. This is because +a) the user might be confused if he sees "Player has $0" on client A but +"Player has $10" on client B and +b) Often game handling depends on those values, e.g. if the example above +happens the computer might quit the game for the Player on client A because +he/she doesn't have enough money. But the game continues on client B. +Another not that easy task is the network protocol itself. You have to write +several send() and receive() functions which apply changed values of properties +to the local property. + +KGameProperty is designed to do all of this for you. KGameProperty is +implemented as a template so you can use it theoretically for every type of data +- even for your self defined classes. + + +3.1 Using KGameProperty +----------------------- +It is basically very easy to use a KGameProperty. You first create your own +class containing the property, e.g: +class MyGame : public KGame +{ +[...] +protected: + KGamePropertyInt money; + KGamePropertyQString name; + KGameProperty myProperty; +}; +KGamePropertyInt is just a typedef for KGameProperty - just like +KGamePropertyQString. Now you need to register the properties in the constructor +of the class to the KGamePropertyHandler: +MyGame::MyGame() : KGame(myCookie) +{ + money.registerData(KGamePropertyBase::IdUser+1, dataHandler(), "Money"); + name.registerData(KGamePropertyBase::IdUser+2, this, "Name"); + myProperty.registerData(KGamePropertyBase::IdUser+3, dataHandler(), "MyProperty"); +} +-> You need to specify a *unique* ID. This ID must be greater than +KGamePropertyBase::IdUser. IDs below this are reserved for KGame. Probably this +will be changed so that you cannot use IDs below IdUser in the future. Then you +have to specify the dataHandler(). You can also use a KGame or KPlayer pointer. +This will automatically use KGame::dataHandler() or KPlayer::dataHandler(). +Finally you *can* provide a name for the property. This will be used for +debugging in KGameDebugDialog. If you want to save some memory you can leave +this out. +Note that if you use pointers to create the properties dynamically they are +*not* deleted automatically! You MUST delete them yourself! +Now you can use the KGameProperty like every variable else. See also Section +"3.3 Concepts" for restrictions in use. + +3.2 Custom Classes +------------------ +To make custom classes possible you have to implement several operators for your +them: you need at least << and >> for QDataStream as well as "==" for your own +class. To overload the "<<" you would e.g. do something like this: +QDataStream& operator<<(QDataStream& stream, MyData& data) +{ + int type = data.type; + QString name = data.name; + stream << type << name; + return stream; +} +So you basically just have to split your class to several basic types and stream +them. + +3.3 Concepts +------------ +You can use KGameProperty basically in two completely different ways. You can +also use a mixture of both but this is not recommended. The default behaviour +and therefore also the recommended is the "clean" way: +a) Always Consistent. This means that a KGameProperty has always the same value +on *every* client. This is achieved by using KGameProperty::send() whenever you +want to change the value using "=". You can still use changeValue() or +setLocal() but send() will be the default. If you use send() then the value of +the property does *NOT* change immediately. It is just sent to the +KMessageServer which forwards the value to all clients. As soon as the new value +is received from the message server the KGamePropertyHandler (a collection class +for KGameProperty) calls KGameProperty::load() and changes the value of the +property. So the game first has to go into the event loop, where the message is +received. This means to you that you cannot do this: +myIntProperty = 10; +int value = myIntProperty; +As myIntPoperty still has the old value when "value = myIntProperty" is called. +This might seem to be quite complex, but +KGamePropertyHandler::signalPropertyChanged() is emitted whenever a new value is +assigned so you can connect to this and work immediately with the new value. +You gain the certainty that the value is the same on every client every time. +That will safe you a lot of time debugging! +Another way is the "dirty" way: +b) Not Always Consistent. Sometimes you really *want* to do something like +myIntProperty = 10; +int value = myIntProperty; +but this is not possible with the default behaviour. If you call +KGameProperty::setAlwaysConsistent(false) in the constructor (right after +registerData()) you get another behaviour. "=" means changeValue() now. +changeValue() also uses send() to change the value but additionally calls +setLocal() to create a local copy of the property. This copy now has the value +you supplied with "=" and is deleted again as soon as any value from the network +is received. + +4. KGameIO +---------- +The class KGameIO is used to let the players communicate with the server. You +can plug as many KGameIO objects into a player as you want, e.g. you can plug a +KGameMouseIO and a KGameKeyIO into a player so that you can control the player +with the mouse and the keyboard - e.g. in a breakout game. +You can probably see the advantage: as most of the control stuff is common in a +lot of games you can use the same IO class in many different games with very +small adjustments. +You could also put all the IO stuff directly into your KPlayer object, like +sendBet(int money) for a poker game. But there is a major disadvantage and I'm +very sure you don't want to use a KPlayer object for your IO stuff as soon as +you know which disadvantage: +KGameIO is designed to be able to switch between different IOs "on the fly". So +you might have a KGamePlayerIO, derived from KGameIO, for your game. But now +this player (who "owns"/uses the KGamePlayerIO) leaves the game (e.g. because he +was a remote player). So now the game would be over for every player as one +player is now out of order. But with KGameIO you can just let any of the +remaining clients create a KGameComputerIO and plug this into the player. So the +player now is controlled by the computer and the game can continue. + +Think about it! You don't have to care about removing players when a player +leaves as you can just replace it! The same works the other way round: imagine a +game with 10 player (e.g. 5 human and 5 computer players) that has already +started. You cannot add any further players without restarting. So if there are +any additional player you can just call KPlayer::removeGameIO() which removes +the IO of a computer player and then call KPlayer::addGameIO() for the same +player which adds a GameIO for new human player. That's all! + +To achieve this you just have to make sure that you make *all* of your IO +operations through a KGameIO! So instead of using MyPlayer::sendBet(int money) +you should use something like MyIO::sendBet(). The amount of money would +probably be calculated by the game IO itself. + + + +5. Debugging +------------ +The general debugging concept (if there is one at all) or general debugging +hints are not yet written. Feel free to do so + +5.1 KGameDebugDialog +-------------------- +A nice way of debugging a KGame based game is the KGameDebugDialog. Basically +all you have to do is to add something like "Debug" to your game's menu and add +a slot like +slotDebug() +{ + KGameDebugDialog* dialog = new KGameDebugDialog(mGame, this); + connect(dialog, SIGNAL(finished()), dialog, SLOT(slotDelayedDestruct())); + dialog->show(); +} +that's it. +You can now click on that menu entry and you get a non-modal dialog where you +can start to debug :-) +The dialog consist of several pages. You can easily add your own using +KDialogBase::addVBoxPage() (for example). + +5.1.1 Debug KGame +----------------- +The first page, "Debug KGame" shows on the left most or even all status values of +KGame. That contains e.g. minPlayers(), isAdmin(), gameStatus(), ... +The right side is probably the more important one. It lists *all* KGameProperties +which have been inserted to this KGame object (only to this KGame object - not +the ones that have been added to the players!). Most of the status variables of +the left side are here again as they are implemented as KGameProperty. You can +see the name of the property (together with its ID), its value and the policy +this property uses. Note that "unknwon" will be displayed as name of the +property if you haven't supplied one. See KGamePropertyBase::registerData() for +info. You probably always want to supply a name for the property to debug it +easily. In the future there will be something like +KGamePropertyHandler::setDebug() so that you can switch off debugging and save +the memory of the names in a release version. +For as long as you use standard types for your properties (int, long, bool, +...) you should always be able to see the value of the property. If you just see +"unknown" then this type has not been implemented. You can connect to the signal +KGamePropertyHandler::signalRequestValue() and supply a QString with the value +yourself. If you do so for a standard type please also submit a bug report! + +Currently the dialog does *not* update automatically! So you alway have to click +the "update" button when you want a current value. There are several reasons for +this (and one of them is that i'm too lazy to implement the update ;)). E.g. +often (very often) a property is just in the background - stores e.g. the +available money in a game. But you don't want it to update whenever the value +changes (a player receives/pays money) but only when the value on the screen +changes. + +5.1.2 Debug Players +------------------- +This page consists of three widgets. On the very left there is a list of all +players in the game. Only the IDs are displayed to save space. If you click one +the other widgets are filled with content. These widgets are quite much the same +as the ones in "Debug KGame" - the left shows the value of the functions and the +right one displays all KProperties of a player. Not much to say here - except: +See "Debug KGame". + +If you change to another player the value are also updated. + +5.1.3 Debug Messages +-------------------- +This page is probably not as important as the other ones. It displays *every* +message that is sent through the KGame object. As a KGameProperry also send +messages you probably get a lot of them... +You can exclude message IDs from being displayed (e.g. all game properties). +You can also change the sorting of the list to see all messages of a certain ID. +The default is to sort by time (which is displayed on the left side). + diff --git a/libkdegames/kgame/Makefile.am b/libkdegames/kgame/Makefile.am new file mode 100644 index 00000000..e0117780 --- /dev/null +++ b/libkdegames/kgame/Makefile.am @@ -0,0 +1,29 @@ + +noinst_LTLIBRARIES = libkgame.la + +# compile-order doesn't matter here but maybe we will split these section soon + +KGAME = kgame.cpp kplayer.cpp kgamenetwork.cpp kgameproperty.cpp \ + kgamemessage.cpp kgameio.cpp kgameprocess.cpp kgamechat.cpp \ + kgamepropertyhandler.cpp kgameerror.cpp kgamesequence.cpp +KGAME_H = kgame.h kplayer.h kgamenetwork.h kgameproperty.h kgamemessage.h \ + kgameio.h kgameprocess.h kgamepropertyarray.h \ + kgamepropertylist.h kgamechat.h kgamepropertyhandler.h \ + kgameerror.h kgamesequence.h kgameversion.h + +KMESSAGE = kmessageio.cpp kmessageserver.cpp kmessageclient.cpp +KMESSAGE_H = kmessageio.h kmessageserver.h kmessageclient.h + +libkgameincludedir=$(includedir)/kgame +libkgame_la_SOURCES = $(KMESSAGE) $(KGAME) + +libkgameinclude_HEADERS = $(KMESSAGE_H) $(KGAME_H) + +INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes) +METASOURCES = AUTO + +SUBDIRS = . dialogs + +messages: +# $(XGETTEXT) `find . -name \*.h -o -name \*.cpp -o -name \*.cc` -o $(podir)/libkdegames.pot + diff --git a/libkdegames/kgame/README.LIB b/libkdegames/kgame/README.LIB new file mode 100644 index 00000000..512edbac --- /dev/null +++ b/libkdegames/kgame/README.LIB @@ -0,0 +1,12 @@ +some thoughts and comments about the lib - usually for KGame hackers + +- setMin/MaxPlayers() etc. use KGameProperty::changeValue() which is slightly + unclean but as these functions can only called by the ADMIN it doesn't matter. +- AB: KGamePropertyList && KGamePropertyArray: + for PolicyClean||PolicyDirty the values are streamed into a QDataStream as usual + for PolicyDirty||PolicyLocal the values are streamed as well but + additionally command() is called immediately. The values are read from + the stream there. This is some kind of performance loss as it would be + faster *not* to stream it but imediately call e.g. insert(). But it will + probably save a *lot* of bugs! + diff --git a/libkdegames/kgame/TODO b/libkdegames/kgame/TODO new file mode 100644 index 00000000..2f100b8a --- /dev/null +++ b/libkdegames/kgame/TODO @@ -0,0 +1,41 @@ +- 28.02.2001: Direct computer player for kpoker like games support needs to be + improved. UPDATE (01/10/06): but what is needed there? +- 05.03.2001: Documentation. I am thinking of an explaination of the + class + methods and example code for the "key" methods. A sample + implementation in a small game (like the current kdenonbeta/kgame + but with a real sense - mabye use the QT tic-tac-toe example?) + would be very great (this could be stuff of a tutorial instead of + KGame documentation) + MH: Even better idea +- 03.06.2001: can KGameNetwork::sendSystemMessage be made protected (maybe using + friends)? sendSystenMessage AND sendMessage is very confusing to + the user... +- 03.06.2001: can we translate the group of a KPlayer? Probably not as there are + no international connections possible then... maybe a group id? +- 05.06.2001: KGameDialog::saveConfig(KConfig*) might be useful (as well as + KGameDialog::loadConfig(KConfig*). Should set an own group in the + config file (setGroup("KGameDialog")). Problem: shalll network + settings be saved? Could be used for startup configuration (i.e. + load the config of the previous game) otherwise. +- 21.06.2001: KPlayerPropertyArray does not yet support at() and operator[] + assignments. Need to check whether the method from QBitArray + can be applied +- 02.04.2001: VERY DANGEROUS: property1=property2 does NOT assign the values, e.g. int + but assignes the whole property, i.e. you have then two properties with + the same id and everything is wrong + 01/09/09: FIXED! (AB) TODO: check if this behavior also appears in + KGamePropertyList and KGamePropertyArray. Althogh this should not + be the case +- 23.09.2001: does the virtual destructor make sense for KGamePropertyBase? +- 29.09.2001: GGZ integration. I (Andi) already volunteered for this - it's just + here so that I don't forget it +- 06.10.2001: add KGamePropertyHandler::setDebug(false) to clear all debug names + (and to not accept new names) of KGameProperty. Will save some + memory +- 06.10.2001: If one kicks a player (in KGameDialog) the client should be kicked + as well. Perhaps always disconnect a client when all players from + it have disappeared? +- 07.10.2001: display (List) or (Array) for KGameProperty[List|Array] in + KGameDebugDialog as value +- 08.10.2001: KGamePropertyList|KGamePropertyArray must be ported to new QT3 API + (e.g. erase instead of remove, ...) diff --git a/libkdegames/kgame/dialogs/Makefile.am b/libkdegames/kgame/dialogs/Makefile.am new file mode 100644 index 00000000..1b9de53c --- /dev/null +++ b/libkdegames/kgame/dialogs/Makefile.am @@ -0,0 +1,17 @@ + +noinst_LTLIBRARIES = libkgamedialogs.la + +# compile-order doesn't matter here but maybe we will split these section soon + + +libkgamedialogs_la_SOURCES = kgamedialog.cpp kgameconnectdialog.cpp kgameerrordialog.cpp kgamedebugdialog.cpp kgamedialogconfig.cpp + +libkgamedialogsincludedir=$(includedir)/kgame +libkgamedialogsinclude_HEADERS = kgamedialog.h kgameconnectdialog.h kgameerrordialog.h kgamedebugdialog.h kgamedialogconfig.h + +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/kgame $(all_includes) +METASOURCES = AUTO + +messages: +# $(XGETTEXT) `find . -name \*.h -o -name \*.cpp -o -name \*.cc` -o $(podir)/libkdegames.pot + diff --git a/libkdegames/kgame/dialogs/kgameconnectdialog.cpp b/libkdegames/kgame/dialogs/kgameconnectdialog.cpp new file mode 100644 index 00000000..4d2d90e0 --- /dev/null +++ b/libkdegames/kgame/dialogs/kgameconnectdialog.cpp @@ -0,0 +1,278 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#include "kgameconnectdialog.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class KGameConnectWidgetPrivate +{ + public: + KGameConnectWidgetPrivate() + { + mPort = 0; + mHost = 0; + mButtonGroup = 0; + mBrowser = 0; + } + + KIntNumInput* mPort; + QLineEdit* mHost; //KLineEdit? + QVButtonGroup* mButtonGroup; + QComboBox *mClientName; + QLabel *mClientNameLabel; + DNSSD::ServiceBrowser *mBrowser; + QLabel *mServerNameLabel; + QLineEdit *mServerName; + QString mType; +}; + +KGameConnectWidget::KGameConnectWidget(QWidget* parent) : QWidget(parent) +{ + d = new KGameConnectWidgetPrivate; + + QVBoxLayout* vb = new QVBoxLayout(this, KDialog::spacingHint()); + d->mButtonGroup = new QVButtonGroup(this); + vb->addWidget(d->mButtonGroup); + connect(d->mButtonGroup, SIGNAL(clicked(int)), this, SLOT(slotTypeChanged(int))); + (void)new QRadioButton(i18n("Create a network game"), d->mButtonGroup); + (void)new QRadioButton(i18n("Join a network game"), d->mButtonGroup); + + QGrid* g = new QGrid(2, this); + vb->addWidget(g); + g->setSpacing(KDialog::spacingHint()); + d->mServerNameLabel = new QLabel(i18n("Game name:"), g); + d->mServerName = new QLineEdit(g); + d->mClientNameLabel = new QLabel(i18n("Network games:"), g); + d->mClientName = new QComboBox(g); + connect(d->mClientName,SIGNAL(activated(int)),SLOT(slotGameSelected(int))); + (void)new QLabel(i18n("Port to connect to:"), g); + d->mPort = new KIntNumInput(g); + (void)new QLabel(i18n("Host to connect to:"), g); + d->mHost = new QLineEdit(g); + + QPushButton *button=new QPushButton(i18n("&Start Network"), this); + connect(button, SIGNAL(clicked()), this, SIGNAL(signalNetworkSetup())); + vb->addWidget(button); + // Hide until type is set + d->mClientName->hide(); + d->mClientNameLabel->hide(); + d->mServerName->hide(); + d->mServerNameLabel->hide(); +} + +void KGameConnectWidget::showDnssdControls() +{ + if (!d->mBrowser) return; + if (d->mHost->isEnabled()) { // client + d->mClientName->show(); + d->mClientNameLabel->show(); + d->mServerName->hide(); + d->mServerNameLabel->hide(); + slotGameSelected(d->mClientName->currentItem()); + } else { + d->mClientName->hide(); + d->mClientNameLabel->hide(); + d->mServerName->show(); + d->mServerNameLabel->show(); + } +} + +void KGameConnectWidget::setType(const QString& type) +{ + d->mType = type; + delete d->mBrowser; + d->mBrowser = new DNSSD::ServiceBrowser(type); + connect(d->mBrowser,SIGNAL(finished()),SLOT(slotGamesFound())); + d->mBrowser->startBrowse(); + showDnssdControls(); +} + +void KGameConnectWidget::slotGamesFound() +{ + bool autoselect=false; + if (!d->mClientName->count()) autoselect=true; + d->mClientName->clear(); + QStringList names; + QValueList::ConstIterator itEnd = d->mBrowser->services().end(); + for (QValueList::ConstIterator it = d->mBrowser->services().begin(); + it!=itEnd; ++it) names << (*it)->serviceName(); + d->mClientName->insertStringList(names); + if (autoselect && d->mClientName->count()) slotGameSelected(0); +} + +void KGameConnectWidget::setName(const QString& name) +{ + d->mServerName->setText(name); +} + +QString KGameConnectWidget::gameName() const +{ + return d->mServerName->text(); +} + +QString KGameConnectWidget::type() const +{ + return d->mType; +} + +void KGameConnectWidget::slotGameSelected(int nr) +{ + if (nr>=(d->mBrowser->services().count()) || nr<0) return; + if (!d->mHost->isEnabled()) return; // this is server mode, do not overwrite host and port controls + DNSSD::RemoteService::Ptr srv = d->mBrowser->services()[nr]; + if (!srv->isResolved() && !srv->resolve()) return; + d->mHost->setText(srv->hostName()); + d->mPort->setValue(srv->port()); +} +KGameConnectWidget::~KGameConnectWidget() +{ + delete d->mBrowser; + delete d; +} + +QString KGameConnectWidget::host() const +{ + if (d->mHost->isEnabled()) { + return d->mHost->text(); + } else { + return QString::null; + } +} + +unsigned short int KGameConnectWidget::port() const +{ + return d->mPort->value(); +} + +void KGameConnectWidget::setHost(const QString& host) +{ + d->mHost->setText(host); +} + +void KGameConnectWidget::setPort(unsigned short int port) +{ + d->mPort->setValue(port); +} + +void KGameConnectWidget::setDefault(int state) +{ + d->mButtonGroup->setButton(state); + slotTypeChanged(state); +} + +void KGameConnectWidget::slotTypeChanged(int t) +{ + if (t == 0) { + d->mHost->setEnabled(false); + } else if (t == 1) { + d->mHost->setEnabled(true); + } + showDnssdControls(); + emit signalServerTypeChanged(t); +} + +class KGameConnectDialogPrivate +{ + public: + KGameConnectDialogPrivate() + { + mConnect = 0; + } + + KGameConnectWidget* mConnect; +}; + +// buttonmask =Ok|Cancel +KGameConnectDialog::KGameConnectDialog(QWidget* parent,int buttonmask) : KDialogBase(Plain, + i18n("Network Game"),buttonmask , Ok, parent, 0, true, buttonmask!=0) +{ + d = new KGameConnectDialogPrivate; + QVBoxLayout* vb = new QVBoxLayout(plainPage(), spacingHint()); + d->mConnect = new KGameConnectWidget(plainPage()); + vb->addWidget(d->mConnect); +} + +KGameConnectDialog::~KGameConnectDialog() +{ + delete d; +} + +int KGameConnectDialog::initConnection( unsigned short int& port, + QString& host, QWidget* parent, bool server) +{ + KGameConnectDialog d(parent); + d.setHost(host); + d.setPort(port); + if (server) { + d.setDefault(0); + } else { + d.setDefault(1); + } + + int result = d.exec(); + if (result == QDialog::Accepted) { + host = d.host(); + port = d.port(); + } + return result; +} + +QString KGameConnectDialog::host() const +{ + return d->mConnect->host(); +} + +unsigned short int KGameConnectDialog::port() const +{ + return d->mConnect->port(); +} + +void KGameConnectDialog::setHost(const QString& host) +{ + d->mConnect->setHost(host); +} + +void KGameConnectDialog::setPort(unsigned short int port) +{ + d->mConnect->setPort(port); +} + +void KGameConnectDialog::setDefault(int state) +{ + d->mConnect->setDefault(state); +} + + + +#include "kgameconnectdialog.moc" + diff --git a/libkdegames/kgame/dialogs/kgameconnectdialog.h b/libkdegames/kgame/dialogs/kgameconnectdialog.h new file mode 100644 index 00000000..acbf21d2 --- /dev/null +++ b/libkdegames/kgame/dialogs/kgameconnectdialog.h @@ -0,0 +1,169 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KGAMECONNECTDIALOG_H__ +#define __KGAMECONNECTDIALOG_H__ + +#include + +class KGameConnectDialogPrivate; +class KGameConnectWidgetPrivate; + +class KGameConnectWidget : public QWidget +{ + Q_OBJECT +public: + KGameConnectWidget(QWidget* parent); + virtual ~KGameConnectWidget(); + + /** + * @param host The host to connect to by default + **/ + void setHost(const QString& host); + + /** + * @return The host to connect to or QString::null if the user wants to + * be the MASTER + **/ + QString host() const; + + /** + * @param port The port that will be shown by default + **/ + void setPort(unsigned short int port); + + /** + * @return The port to connect to / to listen + **/ + unsigned short int port() const; + + /** + * Specifies which state is the default (0 = server game; 1 = join game) + * @param state The default state. 0 For a server game, 1 to join a game + **/ + void setDefault(int state); + + /** + * Sets DNS-SD service type, both for publishing and browsing + * @param type Service type (something like _kwin4._tcp). + * It should be unique for application. + * @since 3.4 + **/ + void setType(const QString& type); + + /** + * @return service type + */ + QString type() const; + + /** + * Set game name for publishing. + * @param name Game name. Important only for server mode. If not + * set hostname will be used. In case of name conflict -2, -3 and so on will be added to name. + */ + void setName(const QString& name); + + /** + * @return game name. + */ + QString gameName() const; + +protected slots: + /** + * The type has changed, ie the user switched between creating or + * joining. + **/ + void slotTypeChanged(int); + void slotGamesFound(); + void slotGameSelected(int); + +signals: + void signalNetworkSetup(); + void signalServerTypeChanged(int); + +private: + void showDnssdControls(); + KGameConnectWidgetPrivate* d; + +}; + +/** + * @short Dialog to ask for host and port + * + * This Dialog is used to create a game. You call initConnection(port, + * QString::null, parent, true) to create a network game (as a server) + * or initConnection(port, host, parent) to join a network game. + * + * @author Andreas Beckermann + **/ +class KGameConnectDialog : public KDialogBase +{ + Q_OBJECT +public: + KGameConnectDialog(QWidget* parent = 0,int buttonmask=Ok|Cancel); + virtual ~KGameConnectDialog(); + + /** + * Shows a dialog to either connect to an existing game or to create a + * server game, depending on user's choice. + * @param port The port the user wants to connect to. + * @param host The host the user wants to connect to. Will be + * QString::null if server game is chosen + * @param parent The parent of the dialog + * @param server True to create a network game per default, false to + * join a game by default + **/ + static int initConnection(unsigned short int& port, QString& host, QWidget* parent, bool server = false); + + /** + * @param host The host to connect to by default + **/ + void setHost(const QString& host); + + /** + * @return The host to connect to or QString::null if the user wants to + * be the MASTER + **/ + QString host() const; + + /** + * @param port The port that will be shown by default + **/ + void setPort(unsigned short int port); + + /** + * @return The port to connect to / to listen + **/ + unsigned short int port() const; + + /** + * Specifies which state is the default (0 = server game; 1 = join game) + * @param state The default state. 0 For a server game, 1 to join a game + **/ + void setDefault(int state); + +signals: + void signalNetworkSetup(); + +private: + KGameConnectDialogPrivate* d; +}; + +#endif diff --git a/libkdegames/kgame/dialogs/kgamedebugdialog.cpp b/libkdegames/kgame/dialogs/kgamedebugdialog.cpp new file mode 100644 index 00000000..b112b04c --- /dev/null +++ b/libkdegames/kgame/dialogs/kgamedebugdialog.cpp @@ -0,0 +1,548 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kgamedebugdialog.h" + +#include "kgamemessage.h" +#include "kgame.h" +#include "kplayer.h" +#include "kgamepropertyhandler.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + + +class KGameDebugDialogPrivate +{ +public: + KGameDebugDialogPrivate() + { + mGame = 0; + + mGamePage = 0; + mGameProperties = 0; + mGameAddress = 0; + mGameId = 0; + mGameCookie = 0; + mGameMaster = 0; + mGameAdmin = 0; + mGameOffering = 0; + mGameStatus = 0; + mGameRunning = 0; + mGameMaxPlayers = 0; + mGameMinPlayers = 0; + mGamePlayerCount = 0; + + mPlayerPage = 0; + mPlayerList = 0; + mPlayerProperties = 0; + mPlayerAddress = 0; + mPlayerId = 0; + mPlayerName = 0; + mPlayerGroup = 0; + mPlayerUserId = 0; + mPlayerMyTurn = 0; + mPlayerAsyncInput= 0; + mPlayerKGameAddress = 0; + mPlayerVirtual = 0; + mPlayerActive = 0; + mPlayerRtti = 0; + mPlayerNetworkPriority = 0; + + mMessagePage = 0; + mMessageList = 0; + mHideIdList = 0; + } + + const KGame* mGame; + + QFrame* mGamePage; + KListView* mGameProperties; + QListViewItem* mGameAddress; + QListViewItem* mGameId; + QListViewItem* mGameCookie; + QListViewItem* mGameMaster; + QListViewItem* mGameAdmin; + QListViewItem* mGameOffering; + QListViewItem* mGameStatus; + QListViewItem* mGameRunning; + QListViewItem* mGameMaxPlayers; + QListViewItem* mGameMinPlayers; + QListViewItem* mGamePlayerCount; + + QFrame* mPlayerPage; + KListBox* mPlayerList; + KListView* mPlayerProperties; + QListViewItem* mPlayerAddress; + QListViewItem* mPlayerId; + QListViewItem* mPlayerName; + QListViewItem* mPlayerGroup; + QListViewItem* mPlayerUserId; + QListViewItem* mPlayerMyTurn; + QListViewItem* mPlayerAsyncInput; + QListViewItem* mPlayerKGameAddress; + QListViewItem* mPlayerVirtual; + QListViewItem* mPlayerActive; + QListViewItem* mPlayerRtti; + QListViewItem* mPlayerNetworkPriority; + + QFrame* mMessagePage; + KListView* mMessageList; + KListBox* mHideIdList; +}; + +KGameDebugDialog::KGameDebugDialog(KGame* g, QWidget* parent, bool modal) : + KDialogBase(Tabbed, i18n("KGame Debug Dialog"), Close, Close, + parent, 0, modal, true) +{ + d = new KGameDebugDialogPrivate; + + initGamePage(); + initPlayerPage(); + initMessagePage(); + + setKGame(g); +} + +KGameDebugDialog::~KGameDebugDialog() +{ + delete d; +} + +void KGameDebugDialog::initGamePage() +{ + d->mGamePage = addPage(i18n("Debug &KGame")); + QVBoxLayout* topLayout = new QVBoxLayout(d->mGamePage, marginHint(), spacingHint()); + QHBoxLayout* layout = new QHBoxLayout(topLayout); + + KListView* v = new KListView(d->mGamePage); + v->addColumn(i18n("Data")); + v->addColumn(i18n("Value")); + layout->addWidget(v); + + d->mGameProperties = new KListView(d->mGamePage); + d->mGameProperties->addColumn(i18n("Property")); + d->mGameProperties->addColumn(i18n("Value")); + d->mGameProperties->addColumn(i18n("Policy")); + layout->addWidget(d->mGameProperties); + + QPushButton* b = new QPushButton(i18n("Update"), d->mGamePage); + connect(b, SIGNAL(pressed()), this, SLOT(slotUpdateGameData())); + topLayout->addWidget(b); + +// game data + d->mGameAddress = new QListViewItem(v, i18n("KGame Pointer")); + d->mGameId = new QListViewItem(v, i18n("Game ID")); + d->mGameCookie = new QListViewItem(v, i18n("Game Cookie")); + d->mGameMaster = new QListViewItem(v, i18n("Is Master")); + d->mGameAdmin = new QListViewItem(v, i18n("Is Admin")); + d->mGameOffering = new QListViewItem(v, i18n("Is Offering Connections")); + d->mGameStatus = new QListViewItem(v, i18n("Game Status")); + d->mGameRunning = new QListViewItem(v, i18n("Game is Running")); + d->mGameMaxPlayers = new QListViewItem(v, i18n("Maximal Players")); + d->mGameMinPlayers = new QListViewItem(v, i18n("Minimal Players")); + d->mGamePlayerCount = new QListViewItem(v, i18n("Players")); +} + +void KGameDebugDialog::initPlayerPage() +{ + d->mPlayerPage = addPage(i18n("Debug &Players")); + QVBoxLayout* topLayout = new QVBoxLayout(d->mPlayerPage, marginHint(), spacingHint()); + QHBoxLayout* layout = new QHBoxLayout(topLayout); + + //TODO: connect to the KGame signals for joined/removed players!!! + QVBoxLayout* listLayout = new QVBoxLayout(layout); + QLabel* listLabel = new QLabel(i18n("Available Players"), d->mPlayerPage); + listLayout->addWidget(listLabel); + d->mPlayerList = new KListBox(d->mPlayerPage); + connect(d->mPlayerList, SIGNAL(executed(QListBoxItem*)), this, SLOT(slotUpdatePlayerData(QListBoxItem*))); + listLayout->addWidget(d->mPlayerList); + d->mPlayerList->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); + + KListView* v = new KListView(d->mPlayerPage); + layout->addWidget(v); + v->addColumn(i18n("Data")); + v->addColumn(i18n("Value")); + + d->mPlayerProperties = new KListView(d->mPlayerPage); + d->mPlayerProperties->addColumn(i18n("Property")); + d->mPlayerProperties->addColumn(i18n("Value")); + d->mPlayerProperties->addColumn(i18n("Policy")); + layout->addWidget(d->mPlayerProperties); + + QPushButton* b = new QPushButton(i18n("Update"), d->mPlayerPage); + connect(b, SIGNAL(pressed()), this, SLOT(slotUpdatePlayerList())); + topLayout->addWidget(b); + + d->mPlayerAddress = new QListViewItem(v, i18n("Player Pointer")); + d->mPlayerId = new QListViewItem(v, i18n("Player ID")); + d->mPlayerName = new QListViewItem(v, i18n("Player Name")); + d->mPlayerGroup = new QListViewItem(v, i18n("Player Group")); + d->mPlayerUserId = new QListViewItem(v, i18n("Player User ID")); + d->mPlayerMyTurn = new QListViewItem(v, i18n("My Turn")); + d->mPlayerAsyncInput = new QListViewItem(v, i18n("Async Input")); + d->mPlayerKGameAddress = new QListViewItem(v, i18n("KGame Address")); + d->mPlayerVirtual = new QListViewItem(v, i18n("Player is Virtual")); + d->mPlayerActive = new QListViewItem(v, i18n("Player is Active")); + d->mPlayerRtti = new QListViewItem(v, i18n("RTTI")); + d->mPlayerNetworkPriority = new QListViewItem(v, i18n("Network Priority")); +} + +void KGameDebugDialog::initMessagePage() +{ + d->mMessagePage = addPage(i18n("Debug &Messages")); + QGridLayout* layout = new QGridLayout(d->mMessagePage, 11, 7, marginHint(), spacingHint()); + d->mMessageList = new KListView(d->mMessagePage); + layout->addMultiCellWidget(d->mMessageList, 0, 9, 0, 3); + d->mMessageList->addColumn(i18n("Time")); + d->mMessageList->addColumn(i18n("ID")); + d->mMessageList->addColumn(i18n("Receiver")); + d->mMessageList->addColumn(i18n("Sender")); + d->mMessageList->addColumn(i18n("ID - Text")); + + QPushButton* hide = new QPushButton(i18n("&>>"), d->mMessagePage); + connect(hide, SIGNAL(pressed()), this, SLOT(slotHideId())); + layout->addWidget(hide, 4, 4); + + QPushButton* show = new QPushButton(i18n("&<<"), d->mMessagePage); + connect(show, SIGNAL(pressed()), this, SLOT(slotShowId())); + layout->addWidget(show, 6, 4); + + QLabel* l = new QLabel(i18n("Do not show IDs:"), d->mMessagePage); + layout->addMultiCellWidget(l, 0, 0, 5, 6); + d->mHideIdList = new KListBox(d->mMessagePage); + layout->addMultiCellWidget(d->mHideIdList, 1, 8, 5, 6); + + QPushButton* clear = new KPushButton(KStdGuiItem::clear(), d->mMessagePage); + connect(clear, SIGNAL(pressed()), this, SLOT(slotClearMessages())); + layout->addMultiCellWidget(clear, 10, 10, 0, 6); + //TODO: "show all but..." and "show nothing but..." +} + +void KGameDebugDialog::clearPlayerData() +{ + d->mPlayerAddress->setText(1, ""); + d->mPlayerId->setText(1, ""); + d->mPlayerName->setText(1, ""); + d->mPlayerGroup->setText(1, ""); + d->mPlayerUserId->setText(1, ""); + d->mPlayerMyTurn->setText(1, ""); + d->mPlayerAsyncInput->setText(1, ""); + d->mPlayerKGameAddress->setText(1, ""); + d->mPlayerVirtual->setText(1, ""); + d->mPlayerActive->setText(1, ""); + d->mPlayerRtti->setText(1, ""); + d->mPlayerNetworkPriority->setText(1, ""); + + d->mPlayerProperties->clear(); +} + +void KGameDebugDialog::clearGameData() +{ + d->mGameAddress->setText(1, ""); + d->mGameId->setText(1, ""); + d->mGameCookie->setText(1, ""); + d->mGameMaster->setText(1, ""); + d->mGameAdmin->setText(1, ""); + d->mGameOffering->setText(1, ""); + d->mGameStatus->setText(1, ""); + d->mGameRunning->setText(1, ""); + d->mGameMaxPlayers->setText(1, ""); + d->mGameMinPlayers->setText(1, ""); + + d->mGameProperties->clear(); +} + +void KGameDebugDialog::slotUpdatePlayerData() +{ + if (!d->mGame || d->mPlayerList->currentItem() == -1) { + return; + } + slotUpdatePlayerData(d->mPlayerList->item(d->mPlayerList->currentItem())); +} + +void KGameDebugDialog::slotUpdatePlayerList() +{ + QListBoxItem* i = d->mPlayerList->firstItem(); + for (; i; i = d->mPlayerList->firstItem()) { + removePlayer(i); + } + + QPtrList list = *d->mGame->playerList(); + for (KPlayer* p = list.first(); p; p = list.next()) { + addPlayer(p); + } +} + +void KGameDebugDialog::slotUpdateGameData() +{ + if (!d->mGame) { + d->mGameAddress->setText(1, i18n("NULL pointer")); + return; +} + + clearGameData(); + + QString buf; + buf.sprintf("%p", d->mGame); + d->mGameAddress->setText(1, buf); + d->mGameId->setText(1, QString::number(d->mGame->gameId())); + d->mGameCookie->setText(1, QString::number(d->mGame->cookie())); + d->mGameMaster->setText(1, d->mGame->isMaster() ? i18n("True") : i18n("False")); + d->mGameAdmin->setText(1, d->mGame->isAdmin() ? i18n("True") : i18n("False")); + d->mGameOffering->setText(1, d->mGame->isOfferingConnections() ? i18n("True") : i18n("False")); + d->mGameStatus->setText(1, QString::number(d->mGame->gameStatus())); + d->mGameRunning->setText(1, d->mGame->isRunning() ? i18n("True") : i18n("False")); + d->mGameMaxPlayers->setText(1, QString::number(d->mGame->maxPlayers())); + d->mGameMinPlayers->setText(1, QString::number(d->mGame->minPlayers())); + d->mGamePlayerCount->setText(1, QString::number(d->mGame->playerCount())); + +//TODO ios + + KGamePropertyHandler* handler = d->mGame->dataHandler(); + QIntDictIterator it(handler->dict()); + while (it.current()) { + QString policy; + switch (it.current()->policy()) { + case KGamePropertyBase::PolicyClean: + policy = i18n("Clean"); + break; + case KGamePropertyBase::PolicyDirty: + policy = i18n("Dirty"); + break; + case KGamePropertyBase::PolicyLocal: + policy = i18n("Local"); + break; + case KGamePropertyBase::PolicyUndefined: + default: + policy = i18n("Undefined"); + break; + } + (void) new QListViewItem(d->mGameProperties, + handler->propertyName(it.current()->id()), + handler->propertyValue(it.current()), + policy); +// kdDebug(11001) << k_funcinfo << ": checking for all game properties: found property name " << name << endl; + ++it; + } +} + +void KGameDebugDialog::slotUpdatePlayerData(QListBoxItem* item) +{ + if (!item || !d->mGame) { + return; + } + + KPlayer* p = d->mGame->findPlayer(item->text().toInt()); + + if (!p) { + kdError(11001) << k_funcinfo << ": cannot find player" << endl; + return; + } + + clearPlayerData(); + + QString buf; + buf.sprintf("%p", p); + d->mPlayerAddress->setText(1, buf); + d->mPlayerId->setText(1, QString::number(p->id())); + d->mPlayerName->setText(1, p->name()); + d->mPlayerGroup->setText(1, p->group()); + d->mPlayerUserId->setText(1, QString::number(p->userId())); + d->mPlayerMyTurn->setText(1, p->myTurn() ? i18n("True") : i18n("False")); + d->mPlayerAsyncInput->setText(1, p->asyncInput() ? i18n("True") : i18n("False")); + buf.sprintf("%p", p->game()); + d->mPlayerKGameAddress->setText(1, buf); + d->mPlayerVirtual->setText(1, p->isVirtual() ? i18n("True") : i18n("False")); + d->mPlayerActive->setText(1, p->isActive() ? i18n("True") : i18n("False")); + d->mPlayerRtti->setText(1, QString::number(p->rtti())); + d->mPlayerNetworkPriority->setText(1, QString::number(p->networkPriority())); + +//TODO ios + +// Properties + KGamePropertyHandler * handler = p->dataHandler(); + QIntDictIterator it((handler->dict())); + while (it.current()) { + QString policy; + switch (it.current()->policy()) { + case KGamePropertyBase::PolicyClean: + policy = i18n("Clean"); + break; + case KGamePropertyBase::PolicyDirty: + policy = i18n("Dirty"); + break; + case KGamePropertyBase::PolicyLocal: + policy = i18n("Local"); + break; + case KGamePropertyBase::PolicyUndefined: + default: + policy = i18n("Undefined"); + break; + } + (void)new QListViewItem(d->mPlayerProperties, + handler->propertyName(it.current()->id()), + handler->propertyValue(it.current()), + policy); + ++it; + } +} + +void KGameDebugDialog::clearPages() +{ + clearPlayerData(); + clearGameData(); + d->mPlayerList->clear(); + slotClearMessages(); +} + +void KGameDebugDialog::setKGame(const KGame* g) +{ + slotUnsetKGame(); + d->mGame = g; + if (g) { + //TODO: connect to the KGame signals for joined/removed players!!! + connect(d->mGame, SIGNAL(destroyed()), this, SLOT(slotUnsetKGame())); +// connect(); + + QPtrList list = *d->mGame->playerList(); + for (KPlayer* p = list.first(); p; p = list.next()) { + addPlayer(p); + } + + slotUpdateGameData(); + + connect(d->mGame, SIGNAL(signalMessageUpdate(int, Q_UINT32, Q_UINT32)), this, SLOT(slotMessageUpdate(int, Q_UINT32, Q_UINT32))); + } +} + +void KGameDebugDialog::slotUnsetKGame() +{ + if (d->mGame) { + disconnect(d->mGame, 0, this, 0); + } + d->mGame = 0; + clearPages(); +} + +void KGameDebugDialog::addPlayer(KPlayer* p) +{ + if (!p) { + kdError(11001) << "trying to add NULL player" << endl; + return; + } + + (void) new QListBoxText(d->mPlayerList, QString::number(p->id())); + //TODO connect to signals, like deleted/removed, ... +} + +void KGameDebugDialog::removePlayer(QListBoxItem* i) +{ + if (!i || !d->mGame) { + return; + } + KPlayer* p = d->mGame->findPlayer(i->text().toInt()); + if (!p) { + return; + } + disconnect(p, 0, this, 0); + if (i->isSelected()) { + clearPlayerData(); + } + delete i; +} + +void KGameDebugDialog::slotMessageUpdate(int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ + if (!showId(msgid)) { + return; + } + QString msgidText = KGameMessage::messageId2Text(msgid); + if (msgidText.isNull()) { + if (msgid > KGameMessage::IdUser) { + emit signalRequestIdName(msgid-KGameMessage::IdUser, true, msgidText); + } else { + emit signalRequestIdName(msgid, false, msgidText); + } + if (msgidText.isNull()) { + msgidText = i18n("Unknown"); + } + } + (void) new QListViewItem( d->mMessageList, QTime::currentTime().toString(), + QString::number(msgid), QString::number(receiver), + QString::number(sender), msgidText); +} + +void KGameDebugDialog::slotClearMessages() +{ + d->mMessageList->clear(); +} + +void KGameDebugDialog::slotShowId() +{ +/* QListBoxItem* i = d->mHideIdList->firstItem(); + for (; i; i = i->next()) { + if (i->selected()) { + d->mHideIdList->removeItem(i->); + } + }*/ + if (!d->mHideIdList->currentItem()) { + return; + } + d->mHideIdList->removeItem(d->mHideIdList->currentItem()); +} + +void KGameDebugDialog::slotHideId() +{ + if (!d->mMessageList->currentItem()) { + return; + } + int msgid = d->mMessageList->currentItem()->text(1).toInt(); + if (!showId(msgid)) { + return; + } + (void)new QListBoxText(d->mHideIdList, QString::number(msgid)); +} + +bool KGameDebugDialog::showId(int msgid) +{ + QListBoxItem* i = d->mHideIdList->firstItem(); + for (; i; i = i->next()) { + if (i->text().toInt() == msgid) { + return false; + } + } + return true; +} + + +#include "kgamedebugdialog.moc" diff --git a/libkdegames/kgame/dialogs/kgamedebugdialog.h b/libkdegames/kgame/dialogs/kgamedebugdialog.h new file mode 100644 index 00000000..65afc92a --- /dev/null +++ b/libkdegames/kgame/dialogs/kgamedebugdialog.h @@ -0,0 +1,149 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KGAMEDEBUGDIALOG_H__ +#define __KGAMEDEBUGDIALOG_H__ + +#include +#include + +class KGame; +class KGameIO; +class KPlayer; +class KGamePropertyBase; + +class KGameDebugDialogPrivate; + +class KDE_EXPORT KGameDebugDialog : public KDialogBase +{ + Q_OBJECT +public: + KGameDebugDialog(KGame* g, QWidget* parent, bool modal = false); + ~KGameDebugDialog(); + + /** + * Automatically connects the KGame object to all error dependant slots. + * Create a KGameErrorDialog object, call this function and forget + * everything. + * @param g The KGame which will emit the erorrs (or not ;-) ) + **/ + void setKGame(const KGame* g); + +public slots: + /** + * Unsets a @ref KGame which has been set using @ref setKGame before. + * This is called automatically when the @ref KGame object is destroyed + * and you normally don't have to call this yourself. + * + * Note that @ref setKGame also unsets an already existing @ref KGame + * object if exising. + **/ + void slotUnsetKGame(); + + /** + * Update the data of the @ref KGame object + **/ + void slotUpdateGameData(); + + /** + * Update the properties of the currently selected player + **/ + void slotUpdatePlayerData(); + + /** + * Updates the list of players and calls @ref clearPlayerData. Note that + * after this call NO player is selected anymore. + **/ + void slotUpdatePlayerList(); + + void slotClearMessages(); + +signals: + /** + * This signal is emitted when the "debug messages" page couldn't find + * the name of a message id. This is usually the case for user-defined + * messages. KGameDebugDialog asks you to give the msgid a name. + * @param messageid The ID of the message. As given to @ref + * KGame::sendMessage + * @param userid User defined msgIds are internally increased by + * @ref KGameMessage::IdUser. You don't have to care about this but if + * this signal is emitted with userid=false (shouldn't happen) then the + * name of an internal message as defined in @ref + * KGameMessage::GameMessageIds couldn't be found. + * @param name The name of the msgid. You have to fill this! + **/ + void signalRequestIdName(int messageid, bool userid, QString& name); + +protected: + void clearPages(); + + /** + * Clear the data of the player view. Note that the player list is NOT + * cleared. + **/ + void clearPlayerData(); + + /** + * Clear the data view of the @ref KGame object + **/ + void clearGameData(); + + /** + * Add a new player to the player list + **/ + void addPlayer(KPlayer* p); + + /** + * Remove a player from the list + **/ + void removePlayer(QListBoxItem* item); + + /** + * @return Whether messages with this msgid shall be displayed or not + **/ + bool showId(int msgid); + +protected slots: + /** + * Update the data of the player specified in item + * @param item The @ref QListBoxItem of the player to be updated. Note + * that the text of this item MUST be the ID of the player + **/ + void slotUpdatePlayerData(QListBoxItem* item); + + void slotShowId(); + void slotHideId(); + + /** + * A message has been received - see @ref KGame::signalMessageUpdate + **/ + void slotMessageUpdate(int msgid, Q_UINT32 receiver, Q_UINT32 sender); + +private: + void initGamePage(); + void initPlayerPage(); + void initMessagePage(); + +private: + KGameDebugDialogPrivate* d; +}; + + +#endif diff --git a/libkdegames/kgame/dialogs/kgamedialog.cpp b/libkdegames/kgame/dialogs/kgamedialog.cpp new file mode 100644 index 00000000..dc564b8e --- /dev/null +++ b/libkdegames/kgame/dialogs/kgamedialog.cpp @@ -0,0 +1,347 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include +#include + +#include + +#include "kgame.h" +#include "kplayer.h" +#include "kgamedialogconfig.h" + +#include "kgamedialog.h" + +#include "kgamedialog.moc" + +class KGameDialogPrivate +{ +public: + KGameDialogPrivate() + { + mGamePage = 0; + mNetworkPage = 0; + mMsgServerPage = 0; + mTopLayout = 0; + + mNetworkConfig = 0; + mGameConfig = 0; + + mOwner = 0; + mGame = 0; + } + + QVBox* mGamePage; + QVBox* mNetworkPage; + QVBox* mMsgServerPage;// unused here? + QVBoxLayout* mTopLayout; + KGameDialogNetworkConfig* mNetworkConfig; + KGameDialogGeneralConfig* mGameConfig; + +// a list of all config widgets added to this dialog + QPtrList mConfigWidgets; + +// just pointers: + KPlayer* mOwner; + KGame* mGame; +}; + +KGameDialog::KGameDialog(KGame* g, KPlayer* owner, const QString& title, + QWidget* parent, bool modal) + : KDialogBase(Tabbed, title, Ok|Default|Apply, + Ok, parent, 0, modal, true) +{ + init(g, owner); +} + +KGameDialog::KGameDialog(KGame* g, KPlayer* owner, const QString& title, + QWidget* parent, long initConfigs, int chatMsgId, bool modal) + : KDialogBase(Tabbed, title, Ok|Default|Apply, + Ok, parent, 0, modal, true) +{ + init(g, owner); + if ((ConfigOptions)initConfigs!=NoConfig) { + initDefaultDialog((ConfigOptions)initConfigs, chatMsgId); + } +} + +void KGameDialog::init(KGame* g, KPlayer* owner) +{ +//AB: do we need a "Cancel" Button? currently removed + +// kdDebug(11001) << k_funcinfo << ": this=" << this << endl; + d = new KGameDialogPrivate; + + setOwner(owner); + setKGame(g); + if (g) { + setAdmin(g->isAdmin()); + } else { + setAdmin(false); + } +} + +void KGameDialog::initDefaultDialog(ConfigOptions initConfigs, int chatMsgId) +{ + if (initConfigs & GameConfig) { + kdDebug() << "add gameconf" << endl; + addGameConfig(new KGameDialogGeneralConfig(0)); + } + if (initConfigs & NetworkConfig) { + addNetworkConfig(new KGameDialogNetworkConfig(0)); + } + if (initConfigs & (MsgServerConfig) ) { + addMsgServerConfig(new KGameDialogMsgServerConfig(0)); + } + if (initConfigs & ChatConfig) { + KGameDialogChatConfig * c = new KGameDialogChatConfig(chatMsgId, 0); + if (d->mGamePage) { + addChatWidget(c, d->mGamePage); + } else { + addConfigPage(c, i18n("&Chat")); + } + } + if (initConfigs & BanPlayerConfig) { + // add the connection management system - ie the widget where the ADMIN can + // kick players out + if (d->mNetworkPage) { + // put it on the network page + addConnectionList(new KGameDialogConnectionConfig(0), d->mNetworkPage); + } else { + // if no network page available put it on an own page + addConfigPage(new KGameDialogConnectionConfig(0), i18n("C&onnections")); + } + } +} + +KGameDialog::~KGameDialog() +{ +// kdDebug(11001) << "DESTRUCT KGameDialog" << this << endl; + d->mConfigWidgets.setAutoDelete(true); + d->mConfigWidgets.clear(); + delete d; +} + +void KGameDialog::addGameConfig(KGameDialogGeneralConfig* conf) +{ + if (!conf) { + return; + } + d->mGameConfig = conf; + d->mGamePage = addConfigPage(d->mGameConfig, i18n("&Game")); +} + +void KGameDialog::addNetworkConfig(KGameDialogNetworkConfig* netConf) +{ + if (!netConf) { + return; + } + d->mNetworkConfig = netConf; + d->mNetworkPage = addConfigPage(netConf, i18n("&Network")); +} + +void KGameDialog::addMsgServerConfig(KGameDialogMsgServerConfig* msgConf) +{ + if (!msgConf) { + return; + } + d->mMsgServerPage = addConfigPage(msgConf, i18n("&Message Server")); +} + +void KGameDialog::addChatWidget(KGameDialogChatConfig* chat, QVBox* parent) +{ + if (!chat) { + return; + } + if (!parent) { + parent = d->mGamePage; + } + if (!parent) { + kdError(11001) << "cannot add chat widget without page" << endl; + return; + } + addConfigWidget(chat, parent); +} + +void KGameDialog::addConnectionList(KGameDialogConnectionConfig* c, QVBox* parent) +{ + if (!c) { + return; + } + if (!parent) { + parent = d->mNetworkPage; + } + if (!parent) { + kdError(11001) << "Cannot add connection list without page" << endl; + return; + } + addConfigWidget(c, parent); +} + +QVBox *KGameDialog::configPage(ConfigOptions which) +{ + QVBox *box = 0; + switch(which) + { + case NetworkConfig: + box = d->mNetworkPage; + break; + case GameConfig: + box = d->mGamePage; + break; + case MsgServerConfig: + box = d->mMsgServerPage; + break; + default: + kdError(11001) << k_funcinfo << ": Parameter " << which << " not supported" << endl; + } + return box; +} + +QVBox* KGameDialog::addConfigPage(KGameDialogConfig* widget, const QString& title) +{ + if (!widget) { + kdError(11001) << "Cannot add NULL config widget" << endl; + return 0; + } + QVBox* page = addVBoxPage(title); + addConfigWidget(widget, page); + return page; +} + +void KGameDialog::addConfigWidget(KGameDialogConfig* widget, QWidget* parent) +{ + if (!widget) { + kdError(11001) << "Cannot add NULL config widget" << endl; + return; + } + if (!parent) { + kdError(11001) << "Cannot reparent to NULL widget" << endl; + return; + } +// kdDebug(11001) << "reparenting widget" << endl; + widget->reparent(parent, QPoint(0,0)); + d->mConfigWidgets.append(widget); + connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(slotRemoveConfigWidget(QObject*))); + if (!d->mGame) { + kdWarning(11001) << "No game has been set!" << endl; + } else { + widget->setKGame(d->mGame); + widget->setAdmin(d->mGame->isAdmin()); + } + if (!d->mOwner) { + kdWarning(11001) << "No player has been set!" << endl; + } else { + widget->setOwner(d->mOwner); + } + widget->show(); +} + +KGameDialogGeneralConfig* KGameDialog::gameConfig() const +{ return d->mGameConfig; } +KGameDialogNetworkConfig* KGameDialog::networkConfig() const +{ return d->mNetworkConfig; } + +void KGameDialog::slotApply() +{ + submitToKGame(); +} + +void KGameDialog::slotDefault() +{ + if (!d->mGame) { + return; + } + +//TODO *only* call setKGame/setOwner for the *current* page!! + setKGame(d->mGame); + setOwner(d->mOwner); +} + +void KGameDialog::slotOk() +{ + slotApply(); + QDialog::accept(); +} + +void KGameDialog::setOwner(KPlayer* owner) +{ +//AB: note: NULL player is ok! + d->mOwner = owner; + for (int unsigned i = 0; i < d->mConfigWidgets.count(); i++) { + if (d->mConfigWidgets.at(i)) { + d->mConfigWidgets.at(i)->setOwner(d->mOwner); + //TODO: hide playerName in KGameDialogGeneralConfig + } else { + kdError(11001) << "NULL widget??" << endl; + } + } +} + +void KGameDialog::setKGame(KGame* g) +{ + if (d->mGame) { + disconnect(d->mGame, 0, this, 0); + } + d->mGame = g; + for (int unsigned i = 0; i < d->mConfigWidgets.count(); i++) { + d->mConfigWidgets.at(i)->setKGame(d->mGame); + } + if (d->mGame) { + setAdmin(d->mGame->isAdmin()); + connect(d->mGame, SIGNAL(destroyed()), this, SLOT(slotUnsetKGame())); + connect(d->mGame, SIGNAL(signalAdminStatusChanged(bool)), + this, SLOT(setAdmin(bool))); + } +} + +void KGameDialog::setAdmin(bool admin) +{ + for (int unsigned i = 0; i < d->mConfigWidgets.count(); i++) { + d->mConfigWidgets.at(i)->setAdmin(admin); + } +} + +void KGameDialog::slotUnsetKGame() // called when KGame is destroyed +{ setKGame(0); } + +void KGameDialog::submitToKGame() +{ + if (!d->mGame) { + kdError(11001) << k_funcinfo << ": no game has been set" << endl; + return; + } + if (!d->mOwner) { + kdError(11001) << k_funcinfo << ": no player has been set" << endl; + return; + } + + for (int unsigned i = 0; i < d->mConfigWidgets.count(); i++) { +// kdDebug(11001) << "submit to kgame " << i << endl; + d->mConfigWidgets.at(i)->submitToKGame(d->mGame, d->mOwner); +// kdDebug(11001) << "done: submit to kgame " << i << endl; + } +} + +void KGameDialog::slotRemoveConfigWidget(QObject* configWidget) +{ + d->mConfigWidgets.removeRef((KGameDialogConfig*)configWidget); +} + diff --git a/libkdegames/kgame/dialogs/kgamedialog.h b/libkdegames/kgame/dialogs/kgamedialog.h new file mode 100644 index 00000000..f7a0c6e5 --- /dev/null +++ b/libkdegames/kgame/dialogs/kgamedialog.h @@ -0,0 +1,320 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// NAMING +// please follow these naming rules if you add/change classes: +// the main dialog is named KGameDialog and the base config widget +// KGameDialogConfig. All config widgets are named KGameDialogXYZConfig (where +// XYZ = the name of the config widget, like "general" or "network") and are +// inherited from KGameDialogConfig. + +#ifndef __KGAMEDIALOG_H__ +#define __KGAMEDIALOG_H__ + +#include +#include +class QGridLayout; +class QVBoxLayout; +class QListBoxItem; + +class KGame; +class KPlayer; +class KGamePropertyBase; + +class KGameDialogConfig; +class KGameDialogGeneralConfig; +class KGameDialogNetworkConfig; +class KGameDialogMsgServerConfig; +class KGameDialogChatConfig; +class KGameDialogConnectionConfig; + +class KGameDialogPrivate; +/** + * TODO: rewrite entire documentation. Nearly nothing is valid anymore. + * The main configuration dialog for KGame. Here all players meat each other, + * every player can see how many players connected (and their names) and the + * ADMIN can even "kick" players out. You can talk to each other (using + * KGameChat and the ADMIN can define the maxPlayers/minPlayers as well as the + * number of computer players. + * + * + * AB: setDefaultXYZ is obsolete!! + * You will usually create an instance of KGameDialog or any derived class and + * call setDefaultXYZ methods. Example (maybe + * obsoleted parameters - docu is currently changing very fast): + * \code + * KGameDialog dlg(kgame, i18n("New Game"), localPlayer, this, true, + * ID_CHAT); + * dlg.setDefaultNetworkInfo(port, host); // AB: obsolete! + * dlg.exec(); + * \endcode + * This will create a default modal dialog with the title "New Game". You don't + * have to do more than this. + * + * @short Main configuration dialog for KGame + * @author Andreas Beckermann + **/ +class KDE_EXPORT KGameDialog : public KDialogBase +{ + Q_OBJECT +public: + + enum ConfigOptions + { + NoConfig = 0, + ChatConfig = 1, + GameConfig = 2, + NetworkConfig = 4, + MsgServerConfig = 8, + BanPlayerConfig = 16, + AllConfig = 0xffff + }; + + /** + * Create an empty KGameDialog. You can add widgets using + * addConfigPage. + * @param g The KGame object of this game + * @param owner The KPlayer object who is responsible for this + * dialog, aka "the local player" + * @param title The title of the dialog - see KDialog::setCaption + * @param parent The parent of the dialog + * @param modal Whether the dialog is modal or not + **/ + KGameDialog(KGame* g, KPlayer* owner, const QString& title, + QWidget* parent, bool modal = false); + + /** + * Create a KGameDialog with the standard configuration widgets. This + * creates the following widgets: + *
    + *
  • KGameDialogGeneralConfig + *
  • KGameDialogNetworkConfig + *
  • KGameDialogMsgServerConfig + *
  • KGameDialogChatConfig + *
  • KGameDialogConnectionConfig + *
+ * If you want to use your own implementations (or none) of the widgets + * above you should subclass KGameDialog. Use addGameConfig, + * addNetworkConfig, addMsgConfig, addChatWidget and + * addConnectionList in this case. + * + * If you want to add further configuration widget you can simply use + * addConfigPage + * @param g The KGame object of this game + * @param owner The KPlayer object who is responsible for this + * dialog, aka "the local player" + * @param title The title of the dialog - see KDialog::setCaption + * @param parent The parent of the dialog + * @param modal Whether the dialog is modal or not + * @param initConfigs whether the default KGameDialogConfig widgets + * shall be created using initDefaultDialog. Use false if you want + * to use custom widgets. + * @param chatMsgId The ID of Chat messages. See KGameChat. Unused + * if initConfigs = false + **/ + KGameDialog(KGame* g, KPlayer* owner, const QString& title, + QWidget* parent, long initConfigs = AllConfig, + int chatMsgId = 15432, bool modal = false); + + virtual ~KGameDialog(); + + + /** + * Change the owner of the dialog. This will be used as the fromPlayer in + * KGameChat and will receive the entered player name. + * @param owner The owner of the dialog. It must already be added to the + * KGame object! + * + * Calls the KGameDialogConfig::setOwner implementation of all + * widgets that have been added by addConfigWidget + * @param owner The new owner player of this dialog must already be + * added to the KGame object. Can even be NULL (then no player + * configuration is made) + **/ + void setOwner(KPlayer* owner); + + /** + * Change the KGame object this dialog is used for. + * + * Calls the KGameDialogConfig::setKGame implementation of all + * widgets that have been added by addConfigWidget + * @param g The new KGame object + **/ + void setKGame(KGame* g); + + /** + * This will submit all configuration data to the KGame object. + * Automatically called by slotApply and slotOk + * There is no need to replace this unless you + * want to add widgets which are not derived from those classes + **/ + virtual void submitToKGame(); + + /** + * Adds a KGameChat to the dialog. If no parent is specified the + * game page will be used. + * @param chat The chat widget + * @param parent The parent of the chat widget. This MUST be an + * already added config widget. Note that the game page will be used + * if parent is 0. + **/ + void addChatWidget(KGameDialogChatConfig* chat, QVBox* parent = 0); + + /** + * Add a connection list to the dialog. The list consists of a + * KLisBox containing all players in the current game (see + * KGame::playerList). The admin can "ban" players, ie kick them out of + * the game. + * + * This is another not-really-config-config-widget. It just displays the + * connections and lets you ban players. + * @param c The KGameDialogConnectionConfig object + * @param parent The parent of the widget. If 0 the networkConfig + * page is used. + **/ + void addConnectionList(KGameDialogConnectionConfig* c, QVBox* parent = 0); + + /** + * Add a new page to the dialog. The page will contain you new config + * widget and will have your provided title. + * + * The widget will be reparented to this dialog. This also calls + * KGameDialogConfig::setKGame and KGameDialogConfig::setOwner. + * @param widget The new config widget + * @param title The title of the newly added page. + * @return The newly added page which contains your config widget. + **/ + QVBox* addConfigPage(KGameDialogConfig* widget, const QString& title); + + /** + * @return The QVBox of the given key, The key is from ConfigOptions + * Note that not all are supported yet + **/ + QVBox *configPage(ConfigOptions which); + + /** + * @return The default netowrk config. Note that this always returns 0 if + * you did not specify NetworkConfig in the constructor! + **/ + KGameDialogNetworkConfig* networkConfig() const; + + /** + * @return The default game config. Note that this always returns 0 if + * you did not specify GameConfig in the constructor! + **/ + KGameDialogGeneralConfig* gameConfig() const; + + /** + * Add a config widget to the specified parent. Usually you call + * addConfigPage for one widget and addConfigWidget for another to add + * it to the same page. Just use the returned page of + * addConfigPage. + **/ + void addConfigWidget(KGameDialogConfig* widget, QWidget* parent); + + /** + * Used to add the main network config widget in a new page. Use this to + * make networkConfig return something useful. + **/ + void addNetworkConfig(KGameDialogNetworkConfig* netConf); + + /** + * Add the main game config widget in a new page. Use this to make + * gameConfig return something useful. + **/ + void addGameConfig(KGameDialogGeneralConfig* conf); + + /** + * Used to add the message server config widget in a new page. + **/ + void addMsgServerConfig(KGameDialogMsgServerConfig* conf); + +protected: + + /** + * This is used to create a dialog containing all the default widgets. + * + * You may want to use this if you just want to use your own + * configuration widgets which inherit the standard ones. + * + * Note that if one of the widgets is NULL the default implementation + * will be used! (except the chat widget - you need to create it + * yourself as you have to provide a message id) + * @param initConfigs The widgets to be created + * @param chatMsgId The msgid for the chat config (only if specified in + * initConfigs) - see KGameDialogChatConfig + **/ + void initDefaultDialog(ConfigOptions initConfigs, int chatMsgId = 15432); + + /** + * Go through all config widgets and call their + * KGameDialogConfig::setKGame and KGameDialogConfig::setOwner implementation + * + * This function could be private and probably will be very soon. + * Don't use it yourself + **/ + void configureConfigWidgets(); + +protected slots: + /** + * Called when the user clicks on Ok. Calls slotApply and + * QDialog::accept() + **/ + virtual void slotOk(); + + /** + * Just calls submitToKGame() + **/ + virtual void slotApply(); + + /** + * Sets the default values for the configuration widgets. Set these + * values by (e.g.) setDefaultMaxPlayers() + * @deprecated + **/ + virtual void slotDefault(); + + /** + * Called when the KGame object is destroyed. Calls setKGame(0) so + * that all widgets can disconnect their slots and so on. + **/ + void slotUnsetKGame(); + + /** + * Called when the ADMIN status of this KGame client changes. See + * KGameNetwork::signalAdminStatusChanged + * @param isAdmin TRUE if this client is now the ADMIN otherwise FALSE + **/ + void setAdmin(bool isAdmin); + + /** + * Remove a config widget from the widget list. + * @see QObject::destroyed + **/ + void slotRemoveConfigWidget(QObject* configWidget); + +private: + void init(KGame*, KPlayer*); + +private: + KGameDialogPrivate* d; +}; + +#endif diff --git a/libkdegames/kgame/dialogs/kgamedialogconfig.cpp b/libkdegames/kgame/dialogs/kgamedialogconfig.cpp new file mode 100644 index 00000000..27f91cd1 --- /dev/null +++ b/libkdegames/kgame/dialogs/kgamedialogconfig.cpp @@ -0,0 +1,773 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kgamedialogconfig.h" + +#include "kgame.h" +#include "kplayer.h" +#include "kgamechat.h" +#include "kgameconnectdialog.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "kgamedialogconfig.moc" + +class KGameDialogConfigPrivate +{ +public: + KGameDialogConfigPrivate() + { + mOwner = 0; + mGame = 0; + + mAdmin = false; + } + + bool mAdmin; + KGame* mGame; + KPlayer* mOwner; +}; + +KGameDialogConfig::KGameDialogConfig(QWidget* parent) : QWidget(parent) +{ + d = new KGameDialogConfigPrivate; +} + +KGameDialogConfig::~KGameDialogConfig() +{ + kdDebug(11001) << k_funcinfo << endl; + delete d; +} + +void KGameDialogConfig::setKGame(KGame* g) +{ + d->mGame = g; +} + +void KGameDialogConfig::setOwner(KPlayer* p) +{ + d->mOwner = p; +} + +void KGameDialogConfig::setAdmin(bool a) +{ + d->mAdmin = a; +} + +KGame* KGameDialogConfig::game() const +{ return d->mGame; } +bool KGameDialogConfig::admin() const +{ return d->mAdmin; } +KPlayer* KGameDialogConfig::owner() const +{ return d->mOwner; } + +/////////////////////////// KGameDialogNetworkConfig ///////////////////////// +class KGameDialogNetworkConfigPrivate +{ +public: + KGameDialogNetworkConfigPrivate() + { + mInitConnection = 0; + mNetworkLabel = 0; + mDisconnectButton = 0; + mConnect = 0; + mDefaultServer=true; + + } + + // QPushButton* mInitConnection; + QHGroupBox* mInitConnection; + QLabel* mNetworkLabel; + QPushButton *mDisconnectButton; + + bool mDefaultServer; + QString mDefaultHost; + unsigned short int mDefaultPort; + KGameConnectWidget *mConnect; +}; + + +KGameDialogNetworkConfig::KGameDialogNetworkConfig(QWidget* parent) + : KGameDialogConfig(parent) +{ +// kdDebug(11001) << k_funcinfo << ": this=" << this << endl; + d = new KGameDialogNetworkConfigPrivate(); + + QVBoxLayout* topLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint(), "toplayout"); + + QHBoxLayout *hb = new QHBoxLayout(topLayout, KDialog::spacingHint()); + + d->mNetworkLabel = new QLabel(this); + hb->addWidget(d->mNetworkLabel); + + d->mDisconnectButton=new QPushButton(i18n("Disconnect"),this); + connect(d->mDisconnectButton, SIGNAL(clicked()), this, SLOT(slotExitConnection())); + hb->addWidget(d->mDisconnectButton); + + d->mInitConnection = new QHGroupBox(i18n("Network Configuration"), this); + topLayout->addWidget(d->mInitConnection); + + d->mConnect = new KGameConnectWidget(d->mInitConnection); + connect(d->mConnect, SIGNAL(signalNetworkSetup()), this, SLOT(slotInitConnection())); + connect(d->mConnect, SIGNAL(signalServerTypeChanged(int)), + this, SIGNAL(signalServerTypeChanged(int))); + + // Needs to be AFTER the creation of the dialogs + setConnected(false); + setDefaultNetworkInfo("localhost", 7654,true); +} + +KGameDialogNetworkConfig::~KGameDialogNetworkConfig() +{ + kdDebug(11001) << k_funcinfo << endl; + delete d; +} + +void KGameDialogNetworkConfig::slotExitConnection() +{ + kdDebug(11001) << k_funcinfo << " !!!!!!!!!!!!!!!!!!!!!!!" << endl; + if (game()) game()->disconnect(); + setConnected(false,false); +} + +void KGameDialogNetworkConfig::slotInitConnection() +{ + kdDebug(11001) << k_funcinfo << endl; + bool connected = false; + bool master = true; + unsigned short int port = d->mConnect->port(); + QString host = d->mConnect->host(); + + if (host.isNull()) { + master = true; + if (game()) { + game()->setDiscoveryInfo(d->mConnect->type(),d->mConnect->gameName()); + connected = game()->offerConnections(port); + } + } else { + master = false; + if (game()) { + connected = game()->connectToServer(host, port); + } + // We need to learn about failed connections + if (game()) { + connect(game(), SIGNAL(signalConnectionBroken()), + this, SLOT(slotConnectionBroken())); + } + } + setConnected(connected, master); +} + +void KGameDialogNetworkConfig::slotConnectionBroken() +{ + kdDebug(11001) << k_funcinfo << endl; + setConnected(false,false); + KMessageBox::error(this, i18n("Cannot connect to the network")); +} + +void KGameDialogNetworkConfig::setConnected(bool connected, bool master) +{ + if (!connected) { + d->mNetworkLabel->setText(i18n("Network status: No Network")); + d->mInitConnection->setEnabled(true); + d->mDisconnectButton->setEnabled(false); + return; + } + if (master) { + d->mNetworkLabel->setText(i18n("Network status: You are MASTER")); + } else { + d->mNetworkLabel->setText(i18n("Network status: You are connected")); + } + d->mInitConnection->setEnabled(false); + d->mDisconnectButton->setEnabled(true); +} + +void KGameDialogNetworkConfig::submitToKGame(KGame* , KPlayer* ) +{ +} + +void KGameDialogNetworkConfig::setKGame(KGame* g) +{ + KGameDialogConfig::setKGame(g); + if (!game()) { + setConnected(false); + return; + } + setConnected(game()->isNetwork(), game()->isMaster()); +} + +void KGameDialogNetworkConfig::setDefaultNetworkInfo(const QString& host, unsigned short int port,bool server) +{ + d->mDefaultPort = port; + d->mDefaultHost = host; + d->mDefaultServer = server; + + d->mConnect->setHost(host); + d->mConnect->setPort(port); + if (server) { + d->mConnect->setDefault(0); + } else { + d->mConnect->setDefault(1); + } +} + +void KGameDialogNetworkConfig::setDiscoveryInfo(const QString& type, const QString& name) +{ + d->mConnect->setType(type); + d->mConnect->setName(name); +} + +/////////////////////////// KGameDialogGeneralConfig ///////////////////////// +class KGameDialogGeneralConfigPrivate +{ +public: + KGameDialogGeneralConfigPrivate() + { + mTopLayout = 0; + mName = 0; + } + + QLineEdit* mName; + + QVBoxLayout* mTopLayout; +}; + +KGameDialogGeneralConfig::KGameDialogGeneralConfig(QWidget* parent, bool initializeGUI) + : KGameDialogConfig(parent) +{ +// kdDebug(11001) << k_funcinfo << ": this=" << this << endl; + d = new KGameDialogGeneralConfigPrivate; + + if (initializeGUI) { + d->mTopLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint()); + d->mTopLayout->setAutoAdd(true); + + QWidget* nameWidget = new QWidget(this); + QHBoxLayout* l = new QHBoxLayout(nameWidget); + QLabel* nameLabel = new QLabel(i18n("Your name:"), nameWidget); + l->addWidget(nameLabel); + d->mName = new QLineEdit(nameWidget); + l->addWidget(d->mName); + } +} + +KGameDialogGeneralConfig::~KGameDialogGeneralConfig() +{ + kdDebug(11001) << k_funcinfo << endl; + delete d; +} + +void KGameDialogGeneralConfig::setPlayerName(const QString& name) +{ + if (d->mName) { + d->mName->setText(name); + } +} + +QString KGameDialogGeneralConfig::playerName() const +{ + return d->mName ? d->mName->text() : QString::null; +} + +void KGameDialogGeneralConfig::setOwner(KPlayer* p) +{ + if (owner()) { + owner()->disconnect(this); + } + KGameDialogConfig::setOwner(p); + if (!owner()) { + // can this config be used at all? + // maybe call hide() + return; + } + connect(owner(), SIGNAL(signalPropertyChanged(KGamePropertyBase*, KPlayer*)), + this, SLOT(slotPropertyChanged(KGamePropertyBase*, KPlayer*))); + setPlayerName(p->name()); + //TODO: connect signalPropertyChanged and check for playername changes! +} + +void KGameDialogGeneralConfig::setKGame(KGame* g) +{ + KGameDialogConfig::setKGame(g); + if (!g) { + // TODO + // can this config be used at all? + // maybe call hide() + return; + } +} + +void KGameDialogGeneralConfig::setAdmin(bool admin) +{ + KGameDialogConfig::setAdmin(admin); +// enable/disable widgets + +} + +void KGameDialogGeneralConfig::submitToKGame(KGame* g, KPlayer* p) +{ +//FIXME + if (p) { + p->setName(playerName()); + } + if (g) { + } +} + +void KGameDialogGeneralConfig::slotPropertyChanged(KGamePropertyBase* prop, KPlayer* p) +{ + if (!prop || !p || p != owner()) { + return; + } + switch (prop->id()) { + case KGamePropertyBase::IdName: + setPlayerName(p->name()); + break; + default: + break; + } +} + +class KGameDialogMsgServerConfigPrivate +{ +public: + KGameDialogMsgServerConfigPrivate() + { + senderLayout = 0; + localLayout = 0; + + changeMaxClients = 0; + changeAdmin= 0; + removeClient= 0; + noAdmin = 0; + + noMaster = 0; + } + + QVBoxLayout* senderLayout; + QHBoxLayout* localLayout; + + QPushButton* changeMaxClients; + QPushButton* changeAdmin; + QPushButton* removeClient; + QLabel* noAdmin; + + QLabel* noMaster; +}; + + +// TODO: change ADMIN ID, remove CLIENTS, change MAXCLIENTS +// we do everything here with QPushButtons as we want to wait a moment before +// continuing - the message must be sent over network first +KGameDialogMsgServerConfig::KGameDialogMsgServerConfig(QWidget* parent) + : KGameDialogConfig(parent) +{ + d = new KGameDialogMsgServerConfigPrivate; + + QVBoxLayout* topLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint()); + d->senderLayout = new QVBoxLayout(topLayout); + d->localLayout = new QHBoxLayout(topLayout); +} + +KGameDialogMsgServerConfig::~KGameDialogMsgServerConfig() +{ + kdDebug(11001) << k_funcinfo << endl; + delete d; +} + +void KGameDialogMsgServerConfig::setKGame(KGame* g) +{ + KGameDialogConfig::setKGame(g); + //TODO display the ID of the admin if we aren't + // connect(g, SIGNAL(signalAdminChanged(int)), this, SLOT(slotChangeIsAdmin(int)));//TODO + if (!game()) { + // we cannot do anything without a KGame object! + setAdmin(false); + return; + } + setAdmin(game()->isAdmin()); + setHasMsgServer(game()->messageServer()); +} + + +void KGameDialogMsgServerConfig::slotChangeMaxClients() +{ + if (!game()) { + kdError(11001) << k_funcinfo << ": no valid game object available!" << endl; + return; + } + if (!game()->isAdmin()) { + kdError(11001) << k_funcinfo << ": only ADMIN is allowed to call this!" << endl; + return; + } + int max; +// edit->setText(QString::number()); // current max clients! //TODO + + QDialog* dialog = new QDialog(); + dialog->setCaption(i18n("Maximal Number of Clients")); + QHBoxLayout* l = new QHBoxLayout(dialog, KDialog::marginHint(), KDialog::spacingHint()); + l->setAutoAdd(true); + + (void) new QLabel(i18n("Maximal number of clients (-1 = infinite):"), dialog); + QLineEdit* edit = new QLineEdit(dialog);//TODO: use KIntNumInput +// edit->setText(QString::number(max)); // current max clients! //TODO + if (dialog->exec() == QDialog::Accepted) { + bool ok; + max = edit->text().toInt(&ok); + if (ok) { + game()->setMaxClients(max); + } + } + +} + +void KGameDialogMsgServerConfig::slotRemoveClient() +{ +} + +void KGameDialogMsgServerConfig::slotChangeAdmin() +{ + if (!game()) { + kdError(11001) << k_funcinfo << ": no valid game object available!" << endl; + return; + } + if (!admin()) { + kdError(11001) << k_funcinfo << ": only ADMIN is allowed to call this!" << endl; + return; + } + //TODO + Q_UINT32 newAdmin = 0; +// newAdmin = ; + game()->electAdmin(newAdmin); +} + +void KGameDialogMsgServerConfig::removeClient(Q_UINT32 /*id*/) +{ +//TODO +} + +void KGameDialogMsgServerConfig::setAdmin(bool a) +{ + if (admin() == a) { + // no need to do anything + return; + } + KGameDialogConfig::setAdmin(a); + if (admin()) { + if (d->noAdmin) { + delete d->noAdmin; + d->noAdmin = 0; + } + d->changeMaxClients = new QPushButton(i18n("Change Maximal Number of Clients"), this); + connect(d->changeMaxClients, SIGNAL(pressed()), this, SLOT(slotChangeMaxClients())); + d->changeAdmin = new QPushButton(i18n("Change Admin"), this); + connect(d->changeAdmin, SIGNAL(pressed()), this, SLOT(slotChangeAdmin())); + d->removeClient = new QPushButton(i18n("Remove Client with All Players"), this); + connect(d->removeClient, SIGNAL(pressed()), this, SLOT(slotRemoveClient())); + d->senderLayout->addWidget(d->changeMaxClients); + d->senderLayout->addWidget(d->changeAdmin); + d->senderLayout->addWidget(d->removeClient); + } else { + if (d->changeMaxClients) { + delete d->changeMaxClients; + d->changeMaxClients = 0; + } + if (d->changeAdmin) { + delete d->changeAdmin; + d->changeAdmin = 0; + } + if (d->removeClient) { + delete d->removeClient; + d->removeClient = 0; + } + d->noAdmin = new QLabel(i18n("Only the admin can configure the message server!"), this); + d->senderLayout->addWidget(d->noAdmin); + } +} + + +void KGameDialogMsgServerConfig::setHasMsgServer(bool has) +{ + if (!has) { + // delete all inputs + if (!d->noMaster) { + d->noMaster = new QLabel(i18n("You don't own the message server"), this); + d->localLayout->addWidget(d->noMaster); + } + return; + } + if (d->noMaster) { + delete d->noMaster; + d->noMaster = 0; + } + //TODO + // list all connections, data (max clients) and so on + // cannot be done above (together with QPushButtons) as it is possible that + // this client is ADMIN but not MASTER (i.e. doesn't own the messageserver) +} + + +class KGameDialogChatConfigPrivate +{ +public: + KGameDialogChatConfigPrivate() + { + mChat = 0; + } + + KGameChat* mChat; +}; + +KGameDialogChatConfig::KGameDialogChatConfig(int chatMsgId, QWidget* parent) + : KGameDialogConfig(parent) +{ + d = new KGameDialogChatConfigPrivate; + QVBoxLayout* topLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint()); + topLayout->setAutoAdd(true); + QHGroupBox* b = new QHGroupBox(i18n("Chat"), this); + d->mChat = new KGameChat(0, chatMsgId, b); +} + +KGameDialogChatConfig::~KGameDialogChatConfig() +{ + kdDebug(11001) << k_funcinfo << endl; + delete d; +} + +void KGameDialogChatConfig::setKGame(KGame* g) +{ + KGameDialogConfig::setKGame(g); + d->mChat->setKGame(game()); + if (!game()) { + hide(); + } else { + show(); + } +} + +void KGameDialogChatConfig::setOwner(KPlayer* p) +{ + KGameDialogConfig::setOwner(p); + if (!owner()) { + hide(); + return; + } + d->mChat->setFromPlayer(owner()); + show(); +} + + + +class KGameDialogConnectionConfigPrivate +{ +public: + KGameDialogConnectionConfigPrivate() + { + mPlayerBox = 0; + } + + QPtrDict mItem2Player; + KListBox* mPlayerBox; +}; + +KGameDialogConnectionConfig::KGameDialogConnectionConfig(QWidget* parent) + : KGameDialogConfig(parent) +{ + //TODO: prevent player to ban himself + d = new KGameDialogConnectionConfigPrivate; + QVBoxLayout* topLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint()); + topLayout->setAutoAdd(true); + QHGroupBox* b = new QHGroupBox(i18n("Connected Players"), this); + d->mPlayerBox = new KListBox(b); + setMinimumHeight(100); +} + +KGameDialogConnectionConfig::~KGameDialogConnectionConfig() +{ + kdDebug(11001) << k_funcinfo << endl; + // d->mIem2Player.clear(); + delete d; +} + +void KGameDialogConnectionConfig::setKGame(KGame* g) +{ + if (game()) { + disconnect(game(), 0, this, 0); + } + KGameDialogConfig::setKGame(g); + slotClearPlayers(); + if (game()) { +// react to changes in KGame::playerList() + connect(game(), SIGNAL(signalPlayerJoinedGame(KPlayer*)), + this, SLOT(slotPlayerJoinedGame(KPlayer*))); + connect(game(), SIGNAL(signalPlayerLeftGame(KPlayer*)), + this, SLOT(slotPlayerLeftGame(KPlayer*))); + + KGame::KGamePlayerList l = *game()->playerList(); + for (KPlayer* p = l.first(); p; p = l.next()) { + slotPlayerJoinedGame(p); + } + } +} + +void KGameDialogConnectionConfig::setOwner(KPlayer* p) +{ + KGameDialogConfig::setOwner(p); +} + +void KGameDialogConnectionConfig::setAdmin(bool a) +{ + if (!game()) {// not possible... in theory + return; + } + if (admin()) { + disconnect(game(), SIGNAL(executed(QListBoxItem*)), this, 0); + } + KGameDialogConfig::setAdmin(a); + if (admin()) { + connect(d->mPlayerBox, SIGNAL(executed(QListBoxItem*)), this, + SLOT(slotKickPlayerOut(QListBoxItem*))); + } +} + +QListBoxItem* KGameDialogConnectionConfig::item(KPlayer* p) const +{ + QPtrDictIterator it(d->mItem2Player); + while (it.current()) { + if (it.current() == p) { + return (QListBoxItem*)it.currentKey(); + } + ++it; + } + return 0; +} + +void KGameDialogConnectionConfig::slotClearPlayers() +{ + QPtrDictIterator it(d->mItem2Player); + while (it.current()) { + slotPlayerLeftGame(it.current()); + ++it; + } + + if (d->mItem2Player.count() > 0) { + kdWarning(11001) << k_funcinfo << ": itemList wasn't cleared properly" << endl; + d->mItem2Player.clear(); + } + if (d->mPlayerBox->count() > 0) { + kdWarning(11001) << k_funcinfo << ": listBox wasn't cleared properly" << endl; + d->mPlayerBox->clear(); + } + +} + +void KGameDialogConnectionConfig::slotPlayerJoinedGame(KPlayer* p) +{ + if (!p) { + kdError(11001) << k_funcinfo << ": Cannot add NULL player" << endl; + } + if (d->mItem2Player[p]) { + kdError(11001) << k_funcinfo << ": attempt to double add player" << endl; + return; + } + kdDebug(11001) << k_funcinfo << ": add player " << p->id() << endl; + QListBoxText* t = new QListBoxText(p->name()); + d->mItem2Player.insert(t, p); + d->mPlayerBox->insertItem(t); + + connect(p, SIGNAL(signalPropertyChanged(KGamePropertyBase*, KPlayer*)), + this, SLOT(slotPropertyChanged(KGamePropertyBase*, KPlayer*))); + +} + +void KGameDialogConnectionConfig::slotPlayerLeftGame(KPlayer* p) +{ + // disconnect first + this->disconnect(p); + if (!item(p)) { + kdError(11001) << k_funcinfo << ": cannot find " << p->id() + << " in list" << endl; + return; + } + d->mPlayerBox->removeItem(d->mPlayerBox->index(item(p))); + +} + +void KGameDialogConnectionConfig::slotKickPlayerOut(QListBoxItem* item) +{ + kdDebug(11001) << "kick player out" << endl; + KPlayer* p = d->mItem2Player[item]; + if (!p) { + kdError(11001) << "invalid item selected - no player found" << endl; + return; + } + if (!game()) { + kdWarning(11001) << "no game set" << endl; + return; + } + if (!admin()) { + kdDebug(11001) << "Only the ADMIN can kick players" << endl; + return; + } + if (p == owner()) { // you wanna ban the ADMIN ?? + kdDebug(11001) << "you cannot kick the ADMIN" << endl; + return; + } + + if (KMessageBox::questionYesNo(this, i18n("Do you want to ban player \"%1\" from the game?").arg( + p->name()), QString::null, i18n("Ban Player"), i18n("Do Not Ban")) == KMessageBox::Yes) { + kdDebug(11001) << "will remove player " << p << endl; + game()->removePlayer(p); +// d->mPlayerBox->removeItem(d->mPlayerBox->index(item)); // should be done by signalPlayerLeftGame + } else { + kdDebug(11001) << "will NOT remove player " << p << endl; + } +} + +void KGameDialogConnectionConfig::slotPropertyChanged(KGamePropertyBase* prop, KPlayer* player) +{ + if(prop->id() == KGamePropertyBase::IdName) { + QListBoxText* old = 0; + QPtrDictIterator it(d->mItem2Player); + while (it.current() && !old) { + if (it.current() == player) { + old = (QListBoxText*)it.currentKey(); + } + ++it; + } + QListBoxText* t = new QListBoxText(player->name()); + d->mPlayerBox->changeItem(t, d->mPlayerBox->index(old)); + d->mItem2Player.remove(old); + d->mItem2Player.insert(t, player); + } +} + diff --git a/libkdegames/kgame/dialogs/kgamedialogconfig.h b/libkdegames/kgame/dialogs/kgamedialogconfig.h new file mode 100644 index 00000000..b7d30b23 --- /dev/null +++ b/libkdegames/kgame/dialogs/kgamedialogconfig.h @@ -0,0 +1,362 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// NAMING +// please follow these naming rules if you add/change classes: +// the main dialog is named KGameDialog and the base config widget +// KGameDialogConfig. All config widgets are named KGameDialogXYZConfig (where +// XYZ = the name of the config widget, like "general" or "network") and are +// inherited from KGameDialogConfig. + +#ifndef __KGAMEDIALOGCONFIG_H__ +#define __KGAMEDIALOGCONFIG_H__ + +#include +#include + +class QGridLayout; +class QVBoxLayout; +class QListBoxItem; + +class KGame; +class KPlayer; +class KGamePropertyBase; + +class KGameDialogConfigPrivate; +/** + * Base class for configuration widgets. + * + * You can inherit from this and implement @ref submitToKGame, @ref + * setOwner and @ref setKGame to create your personal @ref KGame configuration widget :-) + * @short Base class for configuration widgets + * @author Andreas Beckermann + **/ +class KDE_EXPORT KGameDialogConfig : public QWidget +{ + Q_OBJECT +public: + KGameDialogConfig(QWidget* parent = 0); + virtual ~KGameDialogConfig(); + + /** + * Called by @ref KGameDialog to submit all settings to the KGame + * Object. + * You have to replace this if you add your own widgets! + * @param g A pointer to your KGame. + * @param p A pointer to the player owning this dialog + **/ + virtual void submitToKGame(KGame* g, KPlayer* p) = 0; + + /** + * The owner player of the dialog has been changed. The default + * changes the pointer for owner so don't forget to call the + * default implementation if you overwrite this! + * + * You can use this e.g. to change a line edit widget containing the + * player name. + * + * Note: even NULL players are allowed! + * @param p The new owner player of the dialog + **/ + virtual void setOwner(KPlayer* p); + + /** + * The KGame object of the dialog has been changed. The default + * implementation changes the pointer for game so don't forget to + * call the default implementation if you overwrite this! + * + * You can use this e.g. to re-read the min/max player settings. + * @param g The KGame object + **/ + virtual void setKGame(KGame* g); + + /** + * The admin status has been changed. + * If the KGame object of this config widget is the + * admin the user is allowed to configure it. Otherwise most + * widgets will have to be disabled. Note that you don't necessarily + * need to deactivate all widget - e.g. the player name must be + * configured by the player. Mainly the KGame configuration can be done + * by the admin only. + * + * By default this does nothing. Changes the value for admin so + * don't forget to call the default implementation in derived classes! + * @param admin Whether the KGame object of this dialog can be + * configured + **/ + virtual void setAdmin(bool admin); + + /** + * A pointer to the KGame object that has been set by @ref setKGame. + * + * Note that NULL is allowed! + * @return The KGame object assigned to this dialog + **/ + KGame* game() const; + + /** + * A pointer to the KPlayer object that has been set by @ref + * setOwner. + * + * Note that NULL is allowed! + * @return The owner of the dialog + **/ + KPlayer* owner() const; + + /** + * @return True if the owner is ADMIN otherwise FALSE. See also + * @ref setAdmin + **/ + bool admin() const; + +protected: + +private: + KGameDialogConfigPrivate* d; +}; + +/** + * The main game configuration widget. + * + * It currently contains a line edit for the name of the player only. You can + * add widgets by using the KGameDialogGeneralConfig as parent parameter as it + * uses QLayout::autoAdd == true. + * @author Andreas Beckermann + **/ +class KGameDialogGeneralConfigPrivate; +class KGameDialogGeneralConfig : public KGameDialogConfig +{ + Q_OBJECT +public: + /** + * Construct a KGameDialogGeneralConfig. Currently it contains a line + * edit widget to change the player name only. + * + * If you just want to add more widgets you can just create your widgets + * with the KGameDialogGeneralConfig as parent as it uses + * QLayout::setAutoAdd(true). + * + * @param parent Parent widget for this dialog. + * @param initializeGUI If you really don't want to use the + * predefined widget and/or layout use FALSE here. Note that then none + * of the predefined widgets (currently only the name of the player) + * will exist anymore. + * + **/ + KGameDialogGeneralConfig(QWidget* parent = 0, bool initializeGUI = true); + virtual ~KGameDialogGeneralConfig(); + + /** + * Called by @ref KGameDialog to submit all settings to the KGame + * Object. + * You have to replace this if you add your own widgets! + * @param g A pointer to your KGame. + * @param p A pointer to the player owning this dialog + **/ + virtual void submitToKGame(KGame* g, KPlayer* p); + + /** + * Change the owner of the config widget. + * + * Changes the playername in the line edit + * @param p The new owner player + **/ + virtual void setOwner(KPlayer* p); + + /** + * See @ref KGameDialogConfig::setKGame + * + * Sets the default values of all KGame related predefined widgets + * (currently none) + **/ + virtual void setKGame(KGame* g); + + /** + * See @ref KGameDialogConfig::setAdmin + * + * This deactivates the min/max player widgets + **/ + virtual void setAdmin(bool admin); + +protected slots: + void slotPropertyChanged(KGamePropertyBase*, KPlayer*); + +protected: + void setPlayerName(const QString& name); + + QString playerName() const; + +private: + KGameDialogGeneralConfigPrivate* d; +}; + +class KGameDialogNetworkConfigPrivate; +class KDE_EXPORT KGameDialogNetworkConfig : public KGameDialogConfig +{ + Q_OBJECT +public: + KGameDialogNetworkConfig(QWidget* parent = 0); + virtual ~KGameDialogNetworkConfig(); + + + void disableInitConnection(); + + /** + * Called by @ref KGameDialog to submit all settings to the KGame + * Object. + * You have to replace this if you add your own widgets! + * @param g A pointer to your KGame. + * @param p A pointer to the player owning this dialog + **/ + virtual void submitToKGame(KGame* g, KPlayer* p); + + virtual void setKGame(KGame* g); + + /** + * This sets the default port and host used in @ref KGameConnectDialog. + * The user will be able to change these defaults! + * + * If you don't call this then host "localhost" and port "0" is used. + * You are strongly encouraged to change at least the port! + * @param port The default port to connect to / listen on + * @param host The default host to connect to + **/ + void setDefaultNetworkInfo(const QString& host, unsigned short int port,bool server=true); + + /** + * Set service type that will be published or browsed for and game name that will be displayed in + * server browser. Without this publishing and discovery of LAN servers will not be enabled. + * @param name Game name. Important only for server mode. If not + * set hostname will be used. In case of name conflict -2, -3 and so on will be added to name. + * @param type Service type (something like _kwin4._tcp). It should be unique for application. + * @since 3.4 + **/ + void setDiscoveryInfo(const QString& type, const QString& name=QString::null); + +signals: + /** + * This signal is emmited if the user changes the server type (client/server) + * in the network configuration dialog. + * + * @param t - type type (0/1) of the connection + **/ + void signalServerTypeChanged(int); + + +protected: + void setConnected(bool connected, bool master = false); + +protected slots: + void slotInitConnection(); + void slotExitConnection(); + void slotConnectionBroken(); + + +private: + KGameDialogNetworkConfigPrivate* d; +}; + +class KGameDialogMsgServerConfigPrivate; +class KGameDialogMsgServerConfig : public KGameDialogConfig +{ + Q_OBJECT +public: + KGameDialogMsgServerConfig(QWidget* parent = 0); + virtual ~KGameDialogMsgServerConfig(); + + virtual void submitToKGame(KGame*, KPlayer*) {} + + void setHasMsgServer(bool); + + virtual void setKGame(KGame* g); + virtual void setAdmin(bool); + +protected slots: + void slotChangeMaxClients(); + void slotChangeAdmin(); + void slotRemoveClient(); + +protected: + void removeClient(Q_UINT32 id); + +private: + KGameDialogMsgServerConfigPrivate* d; +}; + +class KGameDialogChatConfigPrivate; +/** + * This is not really a configuration widget but rather a simple chat widget. + * This widget does nothing but just providing a @ref KGameChat object. + * @short A chat widget inside a @ref KGameDialog + * @author Andreas Beckermann + **/ +class KGameDialogChatConfig : public KGameDialogConfig +{ + Q_OBJECT +public: + KGameDialogChatConfig(int chatMsgId, QWidget* parent = 0); + virtual ~KGameDialogChatConfig(); + + virtual void setKGame(KGame* g); + virtual void setOwner(KPlayer* p); + + virtual void submitToKGame(KGame* g, KPlayer* p) { Q_UNUSED(g); Q_UNUSED(p); } + +private: + KGameDialogChatConfigPrivate* d; +}; + +/** + * @short Lists all connected players and gives the ability to kick them off the + * game + **/ +class KGameDialogConnectionConfigPrivate; +class KGameDialogConnectionConfig : public KGameDialogConfig +{ + Q_OBJECT +public: + KGameDialogConnectionConfig(QWidget* parent = 0); + virtual ~KGameDialogConnectionConfig(); + + virtual void setKGame(KGame* g); + virtual void setOwner(KPlayer* p); + virtual void setAdmin(bool admin); + + virtual void submitToKGame(KGame* g, KPlayer* p) { Q_UNUSED(g); Q_UNUSED(p); } + +protected: + /** + * @param p A player + * @return The QListBoxItem that belongs to the player @p p + **/ + QListBoxItem* item(KPlayer* p) const; + +protected slots: + void slotKickPlayerOut(QListBoxItem* item); + void slotPropertyChanged(KGamePropertyBase* prop, KPlayer* p); + void slotPlayerLeftGame(KPlayer* p); + void slotPlayerJoinedGame(KPlayer* p); + void slotClearPlayers(); + +private: + KGameDialogConnectionConfigPrivate* d; + +}; +#endif diff --git a/libkdegames/kgame/dialogs/kgameerrordialog.cpp b/libkdegames/kgame/dialogs/kgameerrordialog.cpp new file mode 100644 index 00000000..1750892c --- /dev/null +++ b/libkdegames/kgame/dialogs/kgameerrordialog.cpp @@ -0,0 +1,129 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include + +#include "kgame.h" + +#include "kgameerrordialog.h" + +class KGameErrorDialogPrivate +{ +public: + KGameErrorDialogPrivate() + { + mGame = 0; + } + + const KGame* mGame; +}; + +KGameErrorDialog::KGameErrorDialog(QWidget* parent) : QObject(parent) +{ + d = new KGameErrorDialogPrivate; +} + +KGameErrorDialog::~KGameErrorDialog() +{ + delete d; +} + +void KGameErrorDialog::setKGame(const KGame* g) +{ + slotUnsetKGame(); + d->mGame = g; + + connect(d->mGame, SIGNAL(destroyed()), this, SLOT(slotUnsetKGame())); + +// the error signals: + connect(d->mGame, SIGNAL(signalNetworkErrorMessage(int, QString)), + this, SLOT(slotError(int, QString))); + connect(d->mGame, SIGNAL(signalConnectionBroken()), + this, SLOT(slotServerConnectionLost())); + connect(d->mGame, SIGNAL(signalClientDisconnected(Q_UINT32,bool)), + this, SLOT(slotClientConnectionLost(Q_UINT32,bool))); +} + +void KGameErrorDialog::slotUnsetKGame() +{ + if (d->mGame) { + disconnect(d->mGame, 0, this, 0); + } + d->mGame = 0; +} + +void KGameErrorDialog::error(const QString& errorText, QWidget* parent) +{ KMessageBox::error(parent, errorText); } + +void KGameErrorDialog::slotServerConnectionLost() +{ +// TODO: add IP/port of the server + QString message = i18n("Connection to the server has been lost!"); + error(message, (QWidget*)parent()); +} + +void KGameErrorDialog::slotClientConnectionLost(Q_UINT32 /*id*/,bool) +{ +//TODO: add IP/port of the client + QString message; +// if (c) { +// message = i18n("Connection to client has been lost!\nID: %1\nIP: %2").arg(c->id()).arg(c->IP()); +// } else { +// message = i18n("Connection to client has been lost!"); +// } + message = i18n("Connection to client has been lost!"); + error(message, (QWidget*)parent()); +} + +void KGameErrorDialog::slotError(int errorNo, QString text) +{ + QString message = i18n("Received a network error!\nError number: %1\nError message: %2").arg(errorNo).arg(text); + error(message, (QWidget*)parent()); +} + +void KGameErrorDialog::connectionError(QString s) +{ + QString message; + if (s.isNull()) { + message = i18n("No connection could be created."); + } else { + message = i18n("No connection could be created.\nThe error message was:\n%1").arg(s); + } + error(message, (QWidget*)parent()); +} + + + +// should become the real dialog - currently we just use messageboxes +// -> maybe unused forever +KGameErrorMessageDialog::KGameErrorMessageDialog(QWidget* parent) + : KDialogBase(Plain, i18n("Error"), Ok, Ok, parent, 0, true, true) +{ +} + +KGameErrorMessageDialog::~KGameErrorMessageDialog() +{ +} + + + +#include "kgameerrordialog.moc" diff --git a/libkdegames/kgame/dialogs/kgameerrordialog.h b/libkdegames/kgame/dialogs/kgameerrordialog.h new file mode 100644 index 00000000..c1dbd1ca --- /dev/null +++ b/libkdegames/kgame/dialogs/kgameerrordialog.h @@ -0,0 +1,113 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KGAMEERRORDIALOG_H__ +#define __KGAMEERRORDIALOG_H__ + +#include + +class KGame; +class KGameErrorDialogPrivate; + +/** + * Use error(), warning() and information() to display the information about a + * network game. Maybe a better solution is to use KMessageBoxes + * You can connect to the public slots, too - they will call the static + * functions, so that you can always have a KGameErrorDialog object lying around + * without losing much memory (a KGameErrorMessageDialog Object will be + * created) + * @short Error handling for KGame + * @author Andreas Beckermann + **/ +class KGameErrorDialog : public QObject +{ + Q_OBJECT +public: + KGameErrorDialog(QWidget* parent); + ~KGameErrorDialog(); + + /** + * Automatically connects the KGame object to all error dependant slots. + * Create a KGameErrorDialog object, call this function and forget + * everything. + * @param g The KGame which will emit the erorrs (or not ;-) ) + **/ + void setKGame(const KGame* g); + + /** + * KGame couldn't establish a connection. Use this if + * KGame::initConnection returns false + * @param s A string that describes the error further (like port is + * already in use). Will be ignored if QString::null + **/ + void connectionError(QString s = QString::null); + +public slots: + void slotError(int error, QString text); + + /** + * The connection to the @ref KMessageServer has been lost + * + * See @ref KGameNetwork::signalConnectionBroken + **/ + void slotServerConnectionLost(); + + /** + * The connection to a client has been lost by accident + * + * See @ref KGameNetwork::signalClientDisconnected + **/ + void slotClientConnectionLost(Q_UINT32 clientID,bool broken); + + /** + * Unsets a @ref KGame which has been set using @ref setKGame before. + * This is called automatically when the @ref KGame object is destroyed + * and you normally don't have to call this yourself. + * + * Note that @ref setKGame also unsets an already existing @ref KGame + * object if exising. + **/ + void slotUnsetKGame(); + +protected: + void error(const QString& errorText, QWidget* parent = 0); + +private: + KGameErrorDialogPrivate* d; +}; + +/** + * The real class for error messages. KGameErrorDialog uses this to create error + * messages (not yet). + * Use @ref KGameErrorDialog instead. + * @short Internally used by @ref KGameErrorDialog + * @author Andreas Beckermann + **/ +class KGameErrorMessageDialog : public KDialogBase +{ + Q_OBJECT +public: + KGameErrorMessageDialog(QWidget* parent); + ~KGameErrorMessageDialog(); + +private: +}; + +#endif diff --git a/libkdegames/kgame/kgame.cpp b/libkdegames/kgame/kgame.cpp new file mode 100644 index 00000000..101dbfcc --- /dev/null +++ b/libkdegames/kgame/kgame.cpp @@ -0,0 +1,1475 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ + +#include "kgame.h" +#include "kgame.moc" +#include "kgamepropertyhandler.h" +#include "kgameproperty.h" +#include "kplayer.h" +#include "kgameio.h" +#include "kgameerror.h" +#include "kgamesequence.h" + +#include "kgamemessage.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#define KGAME_LOAD_COOKIE 4210 + +// try to place as much as possible here +// many things are *not* possible here as KGame has to use some inline function +class KGamePrivate +{ +public: + KGamePrivate() + { + mUniquePlayerNumber = 0; + mPolicy=KGame::PolicyLocal; + mGameSequence = 0; + } + + int mUniquePlayerNumber; + QPtrQueue mAddPlayerList;// this is a list of to-be-added players. See addPlayer() docu + KRandomSequence* mRandom; + KGame::GamePolicy mPolicy; + KGameSequence* mGameSequence; + + + KGamePropertyHandler* mProperties; + + // player lists + KGame::KGamePlayerList mPlayerList; + KGame::KGamePlayerList mInactivePlayerList; + + //KGamePropertys + KGamePropertyInt mMaxPlayer; + KGamePropertyUInt mMinPlayer; + KGamePropertyInt mGameStatus; // Game running? + QValueList mInactiveIdList; + +}; + +// ------------------- GAME CLASS -------------------------- +KGame::KGame(int cookie,QObject* parent) : KGameNetwork(cookie,parent) +{ + kdDebug(11001) << k_funcinfo << " - " << this << ", sizeof(KGame)=" << sizeof(KGame) << endl; + d = new KGamePrivate; + + d->mProperties = new KGamePropertyHandler(this); + + d->mProperties->registerHandler(KGameMessage::IdGameProperty, + this,SLOT(sendProperty(int, QDataStream&, bool* )), + SLOT(emitSignal(KGamePropertyBase *))); + d->mMaxPlayer.registerData(KGamePropertyBase::IdMaxPlayer, this, i18n("MaxPlayers")); + d->mMaxPlayer.setLocal(-1); // Infinite + d->mMinPlayer.registerData(KGamePropertyBase::IdMinPlayer, this, i18n("MinPlayers")); + d->mMinPlayer.setLocal(0); // Always ok + d->mGameStatus.registerData(KGamePropertyBase::IdGameStatus, this, i18n("GameStatus")); + d->mGameStatus.setLocal(Init); + // d->mUniquePlayerNumber = 0; + d->mRandom = new KRandomSequence; + d->mRandom->setSeed(0); + + connect(this, SIGNAL(signalClientConnected(Q_UINT32)), + this, SLOT(slotClientConnected(Q_UINT32))); + connect(this, SIGNAL(signalClientDisconnected(Q_UINT32,bool)), + this, SLOT(slotClientDisconnected(Q_UINT32,bool))); + connect(this, SIGNAL(signalConnectionBroken()), + this, SLOT(slotServerDisconnected())); + + setGameSequence(new KGameSequence()); + + // BL: FIXME This signal does no longer exist. When we are merging + // MH: super....and how do I find out about the lost conenction now? + // KGame and KGameNetwork, this could be improved! +// connect(this,SIGNAL(signalConnectionLost(KGameClient *)), +// this,SLOT(slotConnectionLost(KGameClient *))); +} + +KGame::~KGame() +{ + kdDebug(11001) << k_funcinfo << endl; +// Debug(); + reset(); + delete d->mGameSequence; + delete d->mRandom; + delete d; + kdDebug(11001) << k_funcinfo << " done" << endl; +} + +bool KGame::reset() +{ + deletePlayers(); + deleteInactivePlayers(); + return true; +} + +void KGame::deletePlayers() +{ +// kdDebug(11001) << k_funcinfo << endl; + KGamePlayerList tmp = d->mPlayerList; // in case of PolicyClean player=d->mPlayerList.first() is infinite + KPlayer *player; + while((player=tmp.first())) + { + delete player; // delete and removes the player + tmp.removeFirst(); + } +// kdDebug(11001) << k_funcinfo << " done" << endl; +} + +void KGame::deleteInactivePlayers() +{ + KPlayer *player; + while((player=d->mInactivePlayerList.first())) + { + //player->setGame(0); // prevent call backs + d->mInactivePlayerList.remove(player); + delete player; + } +} + +bool KGame::load(QString filename,bool reset) +{ + if (filename.isNull()) + { + return false; + } + QFile f(filename); + if (!f.open(IO_ReadOnly)) + { + return false; + } + QDataStream s( &f ); + load(s,reset); + f.close(); + return true; +} + +bool KGame::load(QDataStream &stream,bool reset) +{ return loadgame(stream, false,reset); } + +bool KGame::loadgame(QDataStream &stream, bool network,bool resetgame) +{ + // Load Game Data + + // internal data + Q_INT32 c; + stream >> c; // cookie + + if (c!=cookie()) + { + kdWarning(11001) << "Trying to load different game version we="<id() << " to indirect emit" <load(stream); + + // If there is additional data to be loaded before players are loaded then do + // this here. + emit signalLoadPrePlayers(stream); + + // Load Playerobjects + uint playercount; + stream >> playercount; + kdDebug(11001) << "Loading KGame " << playercount << " KPlayer objects " << endl; + for (i=0;i> cookie; + if (cookie==KGAME_LOAD_COOKIE) { + kdDebug(11001) << " Game loaded propertly"<unlockDirectEmit(); + for ( player=playerList()->first(); player != 0; player=playerList()->next() ) + { + player->dataHandler()->unlockDirectEmit(); + // kdDebug(11001) << "Player "<id() << " to direct emit" <mUniquePlayerNumber; + int newseed=(int)d->mRandom->getLong(65535); + stream << newseed; + d->mRandom->setSeed(newseed); + + // Properties + dataHandler()->save(stream); + + // Save all data that need to be saved *before* the players are saved + emit signalSavePrePlayers(stream); + + if (saveplayers) + { + savePlayers(stream,playerList()); + } + else + { + stream << (uint)0; // no players saved + } + + stream << (Q_INT16)KGAME_LOAD_COOKIE; + + emit signalSave(stream); + return true; +} + +void KGame::savePlayer(QDataStream &stream,KPlayer* p) +{ +// this could be in KGameMessage as well + stream << (Q_INT32)p->rtti(); + stream << (Q_INT32)p->id(); + stream << (Q_INT32)p->calcIOValue(); + p->save(stream); +} + +void KGame::savePlayers(QDataStream &stream, KGamePlayerList *list) +{ + if (!list) + { + list=playerList(); + } + + Q_INT32 cnt=list->count(); + kdDebug(11001) << "Saving KGame " << cnt << " KPlayer objects " << endl; + stream << cnt; + KPlayer *player; + for ( player=list->first(); player != 0; player=list->next() ) + { + savePlayer(stream,player); + } +} + +KPlayer *KGame::createPlayer(int /*rtti*/,int /*io*/,bool /*isvirtual*/) +{ + kdWarning(11001) << " No user defined player created. Creating default KPlayer. This crashes if you have overwritten KPlayer!!!! " << endl; + return new KPlayer; +} +KPlayer *KGame::loadPlayer(QDataStream& stream,bool isvirtual) +{ + Q_INT32 rtti,id,iovalue; + stream >> rtti >> id >> iovalue; + KPlayer *newplayer=findPlayer(id); + if (!newplayer) + { + kdDebug(11001) << k_funcinfo << "Player "<< id << " not found...asking user to create one " << endl; + newplayer=createPlayer(rtti,iovalue,isvirtual); + //emit signalCreatePlayer(newplayer,rtti,iovalue,isvirtual,this); + } + /* + if (!newplayer) + { + kdWarning(11001) << " No user defined player created. Creating default KPlayer. This crashes if you have overwritten KPlayer!!!! " << endl; + newplayer=new KPlayer; + } + else + { + kdDebug(11001) << " USER Player " << newplayer << " done player->rtti=" << newplayer->rtti() << " rtti=" << rtti << endl; + } + */ + newplayer->load(stream); + if (isvirtual) + { + newplayer->setVirtual(true); + } + return newplayer; +} + +// ----------------- Player handling ----------------------- + +KPlayer * KGame::findPlayer(Q_UINT32 id) const +{ + for (QPtrListIterator it(d->mPlayerList); it.current(); ++it) + { + if (it.current()->id() == id) + { + return it.current(); + } + } + for (QPtrListIterator it(d->mInactivePlayerList); it.current(); ++it) + { + if (it.current()->id() == id) + { + return it.current(); + } + } + return 0; +} + +// it is necessary that addPlayer and systemAddPlayer are called in the same +// order. Ie if addPlayer(foo) followed by addPlayer(bar) is called, you must +// not call systemAddPlayer(bar) followed by systemAddPlayer(foo), as the +// mAddPlayerList would get confused. Should be no problem as long as comServer +// and the clients are working correctly. +// BUT: if addPlayer(foo) does not arrive by any reason while addPlayer(bar) +// does, we would be in trouble... +void KGame::addPlayer(KPlayer* newplayer) +{ + kdDebug(11001) << k_funcinfo << ": " << "; maxPlayers=" << maxPlayers() << " playerCount=" << playerCount() << endl; + if (!newplayer) + { + kdFatal(11001) << "trying to add NULL player in KGame::addPlayer()" << endl; + return ; + } + + if (maxPlayers() >= 0 && (int)playerCount() >= maxPlayers()) + { + kdWarning(11001) << "cannot add more than " << maxPlayers() << " players - deleting..." << endl; + delete newplayer; + return; + } + + if (newplayer->id() == 0) + { + d->mUniquePlayerNumber++; + newplayer->setId(KGameMessage::createPlayerId(d->mUniquePlayerNumber, gameId())); + kdDebug(11001) << k_funcinfo << "NEW!!! player " << newplayer << " now has id " << newplayer->id() << endl; + } + else + { + // this could happen in games which use their own ID management by certain + // reasons. that is NOT recommended + kdDebug(11001) << k_funcinfo << "player " << newplayer << " already has an id: " << newplayer->id() << endl; + } + + QByteArray buffer; + QDataStream stream(buffer,IO_WriteOnly); + // We distinguis here what policy we have + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + systemAddPlayer(newplayer); + } + if (policy()==PolicyClean || policy()==PolicyDirty) + { + savePlayer(stream,newplayer); + // Store the player for delayed clean adding + if (policy()==PolicyClean) + { + d->mAddPlayerList.enqueue(newplayer); + } + sendSystemMessage(stream,(int)KGameMessage::IdAddPlayer, 0); + } +} + +void KGame::systemAddPlayer(KPlayer* newplayer) +{ + if (!newplayer) + { + kdFatal(11001) << "trying to add NULL player in KGame::systemAddPlayer()" << endl; + return ; + } + if (newplayer->id() == 0) + { + kdWarning(11001) << k_funcinfo << "player " << newplayer << " has no ID" << endl; + } + + if (findPlayer(newplayer->id())) + { + kdError(11001) << "ERROR: Double adding player !!!!! NOT GOOD !!!!!! " << newplayer->id() << "...I delete it again" << endl; + delete newplayer; + } + else + { + kdDebug(11001) << "Trying to add player " << newplayer <<" maxPlayers="<id() << ") to be removed " << player << endl; + + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + systemRemovePlayer(player,false); + } + if (policy()==PolicyClean || policy()==PolicyDirty) + { + if (!player->isVirtual()) + { + kdDebug(11001) << k_funcinfo << ": sending IdRemovePlayer "<id() << endl; + sendSystemMessage(player->id(), KGameMessage::IdRemovePlayer, 0); + } + } +} + +bool KGame::removePlayer(KPlayer * player, Q_UINT32 receiver) +{//transmit to all clients, or to receiver only + if (!player) + { + kdFatal(11001) << "trying to remove NULL player in KGame::removePlayer()" << endl; + return false; + } + kdDebug(11001) << k_funcinfo << ": id (" << player->id() << ") to be removed " << player << endl; + + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + systemRemovePlayer(player,true); + } + if (policy()==PolicyClean || policy()==PolicyDirty) + { + kdDebug(11001) << k_funcinfo << ": sending IdRemovePlayer "<id() << endl; + sendSystemMessage(player->id(),KGameMessage::IdRemovePlayer, receiver); + } + return true; + // we will receive the message in networkTransmission() +} + +void KGame::systemRemovePlayer(KPlayer* player,bool deleteit) +{ + kdDebug(11001) << k_funcinfo << endl; + if (!player) + { + kdWarning(11001) << "cannot remove NULL player" << endl; + return; + } + if (!systemRemove(player,deleteit)) + { + kdWarning(11001) << "player " << player << "(" << player->id() << ") Could not be found!" << endl; + } + + if (gameStatus()==(int)Run && playerCount()id() << ") to be removed " << p << endl; + + if (d->mPlayerList.count() == 0) + { + result = false; + } + else + { + result = d->mPlayerList.remove(p); + } + + emit signalPlayerLeftGame(p); + + p->setGame(0); + if (deleteit) + { + delete p; + } + + return result; +} + +bool KGame::inactivatePlayer(KPlayer* player) +{ + if (!player) + { + return false; + } + kdDebug(11001) << "Inactivate player " << player->id() << endl; + + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + systemInactivatePlayer(player); + } + if (policy()==PolicyClean || policy()==PolicyDirty) + { + sendSystemMessage(player->id(), KGameMessage::IdInactivatePlayer); + } + + return true; +} + +bool KGame::systemInactivatePlayer(KPlayer* player) +{ + if (!player || !player->isActive()) + { + return false; + } + kdDebug(11001) << " Inactivate player " << player->id() << endl; + + int pid=player->id(); + // Virtual players cannot be deactivated. They will be removed + if (player->isVirtual()) + { + systemRemovePlayer(player,true); + } + else + { + d->mPlayerList.remove(player); + d->mInactivePlayerList.prepend(player); + player->setActive(false); + } + emit signalPlayerLeftGame(player); + if (isAdmin()) + { + d->mInactiveIdList.prepend(pid); + } + return true; +} + +bool KGame::activatePlayer(KPlayer * player) +{ + if (!player) + { + return false; + } + kdDebug(11001) << k_funcinfo << ": activate " << player->id() << endl; + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + systemActivatePlayer(player); + } + if (policy()==PolicyClean || policy()==PolicyDirty) + { + sendSystemMessage(player->id(), KGameMessage::IdActivatePlayer); + } + return true; +} + +bool KGame::systemActivatePlayer(KPlayer* player) +{ + if (!player || player->isActive()) + { + return false; + } + kdDebug(11001) << k_funcinfo << ": activate " << player->id() << endl; + + d->mInactivePlayerList.remove(player); + player->setActive(true); + addPlayer(player); + if (isAdmin()) + { + d->mInactiveIdList.remove(player->id()); + } + return true; +} + +// -------------------- Properties --------------------------- + +void KGame::setMaxPlayers(uint maxnumber) +{ if (isAdmin()) { d->mMaxPlayer.changeValue(maxnumber); } } + +void KGame::setMinPlayers(uint minnumber) +{ if (isAdmin()) { d->mMinPlayer.changeValue(minnumber); } } + +uint KGame::minPlayers() const +{ return d->mMinPlayer.value(); } + +int KGame::maxPlayers() const +{ return d->mMaxPlayer.value(); } + +uint KGame::playerCount() const +{ return d->mPlayerList.count(); } + +int KGame::gameStatus() const +{ return d->mGameStatus.value(); } + +bool KGame::isRunning() const +{ return d->mGameStatus.value() == Run; } + +KGamePropertyHandler* KGame::dataHandler() const +{ return d->mProperties; } + + +KGame::KGamePlayerList* KGame::inactivePlayerList() +{ return &d->mInactivePlayerList; } + +const KGame::KGamePlayerList* KGame::inactivePlayerList() const +{ return &d->mInactivePlayerList; } + +KGame::KGamePlayerList* KGame::playerList() +{ return &d->mPlayerList; } + +const KGame::KGamePlayerList* KGame::playerList() const +{ return &d->mPlayerList; } + +KRandomSequence* KGame::random() const +{ return d->mRandom; } + + +bool KGame::sendPlayerInput(QDataStream &msg, KPlayer *player, Q_UINT32 sender) +{ + if (!player) + { + kdError(11001) << k_funcinfo << ": NULL player" << endl; + return false; + } + if (!isRunning()) + { + kdError(11001) << k_funcinfo << ": game not running" << endl; + return false; + } + + kdDebug(11001) << k_funcinfo << ": transmitting playerInput over network" << endl; + sendSystemMessage(msg, (int)KGameMessage::IdPlayerInput, player->id(), sender); + return true; +} + +bool KGame::systemPlayerInput(QDataStream &msg, KPlayer *player, Q_UINT32 sender) +{ + if (!player) + { + kdError(11001) << k_funcinfo << ": NULL player" << endl; + return false; + } + if (!isRunning()) + { + kdError(11001) << k_funcinfo << ": game not running" << endl; + return false; + } + kdDebug(11001) << "KGame: Got playerInput from messageServer... sender: " << sender << endl; + if (playerInput(msg,player)) + { + playerInputFinished(player); + } + else + { + kdDebug(11001) << k_funcinfo<<": switching off player input"<asyncInput()) + { + player->setTurn(false); // in turn based games we have to switch off input now + } + } + return true; +} + + +KPlayer * KGame::playerInputFinished(KPlayer *player) +{ + kdDebug(11001) << k_funcinfo<<"player input finished for "<id()<setCurrentPlayer(player); + } + // do not call gameSequence()->checkGameOver() to keep backward compatibility! + gameOver = checkGameOver(player); + if (gameOver!=0) + { + if (player) + { + player->setTurn(false); + } + setGameStatus(End); + emit signalGameOver(gameOver,player,this); + } + else if (!player->asyncInput()) + { + player->setTurn(false); // in turn based games we have to switch off input now + if (gameSequence()) + { + QTimer::singleShot(0,this,SLOT(prepareNext())); + } + } + return player; +} + +// Per default we do not do anything +int KGame::checkGameOver(KPlayer *player) +{ + if (gameSequence()) + { + return gameSequence()->checkGameOver(player); + } + return 0; +} + +void KGame::setGameSequence(KGameSequence* sequence) +{ + delete d->mGameSequence; + d->mGameSequence = sequence; + if (d->mGameSequence) + { + d->mGameSequence->setGame(this); + } +} + +KGameSequence* KGame::gameSequence() const +{ + return d->mGameSequence; +} + +void KGame::prepareNext() +{ + if (gameSequence()) + { + // we don't call gameSequence->nextPlayer() to keep old code working + nextPlayer(gameSequence()->currentPlayer()); + } +} + +KPlayer *KGame::nextPlayer(KPlayer *last,bool exclusive) +{ + if (gameSequence()) + { + return gameSequence()->nextPlayer(last, exclusive); + } + return 0; +} + +void KGame::setGameStatus(int status) +{ + kdDebug(11001) << k_funcinfo << ": GAMESTATUS CHANGED to" << status << endl; + if (status==(int)Run && playerCount()mGameStatus = status; +} + +void KGame::networkTransmission(QDataStream &stream, int msgid, Q_UINT32 receiver, Q_UINT32 sender, Q_UINT32 /*clientID*/) +{//clientID is unused + // message targets a playerobject. If we find it we forward the message to the + // player. Otherwise we proceed here and hope the best that the user processes + // the message + +// kdDebug(11001) << k_funcinfo << ": we="<<(int)gameId()<<" id="<isActive()<<" recv="<< receiver << endl; + KPlayer *p=findPlayer(receiver); + if (p && p->isActive()) + { + p->networkTransmission(stream,msgid,sender); + return; + } + if (p) + { + kdDebug(11001) << "player is here but not active" << endl; + } + else + { + kdDebug(11001) << "no player found" << endl; + } + } + // If it is not for a player it is meant for us!!!! Otherwise the + // gamenetwork would not have passed the message to us! + + // GameProperties processed + if (d->mProperties->processMessage(stream, msgid, sender == gameId())) + { +// kdDebug(11001 ) << "KGame: message taken by property - returning" << endl; + return ; + } + + switch(msgid) + { + case KGameMessage::IdSetupGame: // Client: First step in setup game + { + Q_INT16 v; + Q_INT32 c; + stream >> v >> c; + kdDebug(11001) << " ===================> (Client) " << k_funcinfo << ": Got IdSetupGame ================== " << endl; + kdDebug(11001) << "our game id is " << gameId() << " Lib version=" << v << " App Cookie=" << c << endl; + // Verify identity of the network partners + if (c!=cookie()) + { + kdError(11001) << "IdGameSetup: Negotiate Game: cookie mismatch I'am="<(Master) " << k_funcinfo << " - IdSetupGameContinue" << endl; + setupGameContinue(stream, sender); + } + break; + case KGameMessage::IdActivatePlayer: // Activate Player + { + int id; + stream >> id; + kdDebug(11001) << "Got IdActivatePlayer id=" << id << endl; + if (sender!=gameId() || policy()!=PolicyDirty) + { + systemActivatePlayer(findPlayer(id)); + } + } + break; + case KGameMessage::IdInactivatePlayer: // Inactivate Player + { + int id; + stream >> id; + kdDebug(11001) << "Got IdInactivatePlayer id=" << id << endl; + if (sender!=gameId() || policy()!=PolicyDirty) + { + systemInactivatePlayer(findPlayer(id)); + } + } + break; + case KGameMessage::IdAddPlayer: + { + kdDebug(11001) << k_funcinfo << ": Got IdAddPlayer" << endl; + if (sender!=gameId() || policy()!=PolicyDirty) + { + KPlayer *newplayer=0; + // We sent the message so the player is already available + if (sender==gameId()) + { + kdDebug(11001) << "dequeue previously added player" << endl; + newplayer = d->mAddPlayerList.dequeue(); + } + else + { + newplayer=loadPlayer(stream,true); + } + systemAddPlayer(newplayer);// the final, local, adding + //systemAddPlayer(stream); + } + } + break; + case KGameMessage::IdRemovePlayer: // Client should delete player id + { + int id; + stream >> id; + kdDebug(11001) << k_funcinfo << ": Got IdRemovePlayer " << id << endl; + KPlayer *p=findPlayer(id); + if (p) + { + // Otherwise the player is already removed + if (sender!=gameId() || policy()!=PolicyDirty) + { + systemRemovePlayer(p,true); + } + } + else + { + kdWarning(11001) << k_funcinfo << "Cannot find player " << id << endl; + } + } + break; + case KGameMessage::IdGameLoad: + { + kdDebug(11001) << "====> (Client) " << k_funcinfo << ": Got IdGameLoad" << endl; + loadgame(stream,true,false); + } + break; + case KGameMessage::IdGameSetupDone: + { + int cid; + stream >> cid; + kdDebug(11001) << "====> (CLIENT) " << k_funcinfo << ": Got IdGameSetupDone for client " + << cid << " we are =" << gameId() << endl; + sendSystemMessage(gameId(), KGameMessage::IdGameConnected, 0); + } + break; + case KGameMessage::IdGameConnected: + { + int cid; + stream >> cid; + kdDebug(11001) << "====> (ALL) " << k_funcinfo << ": Got IdGameConnected for client "<< cid << " we are =" << gameId() << endl; + emit signalClientJoinedGame(cid,this); + } + break; + + case KGameMessage::IdSyncRandom: // Master forces a new random seed on us + { + int newseed; + stream >> newseed; + kdDebug(11001) << "CLIENT: setting random seed to " << newseed << endl; + d->mRandom->setSeed(newseed); + } + break; + case KGameMessage::IdDisconnect: + { + // if we disconnect we *always* start a local game. + // this could lead into problems if we just change the message server + if (sender != gameId()) + { + kdDebug(11001) << "client " << sender << " leaves game" << endl; + return; + } + kdDebug(11001) << "leaving the game" << endl; + // start a new local game + // no other client is by default connected to this so this call should be + // enough + setMaster(); + } + break; + default: + { + if (msgid < KGameMessage::IdUser) + { + kdError(11001) << "incorrect message id " << msgid << " - emit anyway" + << endl; + } + kdDebug(11001) << k_funcinfo << ": User data msgid " << msgid << endl; + emit signalNetworkData(msgid - KGameMessage::IdUser,((QBuffer*)stream.device())->readAll(),receiver,sender); + } + break; + } + +} + +// called by the IdSetupGameContinue Message - MASTER SIDE +// Here the master needs to decide which players can take part at the game +// and which will be deactivated +void KGame::setupGameContinue(QDataStream& stream, Q_UINT32 sender) +{ + KPlayer *player; + Q_INT32 cnt; + int i; + stream >> cnt; + + QValueList inactivateIds; + + KGamePlayerList newPlayerList; + newPlayerList.setAutoDelete(true); + for (i=0;iid() <<" rawgame=" << KGameMessage::rawGameId(player->id()) << " from sender " << sender << endl; + if (KGameMessage::rawGameId(player->id()) != sender) + { + kdError(11001) << "Client tries to add player with wrong game id - cheat possible" << endl; + } + else + { + newPlayerList.append(player); + kdDebug(11001) << " newplayerlist appended " << player->id() << endl; + } + } + + newPlayersJoin(playerList(),&newPlayerList,inactivateIds); + + + kdDebug(11001) << " Master calculates how many players to activate client has cnt=" << cnt << endl; + kdDebug(11001) << " The game has " << playerCount() << " active players" << endl; + kdDebug(11001) << " The user deactivated "<< inactivateIds.count() << " player already " << endl; + kdDebug(11001) << " MaxPlayers for this game is " << maxPlayers() << endl; + + // Do we have too many players? (After the programmer disabled some?) + // MH: We cannot use have player here as it CHANGES in the loop + // int havePlayers = cnt+playerCount()-inactivateIds.count(); + kdDebug(11001) << " havePlayers " << cnt+playerCount()-inactivateIds.count() << endl; + while (maxPlayers() > 0 && maxPlayers() < (int)(cnt+playerCount() - inactivateIds.count())) + { + kdDebug(11001) << " Still to deacticvate " + << (int)(cnt+playerCount()-inactivateIds.count())-(int)maxPlayers() + << endl; + KPlayer *currentPlayer=0; + int currentPriority=0x7fff; // MAX_UINT (16bit?) to get the maximum of the list + // find lowest network priority which is not yet in the newPlayerList + // do this for the new players + for ( player=newPlayerList.first(); player != 0; player=newPlayerList.next() ) + { + // Already in the list + if (inactivateIds.find(player->id())!=inactivateIds.end()) + { + continue; + } + if (player->networkPriority()networkPriority(); + currentPlayer=player; + } + } + + // find lowest network priority which is not yet in the newPlayerList + // Do this for the network players + for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() ) + { + // Already in the list + if (inactivateIds.find(player->id())!=inactivateIds.end()) + { + continue; + } + if (player->networkPriority()networkPriority(); + currentPlayer=player; + } + } + + // add it to inactivateIds + if (currentPlayer) + { + kdDebug(11001) << "Marking player " << currentPlayer->id() << " for inactivation" << endl; + inactivateIds.append(currentPlayer->id()); + } + else + { + kdError(11001) << "Couldn't find a player to dectivate..That is not so good..." << endl; + break; + } + } + + kdDebug(11001) << "Alltogether deactivated " << inactivateIds.count() << " players" << endl; + + QValueList::Iterator it; + for ( it = inactivateIds.begin(); it != inactivateIds.end(); ++it ) + { + int pid=*it; + kdDebug(11001) << " pid=" << pid << endl; + } + + // Now deactivate the network players from the inactivateId list + //QValueList::Iterator it; + for ( it = inactivateIds.begin(); it != inactivateIds.end(); ++it ) + { + int pid=*it; + if (KGameMessage::rawGameId(pid) == sender) + { + continue; // client's player + } + kdDebug(11001) << " -> the network needs to deactivate " << pid <id(), KGameMessage::IdInactivatePlayer); + } + } + else + { + kdError(11001) << " We should deactivate a player, but cannot find it...not good." << endl; + } + } + + // Now send out the player list which the client can activate + for ( player=newPlayerList.first(); player != 0; player=newPlayerList.next() ) + { + kdDebug(11001) << " newplayerlist contains " << player->id() << endl; + // Only activate what is not in the list + if (inactivateIds.find(player->id())!=inactivateIds.end()) + { + continue; + } + kdDebug(11001) << " -> the client can ******** reactivate ******** " << player->id() << endl; + sendSystemMessage(player->id(), KGameMessage::IdActivatePlayer, sender); + } + + // Save the game over the network + QByteArray bufferS; + QDataStream streamS(bufferS,IO_WriteOnly); + // Save game over netowrk and save players + savegame(streamS,true,true); + sendSystemMessage(streamS,KGameMessage::IdGameLoad,sender); + + + // Only to the client first , as the client will add players + sendSystemMessage(sender, KGameMessage::IdGameSetupDone, sender); +} + +// called by the IdSetupGame Message - CLIENT SIDE +// Client needs to prepare for network transfer +void KGame::setupGame(Q_UINT32 sender) +{ + QByteArray bufferS; + QDataStream streamS(bufferS,IO_WriteOnly); + + // Deactivate all players + KGamePlayerList mTmpList(d->mPlayerList); // we need copy otherwise the removal crashes + Q_INT32 cnt=mTmpList.count(); + kdDebug(11001) << "Client: playerlistcount=" << d->mPlayerList.count() << " tmplistcout=" << cnt << endl; + + streamS << cnt; + + QPtrListIterator it(mTmpList); + KPlayer *player; + while (it.current()) + { + player=it.current(); + systemInactivatePlayer(player); + // Give the new game id to all players (which are inactivated now) + player->setId(KGameMessage::createPlayerId(player->id(),gameId())); + + // Save it for the master to decide what to do + savePlayer(streamS,player); + + ++it; + --cnt; + } + if (d->mPlayerList.count() > 0 || cnt!=0) + { + kdFatal(11001) << "KGame::setupGame(): Player list is not empty! or cnt!=0=" <mRandom->getLong(65535); + sendSystemMessage(newseed,KGameMessage::IdSyncRandom); // Broadcast + d->mRandom->setSeed(newseed); +} + +void KGame::Debug() +{ + KGameNetwork::Debug(); + kdDebug(11001) << "------------------- KGAME -------------------------" << endl; + kdDebug(11001) << "this: " << this << endl; + kdDebug(11001) << "uniquePlayer " << d->mUniquePlayerNumber << endl; + kdDebug(11001) << "gameStatus " << gameStatus() << endl; + kdDebug(11001) << "MaxPlayers : " << maxPlayers() << endl; + kdDebug(11001) << "NoOfPlayers : " << playerCount() << endl; + kdDebug(11001) << "NoOfInactive: " << d->mInactivePlayerList.count() << endl; + kdDebug(11001) << "---------------------------------------------------" << endl; +} + +void KGame::slotClientConnected(Q_UINT32 clientID) +{ + if (isAdmin()) + { + negotiateNetworkGame(clientID); + } +} + +void KGame::slotServerDisconnected() // Client side +{ + kdDebug(11001) << "======= SERVER DISCONNECT ======="<mPlayerList.first(); player != 0; player=d->mPlayerList.next() ) + { + // TODO: CHECK: id=0, could not connect to server in the first place?? + if (KGameMessage::rawGameId(player->id()) != gameId() && gameId()!=0) + { + kdDebug(11001) << "Player " << player->id() << " belongs to a removed game" << endl; + removeList.append(player); + } + } + + for ( player=removeList.first(); player != 0; player=removeList.next() ) + { + bool remove = true; + emit signalReplacePlayerIO(player, &remove); + if (remove) + { + kdDebug(11001) << " ---> Removing player " << player->id() << endl; + systemRemovePlayer(player,true); // no network necessary + } + } + + setMaster(); + kdDebug(11001) << " our game id is after setMaster " << gameId() << endl; + + KGamePlayerList mReList(d->mInactivePlayerList); + for ( player=mReList.first(); player != 0; player=mReList.next() ) + { + // TODO ?check for priority? Sequence should be ok + if ((int)playerCount()id() << " as we are now local" << endl; + } + // TODO clear inactive lists ? + Debug(); + for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() ) + { + player->Debug(); + } + kdDebug(11001) << "+++++++++++" << k_funcinfo << " DONE=" << endl; + emit signalClientLeftGame(0,oldgamestatus,this); +} + +void KGame::slotClientDisconnected(Q_UINT32 clientID,bool /*broken*/) // server side +{ + kdDebug(11001) << "++++(SERVER)+++++++" << k_funcinfo << " clientId=" << clientID << endl; + + int oldgamestatus=gameStatus(); + + KPlayer *player; + KGamePlayerList removeList; + kdDebug(11001) << "Playerlist of client=" << d->mPlayerList.count() << " count" << endl; + for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() ) + { + if (KGameMessage::rawGameId(player->id())==clientID) + { + kdDebug(11001) << "Player " << player->id() << " belongs to the removed game" << endl; + removeList.append(player); + } + } + + for ( player=removeList.first(); player != 0; player=removeList.next() ) + { + // try to replace the KGameIO first + bool remove = true; + emit signalReplacePlayerIO(player, &remove); + if (remove) { + // otherwise (no new KGameIO) remove the player + kdDebug(11001) << " ---> Removing player " << player->id() << endl; + removePlayer(player,0); + } + } + + // Now add inactive players - sequence should be ok + // TODO remove players from removed game + for (unsigned int idx=0;idxmInactiveIdList.count();idx++) + { + QValueList::Iterator it1 = d->mInactiveIdList.at(idx); + player = findPlayer(*it1); + if (((int)playerCount() < maxPlayers() || maxPlayers() < 0) && player && KGameMessage::rawGameId(*it1) != clientID) + { + activatePlayer(player); + } + } + emit signalClientLeftGame(clientID,oldgamestatus,this); +} + + +// -------------------- Synchronisation ----------------------- + +// this initializes a newly connected client. +// we send the number of players (including type) as well as game status and +// properties to the client. After the initialization has been completed both +// clients should have the same status (ie players, properties, etc) +void KGame::negotiateNetworkGame(Q_UINT32 clientID) +{ + kdDebug(11001) << "===========================" << k_funcinfo << ": clientID=" << clientID << " =========================== "<< endl; + if (!isAdmin()) + { + kdError(11001) << k_funcinfo << ": Serious WARNING..only gameAdmin should call this" << endl; + return ; + } + + QByteArray buffer; + QDataStream streamGS(buffer,IO_WriteOnly); + + // write Game setup specific data + //streamGS << (Q_INT32)maxPlayers(); + //streamGS << (Q_INT32)minPlayers(); + + // send to the newly connected client *only* + Q_INT16 v=KGameMessage::version(); + Q_INT32 c=cookie(); + streamGS << v << c; + sendSystemMessage(streamGS, KGameMessage::IdSetupGame, clientID); +} + +bool KGame::sendGroupMessage(const QByteArray &msg, int msgid, Q_UINT32 sender, const QString& group) +{ +// AB: group must not be i18n'ed!! we should better use an id for group and use +// a groupName() for the name // FIXME + KPlayer *player; + for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() ) + { + if (player && player->group()==group) + { + sendMessage(msg,msgid,player->id(), sender); + } + } + return true; +} + +bool KGame::sendGroupMessage(const QDataStream &msg, int msgid, Q_UINT32 sender, const QString& group) +{ return sendGroupMessage(((QBuffer*)msg.device())->buffer(), msgid, sender, group); } + +bool KGame::sendGroupMessage(const QString& msg, int msgid, Q_UINT32 sender, const QString& group) +{ + QByteArray buffer; + QDataStream stream(buffer, IO_WriteOnly); + stream << msg; + return sendGroupMessage(stream, msgid, sender, group); +} + +bool KGame::addProperty(KGamePropertyBase* data) +{ return dataHandler()->addProperty(data); } + +bool KGame::sendPlayerProperty(int msgid, QDataStream& s, Q_UINT32 playerId) +{ return sendSystemMessage(s, msgid, playerId); } + +void KGame::sendProperty(int msgid, QDataStream& stream, bool* sent) +{ + bool s = sendSystemMessage(stream, msgid); + if (s) + { + *sent = true; + } +} + +void KGame::emitSignal(KGamePropertyBase *me) +{ + emit signalPropertyChanged(me,this); +} + +KGamePropertyBase* KGame::findProperty(int id) const +{ return d->mProperties->find(id); } + +KGame::GamePolicy KGame::policy() const +{ + return d->mPolicy; +} +void KGame::setPolicy(GamePolicy p,bool recursive) +{ + // Set KGame policy + d->mPolicy=p; + if (recursive) + { + // Set all KGame property policy + dataHandler()->setPolicy((KGamePropertyBase::PropertyPolicy)p,false); + + // Set all KPLayer (active or inactive) property policy + for (QPtrListIterator it(d->mPlayerList); it.current(); ++it) + { + it.current()->dataHandler()->setPolicy((KGamePropertyBase::PropertyPolicy)p,false); + } + for (QPtrListIterator it(d->mInactivePlayerList); it.current(); ++it) + { + it.current()->dataHandler()->setPolicy((KGamePropertyBase::PropertyPolicy)p,false); + } + } +} + +/* + * vim: et sw=2 + */ diff --git a/libkdegames/kgame/kgame.h b/libkdegames/kgame/kgame.h new file mode 100644 index 00000000..37d8d974 --- /dev/null +++ b/libkdegames/kgame/kgame.h @@ -0,0 +1,932 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ +#ifndef __KGAME_H_ +#define __KGAME_H_ + +#include +#include +#include + +#include "kgamenetwork.h" +#include +class KRandomSequence; + +class KPlayer; +class KGamePropertyBase; +class KGamePropertyHandler; +class KGameSequence; + +class KGamePrivate; + +/** + * @short The main KDE game object + * + * The KGame class is the central game object. A game basically + * consists of following features: + * - Player handling (add, remove,...) + * - Game status (end,start,pause,...) + * - load/save + * - Move (and message) handling + * - nextPlayer and gameOver() + * - Network connection (for KGameNetwork) + * + * Example: + * \code + * KGame *game=new KGame; + * \endcode + * + * + * @author Martin Heni + * + */ +class KDE_EXPORT KGame : public KGameNetwork +{ + Q_OBJECT + +public: + typedef QPtrList KGamePlayerList; + + /** + * The policy of the property. This can be PolicyClean (setVale uses + * send), PolicyDirty (setValue uses changeValue) or + * PolicyLocal (setValue uses setLocal). + * + * A "clean" policy means that the property is always the same on every + * client. This is achieved by calling send which actually changes + * the value only when the message from the MessageServer is received. + * + * A "dirty" policy means that as soon as setValue is called the + * property is changed immediately. And additionally sent over network. + * This can sometimes lead to bugs as the other clients do not + * immediately have the same value. For more information see + * changeValue. + * + * PolicyLocal means that a KGameProperty behaves like ever + * "normal" variable. Whenever setValue is called (e.g. using "=") + * the value of the property is changes immediately without sending it + * over network. You might want to use this if you are sure that all + * clients set the property at the same time. + **/ + enum GamePolicy + { + PolicyUndefined = 0, + PolicyClean = 1, + PolicyDirty = 2, + PolicyLocal = 3 + }; + + /** + * Create a KGame object. The cookie is used to identify your + * game in load/save and network operations. Change this between + * games. + */ + KGame(int cookie=42,QObject* parent=0); + + /** + * Destructs the game + */ + virtual ~KGame(); + + /** + * Gives debug output of the game status + */ + virtual void Debug(); + + /** + * Game status - Use this to Control the game flow. + * The KGame e.g. sets the status to Pause when you have + * less player than the minimum amount + */ + enum GameStatus + { + Init = 0, + Run = 1, + Pause = 2, + End = 3, + Abort = 4, + SystemPause = 5, + Intro = 6, + UserStatus = 7 + }; + + // Properties + /** + * Returns a list of all active players + * + * @return the list of players + */ + KGamePlayerList *playerList(); + + /** + * The same as @ref playerList but returns a const pointer. + **/ + const KGamePlayerList *playerList() const; + + /** + * Returns a list of all inactive players + * @return the list of players + */ + KGamePlayerList *inactivePlayerList(); + + /** + * The same as @ref inactivePlayerList but returns a const pointer. + **/ + const KGamePlayerList *inactivePlayerList() const; + + /** + * Returns a pointer to the game's KRandomSequence. This sequence is + * identical for all network players! + * @return KRandomSequence pointer + */ + KRandomSequence *random() const; + + /** + * @return The KGameSequence object that is currently in use. + * @see setGameSequence + **/ + KGameSequence *gameSequence() const; + + /** + * Is the game running + * @return true/false + */ + bool isRunning() const; + + // Player handling + /** + * Returns the player object for a given player id + * @param id Player id + * @return player object + */ + KPlayer *findPlayer(Q_UINT32 id) const; + + /** + * Set a new @ref KGameSequence to control player management. By default + * KGame uses a normal @ref KGameSequence object. You might want to subclass + * that and provide your own object. + * + * The previous sequence will get deleted. + * @param sequence The new game sequence object. KGame takes ownership and + * will delete it on destruction! + **/ + void setGameSequence(KGameSequence* sequence); + + /** + * Note that KPlayer::save must be implemented properly, as well as + * KPlayer::rtti + * This will only send a message to all clients. The player is _not_ added + * directly! + * See also playerInput which will be called as soon as the + * player really has been added. + * + * Note that an added player will first get into a "queue" and won't be in + * the game. It will be added to the game as soon as systemAddPlayer is + * called what will happen as soon as IdAddPlayer is received. + * + * Note: you probably want to connect to signalPlayerJoinedGame for + * further initialization! + * @param newplayer The player you want to add. KGame will send a message to + * all clients and add the player using systemAddPlayer + **/ + void addPlayer(KPlayer* newplayer); + + /** + * Sends a message over the network, msgid=IdRemovePlayer. + * + * As soon as this message is received by networkTransmission + * systemRemovePlayer is called and the player is removed. + **/ + //AB: TODO: make sendMessage to return if the message will be able to be + //sent, eg if a socket is connected, etc. If sendMessage returns false + //remove the player directly using systemRemovePlayer + bool removePlayer(KPlayer * player) { return removePlayer(player, 0); } + + /** + * Called by the destructor of KPlayer to remove itself from the game + * + **/ + void playerDeleted(KPlayer * player); + + /** + * sends activate player: internal use only? + */ + bool activatePlayer(KPlayer *player); + + /** + * sends inactivate player: internal use only? + */ + bool inactivatePlayer(KPlayer *player); + + /** + * Set the maximal number of players. After this is + * reached no more players can be added. You must be ADMIN to call this (@see + * isAdmin). + * @param maxnumber maximal number of players + */ + void setMaxPlayers(uint maxnumber); + + /** + * What is the maximal number of players? + * @return maximal number of players + */ + int maxPlayers() const; + + /** + * Set the minimal number of players. A game can not be started + * with less player resp. is paused when already running. You must be ADMIN + * to call this (see @ref isAdmin)! + * @param minnumber minimal number of players + */ + void setMinPlayers(uint minnumber); + + /** + * What is the minimal number of players? + * @return minimal number of players + */ + uint minPlayers() const; + + /** + * Returns how many players are plugged into the game + * @return number of players + */ + uint playerCount() const; + + /** + * @deprecated + * Use @ref KGameSequence::nextPlayer instead + **/ + virtual KPlayer * nextPlayer(KPlayer *last,bool exclusive=true); + + // Input events + /** + * Called by KPlayer to send a player input to the + * KMessageServer. + **/ + virtual bool sendPlayerInput(QDataStream &msg,KPlayer *player,Q_UINT32 sender=0); + + /** + * Called when a player input arrives from KMessageServer. + * + * Calls prepareNext (using QTimer::singleShot) if gameOver() + * returns 0. This function should normally not be used outside KGame. + * It could be made non-virtual,protected in a later version. At the + * moment it is a virtual function to give you more control over KGame. + * + * For documentation see playerInput. + **/ + virtual bool systemPlayerInput(QDataStream &msg,KPlayer *player,Q_UINT32 sender=0); + + /** + * This virtual function is called if the KGame needs to create a new player. + * This happens only over a network and with load/save. Doing nothing + * will create a default KPlayer. If you want to have your own player + * you have to create one with the given rtti here. + * Note: If your game uses a player class derived from KPlayer you MUST + * override this function and create your player here. Otherwise the + * game will crash. + * Example: + * \code + * KPlayer *MyGame::createPlayer(int rtti,int io,bool isvirtual) + * { + * KPlayer *player=new MyPlayer; + * if (!isvirtual) // network player ? + * { + * // Define something like this to add the IO modules + * createIO(player,(KGameIO::IOMode)io); + * } + * return player; + * } + * \endcode + * + * @param rtti is the type of the player (0 means default KPlayer) + * @param io is the 'or'ed rtti of the KGameIO's + * @param isvirtual true if player is virtual + */ + virtual KPlayer *createPlayer(int rtti,int io,bool isvirtual); + + // load/save + /** + * Load a saved game, from file OR network. This function has + * to be overwritten or you need to connect to the load signal + * if you have game data other than KGameProperty. + * For file load you should reset() the game before any load attempt + * to make sure you load into an clear state. + * + * @param stream a data stream where you can stream the game from + * @param reset - shall the game be reset before loading + * + * @return true? + */ + virtual bool load(QDataStream &stream,bool reset=true); + + /** + * Same as above function but with different parameters + * + * @param filename - the filename of the file to be opened + * @param reset - shall the game be reset before loading + * + * @return true? + **/ + virtual bool load(QString filename,bool reset=true); + + /** + * Save a game to a file OR to network. Otherwise the same as + * the load function + * + * @param stream a data stream to load the game from + * @param saveplayers If true then all players wil be saved too + * + * @return true? + */ + virtual bool save(QDataStream &stream,bool saveplayers=true); + + /** + * Same as above function but with different parameters + * + * @param filename the filename of the file to be saved + * @param saveplayers If true then all players wil be saved too + * + * @return true? + **/ + virtual bool save(QString filename,bool saveplayers=true); + + /** + * Resets the game, i.e. puts it into a state where everything + * can be started from, e.g. a load game + * Right now it does only need to delete all players + * + * @return true on success + */ + virtual bool reset(); + + + // Game sequence + /** + * returns the game status, ie running,pause,ended,... + * + * @return game status + */ + int gameStatus() const; + + /** + * sets the game status + * + * @param status the new status + */ + void setGameStatus(int status); + + /** + * docu: see KPlayer + **/ + bool addProperty(KGamePropertyBase* data); + + /** + * This is called by KPlayer::sendProperty only! Internal function! + **/ + bool sendPlayerProperty(int msgid, QDataStream& s, Q_UINT32 playerId); + + /** + * This function allows to find the pointer to a player + * property when you know it's id + */ + KGamePropertyBase* findProperty(int id) const; + + /** + * Changes the consistency policy of a property. The + * GamePolicy is one of PolicyClean (default), PolicyDirty or PolicyLocal. + * + * It is up to you to decide how you want to work. + **/ + void setPolicy(GamePolicy p,bool recursive=true); + + /** + * @return The default policy of the property + **/ + GamePolicy policy() const; + + /** + * See KGameNetwork::sendMessage + * + * Send a network message msg with a given message ID msgid to all players of + * a given group (see KPlayer::group) + * @param msg the message which will be send. See messages.txt for contents + * @param msgid an id for this message + * @param sender the id of the sender + * @param group the group of the receivers + * @return true if worked + */ + bool sendGroupMessage(const QByteArray& msg, int msgid, Q_UINT32 sender, const QString& group); + bool sendGroupMessage(const QDataStream &msg, int msgid, Q_UINT32 sender, const QString& group); + bool sendGroupMessage(int msg, int msgid, Q_UINT32 sender, const QString& group); + bool sendGroupMessage(const QString& msg, int msgid, Q_UINT32 sender, const QString& group); + + /** + * This will either forward an incoming message to a specified player + * (see KPlayer::networkTransmission) or + * handle the message directly (e.g. if msgif==IdRemovePlayer it will remove + * the (in the stream) specified player). If both is not possible (i.e. the + * message is user specified data) the signal signalNetworkData is + * emitted. + * + * This emits signalMessageUpdate before doing anything with + * the message. You can use this signal when you want to be notified about + * an update/change. + * @param msgid Specifies the kind of the message. See messages.txt for + * further information + * @param stream The message that is being sent + * @param receiver The is of the player this message is for. 0 For broadcast. + * @param sender + * @param clientID the client from which we received the transmission - hardly used + **/ + virtual void networkTransmission(QDataStream &stream, int msgid, Q_UINT32 receiver, Q_UINT32 sender, Q_UINT32 clientID); + + /** + * Returns a pointer to the KGame property handler + **/ + KGamePropertyHandler* dataHandler() const; + +protected slots: + /** + * Called by KGamePropertyHandler only! Internal function! + **/ + void sendProperty(int msgid, QDataStream& stream, bool* sent); + + /** + * Called by KGamePropertyHandler only! Internal function! + **/ + void emitSignal(KGamePropertyBase *me); + + /** + * @deprecated + * Use KGameSequence::prepareNext() instead + **/ + virtual void prepareNext(); + + + /** + * Calls negotiateNetworkGame() + * See KGameNetwork::signalClientConnected + **/ + void slotClientConnected(Q_UINT32 clientId); + + /** + * This slot is called whenever the connection to a client is lost (ie the + * signal KGameNetwork::signalClientDisconnected is emitted) and will remove + * the players from that client. + * @param clientId The client the connection has been lost to + * @param broken (ignore this - not used) + **/ + void slotClientDisconnected(Q_UINT32 clientId,bool broken); + + /** + * This slot is called whenever the connection to the server is lost (ie the + * signal KGameNetwork::signalConnectionBroken is emitted) and will + * switch to local game mode + **/ + void slotServerDisconnected(); + +signals: + /** + * When a client disconnects from the game usually all players from that + * client are removed. But if you use completely the KGame structure you + * probably don't want this. You just want to replace the KGameIO of the + * (human) player by a computer KGameIO. So this player continues game but + * is from this point on controlled by the computer. + * + * You achieve this by connecting to this signal. It is emitted as soon as a + * client disconnects on all other clients. Make sure to add a new + * KGameIO only once! you might want to use @ref isAdmin for this. If you + * added a new KGameIO set *remove=false otherwise the player is completely + * removed. + * @param player The player that is about to be removed. Add your new + * KGameIO here - but only on one client! + * @param remove Set this to FALSE if you don't want this player to be + * removed completely. + **/ + void signalReplacePlayerIO(KPlayer* player, bool* remove); + + /** + * The game will be loaded from the given stream. Load from here + * the data which is NOT a game or player property. + * It is not necessary to use this signal for a full property game. + * + * This signal is emitted before the players are loaded by + * KGame. See also signalLoad + * + * You must load exactly the same data from the stream that you have saved + * in signalSavePrePlayers. Otherwise player loading will not work + * anymore. + * + * @param stream the load stream + */ + void signalLoadPrePlayers(QDataStream &stream); + + /** + * The game will be loaded from the given stream. Load from here + * the data which is NOT a game or player property. + * It is not necessary to use this signal for a full property game. + * + * @param stream the load stream + */ + void signalLoad(QDataStream &stream); + + /** + * The game will be saved to the given stream. Fill this with data + * which is NOT a game or player property. + * It is not necessary to use this signal for a full property game. + * + * This signal is emitted before the players are saved by + * KGame. See also signalSave + * + * If you can choose between signalSavePrePlayers and signalSave then + * better use signalSave + * + * @param stream the save stream + **/ + void signalSavePrePlayers(QDataStream &stream); + + /** + * The game will be saved to the given stream. Fill this with data + * which is NOT a game or player property. + * It is not necessary to use this signal for a full property game. + * + * @param stream the save stream + */ + void signalSave(QDataStream &stream); + + /** + * Is emmited if a game with a different version cookie is loaded. + * Normally this should result in an error. But maybe you do support + * loading of older game versions. Here would be a good place to do a + * conversion. + * + * @param stream - the load stream + * @param network - true if this is a network connect. False for load game + * @param cookie - the saved cookie. It differs from KGame::cookie() + * @param result - set this to true if you managed to load the game + */ + void signalLoadError(QDataStream &stream,bool network,int cookie, bool &result); + + /** + * We got an user defined update message. This is usually done + * by a sendData in a inherited KGame Object which defines its + * own methods and has to syncronise them over the network. + * Reaction to this is usually a call to a KGame function. + */ + void signalNetworkData(int msgid,const QByteArray& buffer, Q_UINT32 receiver, Q_UINT32 sender); + + /** + * We got an network message. this can be used to notify us that something + * changed. What changed can be seen in the message id. Whether this is + * the best possible method to do this is unclear... + */ + void signalMessageUpdate(int msgid,Q_UINT32 receiver,Q_UINT32 sender); + + /** + * a player left the game because of a broken connection or so! + * + * Note that when this signal is emitted the player is not part of @ref + * playerList anymore but the pointer is still valid. You should do some + * final cleanups here since the player is usually deleted after the signal + * is emitted. + * + * @param player the player who left the game + */ + void signalPlayerLeftGame(KPlayer *player); + + /** + * a player joined the game + * + * @param player the player who joined the game + */ + void signalPlayerJoinedGame(KPlayer *player); + + + /** + * This signal is emmited if a player property changes its value and + * the property is set to notify this change + */ + void signalPropertyChanged(KGamePropertyBase *property, KGame *me); + + /** + * Is emitted after a call to gameOver() returns a non zero + * return code. This code is forwarded to this signal as 'status'. + * + * @param status the return code of gameOver() + * @param current the player who did the last move + * @param me a pointer to the KGame object + */ + void signalGameOver(int status, KPlayer *current, KGame *me); + + /** + * Is emmited after a client is successfully connected to the game. + * The client id is the id of the new game client. An easy way to + * check whether that's us is + * \code + * if (clientid==gameid()) .. // we joined + * else ... // someone joined the game + * \endcode + * @param clientid - The id of the new client + * @param me - our game pointer + */ + void signalClientJoinedGame(Q_UINT32 clientid,KGame *me); + + /** + * This signal is emitted after a network partner left the + * game (either by a broken connection or voluntarily). + * All changes to the network players have already be done. + * If there are not enough players left, the game might have + * been paused. To check this you get the old gamestatus + * before the disconnection as argument here. The id of the + * client who left the game allows to distinguish who left the + * game. If it is 0, the server disconnected and you were a client + * which has been switched back to local play. + * You can use this signal to, e.g. set some menues back to local + * player when they were network before. + * + * @param clientID - 0:server left, otherwise the client who left + * @param oldgamestatus - the gamestatus before the loss + * @param me - our game pointer + **/ + void signalClientLeftGame(int clientID,int oldgamestatus,KGame *me); + + +protected: + /** + * A player input occurred. This is the most important function + * as the given message will contain the current move made by + * the given player. + * Note that you HAVE to overwrite this function. Otherwise your + * game makes no sense at all. + * Generally you have to return TRUE in this function. Only then + * the game sequence is proceeded by calling @ref playerInputFinished + * which in turn will check for game over or the next player + * However, if you have a delayed move, because you e.g. move a + * card or a piece you want to return FALSE to pause the game sequence + * and then manually call @ref playerInputFinished to resume it. + * Example: + * \code + * bool MyClass::playerInput(QDataStream &msg,KPlayer *player) + * { + * Q_INT32 move; + * msg >> move; + * kdDebug() << " Player " << player->id() << " moved to " << move << + * endl; + * return true; + * } + * \endcode + * + * @param msg the move message + * @param player the player who did the move + * @return true - input ready, false: input manual + */ + virtual bool playerInput(QDataStream &msg,KPlayer *player)=0; + + + /** + * Called after the player input is processed by the game. Here the + * checks for game over and nextPlayer (in the case of turn base games) + * are processed. + * Call this manually if you have a delayed move, i.e. your playerInput + * function returns FALSE. If it returns true you need not do anything + * here. + * + * @return the current player + * + **/ + KPlayer *playerInputFinished(KPlayer *player); + + + /** + * This virtual function can be overwritten for your own player management. + * It is called when a new game connects to an existing network game or + * to the network master. In case you do not want all players of both games + * to be present in the new network game, you can deactivate players here. + * This is of particular importance if you have a game with fixed number of + * player like e.g. chess. A network connect needs to disable one player of + * each game to make sense. + * + * Not overwriting this function will activate a default behaviour which + * will deactivate players until the @ref maxPlayers() numebr is reached + * according to the KPlayer::networkPriority() value. Players with a low + * value will be kicked out first. With equal priority players of the new + * client will leave first. This means, not setting this value and not + * overwriting this function will never allow a chess game to add client + * players!!! + * On the other hand setting one player of each game to a networkPriorty of + * say 10, already does most of the work for you. + * + * The parameters of this function are the playerlist of the network game, + * which is @ref playerList(). The second argument is the player list of + * the new client who wants to join and the third argument serves as return + * parameter. All player ID's which are written into this list + * will be removed from the created game. You do this by an + * \code + * inactivate.append(player->id()); + * \endcode + * + * @param oldplayer - the list of the network players + * @param newplayer - the list of the client players + * @param inactivate - the value list of ids to be deactivated + * + **/ + virtual void newPlayersJoin(KGamePlayerList *oldplayer, + KGamePlayerList *newplayer, + QValueList &inactivate) { + Q_UNUSED( oldplayer ); + Q_UNUSED( newplayer ); + Q_UNUSED( inactivate ); + } + + /** + * Save the player list to a stream. Used for network game and load/save. + * Can be overwritten if you know what you are doing + * + * @param stream is the stream to save the player ot + * @param list the optional list is the player list to be saved, default is playerList() + * + **/ + void savePlayers(QDataStream &stream,KGamePlayerList *list=0); + + /** + * Prepare a player for being added. Put all data about a player into the + * stream so that it can be sent to the KGameCommunicationServer using + * addPlayer (e.g.) + * + * This function ensures that the code for adding a player is the same in + * addPlayer as well as in negotiateNetworkGame + * @param stream is the stream to add the player + * @param player The player to add + **/ + void savePlayer(QDataStream& stream,KPlayer* player); + + /** + * Load the player list from a stream. Used for network game and load/save. + * Can be overwritten if you know what you are doing + * + * @param stream is the stream to save the player to + * @param isvirtual will set the virtual flag true/false + * + **/ + KPlayer *loadPlayer(QDataStream& stream,bool isvirtual=false); + + + /** + * inactivates player. Use @ref inactivatePlayer instead! + */ + bool systemInactivatePlayer(KPlayer *player); + + /** + * activates player. Use @ref activatePlayer instead! + */ + bool systemActivatePlayer(KPlayer *player); + + /** + * Adds a player to the game + * + * Use @ref addPlayer to send @ref KGameMessage::IdAddPlayer. As soon as + * this Id is received this function is called, where the player (see @ref + * KPlayer::rtti) is added as well as its properties (see @ref KPlayer::save + * and @ref KPlayer::load) + * + * This method calls the overloaded @ref systemAddPlayer with the created + * player as argument. That method will really add the player. + * If you need to do some changes to your newly added player just connect to + * @ref signalPlayerJoinedGame + */ + + /** + * Finally adds a player to the game and therefore to the list. + **/ + void systemAddPlayer(KPlayer* newplayer); + + /** + * Removes a player from the game + * + * Use removePlayer to send KGameMessage::IdRemovePlayer. As soon + * as this Id is received systemRemovePlayer is called and the player is + * removed directly. + **/ + void systemRemovePlayer(KPlayer* player,bool deleteit); + + /** + * This member function will transmit e.g. all players to that client, as well as + * all properties of these players (at least if they have been added by + * @ref KPlayer::addProperty) so that the client will finally have the same + * status as the master. You want to overwrite this function if you expand + * KGame by any properties which have to be known by all clients. + * + * Only the ADMIN is allowed to call this. + * @param clientID The ID of the message client which has connected + **/ + virtual void negotiateNetworkGame(Q_UINT32 clientID); + + /** + * syncronise the random numbers with all network clients + * not used by KGame - if it should be kept then as public method + */ + void syncRandom(); + + void deletePlayers(); + void deleteInactivePlayers(); + + /** + * @deprecated + * Use @ref KGameSequence instead. + * + * @param player the player who made the last move + * @return anything else but 0 is considered as game over + */ + virtual int checkGameOver(KPlayer *player); + + /** + * Load a saved game, from file OR network. Internal. + * Warning: loadgame must not rely that all players all already + * activated. Actually the network will activate a player AFTER + * the loadgame only. This is not true anymore. But be careful + * anyway. + * + * @param stream a data stream where you can stream the game from + * @param network is it a network call -> make players virtual + * @param reset shall the game be reset before loading + * + * @return true? + */ + virtual bool loadgame(QDataStream &stream, bool network, bool reset); + + /** + * Save a game, to file OR network. Internal. + * + * @param stream a data stream where you can stream the game from + * @param network is it a call from the network or from a file (unused but informative) + * @param saveplayers shall the players be saved too (should be TRUE) + * + * @return true? + */ + virtual bool savegame(QDataStream &stream, bool network,bool saveplayers); + +private: + //AB: this is to hide the "receiver" parameter from the user. It shouldn't be + //used if possible (except for init). + /** + * This is an overloaded function. Id differs from the public one only in + * its parameters: + * + * @param receiver The Client that will receive the message. You will hardly + * ever need this. It it internally used to initialize a newly connected + * client. + **/ + //void addPlayer(KPlayer* newplayer, Q_UINT32 receiver); + + /** + * Just the same as the public one except receiver: + * @param receiver 0 for broadcast, otherwise the receiver. Should only be + * used in special circumstances and not outside KGame. + **/ + bool removePlayer(KPlayer * player, Q_UINT32 receiver); + + /** + * Helping function - game negotiation + **/ + void setupGame(Q_UINT32 sender); + + /** + * Helping function - game negotiation + **/ + void setupGameContinue(QDataStream& msg, Q_UINT32 sender); + + /** + * Removes a player from all lists, removes the @ref KGame pointer from the + * @ref KPlayer and deletes the player. Used by (e.g.) @ref + * systemRemovePlayer + * @return True if the player has been removed, false if the current is not + * found + **/ + bool systemRemove(KPlayer* player,bool deleteit); + + +private: + KGamePrivate* d; +}; + +#endif diff --git a/libkdegames/kgame/kgamechat.cpp b/libkdegames/kgame/kgamechat.cpp new file mode 100644 index 00000000..16ec7c18 --- /dev/null +++ b/libkdegames/kgame/kgamechat.cpp @@ -0,0 +1,341 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001-2002 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kgamechat.h" +#include "kgamechat.moc" + +#include "kgame.h" +#include "kplayer.h" +#include "kgameproperty.h" +#include "kgamemessage.h" + +#include +#include + +#include +#include + +//FIXME: +#define FIRST_ID 2 // first id, that is free of use, aka not defined above + +class KGameChatPrivate +{ +public: + KGameChatPrivate() + { + mFromPlayer = 0; + mGame = 0; + + mToMyGroup = -1; + } + + KGame* mGame; + KPlayer* mFromPlayer; + int mMessageId; + + + QIntDict mIndex2Player; + + QMap mSendId2PlayerId; + int mToMyGroup; // just as the above - but for the group, not for players +}; + +KGameChat::KGameChat(KGame* g, int msgid, QWidget* parent) : KChatBase(parent) +{ + init(g, msgid); +} + +KGameChat::KGameChat(KGame* g, int msgid, KPlayer* fromPlayer, QWidget* parent) : KChatBase(parent) +{ + init(g, msgid); + setFromPlayer(fromPlayer); +} + +KGameChat::KGameChat(QWidget* parent) : KChatBase(parent) +{ + init(0, -1); +} + +KGameChat::~KGameChat() +{ + kdDebug(11001) << k_funcinfo << endl; + delete d; +} + +void KGameChat::init(KGame* g, int msgId) +{ + kdDebug(11001) << k_funcinfo << endl; + d = new KGameChatPrivate; + setMessageId(msgId); + + setKGame(g); +} + +void KGameChat::addMessage(int fromId, const QString& text) +{ + if (!d->mGame) { + kdWarning(11001) << "no KGame object has been set" << endl; + addMessage(i18n("Player %1").arg(fromId), text); + } else { + KPlayer* p = d->mGame->findPlayer(fromId); + if (p) { + kdDebug(11001) << "adding message of player " << p->name() << "id=" << fromId << endl; + addMessage(p->name(), text); + } else { + kdWarning(11001) << "Could not find player id " << fromId << endl; + addMessage(i18n("Unknown"), text); + } + } +} + +void KGameChat::returnPressed(const QString& text) +{ + if (!d->mFromPlayer) { + kdWarning(11001) << k_funcinfo << ": You must set a player first!" << endl; + return; + } + if (!d->mGame) { + kdWarning(11001) << k_funcinfo << ": You must set a game first!" << endl; + return; + } + + kdDebug(11001) << "from: " << d->mFromPlayer->id() << "==" << d->mFromPlayer->name() << endl; + + int id = sendingEntry(); + + if (isToGroupMessage(id)) { + // note: there is currently no support for other groups than the players + // group! It might be useful to send to other groups, too + QString group = d->mFromPlayer->group(); + kdDebug(11001) << "send to group " << group << endl; + int sender = d->mFromPlayer->id(); + d->mGame->sendGroupMessage(text, messageId(), sender, group); + + //TODO + //AB: this message is never received!! we need to connect to + //KPlayer::networkData!!! + //TODO + + } else { + int toPlayer = 0; + if (!isSendToAllMessage(id) && isToPlayerMessage(id)) { + toPlayer = playerId(id); + if (toPlayer == -1) { + kdError(11001) << k_funcinfo << ": don't know that player " + << "- internal ERROR" << endl; + } + } + int receiver = toPlayer; + int sender = d->mFromPlayer->id(); + d->mGame->sendMessage(text, messageId(), receiver, sender); + } +} + +void KGameChat::setMessageId(int msgid) +{ d->mMessageId = msgid; } + +int KGameChat::messageId() const +{ return d->mMessageId; } + +bool KGameChat::isSendToAllMessage(int id) const +{ return (id == KChatBase::SendToAll); } + +bool KGameChat::isToGroupMessage(int id) const +{ return (id == d->mToMyGroup); } + +bool KGameChat::isToPlayerMessage(int id) const +{ +return d->mSendId2PlayerId.contains(id); } + +QString KGameChat::sendToPlayerEntry(const QString& name) const +{ return i18n("Send to %1").arg(name); } + +int KGameChat::playerId(int id) const +{ + if (!isToPlayerMessage(id)) { + return -1; + } + + return d->mSendId2PlayerId[id]; +} + +int KGameChat::sendingId(int playerId) const +{ + QMap::Iterator it; + for (it = d->mSendId2PlayerId.begin(); it != d->mSendId2PlayerId.end(); ++it) { + if (it.data() == playerId) { + return it.key(); + } + } + return -1; +} + +const QString& KGameChat::fromName() const +{ return d->mFromPlayer ? d->mFromPlayer->name() : QString::null; } + +bool KGameChat::hasPlayer(int id) const +{ + return (sendingId(id) != -1); +} + +void KGameChat::setFromPlayer(KPlayer* p) +{ + if (!p) { + kdError(11001) << k_funcinfo << ": NULL player" << endl; + removeSendingEntry(d->mToMyGroup); + d->mFromPlayer = 0; + return; + } + if (d->mFromPlayer) { + changeSendingEntry(p->group(), d->mToMyGroup); + } else { + if (d->mToMyGroup != -1) { + kdWarning(11001) << "send to my group exists already - removing" << endl; + removeSendingEntry(d->mToMyGroup); + } + d->mToMyGroup = nextId(); + addSendingEntry(i18n("Send to My Group (\"%1\")").arg(p->group()), d->mToMyGroup); + } + d->mFromPlayer = p; + kdDebug(11001) << k_funcinfo << " player=" << p << endl; +} + + +void KGameChat::setKGame(KGame* g) +{ + if (d->mGame) { + slotUnsetKGame(); + } + kdDebug(11001) << k_funcinfo << " game=" << g << endl; + d->mGame = g; + + if (d->mGame) { + connect(d->mGame, SIGNAL(signalPlayerJoinedGame(KPlayer*)), + this, SLOT(slotAddPlayer(KPlayer*))); + connect(d->mGame, SIGNAL(signalPlayerLeftGame(KPlayer*)), + this, SLOT(slotRemovePlayer(KPlayer*))); + connect(d->mGame, SIGNAL(signalNetworkData(int, const QByteArray&, Q_UINT32, Q_UINT32)), + this, SLOT(slotReceiveMessage(int, const QByteArray&, Q_UINT32, Q_UINT32))); + connect(d->mGame, SIGNAL(destroyed()), this, SLOT(slotUnsetKGame())); + + QPtrList playerList = *d->mGame->playerList(); + for (int unsigned i = 0; i < playerList.count(); i++) { + slotAddPlayer(playerList.at(i)); + } + } +} + +KGame* KGameChat::game() const +{ + return d->mGame; +} + +KPlayer* KGameChat::fromPlayer() const +{ + return d->mFromPlayer; +} + +void KGameChat::slotUnsetKGame() +{ +//TODO: test this method! + + if (!d->mGame) { + return; + } + disconnect(d->mGame, 0, this, 0); + removeSendingEntry(d->mToMyGroup); + QMap::Iterator it; + for (it = d->mSendId2PlayerId.begin(); it != d->mSendId2PlayerId.end(); ++it) { + removeSendingEntry(it.data()); + } +} + +void KGameChat::slotAddPlayer(KPlayer* p) +{ + if (!p) { + kdError(11001) << k_funcinfo << ": cannot add NULL player" << endl; + return; + } + if (hasPlayer(p->id())) { + kdError(11001) << k_funcinfo << ": player was added before" << endl; + return; + } + + int sendingId = nextId(); + addSendingEntry(comboBoxItem(p->name()), sendingId); + d->mSendId2PlayerId.insert(sendingId, p->id()); + connect(p, SIGNAL(signalPropertyChanged(KGamePropertyBase*, KPlayer*)), + this, SLOT(slotPropertyChanged(KGamePropertyBase*, KPlayer*))); + connect(p, SIGNAL(signalNetworkData(int, const QByteArray&, Q_UINT32, KPlayer*)), + this, SLOT(slotReceivePrivateMessage(int, const QByteArray&, Q_UINT32, KPlayer*))); +} + +void KGameChat::slotRemovePlayer(KPlayer* p) +{ + if (!p) { + kdError(11001) << k_funcinfo << ": NULL player" << endl; + return; + } + if (!hasPlayer(p->id())) { + kdError(11001) << k_funcinfo << ": cannot remove non-existent player" << endl; + return; + } + + int id = sendingId(p->id()); + removeSendingEntry(id); + p->disconnect(this); + d->mSendId2PlayerId.remove(id); +} + +void KGameChat::slotPropertyChanged(KGamePropertyBase* prop, KPlayer* player) +{ + if (prop->id() == KGamePropertyBase::IdName) { +// kdDebug(11001) << "new Name" << endl; + changeSendingEntry(player->name(), sendingId(player->id())); +/* + mCombo->changeItem(comboBoxItem(player->name()), index); + */ + } else if (prop->id() == KGamePropertyBase::IdGroup) { + //TODO + } +} + +void KGameChat::slotReceivePrivateMessage(int msgid, const QByteArray& buffer, Q_UINT32 sender, KPlayer* me) +{ + if (!me || me != fromPlayer()) { + kdDebug() << k_funcinfo << "nope - not for us!" << endl; + return; + } + slotReceiveMessage(msgid, buffer, me->id(), sender); +} + +void KGameChat::slotReceiveMessage(int msgid, const QByteArray& buffer, Q_UINT32 , Q_UINT32 sender) +{ + QDataStream msg(buffer, IO_ReadOnly); + if (msgid != messageId()) { + return; + } + + QString text; + msg >> text; + + addMessage(sender, text); +} + diff --git a/libkdegames/kgame/kgamechat.h b/libkdegames/kgame/kgamechat.h new file mode 100644 index 00000000..6f7ea65d --- /dev/null +++ b/libkdegames/kgame/kgamechat.h @@ -0,0 +1,223 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001-2002 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KGAMECHAT_H__ +#define __KGAMECHAT_H__ + +#include + +#include "kchatbase.h" +#include +class KPlayer; +class KGame; +class KGamePropertyBase; + +class KGameChatPrivate; + +/** + * @short A Chat widget for KGame-based games + * + * Call @ref setFromPlayer() first - this will be used as the "from" part of + * every message you will send. Otherwise it won't work! You can also use the + * fromPlayer parameter in the constructor though... + * + * @author Andreas Beckermann + **/ +class KDE_EXPORT KGameChat : public KChatBase +{ + Q_OBJECT +public: + /** + * Construct a @ref KGame chat widget on @p game that used @p msgid for + * the chat message. The @p fromPlayer is the local player (see @ref + * setFromPlayer). + **/ + KGameChat(KGame* game, int msgid, KPlayer* fromPlayer, QWidget * parent); + + /** + * @overload + * To make use of this widget you need to call @ref setFromPlayer + * manually. + **/ + KGameChat(KGame* game, int msgId, QWidget* parent); + + /** + * @overload + * This constructs a widget that is not usable. You must call at least + * setGame, setFromPlayer and setMessageId manually. + * @since 3.2 + **/ + KGameChat(QWidget* parent); + + virtual ~KGameChat(); + + enum SendingIds { + SendToGroup = 1 + }; + + /** + * This sets the fromPlayer to @p player. The fromPlayer is the + * player that will appear as "from" when you send messages through this + * widget. + * @param player The player of this widget + **/ + void setFromPlayer(KPlayer* player); + + KPlayer* fromPlayer() const; + + /** + * Set the @ref KGame object for this chat widget. All messages will be + * sent through this object. You don't have to implement any send + * functions, just call this function, call @ref setFromPlayer and be + * done :-) + * @param g The @ref KGame object the messages will be sent through + **/ + void setKGame(KGame* g); + + KGame* game() const; + + /** + * @return The id of the messages produced by KGameChat. The id will be + * used in @ref KGame as parameter msgid in the method @ref KGame::sendMessage + **/ + int messageId() const; + + /** + * Change the message id of the chat widget. It is recommended that you + * don't use this but prefer the constructor instead, but in certain + * situations (such as using this widget in Qt designer) it may be + * useful to change the message id. + * + * See also @ref messageId + * @since 3.2 + **/ + void setMessageId(int msgid); + + /** + * reimplemented from @ref KChatBase + * @return @ref KPlayer::name() for the player set by @ref setFromPlayer + **/ + virtual const QString& fromName() const; + + +public slots: + virtual void addMessage(const QString& fromName, const QString& text) { KChatBase::addMessage(fromName, text);} + virtual void addMessage(int fromId, const QString& text); + + void slotReceiveMessage(int, const QByteArray&, Q_UINT32 receiver, Q_UINT32 sender); + +protected: + /** + * @param id The ID of the sending entry, as returned by @ref + * KChatBase::sendingEntry + * @return True if the entry "send to all" was selected, otherwise false + **/ + bool isSendToAllMessage(int id) const; + + /** + * Used to indicate whether a message shall be sent to a group of + * players. Note that this was not yet implemented when this doc was + * written so this description might be wrong. (FIXME) + * @param id The ID of the sending entry, as returned by @ref + * KChatBase::sendingEntry + * @return True if the message is meant to be sent to a group (see @ref + * KPlayer::group), e.g. if "send to my group" was selected. + **/ + bool isToGroupMessage(int id) const; + + + /** + * Used to indicate whether the message shall be sent to a single player + * only. Note that you can also call @ref isSendToAllMessage and @ref + * isToGroupMessage - if both return false it must be a player message. + * This behaviour might be changed later - so don't depend on it. + * + * See also toPlayerId + * @param id The ID of the sending entry, as returned by + * KChatBase::sendingEntry + * @return True if the message shall be sent to a special player, + * otherwise false. + **/ + bool isToPlayerMessage(int id) const; + + /** + * @param id The ID of the sending entry, as returned by + * KChatBase::sendingEntry + * @return The ID of the player (see KPlayer::id) the sending entry + * belongs to. Note that the parameter id is an id as returned by ref + * KChatBase::sendingEntry and the id this method returns is a + * KPlayer ID. If isToPlayerMessage returns false this method + * returns -1 + **/ + int playerId(int id) const; + + /** + * @param playerId The ID of the KPlayer object + * @return The ID of the sending entry (see KChatBase) or -1 if + * the player id was not found. + **/ + int sendingId(int playerId) const; + + /** + * @return True if the player with this ID was added before (see + * slotAddPlayer) + **/ + bool hasPlayer(int id) const; + + /** + * @param name The name of the added player + * @return A string that will be added as sending entry in @ref + * KChatBase. By default this is "send to name" where name is the name + * that you specify. See also KChatBase::addSendingEntry + **/ + virtual QString sendToPlayerEntry(const QString& name) const; + + +protected slots: + /** + * Unsets a KGame object that has been set using setKGame + * before. You don't have to call this - this is usually done + * automatically. + **/ + void slotUnsetKGame(); + + + void slotPropertyChanged(KGamePropertyBase*, KPlayer*); + void slotAddPlayer(KPlayer*); + void slotRemovePlayer(KPlayer*); + + /** + * Called when KPlayer::signalNetworkData is emitted. The message + * gets forwarded to slotReceiveMessage if @p me equals + * fromPlayer. + **/ + void slotReceivePrivateMessage(int msgid, const QByteArray& buffer, Q_UINT32 sender, KPlayer* me); + +protected: + virtual void returnPressed(const QString& text); + +private: + void init(KGame* g, int msgid); + +private: + KGameChatPrivate* d; +}; + +#endif diff --git a/libkdegames/kgame/kgameerror.cpp b/libkdegames/kgame/kgameerror.cpp new file mode 100644 index 00000000..93f40f93 --- /dev/null +++ b/libkdegames/kgame/kgameerror.cpp @@ -0,0 +1,80 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ + +#include "kgameerror.h" +#include "kgamemessage.h" + +#include + +QByteArray KGameError::errVersion(int remoteVersion) +{ + QByteArray b; + QDataStream s(b, IO_WriteOnly); + s << (Q_INT32)KGameMessage::version(); + s << (Q_INT32)remoteVersion; + return b; +} + +QByteArray KGameError::errCookie(int localCookie, int remoteCookie) +{ + QByteArray b; + QDataStream s(b, IO_WriteOnly); + s << (Q_INT32)localCookie; + s << (Q_INT32)remoteCookie; + return b; +} + +QString KGameError::errorText(int errorCode, const QByteArray& message) +{ + QDataStream s(message, IO_ReadOnly); + return errorText(errorCode, s); +} + +QString KGameError::errorText(int errorCode, QDataStream& s) +{ + QString text; + switch (errorCode) { + case Cookie: + { + Q_INT32 cookie1; + Q_INT32 cookie2; + s >> cookie1; + s >> cookie2; + text = i18n("Cookie mismatch!\nExpected Cookie: %1\nReceived Cookie: %2").arg(cookie1).arg(cookie2); + break; + } + case Version: + { + Q_INT32 version1; + Q_INT32 version2; + s >> version1; + s >> version2; + text = i18n("KGame Version mismatch!\nExpected Version: %1\nReceived Version: %2\n").arg(version1).arg(version2); + break; + } + default: + text = i18n("Unknown error code %1").arg(errorCode); + } + return text; +} + diff --git a/libkdegames/kgame/kgameerror.h b/libkdegames/kgame/kgameerror.h new file mode 100644 index 00000000..2916e891 --- /dev/null +++ b/libkdegames/kgame/kgameerror.h @@ -0,0 +1,59 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ +#ifndef __KGAMEERROR_H_ +#define __KGAMEERROR_H_ + +#include + + +class KGameError +{ +public: + KGameError() { } + ~KGameError() { } + + enum ErrorCodes { + Cookie = 0, // Cookie mismatch + Version = 1 // Version mismatch + }; + + /** + * Generate an error message with Erorr Code = ErrCookie + **/ + static QByteArray errCookie(int localCookie, int remoteCookie); + static QByteArray errVersion(int remoteVersion); + + /** + * Create an erorr text using a QDataStream (QByteArray) which was + * created using @ref KGameError. This is the opposite function to all + * the errXYZ() function (e.g. @ref errVersion). + * You want to use this to generate the message that shall be + * displayed to the user. + * @return an error message + **/ + static QString errorText(int errorCode, QDataStream& message); + static QString errorText(int errorCode, const QByteArray& message); + +}; + +#endif diff --git a/libkdegames/kgame/kgameio.cpp b/libkdegames/kgame/kgameio.cpp new file mode 100644 index 00000000..9b3a55e8 --- /dev/null +++ b/libkdegames/kgame/kgameio.cpp @@ -0,0 +1,539 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ + +#include "kgameio.h" +#include "kgameio.moc" +#include "kgame.h" +#include "kplayer.h" +#include "kgamemessage.h" +#include "kmessageio.h" + +#include + +#include +#include +#include + +#include + +// ----------------------- Generic IO ------------------------- +KGameIO::KGameIO() : QObject(0,0) +{ + kdDebug(11001) << k_funcinfo << ": this=" << this << ", sizeof(this)" << sizeof(KGameIO) << endl; + mPlayer = 0; +} + +KGameIO::KGameIO(KPlayer* player) : QObject(0,0) +{ + kdDebug(11001) << k_funcinfo << ": this=" << this << ", sizeof(this)" << sizeof(KGameIO) << endl; + mPlayer = 0; + if (player) + { + player->addGameIO(this); + } +} + +KGameIO::~KGameIO() +{ + kdDebug(11001) << k_funcinfo << ": this=" << this << endl; + // unregister ourselves + if (player()) + { + player()->removeGameIO(this, false); + } +} + +void KGameIO::initIO(KPlayer *p) +{ + setPlayer(p); +} + +void KGameIO::notifyTurn(bool b) +{ + if (!player()) + { + kdWarning(11001) << k_funcinfo << ": player() is NULL" << endl; + return; + } + bool sendit=false; + QByteArray buffer; + QDataStream stream(buffer, IO_WriteOnly); + emit signalPrepareTurn(stream, b, this, &sendit); + if (sendit) + { + QDataStream ostream(buffer,IO_ReadOnly); + Q_UINT32 sender = player()->id(); // force correct sender + kdDebug(11001) << "Prepare turn sendInput" << endl; + sendInput(ostream, true, sender); + } +} + +KGame* KGameIO::game() const +{ + if (!player()) + { + return 0; + } + return player()->game(); +} + +bool KGameIO::sendInput(QDataStream& s, bool transmit, Q_UINT32 sender) +{ + if (!player()) + { + return false; + } + return player()->forwardInput(s, transmit, sender); +} + +void KGameIO::Debug() +{ + kdDebug(11001) << "------------------- KGAMEINPUT --------------------" << endl; + kdDebug(11001) << "this: " << this << endl; + kdDebug(11001) << "rtti : " << rtti() << endl; + kdDebug(11001) << "Player: " << player() << endl; + kdDebug(11001) << "---------------------------------------------------" << endl; +} + + +// ----------------------- Key IO --------------------------- +KGameKeyIO::KGameKeyIO(QWidget *parent) + : KGameIO() +{ + if (parent) + { + kdDebug(11001) << "Key Event filter installed" << endl; + parent->installEventFilter(this); + } +} + +KGameKeyIO::~KGameKeyIO() +{ + if (parent()) + { + parent()->removeEventFilter(this); + } +} + +int KGameKeyIO::rtti() const { return KeyIO; } + +bool KGameKeyIO::eventFilter( QObject *o, QEvent *e ) +{ + if (!player()) + { + return false; + } + + // key press/release + if ( e->type() == QEvent::KeyPress || + e->type() == QEvent::KeyRelease ) + { + QKeyEvent *k = (QKeyEvent*)e; + // kdDebug(11001) << "KGameKeyIO " << this << " key press/release " << k->key() << endl ; + QByteArray buffer; + QDataStream stream(buffer,IO_WriteOnly); + bool eatevent=false; + emit signalKeyEvent(this,stream,k,&eatevent); + QDataStream msg(buffer,IO_ReadOnly); + + if (eatevent && sendInput(msg)) + { + return eatevent; + } + return false; // do not eat otherwise + } + return QObject::eventFilter( o, e ); // standard event processing +} + + +// ----------------------- Mouse IO --------------------------- +KGameMouseIO::KGameMouseIO(QWidget *parent,bool trackmouse) + : KGameIO() +{ + if (parent) + { + kdDebug(11001) << "Mouse Event filter installed tracking=" << trackmouse << endl; + parent->installEventFilter(this); + parent->setMouseTracking(trackmouse); + } +} + +KGameMouseIO::~KGameMouseIO() +{ + if (parent()) + { + parent()->removeEventFilter(this); + } +} + +int KGameMouseIO::rtti() const +{ + return MouseIO; +} + +void KGameMouseIO::setMouseTracking(bool b) +{ + if (parent()) + { + ((QWidget*)parent())->setMouseTracking(b); + } +} + +bool KGameMouseIO::eventFilter( QObject *o, QEvent *e ) +{ + if (!player()) + { + return false; + } +// kdDebug(11001) << "KGameMouseIO " << this << endl ; + + // mouse action + if ( e->type() == QEvent::MouseButtonPress || + e->type() == QEvent::MouseButtonRelease || + e->type() == QEvent::MouseButtonDblClick || + e->type() == QEvent::Wheel || + e->type() == QEvent::MouseMove + ) + { + QMouseEvent *k = (QMouseEvent*)e; + // kdDebug(11001) << "KGameMouseIO " << this << endl ; + QByteArray buffer; + QDataStream stream(buffer,IO_WriteOnly); + bool eatevent=false; + emit signalMouseEvent(this,stream,k,&eatevent); +// kdDebug(11001) << "################# eatevent=" << eatevent << endl; + QDataStream msg(buffer,IO_ReadOnly); + if (eatevent && sendInput(msg)) + { + return eatevent; + } + return false; // do not eat otherwise + } + return QObject::eventFilter( o, e ); // standard event processing +} + + +// ----------------------- KGameProcesPrivate --------------------------- +class KGameProcessIO::KGameProcessIOPrivate +{ +public: + KGameProcessIOPrivate() + { + //mMessageServer = 0; + //mMessageClient = 0; + mProcessIO=0; + } + //KMessageServer *mMessageServer; + //KMessageClient *mMessageClient; + KMessageProcess *mProcessIO; +}; + +// ----------------------- Process IO --------------------------- +KGameProcessIO::KGameProcessIO(const QString& name) + : KGameIO() +{ + kdDebug(11001) << k_funcinfo << ": this=" << this << ", sizeof(this)=" << sizeof(KGameProcessIO) << endl; + d = new KGameProcessIOPrivate; + + //kdDebug(11001) << "================= KMEssageServer ==================== " << endl; + //d->mMessageServer=new KMessageServer(0,this); + //kdDebug(11001) << "================= KMEssageClient ==================== " << endl; + //d->mMessageClient=new KMessageClient(this); + kdDebug(11001) << "================= KMEssageProcessIO ==================== " << endl; + d->mProcessIO=new KMessageProcess(this,name); + kdDebug(11001) << "================= KMEssage Add client ==================== " << endl; + //d->mMessageServer->addClient(d->mProcessIO); + //kdDebug(11001) << "================= KMEssage SetSErver ==================== " << endl; + //d->mMessageClient->setServer(d->mMessageServer); + kdDebug(11001) << "================= KMEssage: Connect ==================== " << endl; + //connect(d->mMessageClient, SIGNAL(broadcastReceived(const QByteArray&, Q_UINT32)), + // this, SLOT(clientMessage(const QByteArray&, Q_UINT32))); + //connect(d->mMessageClient, SIGNAL(forwardReceived(const QByteArray&, Q_UINT32, const QValueList &)), + // this, SLOT(clientMessage(const QByteArray&, Q_UINT32, const QValueList &))); + connect(d->mProcessIO, SIGNAL(received(const QByteArray&)), + this, SLOT(receivedMessage(const QByteArray&))); + //kdDebug(11001) << "Our client is id="<mMessageClient->id() << endl; +} + +KGameProcessIO::~KGameProcessIO() +{ + kdDebug(11001) << k_funcinfo << ": this=" << this << endl; + kdDebug(11001) << "player="<removeGameIO(this,false); + } + if (d->mProcessIO) + { + delete d->mProcessIO; + d->mProcessIO=0; + } + delete d; +} + +int KGameProcessIO::rtti() const +{ + return ProcessIO; +} + +void KGameProcessIO::initIO(KPlayer *p) +{ + KGameIO::initIO(p); + // Send 'hello' to process + QByteArray buffer; + QDataStream stream(buffer, IO_WriteOnly); + Q_INT16 id = p->userId(); + stream << id; + + bool sendit=true; + if (p) + { + emit signalIOAdded(this,stream,p,&sendit); + if (sendit ) + { + Q_UINT32 sender = p->id(); + kdDebug(11001) << "Sending IOAdded to process player !!!!!!!!!!!!!! " << endl; + sendSystemMessage(stream, KGameMessage::IdIOAdded, 0, sender); + } + } +} + +void KGameProcessIO::notifyTurn(bool b) +{ + if (!player()) + { + kdWarning(11001) << k_funcinfo << ": player() is NULL" << endl; + return; + } + bool sendit=true; + QByteArray buffer; + QDataStream stream(buffer,IO_WriteOnly); + stream << (Q_INT8)b; + emit signalPrepareTurn(stream,b,this,&sendit); + if (sendit) + { + Q_UINT32 sender=player()->id(); + kdDebug(11001) << "Sending Turn to process player !!!!!!!!!!!!!! " << endl; + sendSystemMessage(stream, KGameMessage::IdTurn, 0, sender); + } +} + +void KGameProcessIO::sendSystemMessage(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ + sendAllMessages(stream, msgid, receiver, sender, false); +} + +void KGameProcessIO::sendMessage(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ + sendAllMessages(stream, msgid, receiver, sender, true); +} + +void KGameProcessIO::sendAllMessages(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender, bool usermsg) +{ + kdDebug(11001) << "==============> KGameProcessIO::sendMessage (usermsg="<isActive()) return ; + + if (usermsg) + { + msgid+=KGameMessage::IdUser; + } + + kdDebug(11001) << "=============* ProcessIO (" << msgid << "," << receiver << "," << sender << ") ===========" << endl; + + QByteArray buffer; + QDataStream ostream(buffer,IO_WriteOnly); + QBuffer *device=(QBuffer *)stream.device(); + QByteArray data=device->buffer();; + + KGameMessage::createHeader(ostream,sender,receiver,msgid); + // ostream.writeRawBytes(data.data()+device->at(),data.size()-device->at()); + ostream.writeRawBytes(data.data(),data.size()); + kdDebug(11001) << " Adding user data from pos="<< device->at() <<" amount= " << data.size() << " byte " << endl; + //if (d->mMessageClient) d->mMessageClient->sendBroadcast(buffer); + if (d->mProcessIO) + { + d->mProcessIO->send(buffer); + } +} + +//void KGameProcessIO::clientMessage(const QByteArray& receiveBuffer, Q_UINT32 clientID, const QValueList &recv) +void KGameProcessIO::receivedMessage(const QByteArray& receiveBuffer) +{ + QDataStream stream(receiveBuffer,IO_ReadOnly); + int msgid; + Q_UINT32 sender; + Q_UINT32 receiver; + KGameMessage::extractHeader(stream,sender,receiver,msgid); + + kdDebug(11001) << "************* Got process message sender =" << sender + << " receiver=" << receiver << " msgid=" << msgid << endl; + + + // Cut out the header part...to not confuse network code + QBuffer *buf=(QBuffer *)stream.device(); + QByteArray newbuffer; + newbuffer.setRawData(buf->buffer().data()+buf->at(),buf->size()-buf->at()); + QDataStream ostream(newbuffer,IO_ReadOnly); + kdDebug(11001) << "Newbuffer size=" << newbuffer.size() << endl; + + + +// This is a dummy message which allows us the process to talk with its owner + if (msgid==KGameMessage::IdProcessQuery) + { + emit signalProcessQuery(ostream,this); + } + else if (player()) + { + sender = player()->id(); // force correct sender + if (msgid==KGameMessage::IdPlayerInput) + { + sendInput(ostream,true,sender); + } + else + { + player()->forwardMessage(ostream,msgid,receiver,sender); + } + } + else + { + kdDebug(11001) << k_funcinfo << ": Got message from process but no player defined!" << endl; + } + newbuffer.resetRawData(buf->buffer().data()+buf->at(),buf->size()-buf->at()); +} + + +// ----------------------- Computer IO -------------------------- +class KGameComputerIO::KGameComputerIOPrivate +{ +//TODO: maybe these should be KGameProperties!! +public: + KGameComputerIOPrivate() + { + mAdvanceCounter = 0; + mReactionPeriod = 0; + + mPauseCounter = 0; + + mAdvanceTimer = 0; + } + int mAdvanceCounter; + int mReactionPeriod; + + int mPauseCounter; + + QTimer* mAdvanceTimer; +}; + +KGameComputerIO::KGameComputerIO() : KGameIO() +{ + init(); +} + +KGameComputerIO::KGameComputerIO(KPlayer* p) : KGameIO(p) +{ + init(); +} + +void KGameComputerIO::init() +{ + d = new KGameComputerIOPrivate; +} + +KGameComputerIO::~KGameComputerIO() +{ + if (d->mAdvanceTimer) + { + delete d->mAdvanceTimer; + } + delete d; +} + +int KGameComputerIO::rtti() const +{ + return ComputerIO; +} + +void KGameComputerIO::setReactionPeriod(int calls) +{ + d->mReactionPeriod = calls; +} + +int KGameComputerIO::reactionPeriod() const +{ + return d->mReactionPeriod; +} + +void KGameComputerIO::setAdvancePeriod(int ms) +{ + stopAdvancePeriod(); + d->mAdvanceTimer = new QTimer(this); + connect(d->mAdvanceTimer, SIGNAL(timeout()), this, SLOT(advance())); + d->mAdvanceTimer->start(ms); +} + +void KGameComputerIO::stopAdvancePeriod() +{ + if (d->mAdvanceTimer) + { + d->mAdvanceTimer->stop(); + delete d->mAdvanceTimer; + } +} + +void KGameComputerIO::pause(int calls) +{ + d->mPauseCounter = calls; +} + +void KGameComputerIO::unpause() +{ + pause(0); +} + +void KGameComputerIO::advance() +{ + if (d->mPauseCounter > 0) + { + d->mPauseCounter--; + return; + } + else if (d->mPauseCounter < 0) + { + return; + } + d->mAdvanceCounter++; + if (d->mAdvanceCounter >= d->mReactionPeriod) + { + d->mAdvanceCounter = 0; + reaction(); + } +} + +void KGameComputerIO::reaction() +{ + emit signalReaction(); +} + + diff --git a/libkdegames/kgame/kgameio.h b/libkdegames/kgame/kgameio.h new file mode 100644 index 00000000..4d7e0f5b --- /dev/null +++ b/libkdegames/kgame/kgameio.h @@ -0,0 +1,566 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ +#ifndef __KGAMEIO_H__ +#define __KGAMEIO_H__ + +#include +#include +#include +class KPlayer; +class KGame; +class KProcess; + +/** + * \short Base class for IO devices for games + * + * This is the master class for + * creating IO game devices. You cannot use it directly. + * Either take one of the classes derived from it or + * you have to create your own IO class derived from it (more probably). + * + * The idea behind this class is to provide a common interface + * for input devices into your game. By programming a KGameIO + * device you need not distinguish the actual IO in the game + * anymore. All work is done by the IO's. This allows very + * easy reuse in other games as well. + * A further advantage of using the IO's is that you can exchange + * the control of a player at runtime. E.g. you switch a player + * to be controlled by the computer or vice versa. + * + * To achieve this you have to make all of your player inputs through a + * KGameIO. You will usually call KGameIO::sendInput to do so. + * + * @author Martin Heni + */ +class KDE_EXPORT KGameIO : public QObject +{ + Q_OBJECT + +public: + /** + * Constructs a KGameIO object + */ + KGameIO(); + KGameIO(KPlayer*); + virtual ~KGameIO(); + + /** + * Gives debug output of the game status + */ + void Debug(); + + /** + * Identifies the KGameIO via the rtti function + */ + enum IOMode {GenericIO=1,KeyIO=2,MouseIO=4,ProcessIO=8,ComputerIO=16}; + /** + * Run time idendification. Predefined values are from IOMode + * You MUST overwrite this in derived classes! + * + * @return rtti value + */ + virtual int rtti() const = 0; // Computer, network, local, ... + + /** + * This function returns the player who owns this IO + * + * @return the player this IO device is plugged into + */ + KPlayer *player() const {return mPlayer;} + + /** + * Equivalent to player()->game() + * @return the @ref KGame object of this player + **/ + KGame* game() const; + + /** + * Sets the player to which this IO belongs to. This + * is done automatically when adding a device to a + * player + * + * @param p the player + */ + void setPlayer(KPlayer *p) {mPlayer=p;} + + /** + * Init this device by setting the player and e.g. sending an + * init message to the device. This initialisation message is + * very useful for computer players as you can transmit the + * game status to them and only update this status in the setTurn + * commands. + * + * Called by @ref KPlayer::addGameIO only! + */ + virtual void initIO(KPlayer *p); + + /** + * Notifies the IO device that the player's setTurn had been called + * Called by KPlayer + * + * This emits @ref signalPrepareTurn and sends the turn if the send + * parameter is set to true. + * + * @param b turn is true/false + */ + virtual void notifyTurn(bool b); + + /** + * Send an input message using @ref KPlayer::forwardInput + **/ + bool sendInput(QDataStream& stream, bool transmit = true, Q_UINT32 sender = 0); + +signals: + /** + * Signal generated when @ref KPlayer::myTurn changes. This can either be + * when you get the turn status or when you lose it. + * + * The datastream has to be filled with a move. If you set (or leave) the + * send parameter to FALSE then nothing happens: the datastream will be + * ignored. If you set it to TRUE @ref sendInput is used to + * send the move. + * + * Often you want to ignore this signal (leave send=FALSE) and send the + * message later. This is usually the case for a human player as he probably + * doesn't react immediately. But you can still use this e.g. to notify the + * player about the turn change. + * + * Example: + * \code + * void GameWindow::slotPrepareTurn(QDataStream &stream,bool b,KGameIO *input,bool * ) + * { + * KPlayer *player=input->player(); + * if (!player->myTurn()) return ; + * if (!b) return ; // only do something on setTurn(true) + * stream << 1 << 2 << 3; // Some data for the process + * } + * \endcode + * + * @param io the KGameIO object itself + * @param stream the stream into which the move will be written + * @param turn the argument of setTurn + * @param send set this to true to send the generated move using @ref + * sendInput + **/ + void signalPrepareTurn(QDataStream & stream, bool turn, KGameIO *io, bool * send); + + +private: + KPlayer *mPlayer; +}; + +/** + * The KGameKeyIO class. It is used to process keyboard input + * from a widget and create moves for the player it belongs to. + * @author Martin Heni + */ +class KDE_EXPORT KGameKeyIO : public KGameIO +{ + Q_OBJECT + +public: + /** + * Create a keyboard input devices. All keyboards + * inputs of the given widgets are passed through a signal + * handler signalKeyEvent and can be used to generate + * a valid move for the player. + * Note the widget you pass to the constructor must be + * the main window of your application, e.g. view->parentWidget() + * as QT does not forward your keyevents otherwise. This means + * that this might be a different widget comapred to the one you + * use for mouse inputs! + * Example: + * \code + * KGameKeyIO *input; + * input=new KGameKeyIO(myWidget); + * connect(input,SIGNAL(signalKeyEvent(KGameIO *,QDataStream &,QKeyEvent *,bool *)), + * this,SLOT(slotKeyInput(KGameIO *,QDataStream &,QKeyEvent *,bool *))); + * \endcode + * + * @param parent The parents widget whose keyboard events * should be grabbed + */ + KGameKeyIO(QWidget *parent); + virtual ~KGameKeyIO(); + + /** + * The idendification of the IO + * + * @return KeyIO + */ + virtual int rtti() const; + +signals: + /** + * Signal handler for keyboard events. This function is called + * on every keyboard event. If appropriate it can generate a + * move for the player the device belongs to. If this is done + * and the event is eaten eatevent needs to be set to true. + * What move you generate (i.e. what you write to the stream) + * is totally up to you as it will not be evaluated but forwared + * to the player's/game's input move function + * Example: + * \code + * KPlayer *player=input->player(); // Get the player + * Q_INT32 key=e->key(); + * stream << key; + * eatevent=true; + * \endcode + * + * @param io the IO device we belong to + * @param stream the stream where we write our move into + * @param m The QKeyEvent we can evaluate + * @param eatevent set this to true if we processed the event + */ + void signalKeyEvent(KGameIO *io,QDataStream &stream,QKeyEvent *m,bool *eatevent); + +protected: + /** + * Internal method to process the events + */ + bool eventFilter( QObject *o, QEvent *e ); +}; + +/** + * The KGameMouseIO class. It is used to process mouse input + * from a widget and create moves for the player it belongs to. + * @author Martin Heni + */ +class KDE_EXPORT KGameMouseIO : public KGameIO +{ + Q_OBJECT + +public: + /** + * Creates a mouse IO device. It captures all mouse + * event of the given widget and forwards them to the + * signal handler signalMouseEvent. + * Example: + * \code + * KGameMouseIO *input; + * input=new KGameMouseIO(mView); + * connect(input,SIGNAL(signalMouseEvent(KGameIO *,QDataStream &,QMouseEvent *,bool *)), + * this,SLOT(slotMouseInput(KGameIO *,QDataStream &,QMouseEvent *,bool *))); + * \endcode + * + * @param parent The widget whose events should be captured + * @param trackmouse enables mouse tracking (gives mouse move events) + */ + KGameMouseIO(QWidget *parent,bool trackmouse=false); + virtual ~KGameMouseIO(); + + /** + * Manually activate or deactivate mouse tracking + * + * @param b true = tracking on + */ + void setMouseTracking(bool b); + /** + * The idendification of the IO + * + * @return MouseIO + */ + virtual int rtti() const; + +signals: + /** + * Signal handler for mouse events. This function is called + * on every mouse event. If appropriate it can generate a + * move for the player the device belongs to. If this is done + * and the event is eaten eatevent needs to be set to true. + * @see signalKeyEvent + * Example: + * \code + * KPlayer *player=input->player(); // Get the player + * Q_INT32 button=e->button(); + * stream << button; + * eatevent=true; + * \endcode + * + * @param io the IO device we belong to + * @param stream the stream where we write our move into + * @param m The QMouseEvent we can evaluate + * @param eatevent set this to true if we processed the event + */ + void signalMouseEvent(KGameIO *io,QDataStream &stream,QMouseEvent *m,bool *eatevent); + +protected: + /** + * Internal event filter + */ + bool eventFilter( QObject *o, QEvent *e ); + +}; + + +/** + * The KGameProcessIO class. It is used to create a computer player + * via a separate process and communicate transparetly with it. + * Its counterpart is the @ref KGameProcess class which needs + * to be used by the computer player. See its documentation + * for the definition of the computer player. + * @author Martin Heni + */ +class KDE_EXPORT KGameProcessIO : public KGameIO +{ + Q_OBJECT + +public: + /** + * Creates a computer player via a separate process. The process + * name is given as fully qualified filename. + * Example: + * \code + * KGameProcessIO *input; + * input=new KGameProcessIO(executable_file); + * connect(input,SIGNAL(signalPrepareTurn(QDataStream &,bool,KGameIO *,bool *)), + * this,SLOT(slotPrepareTurn(QDataStream &,bool,KGameIO *,bool *))); + * connect(input,SIGNAL(signalProcessQuery(QDataStream &,KGameProcessIO *)), + * this,SLOT(slotProcessQuery(QDataStream &,KGameProcessIO *))); + * \endcode + * + * @param name the filename of the process to start + */ + KGameProcessIO(const QString& name); + + /** + * Deletes the process input devices + */ + virtual ~KGameProcessIO(); + + /** + * The idendification of the IO + * + * @return ProcessIO + */ + int rtti() const; + + /** + * Send a message to the process. This is analogous to the sendMessage + * commands of KGame. It will result in a signal of the computer player + * on which you can react in the process player. + * + * @param stream - the actual data + * @param msgid - the id of the message + * @param receiver - not used + * @param sender - who send the message + */ + void sendMessage(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender); + + /** + * Send a system message to the process. This is analogous to the sendMessage + * commands of KGame. It will result in a signal of the computer player + * on which you can react in the process player. + * + * @param stream - the actual data + * @param msgid - the id of the message + * @param receiver - not used + * @param sender - who send the message + */ + void sendSystemMessage(QDataStream &stream, int msgid, Q_UINT32 receiver, Q_UINT32 sender); + + /** + * Init this device by setting the player and e.g. sending an + * init message to the device. Calling this function will emit + * the IOAdded signal on which you can react and initilise the + * computer player. + * This function is called automatically when adding the IO to + * a player. + */ + void initIO(KPlayer *p); + + /** + * Notifies the IO device that the player's setTurn had been called + * Called by KPlayer. You can react on the @ref signalPrepareTurn to + * prepare a message for the process, i.e. either update it on + * the changes made to the game since the last turn or the initIO + * has been called or transmit your gamestatus now. + * + * @param turn is true/false + */ + virtual void notifyTurn(bool turn); + + protected: + /** + * Internal ~ombined function for all message handling + **/ + void sendAllMessages(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender, bool usermsg); + + protected slots: + /** + * Internal message handler to receive data from the process + */ + void receivedMessage(const QByteArray& receiveBuffer); + + +signals: + /** + * A computer query message is received. This is a 'dummy' + * message sent by the process if it needs to communicate + * with us. It is not forwarded over the network. + * Reacting to this message allows you to 'answer' questions + * of the process, e.g. sending addition data which the process + * needs to calculate a move. + * + * Example: + * \code + * void GameWindow::slotProcessQuery(QDataStream &stream,KGameProcessIO *reply) + * { + * int no; + * stream >> no; // We assume the process sends us an integer question numner + * if (no==1) // but YOU have to do this in the process player + * { + * QByteArray buffer; + * QDataStream out(buffer,IO_WriteOnly); + * reply->sendSystemMessage(out,4242,0,0); // lets reply something... + * } + * } + * \endcode + */ + void signalProcessQuery(QDataStream &stream,KGameProcessIO *me); + + /** + * Signal generated when the computer player is added. + * You can use this to communicated with the process and + * e.g. send initialisation information to the process. + * + * @param game the KGameIO object itself + * @param stream the stream into which the move will be written + * @param p the player itself + * @param send set this to false if no move should be generated + */ + void signalIOAdded(KGameIO *game,QDataStream &stream,KPlayer *p,bool *send); + + +protected: + +private: + class KGameProcessIOPrivate; + KGameProcessIOPrivate* d; +}; + +/** + * \brief KGameIO variant for real-time games + * + * The KGameComputerIO class. It is used to create a LOCAL computer player + * and communicate transparently with it. + * Question: Is this needed or is it overwritten anyway for a real game? + * + * You most probably don't want to use this if you want to design a turn based + * game/player. You'll rather use @ref KGameIO directly, i.e. subclass it + * yourself. You just need to use @ref KGameIO::signalPrepareTurn and/or @ref + * KGameIO::notifyTurn there. + * + * This is rather meant to be of use in real time games. + * + * @author + */ +class KDE_EXPORT KGameComputerIO : public KGameIO +{ + Q_OBJECT + +public: + /** + * Creates a LOCAL computer player + * + */ + KGameComputerIO(); + KGameComputerIO(KPlayer* player); + ~KGameComputerIO(); + + int rtti() const; + + /** + * The number of advance calls until the player (or rather: the IO) + * does something (default: 1). + **/ + void setReactionPeriod(int advanceCalls); + int reactionPeriod() const; + + /** + * Start a QTimer which calls advance every @p ms milli seconds. + **/ + void setAdvancePeriod(int ms); + + void stopAdvancePeriod(); + + /** + * Ignore calls number of advance calls. if calls is -1 then all + * following advance calls are ignored until unpause is called. + * + * This simply prevents the internal advance counter to be increased. + * + * You may want to use this to emulate a "thinking" computer player. Note + * that this means if you increase the advance period (see + * setAdvancePeriod), i.e. if you change the speed of your game, your + * computer player thinks "faster". + * @param calls Number of advance calls to be ignored + **/ + void pause(int calls = -1); + + /** + * Equivalent to pause(0). Immediately continue to increase the internal + * advance counter. + **/ + void unpause(); + +public slots: + /** + * Works kind of similar to QCanvas::advance. Increase the internal + * advance counter. If @p reactionPeriod is reached the counter is set back to + * 0 and @ref signalReaction is emitted. This is when the player is meant + * to do something (move its units or so). + * + * This is very useful if you use QCanvas as you can use this in your + * QCanvas::advance call. The advantage is that if you change the speed + * of the game (i.e. change QCanvas::setAdvancePeriod) the computer + * player gets slower as well. + * + * If you don't use QCanvas you can use setAdvancePeriod to get + * the same result. Alternatively you can just use a QTimer. + * + **/ + virtual void advance(); + +signals: + /** + * This signal is emitted when your computer player is meant to do + * something, or better is meant to be allowed to do something. + **/ + void signalReaction(); + +protected: + /** + * Default implementation simply emits signalReaction + **/ + virtual void reaction(); + +private: + void init(); + +private: + class KGameComputerIOPrivate; + KGameComputerIOPrivate* d; +}; + + +#endif diff --git a/libkdegames/kgame/kgamemessage.cpp b/libkdegames/kgame/kgamemessage.cpp new file mode 100644 index 00000000..6464d407 --- /dev/null +++ b/libkdegames/kgame/kgamemessage.cpp @@ -0,0 +1,156 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ + +#include "kgamemessage.h" + +#include + +#define MESSAGE_VERSION 2 + +Q_UINT32 KGameMessage::createPlayerId(int oldplayerid,Q_UINT32 gameid) +{ + int p; + p = oldplayerid & 0x3ff; // remove game id + p |= (gameid << 10); + return p; +} + +int KGameMessage::rawPlayerId(Q_UINT32 playerid) +{ + return playerid & 0x03ff; +} + +Q_UINT32 KGameMessage::rawGameId(Q_UINT32 playerid) +{ + return (playerid & 0xfc00) >> 10; +} + +bool KGameMessage::isPlayer(Q_UINT32 msgid) +{ + if (msgid & 0xfc00) { + return true; + } else { + return false; + } +} + +bool KGameMessage::isGame(Q_UINT32 msgid) +{ + return !isPlayer(msgid); +} + + +void KGameMessage::createHeader(QDataStream &msg,Q_UINT32 sender,Q_UINT32 receiver,int msgid) +{ + msg << (Q_INT16)sender << (Q_INT16)receiver << (Q_INT16)msgid; +} + +void KGameMessage::extractHeader(QDataStream &msg,Q_UINT32 &sender,Q_UINT32 &receiver,int &msgid) +{ + Q_INT16 d3,d4,d5; + msg >> d3 >> d4 >> d5; + sender=d3;receiver=d4;msgid=d5; +} + +void KGameMessage::createPropertyHeader(QDataStream &msg,int id) +{ + msg << (Q_INT16)id; +} + +void KGameMessage::extractPropertyHeader(QDataStream &msg,int &id) +{ + Q_INT16 d1; + msg >> d1; + id=d1; +} + +void KGameMessage::createPropertyCommand(QDataStream &msg,int cmdid,int pid,int cmd) +{ + createPropertyHeader(msg,cmdid); + msg << (Q_INT16)pid ; + msg << (Q_INT8)cmd ; +} + +void KGameMessage::extractPropertyCommand(QDataStream &msg,int &pid,int &cmd) +{ + Q_INT16 d1; + Q_INT8 d2; + msg >> d1 >> d2; + pid=d1; + cmd=d2; +} + +int KGameMessage::version() +{ + return MESSAGE_VERSION; +} + +QString KGameMessage::messageId2Text(int msgid) +{ +// this should contain all KGameMessage::GameMessageIds +// feel free to add missing ones, to remove obsolete one and even feel free to +// let it be ;-) + switch (msgid) { + case KGameMessage::IdSetupGame: + return i18n("Setup Game"); + case KGameMessage::IdSetupGameContinue: + return i18n("Setup Game Continue"); + case KGameMessage::IdGameLoad: + return i18n("Load Game"); + case KGameMessage::IdGameConnected: + return i18n("Client game connected"); + case KGameMessage::IdGameSetupDone: + return i18n("Game setup done"); + case KGameMessage::IdSyncRandom: + return i18n("Synchronize Random"); + case KGameMessage::IdDisconnect: + return i18n("Disconnect"); + case KGameMessage::IdPlayerProperty: + return i18n("Player Property"); + case KGameMessage::IdGameProperty: + return i18n("Game Property"); + case KGameMessage::IdAddPlayer: + return i18n("Add Player"); + case KGameMessage::IdRemovePlayer: + return i18n("Remove Player"); + case KGameMessage::IdActivatePlayer: + return i18n("Activate Player"); + case KGameMessage::IdInactivatePlayer: + return i18n("Inactivate Player"); + case KGameMessage::IdTurn: + return i18n("Id Turn"); + case KGameMessage::IdError: + return i18n("Error Message"); + case KGameMessage::IdPlayerInput: + return i18n("Player Input"); + case KGameMessage::IdIOAdded: + return i18n("An IO was added"); + case KGameMessage::IdProcessQuery: + return i18n("Process Query"); + case KGameMessage::IdPlayerId: + return i18n("Player ID"); + case KGameMessage::IdUser: // IdUser must be unknown for use, too! + default: + return QString::null; + } +} diff --git a/libkdegames/kgame/kgamemessage.h b/libkdegames/kgame/kgamemessage.h new file mode 100644 index 00000000..4394b4fa --- /dev/null +++ b/libkdegames/kgame/kgamemessage.h @@ -0,0 +1,173 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ +#ifndef __KGAMEMSG_H_ +#define __KGAMEMSG_H_ + +#include +#include + +class KDE_EXPORT KGameMessage +{ + public: + /** + * Creates a fully qualified player ID which contains the original + * player id in the lower bits and the game number in the higher bits. + * Do not rely on the exact bit positions as they are internal. + * + * See also @ref rawPlayerId and @ref rawGameId which are the inverse + * operations + * + * @param playerid the player id - can include a gameid (will get removed) + * @param gameid The game id (<64). 0 For broadcast. + * @return the new player id + */ + static Q_UINT32 createPlayerId(int player, Q_UINT32 game); + + /** + * Returns the raw playerid, that is, a id which does not + * contain the game number encoded in it. See also @ref createPlayerId which + * is the inverse operation. + * + * @param the player id + * @return the raw player id + **/ + static int rawPlayerId(Q_UINT32 playerid); + + /** + * Returns the raw game id, that is, the game id the player + * belongs to. Se also @ref createPlayerId which is the inverse operation. + * + * @param the player id + * @return the raw game id + **/ + static Q_UINT32 rawGameId(Q_UINT32 playerid); + + /** + * Checks whether a message receiver/sender is a player + * + * @param id The ID of the sender/receiver + * @return true/false + */ + static bool isPlayer(Q_UINT32 id); + + /** + * Checks whether the sender/receiver of a message is a game + * + * @param id The ID of the sender/receiver + * @return true/false + */ + static bool isGame(Q_UINT32 id); + + /** + * Creates a message header given cookie,sender,receiver,... + * + * Also puts "hidden" header into the stream which are used by KGameClient + * (message length and magic cookie). If you don't need them remove them + * with @ref dropExternalHeader + */ + static void createHeader(QDataStream &msg, Q_UINT32 sender, Q_UINT32 receiver, int msgid); + + /** + * Retrieves the information like cookie,sender,receiver,... from a message header + * + * Note that it could be necessary to call @ref dropExternalHeader first + */ + static void extractHeader(QDataStream &msg,Q_UINT32 &sender, Q_UINT32 &receiver, int &msgid); + + /** + * Creates a property header given the property id + */ + static void createPropertyHeader(QDataStream &msg, int id); + + /** + * Retrieves the property id from a property message header + */ + static void extractPropertyHeader(QDataStream &msg, int &id); + + /** + * Creates a property header given the property id + */ + static void createPropertyCommand(QDataStream &msg, int cmdid, int pid, int cmd); + + /** + * Retrieves the property id from a property message header + */ + static void extractPropertyCommand(QDataStream &msg, int &pid, int &cmd); + + /** + * @return Version of the network library + */ + static int version(); + + /** + * This function takes a @ref GameMessageIds as argument and returns a + * suitable string for it. This string can't be used to identify a message + * (as it is i18n'ed) but it can make debugging more easy. See also @ref + * KGameDebugDialog. + * @return Either a i18n'ed string (the name of the id) or QString::null if + * the msgid is unknown + **/ + static QString messageId2Text(int msgid); + + + /** + * Message Ids used inside @ref KGame. + * + * You can use your own custom message Id by adding @p IdUser to it. + **/ +// please document every new id with a short comment + enum GameMessageIds { +// game init, game load, disconnect, ... + IdSetupGame=1, // sent to a newly connected player + IdSetupGameContinue=2, // continue the setup + IdGameLoad=3, // load/save the game to the client + IdGameConnected=4, // Client successfully connected to master + IdSyncRandom=5, // new random seed set - sync games + IdDisconnect=6, // KGame object disconnects from game + IdGameSetupDone=7, // New game client is now operational + +// properties + IdPlayerProperty=20, // a player property changed + IdGameProperty=21, // a game property changed + +// player management + IdAddPlayer=30, // add a player + IdRemovePlayer=31, // the player will be removed + IdActivatePlayer=32, // Activate a player + IdInactivatePlayer=33, // Inactivate a player + IdTurn=34, // Turn to be prepared + +// to-be-categorized + IdError=100, // an error occurred + IdPlayerInput=101, // a player input occurred + IdIOAdded=102, // KGameIO got added to a player...init this IO + +// special ids for computer player + IdProcessQuery=220, // Process queries data (process only) + IdPlayerId=221, // PlayerId got changed (process only) + + IdUser=256 // a user specified message + }; +}; + +#endif diff --git a/libkdegames/kgame/kgamenetwork.cpp b/libkdegames/kgame/kgamenetwork.cpp new file mode 100644 index 00000000..9eccb868 --- /dev/null +++ b/libkdegames/kgame/kgamenetwork.cpp @@ -0,0 +1,516 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ + +#include "kgamenetwork.h" +#include "kgamenetwork.moc" +#include "kgamemessage.h" +#include "kgameerror.h" + +#include "kmessageserver.h" +#include "kmessageclient.h" +#include "kmessageio.h" +#include + +#include + +#include + + +class KGameNetworkPrivate +{ +public: + KGameNetworkPrivate() + { + mMessageClient = 0; + mMessageServer = 0; + mDisconnectId = 0; + mService = 0; + } + +public: + KMessageClient* mMessageClient; + KMessageServer* mMessageServer; + Q_UINT32 mDisconnectId; // Stores gameId() over a disconnect process + DNSSD::PublicService* mService; + QString mType; + QString mName; + + int mCookie; +}; + +// ------------------- NETWORK GAME ------------------------ +KGameNetwork::KGameNetwork(int c, QObject* parent) : QObject(parent, 0) +{ + d = new KGameNetworkPrivate; + d->mCookie = (Q_INT16)c; + + // Init the game as a local game, i.e. + // create your own KMessageServer and a KMessageClient connected to it. + setMaster(); + + kdDebug(11001) << k_funcinfo << "this=" << this <<", cookie=" << cookie() << " sizeof(this)="<mMessageClient) { + d->mMessageClient = new KMessageClient (this); + connect (d->mMessageClient, SIGNAL(broadcastReceived(const QByteArray&, Q_UINT32)), + this, SLOT(receiveNetworkTransmission(const QByteArray&, Q_UINT32))); + connect (d->mMessageClient, SIGNAL(connectionBroken()), + this, SIGNAL(signalConnectionBroken())); + connect (d->mMessageClient, SIGNAL(aboutToDisconnect(Q_UINT32)), + this, SLOT(aboutToLoseConnection(Q_UINT32))); + connect (d->mMessageClient, SIGNAL(connectionBroken()), + this, SLOT(slotResetConnection())); + + connect (d->mMessageClient, SIGNAL(adminStatusChanged(bool)), + this, SLOT(slotAdminStatusChanged(bool))); + connect (d->mMessageClient, SIGNAL(eventClientConnected(Q_UINT32)), + this, SIGNAL(signalClientConnected(Q_UINT32))); + connect (d->mMessageClient, SIGNAL(eventClientDisconnected(Q_UINT32, bool)), + this, SIGNAL(signalClientDisconnected(Q_UINT32, bool))); + + // broacast and direct messages are treated equally on receive. + connect (d->mMessageClient, SIGNAL(forwardReceived(const QByteArray&, Q_UINT32, const QValueList&)), + d->mMessageClient, SIGNAL(broadcastReceived(const QByteArray&, Q_UINT32))); + + } else { + // should be no problem but still has to be tested + kdDebug(11001) << k_funcinfo << "Client already exists!" << endl; + } + d->mMessageClient->setServer(d->mMessageServer); +} + +void KGameNetwork::setDiscoveryInfo(const QString& type, const QString& name) +{ + kdDebug() << k_funcinfo << type << ":" << name << endl; + d->mType = type; + d->mName = name; + tryPublish(); +} + +void KGameNetwork::tryPublish() +{ + if (d->mType.isNull() || !isOfferingConnections()) return; + if (!d->mService) d->mService = new DNSSD::PublicService(d->mName,d->mType,port()); + else { + if (d->mType!=d->mService->type()) d->mService->setType(d->mType); + if (d->mName!=d->mService->serviceName()) d->mService->setServiceName(d->mName); + } + if (!d->mService->isPublished()) d->mService->publishAsync(); +} + +void KGameNetwork::tryStopPublishing() +{ + if (d->mService) d->mService->stop(); +} + +bool KGameNetwork::offerConnections(Q_UINT16 port) +{ + kdDebug (11001) << k_funcinfo << "on port " << port << endl; + if (!isMaster()) { + setMaster(); + } + + // Make sure this is 0 + d->mDisconnectId = 0; + + // FIXME: This debug message can be removed when the program is working correct. + if (d->mMessageServer && d->mMessageServer->isOfferingConnections()) { + kdDebug (11001) << k_funcinfo << "Already running as server! Changing the port now!" << endl; + } + + tryStopPublishing(); + kdDebug (11001) << k_funcinfo << "before Server->initNetwork" << endl; + if (!d->mMessageServer->initNetwork (port)) { + kdError (11001) << k_funcinfo << "Unable to bind to port " << port << "!" << endl; + // no need to delete - we just cannot listen to the port +// delete d->mMessageServer; +// d->mMessageServer = 0; +// d->mMessageClient->setServer((KMessageServer*)0); + return false; + } + kdDebug (11001) << k_funcinfo << "after Server->initNetwork" << endl; + tryPublish(); + return true; +} + +bool KGameNetwork::connectToServer (const QString& host, Q_UINT16 port) +{ + if (host.isEmpty()) { + kdError(11001) << k_funcinfo << "No hostname given" << endl; + return false; + } + + // Make sure this is 0 + d->mDisconnectId = 0; + +// if (!d->mMessageServer) { +// // FIXME: What shall we do here? Probably must stop a running game. +// kdWarning (11001) << k_funcinfo << "We are already connected to another server!" << endl; +/// } + + if (d->mMessageServer) { + // FIXME: What shall we do here? Probably must stop a running game. + kdWarning(11001) << "we are server but we are trying to connect to another server! " + << "make sure that all clients connect to that server! " + << "quitting the local server now..." << endl; + stopServerConnection(); + d->mMessageClient->setServer((KMessageIO*)0); + delete d->mMessageServer; + d->mMessageServer = 0; + } + + kdDebug(11001) << " about to set server" << endl; + d->mMessageClient->setServer(host, port); + emit signalAdminStatusChanged(false); // as we delete the connection above isAdmin() is always false now! + + // OK: We say that we already have connected, but this isn't so yet! + // If the connection cannot be established, it will look as being disconnected + // again ("slotConnectionLost" is called). + // Shall we differ between these? + kdDebug(11001) << "connected to " << host << ":" << port << endl; + return true; +} + +Q_UINT16 KGameNetwork::port() const +{ + if (isNetwork()) { + if (isOfferingConnections()) { + return d->mMessageServer->serverPort(); + } else { + return d->mMessageClient->peerPort(); + } + } + return 0; +} + +QString KGameNetwork::hostName() const +{ + return d->mMessageClient->peerName(); +} + +bool KGameNetwork::stopServerConnection() +{ + // We still are the Master, we just don't accept further connections! + tryStopPublishing(); + if (d->mMessageServer) { + d->mMessageServer->stopNetwork(); + return true; + } + return false; +} + +bool KGameNetwork::isOfferingConnections() const +{ return (d->mMessageServer && d->mMessageServer->isOfferingConnections()); } + +void KGameNetwork::disconnect() +{ + // TODO MH + kdDebug(11001) << k_funcinfo << endl; + stopServerConnection(); + if (d->mMessageServer) { + QValueList list=d->mMessageServer->clientIDs(); + QValueList::Iterator it; + for( it = list.begin(); it != list.end(); ++it ) + { + kdDebug(11001) << "Client id=" << (*it) << endl; + KMessageIO *client=d->mMessageServer->findClient(*it); + if (!client) + { + continue; + } + kdDebug(11001) << " rtti=" << client->rtti() << endl; + if (client->rtti()==2) + { + kdDebug(11001) << "DIRECT IO " << endl; + } + else + { + d->mMessageServer->removeClient(client,false); + } + } + } + else + { + kdDebug(11001) << k_funcinfo << "before client->disconnect() id="<mMessageClient->setServer((KMessageIO*)0); + kdDebug(11001) << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++"<mMessageClient->disconnect(); + + kdDebug(11001) << "++++++--------------------------------------------+++++"<mMessageServer) { + //delete d->mMessageServer; + //d->mMessageServer=0; + server=true; + kdDebug(11001) << " server true" << endl; + d->mMessageServer->deleteClients(); + kdDebug(11001) << " server deleteClients" << endl; + } + */ + kdDebug(11001) << k_funcinfo << "DONE" << endl; +} + +void KGameNetwork::aboutToLoseConnection(Q_UINT32 clientID) +{ + kdDebug(11001) << "Storing client id of connection "<mDisconnectId = clientID; +} + +void KGameNetwork::slotResetConnection() +{ + kdDebug(11001) << "Resseting client disconnect id"<mDisconnectId = 0; +} + +void KGameNetwork::electAdmin(Q_UINT32 clientID) +{ + if (!isAdmin()) { + kdWarning(11001) << k_funcinfo << "only ADMIN is allowed to call this!" << endl; + return; + } + QByteArray buffer; + QDataStream stream(buffer,IO_WriteOnly); + stream << static_cast( KMessageServer::REQ_ADMIN_CHANGE ); + stream << clientID; + d->mMessageClient->sendServerMessage(buffer); +} + +void KGameNetwork::setMaxClients(int max) +{ + if (!isAdmin()) { + kdWarning(11001) << k_funcinfo << "only ADMIN is allowed to call this!" << endl; + return; + } + QByteArray buffer; + QDataStream stream(buffer,IO_WriteOnly); + stream << static_cast( KMessageServer::REQ_MAX_NUM_CLIENTS ); + stream << (Q_INT32)max; + d->mMessageClient->sendServerMessage(buffer); +} + +void KGameNetwork::lock() +{ + if (messageClient()) { + messageClient()->lock(); + } +} + +void KGameNetwork::unlock() +{ + if (messageClient()) { + messageClient()->unlock(); + } +} + +// --------------------- send messages --------------------------- + +bool KGameNetwork::sendSystemMessage(int data, int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ + QByteArray buffer; + QDataStream stream(buffer,IO_WriteOnly); + stream << data; + return sendSystemMessage(buffer,msgid,receiver,sender); +} + +bool KGameNetwork::sendSystemMessage(const QString &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ + QByteArray buffer; + QDataStream stream(buffer, IO_WriteOnly); + stream << msg; + return sendSystemMessage(buffer, msgid, receiver, sender); +} + +bool KGameNetwork::sendSystemMessage(const QDataStream &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ return sendSystemMessage(((QBuffer*)msg.device())->buffer(), msgid, receiver, sender); } + +bool KGameNetwork::sendSystemMessage(const QByteArray& data, int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ + QByteArray buffer; + QDataStream stream(buffer,IO_WriteOnly); + if (!sender) { + sender = gameId(); + } + + Q_UINT32 receiverClient = KGameMessage::rawGameId(receiver); // KGame::gameId() + int receiverPlayer = KGameMessage::rawPlayerId(receiver); // KPlayer::id() + + KGameMessage::createHeader(stream, sender, receiver, msgid); + stream.writeRawBytes(data.data(), data.size()); + + /* + kdDebug(11001) << "transmitGameClientMessage msgid=" << msgid << " recv=" + << receiver << " sender=" << sender << " Buffersize=" + << buffer.size() << endl; + */ + + if (!d->mMessageClient) { + // No client created, this should never happen! + // Having a local game means we have our own + // KMessageServer and we are the only client. + kdWarning (11001) << k_funcinfo << "We don't have a client! Should never happen!" << endl; + return false; + } + + if (receiverClient == 0 || receiverPlayer != 0) + { + // if receiverClient == 0 this is a broadcast message. if it is != 0 but + // receiverPlayer is also != 0 we have to send broadcast anyway, because the + // KPlayer object on all clients needs to receive the message. + d->mMessageClient->sendBroadcast(buffer); + } + else + { + d->mMessageClient->sendForward(buffer, receiverClient); + } + return true; +} + +bool KGameNetwork::sendMessage(int data, int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ return sendSystemMessage(data,msgid+KGameMessage::IdUser,receiver,sender); } + +bool KGameNetwork::sendMessage(const QString &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ return sendSystemMessage(msg,msgid+KGameMessage::IdUser,receiver,sender); } + +bool KGameNetwork::sendMessage(const QDataStream &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ return sendSystemMessage(msg, msgid+KGameMessage::IdUser, receiver, sender); } + +bool KGameNetwork::sendMessage(const QByteArray &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ return sendSystemMessage(msg, msgid+KGameMessage::IdUser, receiver, sender); } + +void KGameNetwork::sendError(int error,const QByteArray& message, Q_UINT32 receiver, Q_UINT32 sender) +{ + QByteArray buffer; + QDataStream stream(buffer,IO_WriteOnly); + stream << (Q_INT32) error; + stream.writeRawBytes(message.data(), message.size()); + sendSystemMessage(stream,KGameMessage::IdError,receiver,sender); +} + + +// ----------------- receive messages from the network +void KGameNetwork::receiveNetworkTransmission(const QByteArray& receiveBuffer, Q_UINT32 clientID) +{ + QDataStream stream(receiveBuffer, IO_ReadOnly); + int msgid; + Q_UINT32 sender; // the id of the KGame/KPlayer who sent the message + Q_UINT32 receiver; // the id of the KGame/KPlayer the message is for + KGameMessage::extractHeader(stream, sender, receiver, msgid); +// kdDebug(11001) << k_funcinfo << "id=" << msgid << " sender=" << sender << " recv=" << receiver << endl; + + // No broadcast : receiver==0 + // No player isPlayer(receiver) + // Different game gameId()!=receiver + if (receiver && receiver!=gameId() && !KGameMessage::isPlayer(receiver) ) + { + // receiver=0 is broadcast or player message + kdDebug(11001) << k_funcinfo << "Message not meant for us " + << gameId() << "!=" << receiver << " rawid=" + << KGameMessage::rawGameId(receiver) << endl; + return; + } + else if (msgid==KGameMessage::IdError) + { + QString text; + Q_INT32 error; + stream >> error; + kdDebug(11001) << k_funcinfo << "Got IdError " << error << endl; + text = KGameError::errorText(error, stream); + kdDebug(11001) << "Error text: " << text.latin1() << endl; + emit signalNetworkErrorMessage((int)error,text); + } + else + { + networkTransmission(stream, msgid, receiver, sender, clientID); + } +} + +// -------------- slots for the signals of the client +void KGameNetwork::slotAdminStatusChanged(bool isAdmin) +{ + emit signalAdminStatusChanged(isAdmin); + +// TODO: I'm pretty sure there are a lot of things that should be done here... +} + +void KGameNetwork::Debug() +{ + kdDebug(11001) << "------------------- KNETWORKGAME -------------------------" << endl; + kdDebug(11001) << "gameId " << gameId() << endl; + kdDebug(11001) << "gameMaster " << isMaster() << endl; + kdDebug(11001) << "gameAdmin " << isAdmin() << endl; + kdDebug(11001) << "---------------------------------------------------" << endl; +} + +/* + * vim: et sw=2 + */ diff --git a/libkdegames/kgame/kgamenetwork.h b/libkdegames/kgame/kgamenetwork.h new file mode 100644 index 00000000..6ff5cf94 --- /dev/null +++ b/libkdegames/kgame/kgamenetwork.h @@ -0,0 +1,431 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ +#ifndef __KGAMENETWORK_H_ +#define __KGAMENETWORK_H_ + +#include +#include +#include +class KGameIO; +class KMessageClient; +class KMessageServer; + +class KGameNetworkPrivate; + +/** + * The KGameNetwork class is the KGame class with network + * support. All other features are the same but they are + * now network transparent. It is not used directly but + * only via a KGame object. So you do not really have + * to bother with this object. + * + * @short The main KDE game object + * @author Martin Heni + * @version $Id$ + */ +class KDE_EXPORT KGameNetwork : public QObject +{ + Q_OBJECT + +public: + /** + * Create a KGameNetwork object + */ + KGameNetwork(int cookie=42,QObject* parent=0); + virtual ~KGameNetwork(); + + /** + * Gives debug output of the game status + **/ + virtual void Debug(); + + /** + * @return TRUE if this is a network game - i.e. you are either MASTER or + * connected to a remote MASTER. + **/ + bool isNetwork() const; + + /** + * Is this the game MASTER (i.e. has started theKMessageServer). A + * game has always exactly one MASTER. This is either a KGame object (i.e. a + * Client) or an own MessageServer-process. A KGame object that has the + * MASTER status is always admin. + * + * You probably don't want to use this. It is a mostly internal method which + * will probably become protected. Better use isAdmin + * + * @see isAdmin + * @return Whether this client has started the KMessageServer + **/ + bool isMaster() const; + + /** + * The admin of a game is the one who initializes newly connected clients + * using negotiateNetworkGame and is allowed to configure the game. + * E.g. only the admin is allowed to use KGame::setMaxPlayers. + * + * If one KGame object in the game is MASTER then this client is the admin + * as well. isMaster and isAdmin differ only if the KMessageServer + * is running in an own process. + * @return Whether this client (KGame object) is the admin + **/ + bool isAdmin() const; + + /** + * The unique ID of this game + * + * @return int id + **/ + Q_UINT32 gameId() const; + + /** + * Inits a network game as network MASTER. Note that if the + * KMessageServer is not yet started it will be started here (see + * setMaster). Any existing connection will be disconnected. + * + * If you already offer connections the port is changed. + * + * @param port The port on which the service is offered + * @return true if it worked + **/ + bool offerConnections (Q_UINT16 port); + + /** + * Announces game MASTER on network using DNS-SD. Clients then can discover it using + * DNSSD::ServiceBrowser (or KGameConnectWidget) instead of manually entering + * IP address. + * @param type service type (something like _kwin4._tcp). + * It should be unique for application. + * @param name game name that will be displayed by clients. If not + * set hostname will be used. In case of name conflict -2, -3 and so on will be added to name. + * @since 3.4 + **/ + void setDiscoveryInfo(const QString& type, const QString& name=QString::null); + + /** + * Inits a network game as a network CLIENT + * + * @param host the host to which we want to connect + * @param port the port we want to connect to + * + * @return true if connected + **/ + bool connectToServer(const QString& host, Q_UINT16 port); + + /** + * @since 3.2 + * @return The port we are listening to if offerConnections was called + * or the port we are connected to if connectToServer was called. + * Otherwise 0. + **/ + Q_UINT16 port() const; + + /** + * @since 3.2 + * @return The name of the host that we are currently connected to is + * isNetwork is TRUE and we are not the MASTER, i.e. if connectToServer + * was called. Otherwise this will return "localhost". + **/ + QString hostName() const; + + /** + * Stops offering server connections - only for game MASTER + * @return true + **/ + bool stopServerConnection(); + + /** + * Changes the maximal connection number of the KMessageServer to max. + * -1 Means infinite connections are possible. Note that existing + * connections are not affected, so even if you set this to 0 in a running + * game no client is being disconnected. You can call this only if you are + * the ADMIN! + * + * @see KMessageServer::setMaxClients + * @param max The maximal number of connections possible. + **/ + void setMaxClients(int max); + + //AB: is this now internal only? Can we make it protected (maybe with + //friends)? sendSystemMessage AND sendMessage is very confusing to the + //user. + /** + * Sends a network message msg with a given msg id msgid to all clients. + * Use this to communicate with KGame (e.g. to add a player ot to configure + * the game - usually not necessary). + * + * For your own messages use sendMessage instead! This is mostly + * internal! + * + * @param buffer the message which will be send. See messages.txt for contents + * @param msgid an id for this message. See + * KGameMessage::GameMessageIds + * @param receiver the KGame / KPlayer this message is for. + * @param sender The KGame / KPlayer this message is from (i.e. + * you). You + * probably want to leave this 0, then KGameNetwork will create the correct + * value for you. You might want to use this if you send a message from a + * specific player. + * @return true if worked + */ + // AB: TODO: doc on how "receiver" and "sender" should be created! + bool sendSystemMessage(const QByteArray& buffer, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0); + + /** + * @overload + **/ + bool sendSystemMessage(int data, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0); + + /** + * @overload + **/ + bool sendSystemMessage(const QDataStream &msg, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0); + + /** + * @overload + **/ + bool sendSystemMessage(const QString& msg, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0); + + /** + * Sends a network message + * @param error The error code + * @param message The error message - use KGameError + * @param receiver the KGame / KPlayer this message is for. 0 For + * all + * @param sender The KGame / KPlayer this message is from (i.e. + * you). You probably want to leave this 0, then KGameNetwork will create + * the correct value for you. You might want to use this if you send a + * message from a specific player. + **/ + void sendError(int error, const QByteArray& message, Q_UINT32 receiver=0, Q_UINT32 sender=0); + + /** + * Are we still offer offering server connections - only for game MASTER + * @return true/false + **/ + bool isOfferingConnections() const; + + /** + * Application cookie. this idendifies the game application. It + * help to distinguish between e.g. KPoker and KWin4 + * @return the application cookie + **/ + int cookie() const; + + /** + * Send a network message msg with a given message ID msgid to all clients. + * You want to use this to send a message to the clients. + * + * Note that a message is always sent to ALL clients! This is necessary so + * that all clients always have the same data and can easily be changed from + * network to non-network without restarting the game. If you want a + * specific KGame / KPlayer to react to the message use the + * receiver and sender parameters. See KGameMessage::calsMessageId + * + * SendMessage differs from sendSystemMessage only by the msgid parameter. + * sendSystemMessage is thought as a KGame only mehtod while + * sendMessage is for public use. The msgid parameter will be + * +=KGameMessage::IdUser and in KGame::signalNetworkData msgid will + * be -= KGameMessage::IdUser again, so that one can easily distinguish + * between system and user messages. + * + * Use sendSystemMessage to comunicate with KGame (e.g. by adding a + * player) and sendMessage for your own user message. + * + * Note: a player should send messages through a KGameIO! + * + * @param buffer the message which will be send. See messages.txt for contents + * @param msgid an id for this message. See KGameMessage::GameMessageIds + * @param receiver the KGame / KPlayer this message is for. + * @param sender The KGame / KPlayer this message is from (i.e. + * you). You + * probably want to leave this 0, then KGameNetwork will create the correct + * value for you. You might want to use this if you send a message from a + * specific player. + * @return true if worked + **/ + // AB: TODO: doc on how "receiver" and "sender" should be created! + bool sendMessage(const QByteArray& buffer, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0); + + /** + * This is an overloaded member function, provided for convenience. + **/ + bool sendMessage(const QDataStream &msg, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0); + + /** + * This is an overloaded member function, provided for convenience. + **/ + bool sendMessage(const QString& msg, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0); + + /** + * This is an overloaded member function, provided for convenience. + **/ + bool sendMessage(int data, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0); + + + /** + * Called by ReceiveNetworkTransmission(). Will be overwritten by + * KGame and handle the incoming message. + **/ + virtual void networkTransmission(QDataStream&, int, Q_UINT32, Q_UINT32, Q_UINT32 clientID) = 0; + + + /** + * Disconnect the current connection and establish a new local one. + **/ + void disconnect(); + + + /** + * If you are the ADMIN of the game you can give the ADMIN status away to + * another client. Use this e.g. if you want to quit the game or if you want + * another client to administrate the game (note that disconnect calls + * this automatically). + * @param clientID the ID of the new ADMIN (note: this is the _client_ID + * which has nothing to do with the player IDs. See KMessageServer) + **/ + void electAdmin(Q_UINT32 clientID); + + /** + * Don't use this unless you really know what youre doing! You might + * experience some strange behaviour if you send your messages directly + * through the KMessageClient! + * + * @return a pointer to the KMessageClient used internally to send the + * messages. You should rather use one of the send functions! + **/ + KMessageClient* messageClient() const; + + /** + * Don't use this unless you really know what you are doing! You might + * experience some strange behaviour if you use the message server directly! + * + * @return a pointer to the message server if this is the MASTER KGame + * object. Note that it might be possible that no KGame object contains + * the KMessageServer at all! It might even run stand alone! + **/ + KMessageServer* messageServer() const; + + /** + * You should call this before doing thigs like, e.g. qApp->processEvents(). + * Don't forget to call unlock once you are done! + * + * @see KMessageClient::lock + **/ + virtual void lock(); + + /** + * @see KMessageClient::unlock + **/ + virtual void unlock(); + +signals: + /** + * A network error occurred + * @param error the error code + * @param text the error text + */ + void signalNetworkErrorMessage(int error, QString text); + + /** + * Our connection to the KMessageServer has broken. + * See KMessageClient::connectionBroken + **/ + void signalConnectionBroken(); + + /** + * This signal is emitted whenever the KMessageServer sends us a message that a + * new client connected. KGame uses this to call KGame::negotiateNetworkGame + * for the newly connected client if we are admin (see isAdmin) + * + * @see KMessageClient::eventClientConnected + * + * @param clientID the ID of the newly connected client + **/ + void signalClientConnected(Q_UINT32 clientID); + + /** + * This signal is emitted whenever the KMessageServer sends us a message + * that a connection to a client was detached. The second parameter can be used + * to distinguish between network errors or removing on purpose. + * + * @see KMessageClient::eventClientDisconnected + * + * @param clientID the client that has disconnected + * @param broken true if the connection was lost because of a network error, false + * if the connection was closed by the message server admin. + */ + void signalClientDisconnected(Q_UINT32 clientID, bool broken); + + /** + * This client gets or loses the admin status. + * @see KMessageClient::adminStatusChanged + * @param isAdmin True if this client gets the ADMIN status otherwise FALSE + **/ + void signalAdminStatusChanged(bool isAdmin); + +protected: + /** + * @internal + * Start a KMessageServer object and use it as the MASTER of the game. + * Note that you must not call this if there is already another master + * running! + **/ + void setMaster(); + +protected slots: + /** + * Called by KMessageClient::broadcastReceived() and will check if the + * message format is valid. If it is not, it will generate an error (see + * signalNetworkVersionError and signalNetworkErorrMessage). + * If it is valid, the pure virtual method networkTransmission() is called. + * (This one is overwritten in KGame.) + **/ + void receiveNetworkTransmission(const QByteArray& a, Q_UINT32 clientID); + + /** + * This KGame object receives or loses the admin status. + * @param isAdmin Whether we are admin or not + **/ + void slotAdminStatusChanged(bool isAdmin); + + /** + * Called when the network connection is about to terminate. Is used + * to store the network parameter like the game id + */ + void aboutToLoseConnection(Q_UINT32 id); + + /** + * Called when the network connection is terminated. Used to clean + * up the disconnect parameter + */ + void slotResetConnection(); + + +private: + void tryPublish(); + void tryStopPublishing(); + KGameNetworkPrivate* d; +}; + +#endif diff --git a/libkdegames/kgame/kgameprocess.cpp b/libkdegames/kgame/kgameprocess.cpp new file mode 100644 index 00000000..96efe0ce --- /dev/null +++ b/libkdegames/kgame/kgameprocess.cpp @@ -0,0 +1,158 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ + +#include "kgameprocess.h" +#include "kplayer.h" +#include "kgame.h" +#include "kgamemessage.h" +#include "kmessageio.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#define READ_BUFFER_SIZE 1024 + +// ----------------------- Process Child --------------------------- + +KGameProcess::KGameProcess() : QObject(0,0) +{ + mTerminate=false; + // Check whether a player is set. If not create one! + rFile.open(IO_ReadOnly|IO_Raw,stdin); + wFile.open(IO_WriteOnly|IO_Raw,stdout); + mMessageIO=new KMessageFilePipe(this,&rFile,&wFile); +// mMessageClient=new KMessageClient(this); +// mMessageClient->setServer(mMessageIO); +// connect (mMessageClient, SIGNAL(broadcastReceived(const QByteArray&, Q_UINT32)), +// this, SLOT(receivedMessage(const QByteArray&, Q_UINT32))); + connect (mMessageIO, SIGNAL(received(const QByteArray&)), + this, SLOT(receivedMessage(const QByteArray&))); + fprintf(stderr,"KGameProcess::constructor %p %p\n",&rFile,&wFile); + + mRandom = new KRandomSequence; + mRandom->setSeed(0); +} +KGameProcess::~KGameProcess() +{ + delete mRandom; + //delete mMessageClient; + //delete mMessageServer; + delete mMessageIO; + rFile.close(); + wFile.close(); + fprintf(stderr,"KGameProcess::destructor\n"); +} + + +bool KGameProcess::exec(int argc, char *argv[]) +{ + // Get id and cookie, ... from command line + processArgs(argc,argv); + do + { + mMessageIO->exec(); + } while(!mTerminate); + return true; +} + +// You have to do this to create a message +// QByteArray buffer; +// QDataStream wstream(buffer,IO_WriteOnly); +// then stream data into the stream and call this function +void KGameProcess::sendSystemMessage(QDataStream &stream,int msgid,Q_UINT32 receiver) +{ + fprintf(stderr,"KGameProcess::sendMessage id=%d recv=%d",msgid,receiver); + QByteArray a; + QDataStream outstream(a,IO_WriteOnly); + + QBuffer *device=(QBuffer *)stream.device(); + QByteArray data=device->buffer();; + + KGameMessage::createHeader(outstream,0,receiver,msgid); + outstream.writeRawBytes(data.data(),data.size()); + + //if (mMessageClient) mMessageClient->sendBroadcast(a); + // TODO: The fixed received 2 will cause problems. But how to address the + // proper one? +// if (mMessageClient) mMessageClient->sendForward(a,2); + if (mMessageIO) mMessageIO->send(a); +} + +void KGameProcess::sendMessage(QDataStream &stream,int msgid,Q_UINT32 receiver) +{ + sendSystemMessage(stream,msgid+KGameMessage::IdUser,receiver); +} + +void KGameProcess::processArgs(int argc, char *argv[]) +{ + int v=0; + if (argc>2) + { + v=atoi(argv[2]); + //kdDebug(11001) << "cookie (unused) " << v << endl; + } + if (argc>1) + { + v=atoi(argv[1]); + //kdDebug(11001) << "id (unused) " << v << endl; + } + fprintf(stderr,"processArgs \n"); + fflush(stderr); +} + +void KGameProcess::receivedMessage(const QByteArray& receiveBuffer) +{ + QDataStream stream(receiveBuffer, IO_ReadOnly); + int msgid; + Q_UINT32 sender; + Q_UINT32 receiver; + KGameMessage::extractHeader(stream, sender, receiver, msgid); + fprintf(stderr,"------ receiveNetworkTransmission(): id=%d sender=%d,recv=%d\n",msgid,sender,receiver); + switch(msgid) + { + case KGameMessage::IdTurn: + Q_INT8 b; + stream >> b; + emit signalTurn(stream,(bool)b); + break; + case KGameMessage::IdIOAdded: + Q_INT16 id; + stream >> id; + emit signalInit(stream,(int)id); + break; + default: + emit signalCommand(stream,msgid-KGameMessage::IdUser,receiver,sender); + break; + } +} + +#include "kgameprocess.moc" diff --git a/libkdegames/kgame/kgameprocess.h b/libkdegames/kgame/kgameprocess.h new file mode 100644 index 00000000..a8db4fcd --- /dev/null +++ b/libkdegames/kgame/kgameprocess.h @@ -0,0 +1,242 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ +#ifndef __KGAMEPROCESS_H_ +#define __KGAMEPROCESS_H_ + +#include +#include +#include + +#include "kgameproperty.h" +#include +#include +class KPlayer; +class KMessageFilePipe; + +/** + * This is the process class used on the computer player + * side to communicate with its counterpart KProcessIO class. + * Using these two classes will give fully transparent communication + * via QDataStreams. + */ +class KDE_EXPORT KGameProcess: public QObject +{ + Q_OBJECT + + public: + /** + * Creates a KGameProcess class. Done only in the computer + * player. To activate the communication you have to call + * the exec function of this class which will listen + * to the communication and emit signals to notify you of + * any incoming messages. + * Note: This function will only return after you set + * setTerminate(true) in one of the received signals. + * So you can not do any computer calculation after the exec function. + * Instead you react on the signals which are emitted after a + * message is received and perform the calculations there! + * Example: + * \code + * int main(int argc ,char * argv[]) + * { + * KGameProcess proc; + * connect(&proc,SIGNAL(signalCommand(QDataStream &,int ,int ,int )), + * this,SLOT(slotCommand(QDataStream & ,int ,int ,int ))); + * connect(&proc,SIGNAL(signalInit(QDataStream &,int)), + * this,SLOT(slotInit(QDataStream & ,int ))); + * connect(&proc,SIGNAL(signalTurn(QDataStream &,bool )), + * this,SLOT(slotTurn(QDataStream & ,bool ))); + * return proc.exec(argc,argv); + * } + * \endcode + */ + KGameProcess(); + /** + * Destruct the process + */ + ~KGameProcess(); + + /** + * Enters the event loop of the computer process. Does only + * return on setTerminate(true)! + */ + bool exec(int argc, char *argv[]); + + /** + * Should the computer process leave its exec function? + * Activated if you setTerminate(true); + * + * @return true/false + */ + bool terminate() const {return mTerminate;} + + /** + * Set this to true if the computer process should end, ie + * leave its exec function. + * + * @param b true for exit the exec function + */ + void setTerminate(bool b) {mTerminate=b;} + + /** + * Sends a message to the corresponding KGameIO + * device. Works like the sendSystemMessage but + * for user id's + * + * @param stream the QDataStream containing the message + * @param msgid the message id for the message + * @param receiver unused + */ + void sendMessage(QDataStream &stream,int msgid,Q_UINT32 receiver=0); + + /** + * Sends a system message to the corresonding KGameIO device. + * This will normally be either a performed move or a query + * (IdProcessQuery). The query option is a way to communicate + * with the KGameIO at the other side and e.g. retrieve some + * game relevant data from here. + * Exmaple for a query: + * \code + * QByteArray buffer; + * QDataStream out(buffer,IO_WriteOnly); + * int msgid=KGameMessage::IdProcessQuery; + * out << (int)1; + * proc.sendSystemMessage(out,msgid,0); + * \endcode + * + * @param stream the QDataStream containing the message + * @param msgid the message id for the message + * @param receiver unused + */ + void sendSystemMessage(QDataStream &stream,int msgid,Q_UINT32 receiver=0); + + /** + * Returns a pointer to a KRandomSequence. You can generate + * random numbers via e.g. + * \code + * random()->getLong(100); + * \endcode + * + * @return KRandomSequence pointer + */ + KRandomSequence *random() {return mRandom;} + + protected: + /** + * processes the command line argumens to set up the computer player + * Pass the argumens exactely as given by main() + */ + void processArgs(int argc, char *argv[]); + + protected slots: + /** + * A message is received via the interprocess connection. The + * appropriate signals are called. + */ + void receivedMessage(const QByteArray& receiveBuffer); + + signals: + /** + * The generic communication signal. You have to connect to this + * signal to generate a valid computer response onto arbitrary messages. + * All signals but IdIOAdded and IdTurn end up here! + * Example: + * \code + * void Computer::slotCommand(int &msgid,QDataStream &in,QDataStream &out) + * { + * Q_INT32 data,move; + * in >> data; + * // compute move ... + * move=data*2; + * out << move; + * } + * \endcode + * + * @param inputStream the incoming data stream + * @param msgid the message id of the message which got transmitted to the computer + * @param receiver the id of the receiver + * @param sender the id of the sender + */ + void signalCommand(QDataStream &inputStream,int msgid,int receiver,int sender); + + /** + * This signal is emmited if the computer player should perform a turn. + * Calculations can be made here and the move can then be send back with + * sendSystemMessage with the message id KGameMessage::IdPlayerInput. + * These must provide a move which complies to your other move syntax as + * e.g. produces by keyboard or mouse input. + * Additonal data which have been written into the stream from the + * ProcessIO's signal signalPrepareTurn can be retrieved from the + * stream here. + * Example: + * \code + * void slotTurn(QDataStream &in,bool turn) + * { + * int id; + * int recv; + * QByteArray buffer; + * QDataStream out(buffer,IO_WriteOnly); + * if (turn) + * { + * // Create a move - the format is yours to decide + * // It arrives exactly as this in the kgame inputMove function!! + * Q_INT8 x1,y1,pl; + * pl=-1; + * x1=proc.random()->getLong(8); + * y1=proc.random()->getLong(8); + * // Stream it + * out << pl << x1 << y1; + * id=KGameMessage::IdPlayerInput; + * proc.sendSystemMessage(out,id,0); + * } + * } + * \endcode + * + * @param stream The datastream which contains user data + * @param turn True or false whether the turn is activated or deactivated + * + */ + void signalTurn(QDataStream &stream,bool turn); + + /** + * This signal is emmited when the process is initialized, i.e. added + * to a KPlayer. Initial initialisation can be performed here be reacting + * to the KProcessIO signal signalIOAdded and retrieving the data here + * from the stream. + * It works just as the signalTurn() but is only send when the player is + * added to the game, i.e. it needs some initialization data + * + * @param stream The datastream which contains user data + * @param userid The userId of the player. (Careful to rely on it yet) + */ + void signalInit(QDataStream &stream,int userid); + + protected: + bool mTerminate; + KMessageFilePipe *mMessageIO; + private: + QFile rFile; + QFile wFile; + KRandomSequence* mRandom; +}; +#endif diff --git a/libkdegames/kgame/kgameproperty.cpp b/libkdegames/kgame/kgameproperty.cpp new file mode 100644 index 00000000..68a33bcb --- /dev/null +++ b/libkdegames/kgame/kgameproperty.cpp @@ -0,0 +1,211 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ + +#include "kgameproperty.h" +#include "kgamepropertyhandler.h" +#include "kgamemessage.h" +#include "kplayer.h" +#include "kgame.h" + +#define KPLAYERHANDLER_LOAD_COOKIE 6239 + +KGamePropertyBase::KGamePropertyBase(int id, KGame* parent) +{ + init(); + registerData(id, parent); +} + +KGamePropertyBase::KGamePropertyBase(int id, KPlayer* parent) +{ + init(); + registerData(id, parent); +} + +KGamePropertyBase::KGamePropertyBase(int id, KGamePropertyHandler* owner) +{ + init(); + registerData(id, owner); +} + +KGamePropertyBase::KGamePropertyBase() +{ + init(); +} + +KGamePropertyBase::~KGamePropertyBase() +{ + unregisterData(); +} + +void KGamePropertyBase::init() +{ + mOwner = 0; + setDirty(false); + + // this is very useful and used by e.g. KGameDialog so + // it is activated by default. Big games may profit by deactivating it to get + // a better performance. + setEmittingSignal(true); + + setOptimized(false); + + //setReadOnly(false); + mFlags.bits.locked = false ; // setLocked(false); is NOT possible as it checks whether isLocked() allows to change the status + + // local is default + setPolicy(PolicyLocal); +} + +int KGamePropertyBase::registerData(int id, KGame* owner, QString name) +{ return registerData(id, owner->dataHandler(), name); } + +int KGamePropertyBase::registerData(int id, KPlayer* owner, QString name) +{ return registerData(id, owner->dataHandler(), name); } + +int KGamePropertyBase::registerData( KGamePropertyHandler* owner,PropertyPolicy p, QString name) +{ return registerData(-1, owner,p, name); } + +int KGamePropertyBase::registerData(int id, KGamePropertyHandler* owner, QString name) +{ return registerData(id, owner,PolicyUndefined, name); } + +int KGamePropertyBase::registerData(int id, KGamePropertyHandler* owner,PropertyPolicy p, QString name) +{ +// we don't support changing the id + if (!owner) { + kdWarning(11001) << k_funcinfo << "Resetting owner=0. Sure you want to do this?" << endl; + mOwner=0; + return -1; + } + if (!mOwner) { + if (id==-1) { + id=owner->uniquePropertyId(); + } + mId = id; + mOwner = owner; + mOwner->addProperty(this, name); + if (p!=PolicyUndefined) { + setPolicy(p); + } else { + setPolicy(mOwner->policy()); + } + } + return mId; +} + +void KGamePropertyBase::unregisterData() +{ + if (!mOwner) { + return; + } + mOwner->removeProperty(this); + mOwner = 0; +} + +bool KGamePropertyBase::sendProperty() +{ + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyHeader(s, id()); + save(s); + if (mOwner) { + return mOwner->sendProperty(s); + } else { + kdError(11001) << k_funcinfo << "Cannot send because there is no receiver defined" << endl; + return false; + } +} + +bool KGamePropertyBase::sendProperty(const QByteArray& data) +{ + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyHeader(s, id()); + s.writeRawBytes(data.data(), data.size()); + if (mOwner) { + return mOwner->sendProperty(s); + } else { + kdError(11001) << k_funcinfo << ": Cannot send because there is no receiver defined" << endl; + return false; + } +} + +bool KGamePropertyBase::lock() +{ + if (isLocked()) { + return false; + } + setLock(true); + return true; +} + +bool KGamePropertyBase::unlock(bool force) +{ + if (isLocked() && !force) { + return false; + } + setLock(false); + return true; +} + +void KGamePropertyBase::setLock(bool l) +{ + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyCommand(s, IdCommand, id(), CmdLock); + + s << (Q_INT8)l; + if (mOwner) { + mOwner->sendProperty(s); + } else { + kdError(11001) << k_funcinfo << ": Cannot send because there is no receiver defined" << endl; + return ; + } +} + +void KGamePropertyBase::emitSignal() +{ + //kdDebug(11001) << k_funcinfo << ": mOwnerP="<< mOwner << " id=" << id() << endl; + if (mOwner ) { + mOwner->emitSignal(this); + } else { + kdError(11001) << k_funcinfo << ":id="<> locked; + mFlags.bits.locked = (bool)locked ; + break; + } + } + default: // probably in derived classes + break; + } +} + diff --git a/libkdegames/kgame/kgameproperty.h b/libkdegames/kgame/kgameproperty.h new file mode 100644 index 00000000..c6915606 --- /dev/null +++ b/libkdegames/kgame/kgameproperty.h @@ -0,0 +1,848 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KGAMEPROPERTY_H_ +#define __KGAMEPROPERTY_H_ + +#include + +#include +#include +#include +class KGame; +class KPlayer; +class KGamePropertyHandler; +using namespace std; + +/** + * @short Base class of KGameProperty + * + * The KGamePropertyBase class is the base class of KGameProperty. See + * KGameProperty for further information. + * + * @author Andreas Beckermann + **/ +class KDE_EXPORT KGamePropertyBase +{ +public: + enum PropertyDataIds { // these belong to KPlayer/KGame! + //KPlayer + IdGroup=1, + IdUserId=2, + IdAsyncInput=3, + IdTurn=4, + IdName=5, + + //KGame + IdGameStatus=6, + IdMaxPlayer=7, + IdMinPlayer=8, + + // Input Grabbing + IdGrabInput=16, + IdReleaseInput=17, + + IdCommand, // Reserved for internal use + IdUser=256, + + IdAutomatic=0x7000 // Id's from here on are automatically given (16bit) + }; + + /** + * Commands for advanced properties (Q_INT8) + **/ + enum PropertyCommandIds + { + // General + CmdLock=1, + + // Array + CmdAt=51, + CmdResize=52, + CmdFill=53, + CmdSort=54, + // List (could be the same id's actually) + CmdInsert=61, + CmdAppend=62, + CmdRemove=63, + CmdClear=64 + }; + + /** + * The policy of the property. This can be PolicyClean (setValue uses + * send), PolicyDirty (setValue uses changeValue) or + * PolicyLocal (setValue uses setLocal). + * + * A "clean" policy means that the property is always the same on every + * client. This is achieved by calling send which actually changes + * the value only when the message from the MessageServer is received. + * + * A "dirty" policy means that as soon as setValue is called the + * property is changed immediately. And additionally sent over network. + * This can sometimes lead to bugs as the other clients do not + * immediately have the same value. For more information see + * changeValue. + * + * PolicyLocal means that a KGameProperty behaves like ever + * "normal" variable. Whenever setValue is called (e.g. using "=") + * the value of the property is changes immediately without sending it + * over network. You might want to use this if you are sure that all + * clients set the property at the same time. + **/ + enum PropertyPolicy + { + PolicyUndefined = 0, + PolicyClean = 1, + PolicyDirty = 2, + PolicyLocal = 3 + }; + + + /** + * Constructs a KGamePropertyBase object and calls registerData. + * @param id The id of this property. MUST be UNIQUE! Used to send and + * receive changes in the property of the playere automatically via + * network. + * @param owner The owner of the object. Must be a KGamePropertyHandler which manages + * the changes made to this object, i.e. which will send the new data + **/ + KGamePropertyBase(int id, KGamePropertyHandler* owner); + + KGamePropertyBase(int id, KGame* parent); + KGamePropertyBase(int id, KPlayer* parent); + + /** + * Creates a KGamePropertyBase object without an owner. Remember to call + * registerData! + **/ + KGamePropertyBase(); + + virtual ~KGamePropertyBase(); + + /** + * Changes the consistency policy of a property. The + * PropertyPolicy is one of PolicyClean (defaulz), PolicyDirty or PolicyLocal. + * + * It is up to you to decide how you want to work. + **/ + void setPolicy(PropertyPolicy p) { mFlags.bits.policy = p; } + + /** + * @return The default policy of the property + **/ + PropertyPolicy policy() const { return (PropertyPolicy)mFlags.bits.policy; } + + /** + * Sets this property to emit a signal on value changed. + * As the proerties do not inehrit QObject for optimisation + * this signal is emited via the KPlayer or KGame object + **/ + void setEmittingSignal(bool p) { mFlags.bits.emitsignal=p; } + + /** + * See also setEmittingSignal + * @return Whether this property emits a signal on value change + **/ + bool isEmittingSignal() const { return mFlags.bits.emitsignal; } + + /** + * Sets this property to try to optimize signal and network handling + * by not sending it out when the property value is not changed. + **/ + void setOptimized(bool p) { mFlags.bits.optimize = p ; } + + /** + * See also setOptimize + * @return Whether the property optimizes access (signals,network traffic) + **/ + bool isOptimized() const { return mFlags.bits.optimize; } + + /** + * @return Whether this property is "dirty". See also setDirty + **/ + bool isDirty() const { return mFlags.bits.dirty; } + + /** + * A locked property can only be changed by the player who has set the + * lock. See also setLocked + * @return Whether this property is currently locked. + **/ + bool isLocked() const { return mFlags.bits.locked; } + + /** + * A locked property can only be changed by the player who has set the + * lock. + * + * You can only call this if isLocked is false. A message is sent + * over network so that the property is locked for all players except + * you. + * + * @return returns false if the property can not be locked, i.e. it is already locked + * + **/ + bool lock(); + + /** + * A locked property can only be changed by the player who has set the + * lock. + * + * You can only call this if isLocked is false. A message is sent + * over network so that the property is locked for all players except + * you. + * + * @return returns false if the property can not be locked, i.e. it is already locked + * + **/ + bool unlock(bool force=false); + + /** + * This will read the value of this property from the stream. You MUST + * overwrite this method in order to use this class + * @param s The stream to read from + **/ + virtual void load(QDataStream& s) = 0; + + /** + * Write the value into a stream. MUST be overwritten + **/ + virtual void save(QDataStream& s) = 0; + + /** + * send a command to advanced properties like arrays + * @param stream The stream containing the data of the comand + * @param msgid The ID of the command - see PropertyCommandIds + * @param isSender whether this client is also the sender of the command + **/ + virtual void command(QDataStream &stream, int msgid, bool isSender=false); + + /** + * @return The id of this property + **/ + int id() const { return mId; } + + /** + * @return a type_info of the data this property contains. This is used + * e.g. by KGameDebugDialog + **/ + virtual const type_info* typeinfo() { return &typeid(this); } + + /** + * You have to register a KGamePropertyBase before you can use it. + * + * You MUST call this before you can use KGamePropertyBase! + * + * @param id the id of this KGamePropertyBase object. The id MUST be + * unique, i.e. you cannot have two properties with the same id for one + * player, although (currently) nothing prevents you from doing so. But + * you will get strange results! + * + * @param owner The owner of this data. This will send the data + * using KPropertyHandler::sendProperty whenever you call send + * + * @param p If not 0 you can set the policy of the property here + * + * @param name if not 0 you can assign a name to this property + * + **/ + int registerData(int id, KGamePropertyHandler* owner,PropertyPolicy p, QString name=0); + + /** + * This is an overloaded member function, provided for convenience. + * It differs from the above function only in what argument(s) it accepts. + **/ + int registerData(int id, KGamePropertyHandler* owner, QString name=0); + + /** + * This is an overloaded member function, provided for convenience. + * It differs from the above function only in what argument(s) it accepts. + **/ + int registerData(int id, KGame* owner, QString name=0); + + /** + * This is an overloaded member function, provided for convenience. + * It differs from the above function only in what argument(s) it accepts. + **/ + int registerData(int id, KPlayer* owner, QString name=0); + + /** + * This is an overloaded member function, provided for convenience. + * It differs from the above function only in what argument(s) it accepts. + * In particular you can use this function to create properties which + * will have an automatic id assigned. The new id is returned. + **/ + int registerData(KGamePropertyHandler* owner,PropertyPolicy p=PolicyUndefined, QString name=0); + + void unregisterData(); + + +protected: + /** + * A locked property can only be changed by the player who has set the + * lock. + * + * You can only call this if isLocked is false. A message is sent + * over network so that the property is locked for all players except + * you. + * Usually you use lock and unlock to access this property + * + **/ + void setLock(bool l); + + /** + * Sets the "dirty" flag of the property. If a property is "dirty" i.e. + * KGameProperty::setLocal has been called there is no guarantee + * that all clients share the same value. You have to ensure this + * yourself e.g. by calling KGameProperty::setLocal on every + * client. You can also ignore the dirty flag and continue working withe + * the property depending on your situation. + **/ + void setDirty(bool d) { mFlags.bits.dirty = d ; } + + /** + * Forward the data to the owner of this property which then sends it + * over network. save is used to store the data into a stream so + * you have to make sure that function is working properly if you + * implement your own property! + * + * Note: this sends the current property! + * + * Might be obsolete - KGamePropertyArray still uses it. Is this a bug + * or correct? + **/ + bool sendProperty(); + + /** + * Forward the data to the owner of this property which then sends it + * over network. save is used to store the data into a stream so + * you have to make sure that function is working properly if you + * implement your own property! + * + * This function is used by send to send the data over network. + * This does not send the current value but the explicitly + * given value. + * + * @return TRUE if the message could be sent successfully, otherwise + * FALSE + **/ + bool sendProperty(const QByteArray& b); + + /** + * Causes the parent object to emit a signal on value change + **/ + void emitSignal(); + +protected: + KGamePropertyHandler* mOwner; + + // Having this as a union of the bitfield and the char + // allows us to stream this quantity easily (if we need to) + // At the moment it is not yet transmitted + union Flags { + char flag; + struct { + // unsigned char dosave : 1; // do save this property + // unsigned char delaytransmit : 1; // do not send immediately on + // change but a KPlayer:QTimer + // sends it later on - fast + // changing variables + unsigned char emitsignal : 1; // KPlayer notifies on variable change (true) + //unsigned char readonly : 1; // whether the property can be changed (false) + unsigned char optimize : 1; // whether the property tries to optimize send/emit (false) + unsigned char dirty: 1; // whether the property dirty (setLocal() was used) + unsigned char policy : 2; // whether the property is always consistent (see PropertyPolicy) + unsigned char locked: 1; // whether the property is locked (true) + } bits; + } mFlags; + +private: + friend class KGamePropertyHandler; + void init(); + +private: + int mId; + +}; + +/** + * @short A class for network transparent games + * + * Note: The entire API documentation is obsolete! + * + * The class KGameProperty can store any form of data and will transmit it via + * network whenver you call send. This makes network transparent games + * very easy. You first have to register the data to a KGamePropertyHandler + * using KGamePropertyBase::registerData (which is called by the + * constructor). For the KGamePropertyHandler you can use + * KGame::dataHandler or KPlayer::dataHandler but you can also create your + * own data handler. + * + * There are several concepts you can follow when writing network games. These + * concepts differ completely from the way how data is transferred so you should + * decide which one to use. You can also mix these concepts for a single + * property but we do not recommend this. The concepts: + *
    + *
  • Always Consistent (clean) + *
  • Not Always Consistent (dirty) + *
  • A Mixture (very dirty) + *
+ * I repeat: we do not recommend the third option ("a mixture"). Unless + * you have a good reason for this you will probably introduce some hard to find + * (and to fix) bugs. + * + * @section Always consistent (clean): + * + * This "policy" is default. Whenever you create a KGameProperty it is always + * consistent. This means that consistency is the most important thing for the + * property. This is achieved by using send to change the value of the + * property. send needs a running KMessageServer and therefore + * MUST be plugged into a KGamePropertyHandler using either + * registerData or the constructor. The parent of the dataHandler must be able + * to send messages (see above: the message server must be running). If you use + * send to change the value of a property you won't see the effect + * immediately: The new value is first transferred to the message server which + * queues the message. As soon as all messages in the message server + * which are before the changed property have been transferred the message + * server delivers the new value of the KGameProperty to all clients. A + * QTimer::singleShot is used to queue the messages inside the + * KMessageServer. + * + * This means that if you do the following: + * \code + * KGamePropertyInt myProperty(id, dataHandler()); + * myProperty.initData(0); + * myProperty = 10; + * int value = myProperty.value(); + * \endcode + * then "value" will be "0". initData is used to initialize the property + * (e.g. when the KMessageServer is not yet running or can not yet be + * reached). This is because "myProperty = 10" or "myProperty.send(10)" send a + * message to the KMessageServer which uses QTimer::singleShot to + * queue the message. The game first has to go back into the event loop where + * the message is received. The KGamePropertyHandler receives the new value + * sets the property. So if you need the new value you need to store it in a + * different variable (see setLocal which creates one for you until the + * message is received). The KGamePropertyHandler emits a signal (unless + * you called setEmitSignal with false) when the new value is received: + * KGamePropertyHandler::signalPropertyChanged. You can use this to react + * to a changed property. + * + * This may look quite confusing but it has a big advantage: all + * KGameProperty objects are ensured to have the same value on all clients in + * the game at every time. This way you will save you a lot of trouble as + * debugging can be very difficult if the value of a property changes + * immediately on client A but only after one or two additianal messages + * (function calls, status changes, ...) on client B. + * + * The only disadvantage of this (clean) concept is that you cannot use a + * changed variable immediately but have to wait for the KMessageServer to + * change it. You probably want to use + * KGamePropertyHandler::signalPropertyChanged for this. + * + * @section Not Always Consistent (dirty): + * + * There are a lot of people who don't want to use the (sometimes quite complex) + * "clean" way. You can use setAlwaysConsistent to change the default + * behaviour of the KGameProperty. If a property is not always consistent + * it will use changeValue to send the property. changeValue also uses + * send to send the new value over network but it also uses + * setLocal to create a local copy of the property. This copy is created + * dynamically and is deleted again as soon as the next message from the network + * is received. To use the example above again: + * \code + * KGamePropertyInt myProperty(id, dataHandler()); + * myProperty.setAlwaysConsistent(false); + * myProperty.initData(0); + * myProperty = 10; + * int value = myProperty.value(); + * \endcode + * Now this example will "work" so value now is 10. Additionally the + * KMessageServer receives a message from the local client (just as explained + * above in "Always Consistent"). As soon as the message returns to the local + * client again the local value is deleted, as the "network value" has the same + * value as the local one. So you won't lose the ability to use the always + * consistent "clean" value of the property if you use the "dirty" way. Just use + * networkValue to access the value which is consistent among all clients. + * + * The advantage of this concept is clear: you can use a KGameProperty as + * every other variable as the changes value takes immediate effect. + * Additionally you can be sure that the value is transferred to all clients. + * You will usually not experience serious bugs just because you use the "dirty" + * way. Several events have to happen at once to get these "strange errors" + * which result in inconsistent properties (like "game running" on client A but + * "game ended/paused" on client B). But note that there is a very good reason + * for the existence of these different concepts of KGameProperty. I have + * myself experienced such a "strange error" and it took me several days to find + * the reason until I could fix it. So I personally recommend the "clean" way. + * On the other hand if you want to port a non-network game to a network game + * you will probably start with "dirty" properties as it is you will not have to + * change that much code... + * + * @section A Mixture (very dirty): + * + * You can also mix the concepts above. Note that we really don't recommend + * this. With a mixture I mean something like this: + * \code + * KGamePropertyInt myProperty(id, dataHandler()); + * myProperty.setAlwaysConsistent(false); + * myProperty.initData(0); + * myProperty = 10; + * myProperty.setAlwaysConsistent(true); + * myProperty = 20; + * \endcode + * (totally senseless example, btw) I.e. I am speaking of mixing both concepts + * for a single property. Things like + * \code + * KGamePropertyInt myProperty1(id1, dataHandler()); + * KGamePropertyInt myProperty2(id2, dataHandler()); + * myProperty1.initData(0); + * myProperty2.initData(0); + * myProperty1.setAlwaysConsistent(false); + * myProperty2.setAlwaysConsistent(true); + * myProperty1 = 10; + * myProperty2 = 20; + * \endcode + * are ok. But mixing the concepts for a single property will make it nearly + * impossible to you to debug your game. + * + * So the right thing to do(tm) is to decide in the constructor whether you want + * a "clean" or "dirty" property. + * + * Even if you have decided for one of the concepts you still can manually + * follow another concept than the "policy" of your property. So if you use an + * always consistent KGameProperty you still can manually call + * changeValue as if it was not always consistent. Note that although this is + * also kind of a "mixture" as described above this is very useful sometimes. In + * contrast to the "mixture" above you don't have the problem that you don't + * exactly know which concept you are currently following because you used the + * function of the other concept only once. + * + * @section Custom classes: + * + * If you want to use a custum class with KGameProperty you have to implement the + * operators << and >> for QDataStream: + * \code + * class Card + * { + * public: + * int type; + * int suite; + * }; + * QDataStream& operator<<(QDataStream& stream, Card& card) + * { + * Q_INT16 type = card.type; + * Q_INT16 suite = card.suite; + * s << type; + * s << suite; + * return s; + * } + * QDataStream& operator>>(QDataStream& stream, Card& card) + * { + * Q_INT16 type; + * Q_INT16 suite; + * s >> type; + * s >> suite; + * card.type = (int)type; + * card.suite = (int)suite; + * return s; + * } + * + * class Player : KPlayer + * { + * [...] + * KGameProperty mCards; + * }; + * \endcode + * + * Note: unlike most QT classes KGameProperty objects are *not* deleted + * automatically! So if you create an object using e.g. KGameProperty* data = + * new KGameProperty(id, dataHandler()) you have to put a delete data into your + * destructor! + * + * @author Andreas Beckermann + **/ +template +class KGameProperty : public KGamePropertyBase +{ +public: + /** + * Constructs a KGameProperty object. A KGameProperty object will transmit + * any changes to the KMessageServer and then to all clients in the + * game (including the one that has sent the new value) + * @param id The id of this property. MUST be UNIQUE! Used to send and + * receive changes in the property of the playere automatically via + * network. + * @param owner The parent of the object. Must be a KGame which manages + * the changes made to this object, i.e. which will send the new data. + * Note that in contrast to most KDE/QT classes KGameProperty objects + * are not deleted automatically! + **/ +// TODO: ID: Very ugly - better use something like parent()->propertyId() or so which assigns a free id automatically. + KGameProperty(int id, KGamePropertyHandler* owner) : KGamePropertyBase(id, owner) { init(); } + + /** + * This constructor does nothing. You have to call + * KGamePropertyBase::registerData + * yourself before using the KGameProperty object. + **/ + KGameProperty() : KGamePropertyBase() { init(); } + + virtual ~KGameProperty() {} + + /** + * Set the value depending on the current policy (see + * setConsistent). By default KGameProperty just uses send to set + * the value of a property. This behaviour can be changed by using + * setConsistent. + * @param v The new value of the property + **/ + void setValue(type v) + { + switch (policy()) { + case PolicyClean: + send(v); + break; + case PolicyDirty: + changeValue(v); + break; + case PolicyLocal: + setLocal(v); + break; + default: // NEVER! + kdError(11001) << "Undefined Policy in property " << id() << endl; + return; + } + } + + + /** + * This function sends a new value over network. + * + * Note that the value DOES NOT change when you call this function. This + * function saves the value into a QDataStream and calls + * sendProperty where it gets forwarded to the owner and finally the + * value is sent over network. The KMessageServer now sends the + * value to ALL clients - even the one who called this function. As soon + * as the value from the message server is received load is called + * and _then_ the value of the KGameProperty has been set. + * + * This ensures that a KGameProperty has _always_ the same value on + * _every_ client in the network. Note that this means you can NOT do + * something like + * \code + * myProperty.send(1); + * doSomething(myProperty); + * \endcode + * as myProperty has not yet been set when doSomething is being called. + * + * You are informed about a value change by a singal from the parent of + * the property which can be deactivated by setEmittingSignal because of + * performance (you probably don't have to deactivate it - except you + * want to write a real-time game like Command&Conquer with a lot of + * acitvity). See emitSignal + * + * Note that if there is no KMessageServer accessible - before + * the property has been registered to the KGamePropertyHandler (as + * it is the case e.g. before a KPlayer has been plugged into the + * KGame object) the property is *not* sent but set *locally* (see + * setLocal)! + * + * @param v The new value of the property + * @return whether the property could be sent successfully + * @see setValue setLocal changeValue value + **/ + bool send(type v) + { + if (isOptimized() && mData == v) { + return true; + } + if (isLocked()) { + return false; + } + QByteArray b; + QDataStream stream(b, IO_WriteOnly); + stream << v; + if (!sendProperty(b)) { + setLocal(v); + return false; + } + return true; + } + + /** + * This function sets the value of the property directly, i.e. it + * doesn't send it to the network. + * + * Int contrast to @see you change _only_ the local value when using + * this function. You do _not_ change the value of any other client. You + * probably don't want to use this if you are using a dedicated server + * (which is the only "client" which is allowed to change a value) but + * rather want to use send(). + * + * But if you use your clients as servers (i.e. all clients receive a + * players turn and then calculate the reaction of the game theirselves) + * then you probably want to use setLocal as you can do things like + * \code + * myProperty.setLocal(1); + * doSomething(myProperty); + * \endcode + * on every client. + * + * If you want to set the value locally AND send it over network you + * want to call changeValue! + * + * You can also use setPolicy to set the default policy to + * PolicyLocal. + * + * @see setValue send changeValue value + **/ + bool setLocal(type v) + { + if (isOptimized() && mData == v) { + return false; + } + if (isLocked()) { + return false; + } + mData = v; + setDirty(true); + if (isEmittingSignal()) { + emitSignal(); + } + return true; + } + + /** + * This function does both, change the local value and change the + * network value. The value is sent over network first, then changed + * locally. + * + * This function is a convenience function and just calls send + * followed by setLocal + * + * Note that emitSignal is also called twice: once after + * setLocal and once when the value from send is received + * + * @see send setLocal setValue value + **/ + void changeValue(type v) + { + send(v); + setLocal(v); + } + + /** + * Saves the object to a stream. + * @param stream The stream to save to + **/ + virtual void save(QDataStream &stream) + { + stream << mData; + } + + /** + * @return The local value (see setLocal) if it is existing, + * otherwise the network value which is always consistent on every + * client. + **/ + const type& value() const + { + return mData; + } + + /** + * Reads from a stream and assigns the read value to this object. + * + * This function is called automatically when a new value is received + * over network (i.e. it has been sent using send on this or any + * other client) or when a game is loaded (and maybe on some other + * events). + * + * Also calls emitSignal if isEmittingSignal is TRUE. + * @param s The stream to read from + **/ + virtual void load(QDataStream& s) + { + s >> mData; + setDirty(false); + if (isEmittingSignal()) { + emitSignal(); + } + } + + /** + * This calls setValue to change the value of the property. Note + * that depending on the policy (see setAlwaysConsistent) the + * returned value might be different from the assigned value!! + * + * So if you use setPolicy(PolicyClean): + * \code + * int a, b = 10; + * myProperty = b; + * a = myProperty.value(); + * \endcode + * Here a and b would differ! + * The value is actually set as soon as it is received from the + * KMessageServer which forwards it to ALL clients in the network. + * + * If you use a clean policy (see setPolicy) then + * the returned value is the assigned value + **/ + const type& operator=(const type& t) + { + setValue(t); + return value(); + } + + /** + * This copies the data of property to the KGameProperty object. + * + * Equivalent to setValue(property.value()); + **/ + const type& operator=(const KGameProperty& property) + { + setValue(property.value()); + return value(); + } + + /** + * Yeah, you can do it! + * \code + * int a = myGamePropertyInt; + * \endcode + * If you don't see it: you don't have to use integerData.value() + **/ + operator type() const { return value(); } + + virtual const type_info* typeinfo() { return &typeid(type); } + +private: + void init() { } + +private: + type mData; +}; + + +typedef KGameProperty KGamePropertyInt; +typedef KGameProperty KGamePropertyUInt; +typedef KGameProperty KGamePropertyQString; +typedef KGameProperty KGamePropertyBool; + +#endif diff --git a/libkdegames/kgame/kgamepropertyarray.h b/libkdegames/kgame/kgamepropertyarray.h new file mode 100644 index 00000000..f91bd75c --- /dev/null +++ b/libkdegames/kgame/kgamepropertyarray.h @@ -0,0 +1,309 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KGAMEPROPERTYARRAY_H_ +#define __KGAMEPROPERTYARRAY_H_ + +#include +#include + +#include "kgamemessage.h" +#include "kgameproperty.h" +#include "kgamepropertyhandler.h" + + +template +class KGamePropertyArray : public QMemArray, public KGamePropertyBase +{ +public: + KGamePropertyArray() :QMemArray(), KGamePropertyBase() + { + //kdDebug(11001) << "KGamePropertyArray init" << endl; + } + + KGamePropertyArray( int size ) + { + resize(size); + } + + KGamePropertyArray( const KGamePropertyArray &a ) : QMemArray(a) + { + } + + bool resize( uint size ) + { + if (size!=QMemArray::size()) + { + bool a=true; + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdResize); + s << size ; + if (policy()==PolicyClean || policy()==PolicyDirty) + { + if (mOwner) + { + mOwner->sendProperty(s); + } + } + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + extractProperty(b); +// a=QMemArray::resize(size);// FIXME: return value! + } + return a; + } + else return true; + } + + void setAt(uint i,type data) + { + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdAt); + s << i ; + s << data; + if (policy()==PolicyClean || policy()==PolicyDirty) + { + if (mOwner) + { + mOwner->sendProperty(s); + } + } + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + extractProperty(b); + } + //kdDebug(11001) << "KGamePropertyArray setAt send COMMAND for id="<::size(); i++) + { + s >> data; + QMemArray::at(i)=data; + } + if (isEmittingSignal()) + { + emitSignal(); + } + } + void save(QDataStream &s) + { + //kdDebug(11001) << "KGamePropertyArray save "<::size(); i++) + { + s << at(i); + } + } + + void command(QDataStream &s,int cmd,bool) + { + KGamePropertyBase::command(s, cmd); + //kdDebug(11001) << "Array id="<> i >> data; + QMemArray::at(i)=data; + //kdDebug(11001) << "CmdAt:id="< +#include + +#include +#include + +#define KPLAYERHANDLER_LOAD_COOKIE 6239 + +//---------------------- KGamePropertyHandler ----------------------------------- +class KGamePropertyHandlerPrivate +{ +public: + KGamePropertyHandlerPrivate() + { + } + + QMap mNameMap; + QIntDict mIdDict; + int mUniqueId; + int mId; + KGamePropertyBase::PropertyPolicy mDefaultPolicy; + bool mDefaultUserspace; + int mIndirectEmit; + QPtrQueue mSignalQueue; +}; + +KGamePropertyHandler::KGamePropertyHandler(int id, const QObject* receiver, const char * sendf, const char *emitf, QObject* parent) : QObject(parent) +{ + init(); + registerHandler(id,receiver,sendf,emitf); +} + +KGamePropertyHandler::KGamePropertyHandler(QObject* parent) : QObject(parent) +{ + init(); +} + +KGamePropertyHandler::~KGamePropertyHandler() +{ + clear(); + delete d; +} + +void KGamePropertyHandler::init() +{ + kdDebug(11001) << k_funcinfo << ": this=" << this << endl; + d = new KGamePropertyHandlerPrivate; // for future use - is BC important to us? + d->mId = 0; + d->mUniqueId=KGamePropertyBase::IdAutomatic; + d->mDefaultPolicy=KGamePropertyBase::PolicyLocal; + d->mDefaultUserspace=true; + d->mIndirectEmit=0; +} + + +int KGamePropertyHandler::id() const +{ + return d->mId; +} + +void KGamePropertyHandler::setId(int id) +{ + d->mId = id; +} + +void KGamePropertyHandler::registerHandler(int id,const QObject * receiver, const char * sendf, const char *emitf) +{ + setId(id); + if (receiver && sendf) { + kdDebug(11001) << "Connecting SLOT " << sendf << endl; + connect(this, SIGNAL(signalSendMessage(int, QDataStream &, bool*)), receiver, sendf); + } + if (receiver && emitf) { + kdDebug(11001) << "Connecting SLOT " << emitf << endl; + connect(this, SIGNAL(signalPropertyChanged(KGamePropertyBase *)), receiver, emitf); + } +} + +bool KGamePropertyHandler::processMessage(QDataStream &stream, int id, bool isSender) +{ +// kdDebug(11001) << k_funcinfo << ": id=" << id << " mId=" << d->mId << endl; + if (id != d->mId) { + return false; // Is the message meant for us? + } + KGamePropertyBase* p; + int propertyId; + KGameMessage::extractPropertyHeader(stream, propertyId); +// kdDebug(11001) << k_funcinfo << ": Got property " << propertyId << endl; + if (propertyId==KGamePropertyBase::IdCommand) { + int cmd; + KGameMessage::extractPropertyCommand(stream, propertyId, cmd); +//kdDebug(11001) << k_funcinfo << ": Got COMMAND for id= "<mIdDict.find(propertyId); + if (p) { + if (!isSender || p->policy()==KGamePropertyBase::PolicyClean) { + p->command(stream, cmd, isSender); + } + } else { + kdError(11001) << k_funcinfo << ": (cmd): property " << propertyId << " not found" << endl; + } + return true; + } + p = d->mIdDict.find(propertyId); + if (p) { + //kdDebug(11001) << k_funcinfo << ": Loading " << propertyId << endl; + if (!isSender || p->policy()==KGamePropertyBase::PolicyClean) { + p->load(stream); + } + } else { + kdError(11001) << k_funcinfo << ": property " << propertyId << " not found" << endl; + } + return true; +} + +bool KGamePropertyHandler::removeProperty(KGamePropertyBase* data) +{ + if (!data) { + return false; + } + d->mNameMap.erase(data->id()); + return d->mIdDict.remove(data->id()); +} + +bool KGamePropertyHandler::addProperty(KGamePropertyBase* data, QString name) +{ + //kdDebug(11001) << k_funcinfo << ": " << data->id() << endl; + if (d->mIdDict.find(data->id())) { + // this id already exists + kdError(11001) << " -> cannot add property " << data->id() << endl; + return false; + } else { + d->mIdDict.insert(data->id(), data); + // if here is a check for "is_debug" or so we can add the strings only in debug mode + // and save memory!! + if (!name.isNull()) { + d->mNameMap[data->id()] = name; + //kdDebug(11001) << k_funcinfo << ": nid="<< (data->id()) << " inserted in Map name=" << d->mNameMap[data->id()] <mNameMap[id]).arg(id); + } else { + s = i18n("Unnamed - ID: %1").arg(id); + } + } else { + // Should _never_ happen + s = i18n("%1 unregistered").arg(id); + } + return s; +} + +bool KGamePropertyHandler::load(QDataStream &stream) +{ + // Prevent direct emmiting until all is loaded + lockDirectEmit(); + uint count,i; + stream >> count; + kdDebug(11001) << k_funcinfo << ": " << count << " KGameProperty objects " << endl; + for (i = 0; i < count; i++) { + processMessage(stream, id(),false); + } + Q_INT16 cookie; + stream >> cookie; + if (cookie == KPLAYERHANDLER_LOAD_COOKIE) { + kdDebug(11001) << " KGamePropertyHandler loaded propertly"<mIdDict.count() << " KGameProperty objects " << endl; + stream << (uint)d->mIdDict.count(); + QIntDictIterator it(d->mIdDict); + while (it.current()) { + KGamePropertyBase *base=it.current(); + if (base) { + KGameMessage::createPropertyHeader(stream, base->id()); + base->save(stream); + } + ++it; + } + stream << (Q_INT16)KPLAYERHANDLER_LOAD_COOKIE; + return true; +} + +KGamePropertyBase::PropertyPolicy KGamePropertyHandler::policy() +{ +// kdDebug(11001) << k_funcinfo << ": " << d->mDefaultPolicy << endl; + return d->mDefaultPolicy; +} +void KGamePropertyHandler::setPolicy(KGamePropertyBase::PropertyPolicy p,bool userspace) +{ + // kdDebug(11001) << k_funcinfo << ": " << p << endl; + d->mDefaultPolicy=p; + d->mDefaultUserspace=userspace; + QIntDictIterator it(d->mIdDict); + while (it.current()) { + if (!userspace || it.current()->id()>=KGamePropertyBase::IdUser) { + it.current()->setPolicy((KGamePropertyBase::PropertyPolicy)p); + } + ++it; + } +} + +void KGamePropertyHandler::unlockProperties() +{ + QIntDictIterator it(d->mIdDict); + while (it.current()) { + it.current()->unlock(); + ++it; + } +} + +void KGamePropertyHandler::lockProperties() +{ + QIntDictIterator it(d->mIdDict); + while (it.current()) { + it.current()->lock(); + ++it; + } +} + +int KGamePropertyHandler::uniquePropertyId() +{ + return d->mUniqueId++; +} + +void KGamePropertyHandler::flush() +{ + QIntDictIterator it(d->mIdDict); + while (it.current()) { + if (it.current()->isDirty()) { + it.current()->sendProperty(); + } + ++it; + } +} + +/* Fire all property signal changed which are collected in + * the queque + **/ +void KGamePropertyHandler::lockDirectEmit() +{ + d->mIndirectEmit++; +} + +void KGamePropertyHandler::unlockDirectEmit() +{ + // If the flag is <=0 we emit the queued signals + d->mIndirectEmit--; + if (d->mIndirectEmit<=0) + { + KGamePropertyBase *prop; + while((prop=d->mSignalQueue.dequeue()) != 0) + { + // kdDebug(11001) << "emmiting signal for " << prop->id() << endl; + emit signalPropertyChanged(prop); + } + } +} + +void KGamePropertyHandler::emitSignal(KGamePropertyBase *prop) +{ + // If the indirect flag is set (load and network transmit) + // we cannot emit the signals directly as it can happend that + // a sigal causes an access to a property which is e.g. not + // yet loaded or received + + if (d->mIndirectEmit>0) + { + // Queque the signal + d->mSignalQueue.enqueue(prop); + } + else + { + // directly emit + emit signalPropertyChanged(prop); + } +} + +bool KGamePropertyHandler::sendProperty(QDataStream &s) +{ + bool sent = false; + emit signalSendMessage(id(), s, &sent); + return sent; +} + +KGamePropertyBase *KGamePropertyHandler::find(int id) +{ + return d->mIdDict.find(id); +} + +void KGamePropertyHandler::clear() +{ + kdDebug(11001) << k_funcinfo << id() << endl; + QIntDictIterator it(d->mIdDict); + while (it.toFirst()) { + KGamePropertyBase* p = it.toFirst(); + p->unregisterData(); + if (d->mIdDict.find(p->id())) { + // shouldn't happen - but if mOwner in KGamePropertyBase is NULL + // this might be possible + removeProperty(p); + } + } +} + +QIntDict& KGamePropertyHandler::dict() const +{ + return d->mIdDict; +} + +QString KGamePropertyHandler::propertyValue(KGamePropertyBase* prop) +{ + if (!prop) { + return i18n("NULL pointer"); + } + + int id = prop->id(); + QString name = propertyName(id); + QString value; + + const type_info* t = prop->typeinfo(); + if (*t == typeid(int)) { + value = QString::number(((KGamePropertyInt*)prop)->value()); + } else if (*t == typeid(unsigned int)) { + value = QString::number(((KGamePropertyUInt *)prop)->value()); + } else if (*t == typeid(long int)) { + value = QString::number(((KGameProperty *)prop)->value()); + } else if (*t == typeid(unsigned long int)) { + value = QString::number(((KGameProperty *)prop)->value()); + } else if (*t == typeid(QString)) { + value = ((KGamePropertyQString*)prop)->value(); + } else if (*t == typeid(Q_INT8)) { + value = ((KGamePropertyBool*)prop)->value() ? i18n("True") : i18n("False"); + } else { + emit signalRequestValue(prop, value); + } + + if (value.isNull()) { + value = i18n("Unknown"); + } + return value; +} + +void KGamePropertyHandler::Debug() +{ + kdDebug(11001) << "-----------------------------------------------------------" << endl; + kdDebug(11001) << "KGamePropertyHandler:: Debug this=" << this << endl; + + kdDebug(11001) << " Registered properties: (Policy,Lock,Emit,Optimized, Dirty)" << endl; + QIntDictIterator it(d->mIdDict); + while (it.current()) { + KGamePropertyBase *p=it.current(); + kdDebug(11001) << " "<< p->id() << ": p=" << p->policy() + << " l="<isLocked() + << " e="<isEmittingSignal() + << " o=" << p->isOptimized() + << " d="<isDirty() + << endl; + ++it; + } + kdDebug(11001) << "-----------------------------------------------------------" << endl; +} + +#include "kgamepropertyhandler.moc" diff --git a/libkdegames/kgame/kgamepropertyhandler.h b/libkdegames/kgame/kgamepropertyhandler.h new file mode 100644 index 00000000..6147c071 --- /dev/null +++ b/libkdegames/kgame/kgamepropertyhandler.h @@ -0,0 +1,353 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KGAMEPROPERTYHANDLER_H_ +#define __KGAMEPROPERTYHANDLER_H_ + +#include +#include + +#include "kgameproperty.h" +#include + +class QDataStream; +class KGame; +class KPlayer; +//class KGamePropertyBase; + +class KGamePropertyHandlerPrivate; // wow - what a name ;-) + +/** + * @short A collection class for KGameProperty objects + * + * The KGamePropertyHandler class is some kind of a collection class for + * KGameProperty. You usually don't have to create one yourself, as both + * KPlayer and KGame provide a handler. In most cases you do not even have + * to care about the KGamePropertHandler. KGame and KPlayer implement + * all features of KGamePropertyHandler so you will rather use it there. + * + * You have to use the KGamePropertyHandler as parent for all KGameProperty + * objects but you can also use KPlayer or KGame as parent - then + * KPlayer::dataHandler or KGame::dataHandler will be used. + * + * Every KGamePropertyHandler must have - just like every KGameProperty - + * a unique ID. This ID is provided either in the constructor or in + * registerHandler. The ID is used to assign an incoming message (e.g. a changed + * property) to the correct handler. Inside the handler the property ID is used + * to change the correct property. + * + * The constructor or registerHandler takes 3 addittional arguments: a + * receiver and two slots. The first slot is connected to + * signalSendMessage, the second to signalPropertyChanged. You must provide + * these in order to use the KGamePropertyHandler. + * + * The most important function of KGamePropertyHandler is processMessage + * which assigns an incoming value to the correct property. + * + * A KGamePropertyHandler is also used - indirectly using emitSignal - to + * emit a signal when the value of a property changes. This is done this way + * because a KGameProperty does not inherit QObject because of memory + * advantages. Many games can have dozens or even hundreds of KGameProperty + * objects so every additional variable in KGameProperty would be + * multiplied. + * + **/ +class KDE_EXPORT KGamePropertyHandler : public QObject +{ + Q_OBJECT + +public: + /** + * Construct an unregistered KGamePropertyHandler + * + * You have to call registerHandler before you can use this + * handler! + **/ + KGamePropertyHandler(QObject* parent = 0); + + /** + * Construct a registered handler. + * + * @see registerHandler + **/ + KGamePropertyHandler(int id, const QObject* receiver, const char* sendf, const char* emitf, QObject* parent = 0); + ~KGamePropertyHandler(); + + /** + * Register the handler with a parent. This is to use + * if the constructor without arguments has been chosen. + * Otherwise you need not call this. + * + * @param id The id of the message to listen for + * @param receiver The object that will receive the signals of + * KGamePropertyHandler + * @param send A slot that is being connected to signalSendMessage + * @param emit A slot that is being connected to signalPropertyChanged + **/ + void registerHandler(int id, const QObject *receiver, const char * send, const char *emit); + + /** + * Main message process function. This has to be called by + * the parent's message event handler. If the id of the message + * agrees with the id of the handler, the message is extracted + * and processed. Otherwise false is returned. + * Example: + * \code + * if (mProperties.processMessage(stream,msgid,sender==gameId())) return ; + * \endcode + * + * @param stream The data stream containing the message + * @param id the message id of the message + * @param isSender Whether the receiver is also the sender + * @return true on message processed otherwise false + **/ + bool processMessage(QDataStream &stream, int id, bool isSender ); + + /** + * @return the id of the handler + **/ + int id() const; + + /** + * Adds a KGameProperty property to the handler + * @param data the property + * @param name A description of the property, which will be returned by + * propertyName. This is used for debugging, e.g. in KGameDebugDialog + * @return true on success + **/ + bool addProperty(KGamePropertyBase *data, QString name=0); + + /** + * Removes a property from the handler + * @param data the property + * @return true on success + **/ + bool removeProperty(KGamePropertyBase *data); + + /** + * returns a unique property ID starting called usually with a base of + * KGamePropertyBase::IdAutomatic. This is used internally by + * the property base to assign automtic id's. Not much need to + * call this yourself. + **/ + int uniquePropertyId(); + + + /** + * Loads properties from the datastream + * + * @param stream the datastream to load from + * @return true on success otherwise false + **/ + virtual bool load(QDataStream &stream); + + /** + * Saves properties into the datastream + * + * @param stream the datastream to save to + * @return true on success otherwise false + **/ + virtual bool save(QDataStream &stream); + + /** + * called by a property to send itself into the + * datastream. This call is simply forwarded to + * the parent object + **/ + bool sendProperty(QDataStream &s); + + void sendLocked(bool l); + + /** + * called by a property to emit a signal + * This call is simply forwarded to + * the parent object + **/ + void emitSignal(KGamePropertyBase *data); + + /** + * @param id The ID of the property + * @return A name of the property which can be used for debugging. Don't + * depend on this function! It it possible not to provide a name or to + * provide the same name for multiple properties! + **/ + QString propertyName(int id) const; + + /** + * @param id The ID of the property. See KGamePropertyBase::id + * @return The KGameProperty this ID is assigned to + **/ + KGamePropertyBase *find(int id); + + /** + * Clear the KGamePropertyHandler. Note that the properties are + * not deleted so if you created your KGameProperty + * objects dynamically like + * \code + * KGamePropertyInt* myProperty = new KGamePropertyInt(id, dataHandler()); + * \endcode + * you also have to delete it: + * \code + * dataHandler()->clear(); + * delete myProperty; + * \endcode + **/ + void clear(); + + /** + * Use id as new ID for this KGamePropertyHandler. This is used + * internally only. + **/ + void setId(int id);//AB: TODO: make this protected in KGamePropertyHandler!! + + /** + * Calls KGamePropertyBase::setReadOnly(false) for all properties of this + * player. See also lockProperties + **/ + void unlockProperties(); + + /** + * Set the policy for all kgame variables which are currently registerd in + * the KGame proeprty handler. See KGamePropertyBase::setPolicy + * + * @param p is the new policy for all properties of this handler + * @param userspace if userspace is true (default) only user properties are changed. + * Otherwise the system properties are also changed. + **/ + void setPolicy(KGamePropertyBase::PropertyPolicy p, bool userspace=true); + + /** + * Called by the KGame or KPlayer object or the handler itself to delay + * emmiting of signals. Lockign keeps a counter and unlock is only achieved + * when every lock is canceld by an unlock. + * While this is set signals are quequed and only emmited after this + * is reset. Its deeper meaning is to prevent inconsistencies in a game + * load or network transfer where a emit could access a property not + * yet loaded or transmitted. Calling this by yourself you better know + * what your are doing. + **/ + void lockDirectEmit(); + + /** + * Removes the lock from the emitting of property signals. Corresponds to + * the lockIndirectEmits + **/ + void unlockDirectEmit(); + + /** + * Returns the default policy for this property handler. All properties + * registered newly, will have this property. + **/ + KGamePropertyBase::PropertyPolicy policy(); + + /** + * Calls KGamePropertyBase::setReadOnly(true) for all properties of this + * handler + * + * Use with care! This will even lock the core properties, like name, + * group and myTurn!! + * + * @see unlockProperties + **/ + void lockProperties(); + + /** + * Sends all properties which are marked dirty over the network. This will + * make a forced synchornisation of the properties and mark them all not dirty. + **/ + void flush(); + + /** + * Reference to the internal dictionary + **/ + QIntDict &dict() const; + + /** + * In several situations you just want to have a QString of a + * KGameProperty object. This is the case in the + * KGameDebugDialog where the value of all properties is displayed. This + * function will provide you with such a QString for all the types + * used inside of all KGame classes. If you have a non-standard + * property (probably a self defined class or something like this) you + * also need to connect to signalRequestValue to make this function + * useful. + * @param property Return the value of this KGameProperty + * @return The value of a KGameProperty + **/ + QString propertyValue(KGamePropertyBase* property); + + + /** + * Writes some debug output to the console. + **/ + void Debug(); + + +signals: + /** + * This is emitted by a property. KGamePropertyBase::emitSignal + * calls emitSignal which emits this signal. + * + * This signal is emitted whenever the property is changed. Note that + * you can switch off this behaviour using + * KGamePropertyBase::setEmittingSignal in favor of performance. Note + * that you won't experience any performance loss using signals unless + * you use dozens or hundreds of properties which change very often. + **/ + void signalPropertyChanged(KGamePropertyBase *); + + /** + * This signal is emitted when a property needs to be sent. Only the + * parent has to react to this. + * @param msgid The id of the handler + * @param sent set this to true if the property was sent successfully - + * otherwise don't touch + **/ + void signalSendMessage(int msgid, QDataStream &, bool* sent); // AB shall we change bool* into bool& again? + + /** + * If you call propertyValue with a non-standard KGameProperty + * it is possible that the value cannot automatically be converted into a + * QString. Then this signal is emitted and asks you to provide the + * correct value. You probably want to use something like this to achieve + * this: + * \code + * #include + * void slotRequestValue(KGamePropertyBase* p, QString& value) + * { + * if (*(p->typeinfo()) == typeid(MyType) { + * value = QString(((KGameProperty*)p)->value()); + * } + * } + * \endcode + * + * @param property The KGamePropertyBase the value is requested for + * @param value The value of this property. You have to set this. + **/ + void signalRequestValue(KGamePropertyBase* property, QString& value); + +private: + void init(); + +private: + KGamePropertyHandlerPrivate* d; +}; + +#endif diff --git a/libkdegames/kgame/kgamepropertylist.h b/libkdegames/kgame/kgamepropertylist.h new file mode 100644 index 00000000..3a304e70 --- /dev/null +++ b/libkdegames/kgame/kgamepropertylist.h @@ -0,0 +1,258 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KGAMEPROPERTYLIST_H_ +#define __KGAMEPROPERTYLIST_H_ + +#include + +#include + +#include "kgamemessage.h" +#include "kgameproperty.h" +#include "kgamepropertyhandler.h" + +// AB: also see README.LIB! + +template +class KGamePropertyList : public QValueList, public KGamePropertyBase +{ +public: + /** + * Typedefs + */ + typedef QValueListIterator Iterator; + typedef QValueListConstIterator ConstIterator; + + KGamePropertyList() :QValueList(), KGamePropertyBase() + { + } + + KGamePropertyList( const KGamePropertyList &a ) : QValueList(a) + { + } + + uint findIterator(Iterator me) + { + Iterator it; + uint cnt=0; + for( it = this->begin(); it != this->end(); ++it ) + { + if (me==it) + { + return cnt; + } + cnt++; + } + return this->count(); + } + + Iterator insert( Iterator it, const type& d ) + { + it=QValueList::insert(it,d); + + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdInsert); + int i=findIterator(it); + s << i; + s << d; + if (policy() == PolicyClean || policy() == PolicyDirty) + { + if (mOwner) + { + mOwner->sendProperty(s); + } + } + if (policy() == PolicyDirty || policy() == PolicyLocal) + { + extractProperty(b); + } + return it; + } + + void prepend( const type& d) { insert(this->begin(),d); } + + void append( const type& d ) + { + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdAppend); + s << d; + if (policy() == PolicyClean || policy() == PolicyDirty) + { + if (mOwner) + { + mOwner->sendProperty(s); + } + } + if (policy() == PolicyDirty || policy() == PolicyLocal) + { + extractProperty(b); + } + } + + Iterator erase( Iterator it ) + { + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdRemove); + int i=findIterator(it); + s << i; + if (policy() == PolicyClean || policy() == PolicyDirty) + { + if (mOwner) + { + mOwner->sendProperty(s); + } + } + if (policy() == PolicyDirty || policy() == PolicyLocal) + { + extractProperty(b); + } + //TODO: return value - is it correct for PolicyLocal|PolicyDirty? +// return QValueList::remove(it); + return it; + } + + Iterator remove( Iterator it ) + { + return erase(it); + } + + void remove( const type& d ) + { + Iterator it=find(d); + remove(it); + } + + void clear() + { + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdClear); + if (policy() == PolicyClean || policy() == PolicyDirty) + { + if (mOwner) + { + mOwner->sendProperty(s); + } + } + if (policy() == PolicyDirty || policy() == PolicyLocal) + { + extractProperty(b); + } + } + + void load(QDataStream& s) + { + kdDebug(11001) << "KGamePropertyList load " << id() << endl; + QValueList::clear(); + uint size; + type data; + s >> size; + + for (unsigned int i=0;i> data; + QValueList::append(data); + } + if (isEmittingSignal()) emitSignal(); + } + + void save(QDataStream &s) + { + kdDebug(11001) << "KGamePropertyList save "<count(); + s << size; + Iterator it; + for( it = this->begin(); it != this->end(); ++it ) + { + data=*it; + s << data; + } + } + + void command(QDataStream &s,int cmd,bool) + { + KGamePropertyBase::command(s, cmd); + kdDebug(11001) << "---> LIST id="<> i >> data; + it=this->at(i); + QValueList::insert(it,data); +// kdDebug(11001) << "CmdInsert:id="<id(); + } + else + { + lastId = 0; + } + + kdDebug(11001) << "nextPlayer: lastId="<playerList()->first(); player != 0; player=game()->playerList()->next() ) + { + // Find the first player for a cycle + if (player->id() < minId) + { + minId=player->id(); + minplayer=player; + } + if (player==last) + { + continue; + } + // Find the next player which is bigger than the current one + if (player->id() > lastId && player->id() < nextId) + { + nextId=player->id(); + nextplayer=player; + } + } + + // Cycle to the beginning + if (!nextplayer) + { + nextplayer=minplayer; + } + + kdDebug(11001) << k_funcinfo << " ##### lastId=" << lastId << " exclusive=" + << exclusive << " minId=" << minId << " nextid=" << nextId + << " count=" << game()->playerList()->count() << endl; + if (nextplayer) + { + nextplayer->setTurn(true,exclusive); + } + else + { + return 0; + } + return nextplayer; +} + +// Per default we do not do anything +int KGameSequence::checkGameOver(KPlayer*) +{ + return 0; +} +/* + * vim: et sw=2 + */ diff --git a/libkdegames/kgame/kgamesequence.h b/libkdegames/kgame/kgamesequence.h new file mode 100644 index 00000000..4d26ceee --- /dev/null +++ b/libkdegames/kgame/kgamesequence.h @@ -0,0 +1,87 @@ +/* + This file is part of the KDE games library + Copyright (C) 2003 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2003 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ +#ifndef __KGAMESEQUENCE_H_ +#define __KGAMESEQUENCE_H_ + +#include + +class KPlayer; +class KGame; + +/** + * This class takes care of round or move management as well of the gameover + * condition. It is especially used for round based games. For these games @ref + * nextPlayer and @ref checkGameOver are the most important methods. + * + * You can subclass KGameSequence and use @ref KGame::setGameSequence to use + * your own rules. Note that @ref KGame will take ownership and therefore will + * delete the object on destruction. + * @short Round/move management class + * @author Andreas Beckermann + **/ +class KGameSequence : public QObject +{ + Q_OBJECT +public: + KGameSequence(); + virtual ~KGameSequence(); + + /** + * Select the next player in a turn based game. In an asynchronous game this + * function has no meaning. Overwrite this function for your own game sequence. + * Per default it selects the next player in the playerList + */ + virtual KPlayer* nextPlayer(KPlayer *last, bool exclusive = true); + + virtual void setCurrentPlayer(KPlayer* p); + + /** + * @return The @ref KGame object this sequence is for, or NULL if none. + **/ + KGame* game() const { return mGame; } + + KPlayer* currentPlayer() const { return mCurrentPlayer; } + + /** + * Set the @ref KGame object for this sequence. This is called + * automatically by @ref KGame::setGameSequence and you should not call + * it. + **/ + void setGame(KGame* game); + + /** + * Check whether the game is over. The default implementation always + * returns 0. + * + * @param player the player who made the last move + * @return anything else but 0 is considered as game over + **/ + virtual int checkGameOver(KPlayer *player); + +private: + KGame* mGame; + KPlayer* mCurrentPlayer; +}; + +#endif + diff --git a/libkdegames/kgame/kgameversion.h b/libkdegames/kgame/kgameversion.h new file mode 100644 index 00000000..c3147525 --- /dev/null +++ b/libkdegames/kgame/kgameversion.h @@ -0,0 +1,54 @@ +/* + This file is part of the KDE games library + Copyright (C) 2003 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2003 Martin Heni (martin@heni-online.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ +#ifndef __KGAMEVERSION_H__ +#define __KGAMEVERSION_H__ + +/** + * In this file you find a couple of defines that indicate whether a specific + * feature or function is present in this version of the KGame library. + * + * You don't need this for KDE CVS, but for games that live outside of KDE CVS + * it may be very helpful and a lot easier than writing configure scripts for + * this task. + * + * All defines are prefixed with KGAME_ to avoid conflicts. + **/ + +// KGame::savegame() didn't exist in KDE 3.0 +#define KGAME_HAVE_KGAME_SAVEGAME 1 + +// KGameNetwork::port(), KMessageIO::peerPort() and friends were added in KDE 3.2 +#define KGAME_HAVE_KGAME_PORT 1 + +// KGameNetwork::hostName(), KMessageIO::peerName() and friends were added in KDE 3.2 +#define KGAME_HAVE_KGAME_HOSTNAME 1 + +// KGameSequence class was added in KDE 3.2 +#define KGAME_HAVE_KGAMESEQUENCE 1 + +// KGame::addPlayer() needs to assign an ID to new players, otherwise network is +// broken. this is done in KDE 3.2. +#define KGAME_HAVE_FIXED_ADDPLAYER_ID 1 + +#endif + diff --git a/libkdegames/kgame/kmessageclient.cpp b/libkdegames/kgame/kmessageclient.cpp new file mode 100644 index 00000000..ed9cc966 --- /dev/null +++ b/libkdegames/kgame/kmessageclient.cpp @@ -0,0 +1,373 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include +#include + +#include +#include + +#include "kmessageio.h" +#include "kmessageserver.h" + +#include "kmessageclient.h" + +class KMessageClientPrivate +{ +public: + KMessageClientPrivate () + : adminID (0), connection (0) + {} + + ~KMessageClientPrivate () + { + delete connection; + } + + Q_UINT32 adminID; + QValueList clientList; + KMessageIO *connection; + + bool isLocked; + QValueList delayedMessages; +}; + +KMessageClient::KMessageClient (QObject *parent, const char *name) + : QObject (parent, name) +{ + d = new KMessageClientPrivate (); + d->isLocked = false; +} + +KMessageClient::~KMessageClient () +{ + d->delayedMessages.clear(); + delete d; +} + +// -- setServer stuff + +void KMessageClient::setServer (const QString &host, Q_UINT16 port) +{ + setServer (new KMessageSocket (host, port)); +} + +void KMessageClient::setServer (KMessageServer *server) +{ + KMessageDirect *serverIO = new KMessageDirect (); + setServer (new KMessageDirect (serverIO)); + server->addClient (serverIO); +} + +void KMessageClient::setServer (KMessageIO *connection) +{ + if (d->connection) + { + delete d->connection; + kdDebug (11001) << k_funcinfo << ": We are changing the server!" << endl; + } + + d->connection = connection; + if (connection ) + { + connect (connection, SIGNAL (received(const QByteArray &)), + this, SLOT (processIncomingMessage(const QByteArray &))); + connect (connection, SIGNAL (connectionBroken()), + this, SLOT (removeBrokenConnection ())); + } +} + +// -- id stuff + +Q_UINT32 KMessageClient::id () const +{ + return (d->connection) ? d->connection->id () : 0; +} + +bool KMessageClient::isAdmin () const +{ + return id() != 0 && id() == adminId(); +} + +Q_UINT32 KMessageClient::adminId () const +{ + return d->adminID; +} + +const QValueList &KMessageClient::clientList() const +{ + return d->clientList; +} + +bool KMessageClient::isConnected () const +{ + return d->connection && d->connection->isConnected(); +} + +bool KMessageClient::isNetwork () const +{ + return isConnected() ? d->connection->isNetwork() : false; +} + +Q_UINT16 KMessageClient::peerPort () const +{ + return d->connection ? d->connection->peerPort() : 0; +} + +QString KMessageClient::peerName () const +{ + return d->connection ? d->connection->peerName() : QString::fromLatin1("localhost"); +} + +// --------------------- Sending messages + +void KMessageClient::sendServerMessage (const QByteArray &msg) +{ + if (!d->connection) + { + kdWarning (11001) << k_funcinfo << ": We have no connection yet!" << endl; + return; + } + d->connection->send (msg); +} + +void KMessageClient::sendBroadcast (const QByteArray &msg) +{ + QByteArray sendBuffer; + QBuffer buffer (sendBuffer); + buffer.open (IO_WriteOnly); + QDataStream stream (&buffer); + + stream << static_cast ( KMessageServer::REQ_BROADCAST ); + buffer.QIODevice::writeBlock (msg); + sendServerMessage (sendBuffer); +} + +void KMessageClient::sendForward (const QByteArray &msg, const QValueList &clients) +{ + QByteArray sendBuffer; + QBuffer buffer (sendBuffer); + buffer.open (IO_WriteOnly); + QDataStream stream (&buffer); + + stream << static_cast( KMessageServer::REQ_FORWARD ) << clients; + buffer.QIODevice::writeBlock (msg); + sendServerMessage (sendBuffer); +} + +void KMessageClient::sendForward (const QByteArray &msg, Q_UINT32 client) +{ + sendForward (msg, QValueList () << client); +} + + +// --------------------- Receiving and processing messages + +void KMessageClient::processIncomingMessage (const QByteArray &msg) +{ + if (d->isLocked) + { + d->delayedMessages.append(msg); + return; + } + if (d->delayedMessages.count() > 0) + { + d->delayedMessages.append (msg); + QByteArray first = d->delayedMessages.front(); + d->delayedMessages.pop_front(); + processMessage (first); + } + else + { + processMessage(msg); + } +} + +void KMessageClient::processMessage (const QByteArray &msg) +{ + if (d->isLocked) + { // must NOT happen, since we check in processIncomingMessage as well as in processFirstMessage + d->delayedMessages.append(msg); + return; + } + QBuffer in_buffer (msg); + in_buffer.open (IO_ReadOnly); + QDataStream in_stream (&in_buffer); + + + bool unknown = false; + + Q_UINT32 messageID; + in_stream >> messageID; + switch (messageID) + { + case KMessageServer::MSG_BROADCAST: + { + Q_UINT32 clientID; + in_stream >> clientID; + emit broadcastReceived (in_buffer.readAll(), clientID); + } + break; + + case KMessageServer::MSG_FORWARD: + { + Q_UINT32 clientID; + QValueList receivers; + in_stream >> clientID >> receivers; + emit forwardReceived (in_buffer.readAll(), clientID, receivers); + } + break; + + case KMessageServer::ANS_CLIENT_ID: + { + bool old_admin = isAdmin(); + Q_UINT32 clientID; + in_stream >> clientID; + d->connection->setId (clientID); + if (old_admin != isAdmin()) + emit adminStatusChanged (isAdmin()); + } + break; + + case KMessageServer::ANS_ADMIN_ID: + { + bool old_admin = isAdmin(); + in_stream >> d->adminID; + if (old_admin != isAdmin()) + emit adminStatusChanged (isAdmin()); + } + break; + + case KMessageServer::ANS_CLIENT_LIST: + { + in_stream >> d->clientList; + } + break; + + case KMessageServer::EVNT_CLIENT_CONNECTED: + { + Q_UINT32 id; + in_stream >> id; + + if (d->clientList.contains (id)) + kdWarning (11001) << k_funcinfo << ": Adding a client that already existed!" << endl; + else + d->clientList.append (id); + + emit eventClientConnected (id); + } + break; + + case KMessageServer::EVNT_CLIENT_DISCONNECTED: + { + Q_UINT32 id; + Q_INT8 broken; + in_stream >> id >> broken; + + if (!d->clientList.contains (id)) + kdWarning (11001) << k_funcinfo << ": Removing a client that doesn't exist!" << endl; + else + d->clientList.remove (id); + + emit eventClientDisconnected (id, bool (broken)); + } + break; + + default: + unknown = true; + } + + if (!unknown && !in_buffer.atEnd()) + kdWarning (11001) << k_funcinfo << ": Extra data received for message ID " << messageID << endl; + + emit serverMessageReceived (msg, unknown); + + if (unknown) + kdWarning (11001) << k_funcinfo << ": received unknown message ID " << messageID << endl; +} + +void KMessageClient::processFirstMessage() +{ + if (d->isLocked) + { + return; + } + if (d->delayedMessages.count() == 0) + { + kdDebug(11001) << k_funcinfo << ": no messages delayed" << endl; + return; + } + QByteArray first = d->delayedMessages.front(); + d->delayedMessages.pop_front(); + processMessage (first); +} + +void KMessageClient::removeBrokenConnection () +{ + kdDebug (11001) << k_funcinfo << ": timer single shot for removeBrokenConnection"<connection; + d->connection = 0; + d->adminID = 0; + emit connectionBroken(); + kdDebug (11001) << k_funcinfo << ": Broken:Deleting the connection object DONE" << endl; +} + +void KMessageClient::disconnect () +{ + kdDebug (11001) << k_funcinfo << ": Disconnect:Deleting the connection object" << endl; + + emit aboutToDisconnect(id()); + delete d->connection; + d->connection = 0; + d->adminID = 0; + emit connectionBroken(); + kdDebug (11001) << k_funcinfo << ": Disconnect:Deleting the connection object DONE" << endl; +} + +void KMessageClient::lock () +{ + d->isLocked = true; +} + +void KMessageClient::unlock () +{ + d->isLocked = false; + for (unsigned int i = 0; i < d->delayedMessages.count(); i++) + { + QTimer::singleShot(0, this, SLOT(processFirstMessage())); + } +} + +unsigned int KMessageClient::delayedMessageCount() const +{ + return d->delayedMessages.count(); +} + +#include "kmessageclient.moc" diff --git a/libkdegames/kgame/kmessageclient.h b/libkdegames/kgame/kmessageclient.h new file mode 100644 index 00000000..90364c8d --- /dev/null +++ b/libkdegames/kgame/kmessageclient.h @@ -0,0 +1,422 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KMESSAGECLIENT_H__ +#define __KMESSAGECLIENT_H__ + +#include +#include +#include + +class KMessageIO; +class KMessageServer; +class KMessageClientPrivate; + +/** + @short A client to connect to a KMessageServer + + This class implements a client that can connect to a KMessageServer object. + It can be used to exchange messages between clients. + + Usually you will connect the signals broadcastReceived and forwardReceived to + some specific slots. In these slot methods you can analyse the messages that are + sent to you from other clients. + + To send messages to other clients, use the methods sendBroadcast() (to send to all + clients) or sendForward() (to send to a list of selected clients). + + If you want to communicate with the KMessageServer object directly (on a more low + level base), use the method sendServerMessage to send a command to the server and + connect to the signal serverMessageReceived to see all the incoming messages. + In that case the messages must be of the format specified in KMessageServer. + @author Burkhard Lehner +*/ +class KMessageClient : public QObject +{ + Q_OBJECT + +public: + + /** + Constructor. + Creates an unconnected KMessageClient object. Use setServer() later to connect to a + KMessageServer object. + */ + KMessageClient (QObject *parent = 0, const char *name = 0); + + /** + Destructor. + Disconnects from the server, if any connection was established. + */ + ~KMessageClient (); + + /** + @return The client ID of this client. Every client that is connected to a KMessageServer + has a unique ID number. + + NOTE: As long as the object is not yet connected to the server, and as long as the server + hasn't sent the client ID, this method returns 0. + */ + Q_UINT32 id () const; + + /** + @return Whether or not this client is the server admin. + One of the clients connected to the server is the admin and can administrate the server + (set maximum number of clients, remove clients, ...). + + If you use admin commands without being the admin, these commands are simply ignored by + the server. + + NOTE: As long as you are not connected to a server, this method returns false. + */ + bool isAdmin () const; + + /** + @return The ID of the admin client on the message server. + */ + Q_UINT32 adminId() const; + + /** + @return The list of the IDs of all the message clients connected to the message server. + */ + const QValueList &clientList() const; + + /** + Connects the client to (another) server. + + Tries to connect via a TCP/IP socket to a KMessageServer object + on the given host, listening on the specified port. + + If we were already connected, the old connection is closed. + @param host The name of the host to connect to. Must be either a hostname which can + be resolved to an IP or just an IP + @param port The port to connect to + */ + void setServer (const QString &host, Q_UINT16 port); + + /** + Connects the client to (another) server. + + Connects to the given server, using KMessageDirect. + (The server object has to be in the same process.) + + If we were already connected, the old connection is closed. + @param server The KMessageServer to connect to + */ + void setServer (KMessageServer *server); + + /** + * Corresponds to setServer(0); but also emits the connectionBroken signal + **/ + void disconnect(); + + /** + Connects the client to (another) server. + + To use this method, you have to create a KMessageIO object with new (indeed you must + create an instance of a subclass of KMessageIO, e.g. KMessageSocket or KMessageDirect). + This object must already be connected to the new server. + + Calling this method disconnects any earlier connection, and uses the new KMessageIO + object instead. This object gets owned by the KMessageClient object, so don't delete + or manipulate it afterwards. + + With this method it is possible to change the server on the fly. But be careful that + there are no important messages from the old server not yet delivered. + + NOTE: It is very likely that we will have another client ID on the new server. The + value returned by clientID may be a little outdated until the new server tells us + our new ID. + + NOTE: The two other setServer methods are for convenience. If you use them, you don't + have to create a KMessageIO object yourself. + */ + virtual void setServer (KMessageIO *connection); + + /** + @return True, if a connection to a KMessageServer has been started, and if the + connection is ready for transferring data. (It will return false e.g. as long as + a socket connection hasn't been established, and it will also return false after + a socket connection is broken.) + */ + bool isConnected () const; + + /** + @return TRUE if isConnected() is true AND this is not a local (like + KMessageDirect) connection. + */ + bool isNetwork () const; + + /** + @return 0 if isConnected() is FALSE, otherwise the port number this client is + connected to. See also KMessageIO::peerPort and QSocket::peerPort. + @since 3.2 + */ + Q_UINT16 peerPort () const; + + /** + @since 3.2 + @return "localhost" if isConnected() is FALSE, otherwise the hostname this client is + connected to. See also KMessageIO::peerName() and QSocket::peerName(). + */ + QString peerName() const; + + /** + Sends a message to the KMessageServer. If we are not yet connected to one, nothing + happens. + + Use this method to send a low level command to the server. It has to be in the + format specified in KMessageServer. + + If you want to send messages to other clients, you should use sendBroadcast() + and sendForward(). + @param msg The message to be sent to the server. Must be in the format specified in KMessageServer. + */ + void sendServerMessage (const QByteArray &msg); + + /** + Sends a message to all the clients connected to the server, including ourself. + The message consists of an arbitrary block of data with arbitrary length. + + All the clients will receive an exact copy of this block of data, which will be + processed in their processBroadcast() method. + @param msg The message to be sent to the clients + */ + //AB: processBroadcast doesn't exist!! is processIncomingMessage meant? + void sendBroadcast (const QByteArray &msg); + + /** + Sends a message to all the clients in a list. + The message consists of an arbitrary block of data with arbitrary length. + + All clients will receive an exact copy of this block of data, which will be + processed in their processForward() method. + + If the list contains client IDs that are not defined, they are ignored. If + it contains an ID several times, that client will receive the message several + times. + + To send a message to the admin of the KMessageServer, you can use 0 as clientID, + instead of using the real client ID. + @param msg The message to be sent to the clients + @param clients A list of clients the message should be sent to + */ + //AB: processForward doesn't exist!! is processIncomingMessage meant? + void sendForward (const QByteArray &msg, const QValueList &clients); + + /** + Sends a message to a single client. This is a convenieance method. It calls + sendForward (const QByteArray &msg, const QValueList <Q_UINT32> &clients) + with a list containing only one client ID. + + To send a message to the admin of the KMessageServer, you can use 0 as clientID, + instead of using the real client ID. + @param msg The message to be sent to the client + @param client The id of the client the message shall be sent to + */ + void sendForward (const QByteArray &msg, Q_UINT32 client); + + /** + Once this function is called no message will be received anymore. + processIncomingMessage() gets delayed until unlock() is called. + + Note that all messages are still received, but their delivery (like + broadcastReceived()) get delayed only. + */ + void lock(); + + /** + Deliver every message that was delayed by lock() and actually deliver + all messages that get received from now on. + */ + void unlock(); + + /** + @return The number of messages that got delayed since lock() was called + */ + unsigned int delayedMessageCount() const; + +signals: + /** + This signal is emitted when the client receives a broadcast message from the + KMessageServer, sent by another client. Connect to this signal to analyse the + received message and do the right reaction. + + senderID contains the ID of the client that sent the broadcast message. You can + use this e.g. to send a reply message to only that client. Or you can use it + to ignore broadcast messages that were sent by yourself: + + \code + void myObject::myBroadcastSlot (const QByteArray &msg, Q_UINT32 senderID) + { + if (senderID == ((KMessageClient *)sender())->id()) + return; + ... + } + \endcode + @param msg The message that has been sent to us + @param senderID The ID of the client which sent the message + */ + void broadcastReceived (const QByteArray &msg, Q_UINT32 senderID); + + /** + This signal is emitted when the client receives a forward message from the + KMessageServer, sent by another client. Connect to this signal to analyse the + received message and do the right reaction. + + senderID contains the ID of the client that sent the broadcast message. You can + use this e.g. to send a reply message to only that client. + + receivers contains the list of the clients that got the message. (If this list + only contains one number, this will be your client ID, and it was exclusivly + sent to you.) + + If you don't want to distinguish between broadcast and forward messages and + treat them the same, you can connect forwardReceived signal to the + broadcastReceived signal. (Yes, that's possible! You can connect a Qt signal to + a Qt signal, and the second one can have less parameters.) + + \code + KMessageClient *client = new KMessageClient (); + connect (client, SIGNAL (forwardReceived (const QByteArray &, Q_UINT32, const QValueList &)), + client, SIGNAL (broadcastReceived (const QByteArray &, Q_UINT32))); + \endcode + + Then connect the broadcast signal to your slot that analyzes the message. + @param msg The message that has been sent to us + @param senderID The ID of the client which sent the message + @param receivers All clients which receive this message + */ + void forwardReceived (const QByteArray &msg, Q_UINT32 senderID, const QValueList &receivers); + + /** + This signal is emitted when the connection to the KMessageServer is broken. + Reasons for this can be: a network error, a server breakdown, or you were just kicked + from the server. + + When this signal is sent, the connection is already lost and the client is unconnected. + You can connect to another server by calling setServer() afterwards. But keep in mind that + some important messages might have vanished. + */ + void connectionBroken (); + + /** + This signal is emitted right before the client disconnects. It can be used + to this store the id of the client which is about to be lost. + */ + void aboutToDisconnect(Q_UINT32 id); + + /** + This signal is emitted when this client becomes the admin client or when it loses + the admin client status. Connect to this signal if you have to do any initialization + or cleanup. + @param isAdmin Whether we are now admin or not + */ + void adminStatusChanged (bool isAdmin); + + /** + This signal is emitted when another client has connected + to the server. Connect to this method if that clients needs initialization. + This should usually only be done in one client, e.g. the admin client. + @param clientID The ID of the client that has newly connectd. + */ + void eventClientConnected (Q_UINT32 clientID); + + /** + This signal is emitted when the server has lost the + connection to one of the clients (This could be because of a bad internet connection + or because the client disconnected on purpose). + @param clientID The ID of the client that has disconnected + @param broken true if it was disconnected because of a network error + */ + void eventClientDisconnected (Q_UINT32 clientID, bool broken); + + /** + This signal is emitted on every message that came from the server. You can connect to this + signal to see the messages directly. They are in the format specified in KMessageServer. + + @param msg The message that has been sent to us + @param unknown True when KMessageClient didn't recognize the message, i.e. it contained an unknown + message ID. If you want to add additional message types to the client, connect to this signal, + and if unknown is true, analyse the message by yourself. If you recognized the message, + set unknown to false (Otherwise a debug message will be printed). + */ + //AB: maybe add a setNoEmit() so that the other signals can be deactivated? + //Could be a performance benefit (note: KMessageClient is a time critical + //class!!!) + void serverMessageReceived (const QByteArray &msg, bool &unknown); + +protected: + /** + This slot is called from processIncomingMessage or + processFirstMessage, depending on whether the client is locked or a delayed + message is still here (see lock) + + It processes the message and analyses it. If it is a broadcast or a forward message from + another client, it emits the signal processBroadcast or processForward accordingly. + + If you want to treat additional server messages, you can overwrite this method. Don't + forget to call processIncomingMessage of your superclass! + + At the moment, the following server messages are interpreted: + + MSG_BROADCAST, MSG_FORWARD, ANS_CLIENT_ID, ANS_ADMIN_ID, ANS_CLIENT_LIST + @param msg The incoming message + */ + + virtual void processMessage (const QByteArray& msg); + +protected slots: + /** + This slot is called from the signal KMessageIO::received whenever a message from the + KMessageServer arrives. + + It processes the message and analyses it. If it is a broadcast or a forward message from + another client, it emits the signal processBroadcast or processForward accordingly. + + If you want to treat additional server messages, you can overwrite this method. Don't + forget to call processIncomingMessage() of your superclass! + + At the moment, the following server messages are interpreted: + + MSG_BROADCAST, MSG_FORWARD, ANS_CLIENT_ID, ANS_ADMIN_ID, ANS_CLIENT_LIST + @param msg The incoming message + */ + virtual void processIncomingMessage (const QByteArray &msg); + + /** + Called from unlock() (using QTimer::singleShot) until all delayed + messages are delivered. + */ + void processFirstMessage(); + + /** + This slot is called from the signal KMessageIO::connectionBroken. + + It deletes the internal KMessageIO object, and resets the client to default + values. To connect again to another server, use setServer. + */ + virtual void removeBrokenConnection (); + void removeBrokenConnection2 (); + +private: + KMessageClientPrivate *d; +}; + +#endif diff --git a/libkdegames/kgame/kmessageio.cpp b/libkdegames/kgame/kmessageio.cpp new file mode 100644 index 00000000..f3353277 --- /dev/null +++ b/libkdegames/kgame/kmessageio.cpp @@ -0,0 +1,482 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/* + KMessageIO class and subclasses KMessageSocket and KMessageDirect +*/ + +#include "kmessageio.h" +#include +#include +#include +#include + +// ----------------------- KMessageIO ------------------------- + +KMessageIO::KMessageIO (QObject *parent, const char *name) + : QObject (parent, name), m_id (0) +{} + +KMessageIO::~KMessageIO () +{} + +void KMessageIO::setId (Q_UINT32 id) +{ + m_id = id; +} + +Q_UINT32 KMessageIO::id () +{ + return m_id; +} + +// ----------------------KMessageSocket ----------------------- + +KMessageSocket::KMessageSocket (QString host, Q_UINT16 port, QObject *parent, +const char *name) + : KMessageIO (parent, name) +{ + mSocket = new QSocket (); + mSocket->connectToHost (host, port); + initSocket (); +} + +KMessageSocket::KMessageSocket (QHostAddress host, Q_UINT16 port, QObject +*parent, const char *name) + : KMessageIO (parent, name) +{ + mSocket = new QSocket (); + mSocket->connectToHost (host.toString(), port); + initSocket (); +} + +KMessageSocket::KMessageSocket (QSocket *socket, QObject *parent, const char +*name) + : KMessageIO (parent, name) +{ + mSocket = socket; + initSocket (); +} + +KMessageSocket::KMessageSocket (int socketFD, QObject *parent, const char +*name) + : KMessageIO (parent, name) +{ + mSocket = new QSocket (); + mSocket->setSocket (socketFD); + initSocket (); +} + +KMessageSocket::~KMessageSocket () +{ + delete mSocket; +} + +bool KMessageSocket::isConnected () const +{ + return mSocket->state() == QSocket::Connection; +} + +void KMessageSocket::send (const QByteArray &msg) +{ + QDataStream str (mSocket); + str << Q_UINT8 ('M'); // magic number for begin of message + str.writeBytes (msg.data(), msg.size()); // writes the length (as Q_UINT32) and the data +} + +void KMessageSocket::processNewData () +{ + if (isRecursive) + return; + isRecursive = true; + + QDataStream str (mSocket); + while (mSocket->bytesAvailable() > 0) + { + if (mAwaitingHeader) + { + // Header = magic number + packet length = 5 bytes + if (mSocket->bytesAvailable() < 5) + { + isRecursive = false; + return; + } + + // Read the magic number first. If something unexpected is found, + // start over again, ignoring the data that was read up to then. + + Q_UINT8 v; + str >> v; + if (v != 'M') + { + kdWarning(11001) << k_funcinfo << ": Received unexpected data, magic number wrong!" << endl; + continue; + } + + str >> mNextBlockLength; + mAwaitingHeader = false; + } + else + { + // Data not completely read => wait for more + if (mSocket->bytesAvailable() < (Q_ULONG) mNextBlockLength) + { + isRecursive = false; + return; + } + + QByteArray msg (mNextBlockLength); + str.readRawBytes (msg.data(), mNextBlockLength); + + // send the received message + emit received (msg); + + // Waiting for the header of the next message + mAwaitingHeader = true; + } + } + + isRecursive = false; +} + +void KMessageSocket::initSocket () +{ + connect (mSocket, SIGNAL (error(int)), SIGNAL (connectionBroken())); + connect (mSocket, SIGNAL (connectionClosed()), SIGNAL (connectionBroken())); + connect (mSocket, SIGNAL (readyRead()), SLOT (processNewData())); + mAwaitingHeader = true; + mNextBlockLength = 0; + isRecursive = false; +} + +Q_UINT16 KMessageSocket::peerPort () const +{ + return mSocket->peerPort(); +} + +QString KMessageSocket::peerName () const +{ + return mSocket->peerName(); +} + +// ----------------------KMessageDirect ----------------------- + +KMessageDirect::KMessageDirect (KMessageDirect *partner, QObject *parent, +const char *name) + : KMessageIO (parent, name), mPartner (0) +{ + // 0 as first parameter leaves the object unconnected + if (!partner) + return; + + // Check if the other object is already connected + if (partner && partner->mPartner) + { + kdWarning(11001) << k_funcinfo << ": Object is already connected!" << endl; + return; + } + + // Connect from us to that object + mPartner = partner; + + // Connect the other object to us + partner->mPartner = this; +} + +KMessageDirect::~KMessageDirect () +{ + if (mPartner) + { + mPartner->mPartner = 0; + emit mPartner->connectionBroken(); + } +} + +bool KMessageDirect::isConnected () const +{ + return mPartner != 0; +} + +void KMessageDirect::send (const QByteArray &msg) +{ + if (mPartner) + emit mPartner->received (msg); + else + kdError(11001) << k_funcinfo << ": Not yet connected!" << endl; +} + + +// ----------------------- KMessageProcess --------------------------- + +KMessageProcess::~KMessageProcess() +{ + kdDebug(11001) << "@@@KMessageProcess::Delete process" << endl; + if (mProcess) + { + mProcess->kill(); + delete mProcess; + mProcess=0; + // Remove not send buffers + mQueue.setAutoDelete(true); + mQueue.clear(); + // Maybe todo: delete mSendBuffer + } +} +KMessageProcess::KMessageProcess(QObject *parent, QString file) : KMessageIO(parent,0) +{ + // Start process + kdDebug(11001) << "@@@KMessageProcess::Start process" << endl; + mProcessName=file; + mProcess=new KProcess; + int id=0; + *mProcess << mProcessName << QString("%1").arg(id); + kdDebug(11001) << "@@@KMessageProcess::Init:Id= " << id << endl; + kdDebug(11001) << "@@@KMessgeProcess::Init:Processname: " << mProcessName << endl; + connect(mProcess, SIGNAL(receivedStdout(KProcess *, char *, int )), + this, SLOT(slotReceivedStdout(KProcess *, char * , int ))); + connect(mProcess, SIGNAL(receivedStderr(KProcess *, char *, int )), + this, SLOT(slotReceivedStderr(KProcess *, char * , int ))); + connect(mProcess, SIGNAL(processExited(KProcess *)), + this, SLOT(slotProcessExited(KProcess *))); + connect(mProcess, SIGNAL(wroteStdin(KProcess *)), + this, SLOT(slotWroteStdin(KProcess *))); + mProcess->start(KProcess::NotifyOnExit,KProcess::All); + mSendBuffer=0; + mReceiveCount=0; + mReceiveBuffer.resize(1024); +} +bool KMessageProcess::isConnected() const +{ + kdDebug(11001) << "@@@KMessageProcess::Is conencted" << endl; + if (!mProcess) return false; + return mProcess->isRunning(); +} +void KMessageProcess::send(const QByteArray &msg) +{ + kdDebug(11001) << "@@@KMessageProcess:: SEND("<size() << " BYTE " << endl; + // char *p=mSendBuffer->data(); + // for (int i=0;i<16;i++) printf("%02x ",(unsigned char)(*(p+i)));printf("\n"); + mProcess->writeStdin(mSendBuffer->data(),mSendBuffer->size()); + +} +void KMessageProcess::slotWroteStdin(KProcess * ) +{ + kdDebug(11001) << k_funcinfo << endl; + if (mSendBuffer) + { + delete mSendBuffer; + mSendBuffer=0; + } + writeToProcess(); +} + +void KMessageProcess::slotReceivedStderr(KProcess * proc, char *buffer, int buflen) +{ + int pid=0; + int len; + char *p; + char *pos; +// kdDebug(11001)<<"############# Got stderr " << buflen << " bytes" << endl; + + if (!buffer || buflen==0) return ; + if (proc) pid=proc->pid(); + + + pos=buffer; + do + { + p=(char *)memchr(pos,'\n',buflen); + if (!p) len=buflen; + else len=p-pos; + + QByteArray a; + a.setRawData(pos,len); + QString s(a); + kdDebug(11001) << "PID" <0); +} + + +void KMessageProcess::slotReceivedStdout(KProcess * , char *buffer, int buflen) +{ + kdDebug(11001) << "$$$$$$ " << k_funcinfo << ": Received " << buflen << " bytes over inter process communication" << endl; + + // TODO Make a plausibility check on buflen to avoid memory overflow + while (mReceiveCount+buflen>=mReceiveBuffer.size()) mReceiveBuffer.resize(mReceiveBuffer.size()+1024); + memcpy(mReceiveBuffer.data()+mReceiveCount,buffer,buflen); + mReceiveCount+=buflen; + + // Possbile message + while (mReceiveCount>2*sizeof(long)) + { + long *p1=(long *)mReceiveBuffer.data(); + long *p2=p1+1; + unsigned int len; + if (*p1!=0x4242aeae) + { + kdDebug(11001) << k_funcinfo << ": Cookie error...transmission failure...serious problem..." << endl; +// for (int i=0;iwriteBlock(buffer); + mWriteFile->flush(); + /* + fprintf(stderr,"+++ KMessageFilePipe:: SEND(%d to parent) realsize=%d\n",msg.size(),buffer.size()); + for (int i=0;iatEnd()) { usleep(100); } + + int ch=mReadFile->getch(); + + while (mReceiveCount>=mReceiveBuffer.size()) mReceiveBuffer.resize(mReceiveBuffer.size()+1024); + mReceiveBuffer[mReceiveCount]=(char)ch; + mReceiveCount++; + + // Change for message + if (mReceiveCount>=2*sizeof(long)) + { + long *p1=(long *)mReceiveBuffer.data(); + long *p2=p1+1; + unsigned int len; + if (*p1!=0x4242aeae) + { + fprintf(stderr,"KMessageFilePipe::exec:: Cookie error...transmission failure...serious problem...\n"); +// for (int i=0;i<16;i++) fprintf(stderr,"%02x ",mReceiveBuffer[i]);fprintf(stderr,"\n"); + } + len=(int)(*p2); + if (len==mReceiveCount) + { + //fprintf(stderr,"KMessageFilePipe::exec:: Got Message with len %d\n",len); + + QByteArray msg; + //msg.setRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long)); + msg.duplicate(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long)); + emit received(msg); + //msg.resetRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long)); + mReceiveCount=0; + } + } + + + return ; + + +} + +#include "kmessageio.moc" diff --git a/libkdegames/kgame/kmessageio.h b/libkdegames/kgame/kmessageio.h new file mode 100644 index 00000000..37cf35cd --- /dev/null +++ b/libkdegames/kgame/kmessageio.h @@ -0,0 +1,416 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/* + KMessageIO class and subclasses KMessageSocket and KMessageDirect +*/ + +#ifndef _KMESSAGEIO_H_ +#define _KMESSAGEIO_H_ + +#include +#include +#include +#include +#include +#include +#include + +class QSocket; +class KProcess; +//class QFile; + + +/** + This abstract base class represents one end of a message connections + between two clients. Each client has one object of a subclass of + KMessageIO. Calling /e send() on one object will emit the signal + /e received() on the other object, and vice versa. + + For each type of connection (TCP/IP socket, COM port, direct connection + within the same class) a subclass of KMessageIO must be defined that + implements the pure virtual methods /e send() and /e isConnected(), + and sends the signals. (See /e KMessageSocket for an example implementation.) + + Two subclasses are already included: /e KMessageSocket (connection using + TCP/IP sockets) and /e KMessageDirect (connection using method calls, both + sides must be within the same process). +*/ + +class KMessageIO : public QObject +{ + Q_OBJECT + +public: + /** + * The usual QObject constructor, does nothing else. + **/ + KMessageIO (QObject *parent = 0, const char *name = 0); + + /** + * The usual destructor, does nothing special. + **/ + ~KMessageIO (); + + /** + * The runtime idendifcation + */ + virtual int rtti() const {return 0;} + + /** + * @return Whether this KMessageIO is a network IO or not. + **/ + //virtual bool isNetwork () const = 0; + virtual bool isNetwork () const + { + kdError(11001) << "Calling PURE virtual isNetwork...BAD" << endl; + return false; + } + + /** + This method returns the status of the object, whether it is already + (or still) connected to another KMessageIO object or not. + + This is a pure virtual method, so it has to be implemented in a subclass + of KMessageIO. + */ + //virtual bool isConnected () const = 0; + virtual bool isConnected () const + { + kdError(11001) << "Calling PURE virtual isConencted...BAD" << endl; + return false; + } + + /** + Sets the ID number of this object. This number can for example be used to + distinguish several objects in a server. + + NOTE: Sometimes it is useful to let two connected KMessageIO objects + have the same ID number. You have to do so yourself, KMessageIO doesn't + change this value on its own! + */ + void setId (Q_UINT32 id); + + /** + Queries the ID of this object. + */ + Q_UINT32 id (); + + /** + @since 3.2 + @return 0 in the default implementation. Reimplemented in @ref KMessageSocket. + */ + virtual Q_UINT16 peerPort () const { return 0; } + + /** + @since 3.2 + @return "localhost" in the default implementation. Reimplemented in @ref KMessageSocket + */ + virtual QString peerName () const { return QString::fromLatin1("localhost"); } + + +signals: + /** + This signal is emitted when /e send() on the connected KMessageIO + object is called. The parameter contains the same data array in /e msg + as was used in /e send(). + */ + void received (const QByteArray &msg); + + /** + This signal is emitted when the connection is closed. This can be caused + by a hardware error (e.g. broken internet connection) or because the other + object was killed. + + Note: Sometimes a broken connection can be undetected for a long time, + or may never be detected at all. So don't rely on this signal! + */ + void connectionBroken (); + +public slots: + + /** + This slot sends the data block in /e msg to the connected object, that will + emit /e received(). + + For a concrete class, you have to subclass /e KMessageIO and overwrite this + method. In the subclass, implement this method as an ordinary method, not + as a slot! (Otherwise another slot would be defined. It would work, but uses + more memory and time.) See /e KMessageSocket for an example implementation. + */ + virtual void send (const QByteArray &msg) = 0; + +protected: + Q_UINT32 m_id; +}; + + +/** + This class implements the message communication using a TCP/IP socket. The + object can connect to a server socket, or can use an already connected socket. +*/ + +class KMessageSocket : public KMessageIO +{ + Q_OBJECT + +public: + /** + Connects to a server socket on /e host with /e port. host can be an + numerical (e.g. "192.168.0.212") or symbolic (e.g. "wave.peter.org") + IP address. You can immediately use the /e sendSystem() and + /e sendBroadcast() methods. The messages are stored and sent to the + receiver after the connection is established. + + If the connection could not be established (e.g. unknown host or no server + socket at this port), the signal /e connectionBroken is emitted. + */ + KMessageSocket (QString host, Q_UINT16 port, QObject *parent = 0, + const char *name = 0); + + /** + Connects to a server socket on /e host with /e port. You can immediately + use the /e sendSystem() and /e sendBroadcast() methods. The messages are + stored and sent to the receiver after the connection is established. + + If the connection could not be established (e.g. unknown host or no server + socket at this port), the signal /e connectionBroken is emitted. + */ + KMessageSocket (QHostAddress host, Q_UINT16 port, QObject *parent = 0, + const char *name = 0); + + /** + Uses /e socket to do the communication. + + The socket should already be connected, or at least be in /e connecting + state. + + Note: The /e socket object is then owned by the /e KMessageSocket object. + So don't use it otherwise any more and don't delete it. It is deleted + together with this KMessageSocket object. (Use 0 as parent for the QSocket + object t ensure it is not deleted.) + */ + KMessageSocket (QSocket *socket, QObject *parent = 0, const char *name = 0); + + /** + Uses the socket specified by the socket descriptor socketFD to do the + communication. The socket must already be connected. + + This constructor can be used with a QServerSocket within the (pure + virtual) method /e newConnection. + + Note: The socket is then owned by the /e KMessageSocket object. So don't + manipulate the socket afterwards, especially don't close it. The socket is + automatically closed when KMessageSocket is deleted. + */ + KMessageSocket (int socketFD, QObject *parent = 0, const char *name = 0); + + /** + Destructor, closes the socket. + */ + ~KMessageSocket (); + + /** + * The runtime idendifcation + */ + virtual int rtti() const {return 1;} + + /** + @since 3.2 + @return The port that this object is connected to. See QSocket::peerPort + */ + virtual Q_UINT16 peerPort () const; + + /** + @since 3.2 + @return The hostname this object is connected to. See QSocket::peerName. + */ + virtual QString peerName () const; + + /** + @return TRUE as this is a network IO. + */ + bool isNetwork() const { return true; } + + /** + Returns true if the socket is in state /e connected. + */ + bool isConnected () const; + + /** + Overwritten slot method from KMessageIO. + + Note: It is not declared as a slot method, since the slot is already + defined in KMessageIO as a virtual method. + */ + void send (const QByteArray &msg); + +protected slots: + virtual void processNewData (); + +protected: + void initSocket (); + QSocket *mSocket; + bool mAwaitingHeader; + Q_UINT32 mNextBlockLength; + + bool isRecursive; // workaround for "bug" in QSocket, Qt 2.2.3 or older +}; + + +/** + This class implements the message communication using function calls + directly. It can only be used when both sides of the message pipe are + within the same process. The communication is very fast. + + To establish a communication, you have to create two instances of + KMessageDirect, the first one with no parameters in the constructor, + the second one with the first as parameter: + + /code + KMessageDirect *peer1, *peer2; + peer1 = new KMessageDirect (); // unconnected + peer2 = new KMessageDirect (peer1); // connect with peer1 + /endcode + + The connection is only closed when one of the instances is deleted. +*/ + +class KMessageDirect : public KMessageIO +{ + Q_OBJECT + +public: + /** + Creates an object and connects it to the object given in the first + parameter. Use 0 as first parameter to create an unconnected object, + that is later connected. + + If that object is already connected, the object remains unconnected. + */ + KMessageDirect (KMessageDirect *partner = 0, QObject *parent = 0, const char +*name = 0); + + /** + Destructor, closes the connection. + */ + ~KMessageDirect (); + + /** + * The runtime idendifcation + */ + virtual int rtti() const {return 2;} + + + /** + @return FALSE as this is no network IO. + */ + bool isNetwork() const { return false; } + + /** + Returns true, if the object is connected to another instance. + + If you use the first constructor, the object is unconnected unless another + object is created with this one as parameter. + + The connection can only be closed by deleting one of the objects. + */ + bool isConnected () const; + + /** + Overwritten slot method from KMessageIO. + + Note: It is not declared as a slot method, since the slot is already + defined in KMessageIO as a virtual method. + */ + void send (const QByteArray &msg); + +protected: + KMessageDirect *mPartner; +}; + +class KMessageProcess : public KMessageIO +{ + Q_OBJECT + + public: + KMessageProcess(QObject *parent, QString file); + ~KMessageProcess(); + bool isConnected() const; + void send (const QByteArray &msg); + void writeToProcess(); + + /** + @return FALSE as this is no network IO. + */ + bool isNetwork() const { return false; } + + /** + * The runtime idendifcation + */ + virtual int rtti() const {return 3;} + + + + public slots: + void slotReceivedStdout(KProcess *proc, char *buffer, int buflen); + void slotReceivedStderr(KProcess *proc, char *buffer, int buflen); + void slotProcessExited(KProcess *p); + void slotWroteStdin(KProcess *p); + + private: + QString mProcessName; + KProcess *mProcess; + QPtrQueue mQueue; + QByteArray *mSendBuffer; + QByteArray mReceiveBuffer; + unsigned int mReceiveCount; +}; + +class KMessageFilePipe : public KMessageIO +{ + Q_OBJECT + + public: + KMessageFilePipe(QObject *parent,QFile *readFile,QFile *writeFile); + ~KMessageFilePipe(); + bool isConnected() const; + void send (const QByteArray &msg); + void exec(); + + /** + @return FALSE as this is no network IO. + */ + bool isNetwork() const { return false; } + + /** + * The runtime idendifcation + */ + virtual int rtti() const {return 4;} + + + + private: + QFile *mReadFile; + QFile *mWriteFile; + QByteArray mReceiveBuffer; + unsigned int mReceiveCount; +}; + +#endif diff --git a/libkdegames/kgame/kmessageserver.cpp b/libkdegames/kgame/kmessageserver.cpp new file mode 100644 index 00000000..e857ea31 --- /dev/null +++ b/libkdegames/kgame/kmessageserver.cpp @@ -0,0 +1,515 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "kmessageio.h" +#include "kmessageserver.h" + +// --------------- internal class KMessageServerSocket + +KMessageServerSocket::KMessageServerSocket (Q_UINT16 port, QObject *parent) + : QServerSocket (port, 0, parent) +{ +} + +KMessageServerSocket::~KMessageServerSocket () +{ +} + +void KMessageServerSocket::newConnection (int socket) +{ + emit newClientConnected (new KMessageSocket (socket)); +} + +// ---------------- class for storing an incoming message + +class MessageBuffer +{ + public: + MessageBuffer (Q_UINT32 clientID, const QByteArray &messageData) + : id (clientID), data (messageData) { } + ~MessageBuffer () {} + Q_UINT32 id; + QByteArray data; +}; + +// ---------------- KMessageServer's private class + +class KMessageServerPrivate +{ +public: + KMessageServerPrivate() + : mMaxClients (-1), mGameId (1), mUniqueClientNumber (1), mAdminID (0), mServerSocket (0) + { + mClientList.setAutoDelete (true); + mMessageQueue.setAutoDelete (true); + } + + int mMaxClients; + int mGameId; + Q_UINT16 mCookie; + Q_UINT32 mUniqueClientNumber; + Q_UINT32 mAdminID; + + KMessageServerSocket* mServerSocket; + + QPtrList mClientList; + QPtrQueue mMessageQueue; + QTimer mTimer; + bool mIsRecursive; +}; + + +// ------------------ KMessageServer + +KMessageServer::KMessageServer (Q_UINT16 cookie,QObject* parent) + : QObject(parent, 0) +{ + d = new KMessageServerPrivate; + d->mIsRecursive=false; + d->mCookie=cookie; + connect (&(d->mTimer), SIGNAL (timeout()), + this, SLOT (processOneMessage())); + kdDebug(11001) << "CREATE(KMessageServer=" + << this + << ") cookie=" + << d->mCookie + << " sizeof(this)=" + << sizeof(KMessageServer) + << endl; +} + +KMessageServer::~KMessageServer() +{ + kdDebug(11001) << k_funcinfo << "this=" << this << endl; + Debug(); + stopNetwork(); + deleteClients(); + delete d; + kdDebug(11001) << k_funcinfo << " done" << endl; +} + +//------------------------------------- TCP/IP server stuff + +bool KMessageServer::initNetwork (Q_UINT16 port) +{ + kdDebug(11001) << k_funcinfo << endl; + + if (d->mServerSocket) + { + kdDebug (11001) << k_funcinfo << ": We were already offering connections!" << endl; + delete d->mServerSocket; + } + + d->mServerSocket = new KMessageServerSocket (port); + d->mIsRecursive = false; + + if (!d->mServerSocket || !d->mServerSocket->ok()) + { + kdError(11001) << k_funcinfo << ": Serversocket::ok() == false" << endl; + delete d->mServerSocket; + d->mServerSocket=0; + return false; + } + + kdDebug (11001) << k_funcinfo << ": Now listening to port " + << d->mServerSocket->port() << endl; + connect (d->mServerSocket, SIGNAL (newClientConnected (KMessageIO*)), + this, SLOT (addClient (KMessageIO*))); + return true; +} + +Q_UINT16 KMessageServer::serverPort () const +{ + if (d->mServerSocket) + return d->mServerSocket->port(); + else + return 0; +} + +void KMessageServer::stopNetwork() +{ + if (d->mServerSocket) + { + delete d->mServerSocket; + d->mServerSocket = 0; + } +} + +bool KMessageServer::isOfferingConnections() const +{ + return d->mServerSocket != 0; +} + +//----------------------------------------------- adding / removing clients + +void KMessageServer::addClient (KMessageIO* client) +{ + QByteArray msg; + + // maximum number of clients reached? + if (d->mMaxClients >= 0 && d->mMaxClients <= clientCount()) + { + kdError (11001) << k_funcinfo << ": Maximum number of clients reached!" << endl; + return; + } + + // give it a unique ID + client->setId (uniqueClientNumber()); + kdDebug (11001) << k_funcinfo << ": " << client->id() << endl; + + // connect its signals + connect (client, SIGNAL (connectionBroken()), + this, SLOT (removeBrokenClient())); + connect (client, SIGNAL (received (const QByteArray &)), + this, SLOT (getReceivedMessage (const QByteArray &))); + + // Tell everyone about the new guest + // Note: The new client doesn't get this message! + QDataStream (msg, IO_WriteOnly) << Q_UINT32 (EVNT_CLIENT_CONNECTED) << client->id(); + broadcastMessage (msg); + + // add to our list + d->mClientList.append (client); + + // tell it its ID + QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_CLIENT_ID) << client->id(); + client->send (msg); + + // Give it the complete list of client IDs + QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_CLIENT_LIST) << clientIDs(); + client->send (msg); + + + if (clientCount() == 1) + { + // if it is the first client, it becomes the admin + setAdmin (client->id()); + } + else + { + // otherwise tell it who is the admin + QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_ADMIN_ID) << adminID(); + client->send (msg); + } + + emit clientConnected (client); +} + +void KMessageServer::removeClient (KMessageIO* client, bool broken) +{ + Q_UINT32 clientID = client->id(); + if (!d->mClientList.removeRef (client)) + { + kdError(11001) << k_funcinfo << ": Deleting client that wasn't added before!" << endl; + return; + } + + // tell everyone about the removed client + QByteArray msg; + QDataStream (msg, IO_WriteOnly) << Q_UINT32 (EVNT_CLIENT_DISCONNECTED) << client->id() << (Q_INT8)broken; + broadcastMessage (msg); + + // If it was the admin, select a new admin. + if (clientID == adminID()) + { + if (!d->mClientList.isEmpty()) + setAdmin (d->mClientList.first()->id()); + else + setAdmin (0); + } +} + +void KMessageServer::deleteClients() +{ + d->mClientList.clear(); + d->mAdminID = 0; +} + +void KMessageServer::removeBrokenClient () +{ + if (!sender()->inherits ("KMessageIO")) + { + kdError (11001) << k_funcinfo << ": sender of the signal was not a KMessageIO object!" << endl; + return; + } + + KMessageIO *client = (KMessageIO *) sender(); + + emit connectionLost (client); + removeClient (client, true); +} + + +void KMessageServer::setMaxClients(int c) +{ + d->mMaxClients = c; +} + +int KMessageServer::maxClients() const +{ + return d->mMaxClients; +} + +int KMessageServer::clientCount() const +{ + return d->mClientList.count(); +} + +QValueList KMessageServer::clientIDs () const +{ + QValueList list; + for (QPtrListIterator iter (d->mClientList); *iter; ++iter) + list.append ((*iter)->id()); + return list; +} + +KMessageIO* KMessageServer::findClient (Q_UINT32 no) const +{ + if (no == 0) + no = d->mAdminID; + + QPtrListIterator iter (d->mClientList); + while (*iter) + { + if ((*iter)->id() == no) + return (*iter); + ++iter; + } + return 0; +} + +Q_UINT32 KMessageServer::adminID () const +{ + return d->mAdminID; +} + +void KMessageServer::setAdmin (Q_UINT32 adminID) +{ + // Trying to set the the client that is already admin => nothing to do + if (adminID == d->mAdminID) + return; + + if (adminID > 0 && findClient (adminID) == 0) + { + kdWarning (11001) << "Trying to set a new admin that doesn't exist!" << endl; + return; + } + + d->mAdminID = adminID; + + QByteArray msg; + QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_ADMIN_ID) << adminID; + + // Tell everyone about the new master + broadcastMessage (msg); +} + + +//------------------------------------------- ID stuff + +Q_UINT32 KMessageServer::uniqueClientNumber() const +{ + return d->mUniqueClientNumber++; +} + +// --------------------- Messages --------------------------- + +void KMessageServer::broadcastMessage (const QByteArray &msg) +{ + for (QPtrListIterator iter (d->mClientList); *iter; ++iter) + (*iter)->send (msg); +} + +void KMessageServer::sendMessage (Q_UINT32 id, const QByteArray &msg) +{ + KMessageIO *client = findClient (id); + if (client) + client->send (msg); +} + +void KMessageServer::sendMessage (const QValueList &ids, const QByteArray &msg) +{ + for (QValueListConstIterator iter = ids.begin(); iter != ids.end(); ++iter) + sendMessage (*iter, msg); +} + +void KMessageServer::getReceivedMessage (const QByteArray &msg) +{ + if (!sender() || !sender()->inherits("KMessageIO")) + { + kdError (11001) << k_funcinfo << ": slot was not called from KMessageIO!" << endl; + return; + } + //kdDebug(11001) << k_funcinfo << ": size=" << msg.size() << endl; + KMessageIO *client = (KMessageIO *) sender(); + Q_UINT32 clientID = client->id(); + + //QByteArray *ta=new QByteArray; + //ta->duplicate(msg); + //d->mMessageQueue.enqueue (new MessageBuffer (clientID, *ta)); + + + d->mMessageQueue.enqueue (new MessageBuffer (clientID, msg)); + if (!d->mTimer.isActive()) + d->mTimer.start(0); // AB: should be , TRUE i guess +} + +void KMessageServer::processOneMessage () +{ + // This shouldn't happen, since the timer should be stopped before. But only to be sure! + if (d->mMessageQueue.isEmpty()) + { + d->mTimer.stop(); + return; + } + if (d->mIsRecursive) + { + return; + } + d->mIsRecursive = true; + + MessageBuffer *msg_buf = d->mMessageQueue.head(); + + Q_UINT32 clientID = msg_buf->id; + QBuffer in_buffer (msg_buf->data); + in_buffer.open (IO_ReadOnly); + QDataStream in_stream (&in_buffer); + + QByteArray out_msg; + QBuffer out_buffer (out_msg); + out_buffer.open (IO_WriteOnly); + QDataStream out_stream (&out_buffer); + + bool unknown = false; + + QByteArray ttt=in_buffer.buffer(); + Q_UINT32 messageID; + in_stream >> messageID; + //kdDebug(11001) << k_funcinfo << ": got message with messageID=" << messageID << endl; + switch (messageID) + { + case REQ_BROADCAST: + out_stream << Q_UINT32 (MSG_BROADCAST) << clientID; + // FIXME, compiler bug? + // this should be okay, since QBuffer is subclass of QIODevice! : + // out_buffer.writeBlock (in_buffer.readAll()); + out_buffer.QIODevice::writeBlock (in_buffer.readAll()); + broadcastMessage (out_msg); + break; + + case REQ_FORWARD: + { + QValueList clients; + in_stream >> clients; + out_stream << Q_UINT32 (MSG_FORWARD) << clientID << clients; + // see above! + out_buffer.QIODevice::writeBlock (in_buffer.readAll()); + sendMessage (clients, out_msg); + } + break; + + case REQ_CLIENT_ID: + out_stream << Q_UINT32 (ANS_CLIENT_ID) << clientID; + sendMessage (clientID, out_msg); + break; + + case REQ_ADMIN_ID: + out_stream << Q_UINT32 (ANS_ADMIN_ID) << d->mAdminID; + sendMessage (clientID, out_msg); + break; + + case REQ_ADMIN_CHANGE: + if (clientID == d->mAdminID) + { + Q_UINT32 newAdmin; + in_stream >> newAdmin; + setAdmin (newAdmin); + } + break; + + case REQ_REMOVE_CLIENT: + if (clientID == d->mAdminID) + { + QValueList client_list; + in_stream >> client_list; + for (QValueListIterator iter = client_list.begin(); iter != client_list.end(); ++iter) + { + KMessageIO *client = findClient (*iter); + if (client) + removeClient (client, false); + else + kdWarning (11001) << k_funcinfo << ": removing non-existing clientID" << endl; + } + } + break; + + case REQ_MAX_NUM_CLIENTS: + if (clientID == d->mAdminID) + { + Q_INT32 maximum_clients; + in_stream >> maximum_clients; + setMaxClients (maximum_clients); + } + break; + + case REQ_CLIENT_LIST: + { + out_stream << Q_UINT32 (ANS_CLIENT_LIST) << clientIDs(); + sendMessage (clientID, out_msg); + } + break; + + default: + unknown = true; + } + + // check if all the data has been used + if (!unknown && !in_buffer.atEnd()) + kdWarning (11001) << k_funcinfo << ": Extra data received for message ID " << messageID << endl; + + emit messageReceived (msg_buf->data, clientID, unknown); + + if (unknown) + kdWarning (11001) << k_funcinfo << ": received unknown message ID " << messageID << endl; + + // remove the message, since we are ready with it + d->mMessageQueue.remove(); + if (d->mMessageQueue.isEmpty()) + d->mTimer.stop(); + d->mIsRecursive = false; +} + +void KMessageServer::Debug() +{ + kdDebug(11001) << "------------------ KMESSAGESERVER -----------------------" << endl; + kdDebug(11001) << "MaxClients : " << maxClients() << endl; + kdDebug(11001) << "NoOfClients : " << clientCount() << endl; + kdDebug(11001) << "---------------------------------------------------" << endl; +} + +#include "kmessageserver.moc" diff --git a/libkdegames/kgame/kmessageserver.h b/libkdegames/kgame/kmessageserver.h new file mode 100644 index 00000000..0049a2e7 --- /dev/null +++ b/libkdegames/kgame/kmessageserver.h @@ -0,0 +1,492 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KMESSAGESERVER_H__ +#define __KMESSAGESERVER_H__ + +#include +#include +#include +#include + +class KMessageIO; +class KMessageServerPrivate; + +/** + @short A server for message sending and broadcasting, using TCP/IP connections. + + An object of this class listens for incoming connections via TCP/IP sockets and + creates KMessageSocket objects for every established connection. It receives + messages from the "clients", analyses them and processes an appropriate + reaction. + + You can also use other KMessageIO objects with KMessageServer, not only + TCP/IP socket based ones. Use addClient to connect via an object of any + KMessageIO subclass. (For clients within the same process, you can e.g. use + KMessageDirect.) This object already has to be connected. + + The messages are always packages of an arbitrary length. The format of the messages + is given below. All the data is stored and received with QDataStream, to be + platform independant. + + Setting up a KMessageServer can be done like this: + + \code + KMessageServer *server = new KMessageServer (); + server->initNetwork (TCP/IP-Portnumber); + \endcode + + Usually that is everything you will do. There are a lot of public methods to + administrate the object (maximum number of clients, finding clients, removing + clients, setting the admin client, ...), but this functionality can also + be done by messages from the clients. So you can administrate the object completely + on remote. + + If you want to extend the Server for your own needs (e.g. additional message types), + you can either create a subclass and overwrite the method processOneMessage. + (But don't forget to call the method of the superclass!) Or you can connect to + the signal messageReceived, and analyse the messages there. + + Every client has a unique ID, so that messages can be sent to another dedicated + client or a list of clients. + + One of the clients (the admin) has a special administration right. Some of the + administration messages can only be used with him. The admin can give the admin + status to another client. You can send a message to the admin by using clientID 0. + This is always interpreted as the admin client, independant of its real clientID. + + Here is a list of the messages the KMessageServer understands: + << means, the value is inserted into the QByteArray using QDataStream. The + messageIDs (REQ_BROADCAST, ...) are of type Q_UINT32. + + - QByteArray << static_cast<Q_UINT32>( REQ_BROADCAST ) << raw_data + + When the server receives this message, it sends the following message to + ALL connected clients (a broadcast), where the raw_data is left unchanged: + QByteArray << static_cast <Q_UINT32>( MSG_BROADCAST ) << clientID << raw_data + Q_UINT32 clientID; // the ID of the client that sent the broadcast request + + - QByteArray << static_cast<Q_UINT32>( REQ_FORWARD ) << client_list << raw_data + QValueList <Q_UINT32> client_list; // list of receivers + + When the server receives this message, it sends the following message to + the clients in client_list: + QByteArray << static_cast<Q_UINT32>( MSG_FORWARD ) << senderID << client_list << raw_data + Q_UINT32 senderID; // the sender of the forward request + QValueList <Q_UINT32> client_list; // a copy of the receiver list + + Note: Every client receives the message as many times as he is in the client_list. + Note: Since the client_list is sent to all the clients, every client can see who else + got the message. If you want to prevent this, send a single REQ_FORWARD + message for every receiver. + + - QByteArray << static_cast<Q_UINT32>( REQ_CLIENT_ID ) + + When the server receives this message, it sends the following message to + the asking client: + QByteArray << static_cast<Q_UINT32>( ANS_CLIENT_ID ) << clientID + Q_UINT32 clientID; // The ID of the client who asked for it + + Note: This answer is also automatically sent to a new connected client, so that he + can store his ID. The ID of a client doesn't change during his lifetime, and is + unique for this KMessageServer. + + - QByteArray << static_cast<Q_UINT32>( REQ_ADMIN_ID ) + + When the server receives this message, it sends the following message to + the asking client: + QByteArray << ANS_ADMIN_ID << adminID + Q_UINT32 adminID; // The ID of the admin + + Note: This answer is also automatically sent to a new connected client, so that he + can see if he is the admin or not. It will also be sent to all connected clients + when a new admin is set (see REQ_ADMIN_CHANGE). + + - QByteArray << static_cast<Q_UINT32>( REQ_ADMIN_CHANGE ) << new_admin + Q_UINT32 new_admin; // the ID of the new admin, or 0 for no admin + + When the server receives this message, it sets the admin to the new ID. If no client + with that ID exists, nothing happens. With new_admin == 0 no client is a admin. + ONLY THE ADMIN ITSELF CAN USE THIS MESSAGE! + + Note: The server sends a ANS_ADMIN_ID message to every connected client. + + - QByteArray << static_cast<Q_UINT32>( REQ_REMOVE_CLIENT ) << client_list + QValueList <Q_UINT32> client_list; // The list of clients to be removed + + When the server receives this message, it removes the clients with the ids stored in + client_list, disconnecting the connection to them. + ONLY THE ADMIN CAN USE THIS MESSAGE! + + Note: If one of the clients is the admin himself, he will also be deleted. + Another client (if any left) will become the new admin. + + - QByteArray << static_cast<Q_UINT32>( REQ_MAX_NUM_CLIENTS ) << maximum_clients + Q_INT32 maximum_clients; // The maximum of clients connected, or infinite if -1 + + When the server receives this message, it limits the number of clients to the number given, + or sets it unlimited for maximum_clients == -1. + ONLY THE ADMIN CAN USE THIS MESSAGE! + + Note: If there are already more clients, they are not affected. It only prevents new Clients + to be added. To assure this limit, remove clients afterwards (REQ_REMOVE_CLIENT) + + - QByteArray << static_cast<Q_UINT32>( REQ_CLIENT_LIST ) + + When the server receives this message, it answers by sending a list of IDs of all the clients + that are connected at the moment. So it sends the following message to the asking client: + QByteArray << static_cast<Q_UINT32>( ANS_CLIENT_LIST ) << clientList + QValueList <Q_UINT32> clientList; // The IDs of the connected clients + + Note: This message is also sent to every new connected client, so that he knows the other + clients. + + There are two more messages that are sent from the server to the every client automatically + when a new client connects or a connection to a client is lost: + + QByteArray << static_cast<Q_UINT32>( EVNT_CLIENT_CONNECTED ) << clientID; + Q_UINT32 clientID; // the ID of the new connected client + + QByteArray << static_cast<Q_UINT32>( EVNT_CLIENT_DISCONNECTED ) << clientID; + Q_UINT32 clientID; // the ID of the client that lost the connection + Q_UINT8 broken; // 1 if the network connection was closed, 0 if it was disconnected + // on purpose + + + @author Andreas Beckermann , Burkhard Lehner + @version $Id$ +*/ +class KMessageServer : public QObject +{ + Q_OBJECT + +public: + /** + MessageIDs for messages from a client to the message server. + */ + enum { + REQ_BROADCAST = 1, + REQ_FORWARD, + REQ_CLIENT_ID, + REQ_ADMIN_ID, + REQ_ADMIN_CHANGE, + REQ_REMOVE_CLIENT, + REQ_MAX_NUM_CLIENTS, + REQ_CLIENT_LIST, + REQ_MAX_REQ = 0xffff }; + + /** + * MessageIDs for messages from the message server to a client. + **/ + enum { + MSG_BROADCAST = 101, + MSG_FORWARD, + ANS_CLIENT_ID, + ANS_ADMIN_ID, + ANS_CLIENT_LIST, + EVNT_CLIENT_CONNECTED, + EVNT_CLIENT_DISCONNECTED, + EVNT_MAX_EVNT = 0xffff + }; + + /** + * Create a KGameNetwork object + **/ + KMessageServer(Q_UINT16 cookie = 42, QObject* parent = 0); + + ~KMessageServer(); + + /** + * Gives debug output of the game status + **/ + virtual void Debug(); + +//---------------------------------- TCP/IP server stuff + + /** + * Starts the Communication server to listen for incoming TCP/IP connections. + * + * @param port The port on which the service is offered, or 0 to let the + * system pick a free port + * @return true if it worked + */ + bool initNetwork (Q_UINT16 port = 0); + + /** + * Returns the TCP/IP port number we are listening to for incoming connections. + * (This has to be known by other clients so that they can connect to us. It's + * especially necessary if you used 0 as port number in initNetwork(). + * @return the port number + **/ + Q_UINT16 serverPort () const; + + /** + * Stops listening for connections. The already running connections are + * not affected. + * To listen for connections again call initNetwork again. + **/ + void stopNetwork(); + + /** + * Are we still offer offering server connections? + * @return true, if we are still listening to connections requests + **/ + bool isOfferingConnections() const; + +//---------------------------------- adding / removing clients + +public slots: + /** + * Adds a new @ref KMessageIO object to the communication server. This "client" + * gets a unique ID. + * + * This slot method is automatically called for any incoming TCP/IP + * connection. You can use it to add other types of connections, e.g. + * local connections (KMessageDirect) to the server manually. + * + * NOTE: The @ref KMessageIO object gets owned by the KMessageServer, + * so don't delete or manipulate it afterwards. It is automatically deleted + * when the connection is broken or the communication server is deleted. + * So, add a @ref KMessageIO object to just ONE KMessageServer. + **/ + void addClient (KMessageIO *); + + /** + * Removes the KMessageIO object from the client list and deletes it. + * This destroys the connection, if it already was up. + * Does NOT emit connectionLost. + * Sends an info message to the other clients, that contains the ID of + * the removed client and the value of the parameter broken. + * + * @param io the object to delete and to remove from the client list + * @param broken true if the client has lost connection + * Mostly used internally. You will probably not need this. + **/ + void removeClient (KMessageIO *io, bool broken); + + /** + Deletes all connections to the clients. + */ + void deleteClients(); + +private slots: + /** + * Removes the sender object of the signal that called this slot. It is + * automatically connected to @ref KMessageIO::connectionBroken. + * Emits @ref connectionLost (KMessageIO*), and deletes the @ref KMessageIO object. + * Don't call it directly! + **/ + void removeBrokenClient (); + +public: + /** + * sets the maximum number of clients which can connect. + * If this number is reached, no more clients can be added. + * Setting this number to -1 means unlimited number of clients. + * + * NOTE: Existing connections are not affected. + * So, clientCount > maxClients is possible, if there were already + * more clients than allowed before reducing this value. + * + * @param maxnumber the number of clients + **/ + void setMaxClients(int maxnumber); + + /** + * returns the maximum number of clients + * + * @return the number of clients + **/ + int maxClients() const; + + /** + * returns the current number of connected clients. + * + * @return the number of clients + **/ + int clientCount() const; + + /** + * returns a list of the unique IDs of all clients. + **/ + QValueList clientIDs() const; + + /** + * Find the @ref KMessageIO object to the given client number. + * @param no the client number to look for, or 0 to look for the admin + * @return address of the client, or 0 if no client with that number exists + **/ + KMessageIO *findClient (Q_UINT32 no) const; + + /** + * Returns the clientID of the admin, if there is a admin, 0 otherwise. + * + * NOTE: Most often you don't need to know that id, since you can + * use clientID 0 to specify the admin. + **/ + Q_UINT32 adminID() const; + + /** + * Sets the admin to a new client with the given ID. + * The old admin (if existed) and the new admin will get the ANS_ADMIN message. + * If you use 0 as new adminID, no client will be admin. + **/ + void setAdmin (Q_UINT32 adminID); + + +//------------------------------ ID stuff + + /* + * The unique ID of this game + * + * @return int id + **/ +// int gameId() const; + + /* + * Application cookie. this idendifies the game application. It + * help to distinguish between e.g. KPoker and KWin4 + * + * @return the application cookie + **/ +// int cookie() const; + +//------------------------------ Message stuff + +public: + /** + * Sends a message to all connected clients. + * The message is NOT translated in any way. This method calls + * @ref KMessageIO::send for every client added. + **/ + virtual void broadcastMessage (const QByteArray &msg); + + /** + * Sends a message to a single client with the given ID. + * The message is NOT translated in any way. + * If no client with the given id exists, nothing is done. + * This is just a convenience method. You could also call + * @ref findClient (id)->send(msg) manually, but this method checks for + * errors. + **/ + virtual void sendMessage (Q_UINT32 id, const QByteArray &msg); + + /** + * Sends a message to a list of clients. Their ID is given in ids. If + * a client id is given more than once in the list, the message is also + * sent several times to that client. + * This is just a convenience method. You could also iterate over the + * list of IDs. + **/ + virtual void sendMessage (const QValueList &ids, const QByteArray &msg); + +protected slots: + /** + * This slot receives all the messages from the @ref KMessageIO::received signals. + * It stores the messages in a queue. The messages are later taken out of the queue + * by @ref getReceivedMessage. + * + * NOTE: It is important that this slot may only be called from the signal + * @ref KMessageIO::received, since the sender() object is used to find out + * the client that sent the message! + **/ + virtual void getReceivedMessage (const QByteArray &msg); + + /** + * This slot is called whenever there are elements in the message queue. This queue + * is filled by @ref getReceivedMessage. + * This slot takes one message out of the queue and analyses processes it, + * if it recognizes it. (See message types in the description of the class.) + * After that, the signal @ref messageReceived is emitted. Connect to that signal if + * you want to process other types of messages. + **/ + virtual void processOneMessage (); + +//---------------------------- Signals + +signals: + /** + * A new client connected to the game + * @param client the client object that connected + **/ + void clientConnected (KMessageIO *client); + + /** + * A network connection got broken. Note that the client will automatically get deleted + * after this signal is emitted. The signal is not emitted when the client was removed + * regularly. + * + * @param client the client which left the game + **/ + void connectionLost (KMessageIO *client); + + /** + * This signal is always emitted when a message from a client is received. + * + * You can use this signal to extend the communication server without subclassing. + * Just connect to this signal and analyse the message, if unknown is true. + * If you recognize a message and process it, set unknown to false, otherwise + * a warning message is printed. + * + * @param data the message data + * @param clientID the ID of the KMessageIO object that received the message + * @param unknown true, if the message type is not known by the KMessageServer + **/ + void messageReceived (const QByteArray &data, Q_UINT32 clientID, bool &unknown); + +protected: + /** + * @return A unique number which can be used as the id of a @ref KMessageIO. It is + * incremented after every call so if you need the id twice you have to save + * it anywhere. It's currently used to initialize newly connected clints only. + **/ + Q_UINT32 uniqueClientNumber() const; + +private: + KMessageServerPrivate* d; +}; + + +/** + Internal class of KMessageServer. Creates a server socket and waits for + connections. + + NOTE: This has to be here in the header file, because it is a subclass from + QObject and has to go through the moc. + + @short An internal class for KServerSocket + @author Burkhard Lehner +*/ +class KMessageServerSocket : public QServerSocket +{ + Q_OBJECT + +public: + KMessageServerSocket (Q_UINT16 port, QObject *parent = 0); + ~KMessageServerSocket (); + + void newConnection (int socket); + +signals: + void newClientConnected (KMessageIO *client); +}; + + + +#endif diff --git a/libkdegames/kgame/kmessageserver.png b/libkdegames/kgame/kmessageserver.png new file mode 100644 index 00000000..07fba6c5 Binary files /dev/null and b/libkdegames/kgame/kmessageserver.png differ diff --git a/libkdegames/kgame/kplayer.cpp b/libkdegames/kgame/kplayer.cpp new file mode 100644 index 00000000..0f8ea184 --- /dev/null +++ b/libkdegames/kgame/kplayer.cpp @@ -0,0 +1,446 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ + + +#include "kgame.h" +#include "kgameio.h" +#include "kplayer.h" +#include "kgamemessage.h" +#include "kgamepropertyhandler.h" + +#include +#include + +#include + +#include +#include + +#define KPLAYER_LOAD_COOKIE 7285 + +class KPlayerPrivate +{ +public: + KPlayerPrivate() + { + mNetworkPlayer = 0; + } + + Q_UINT32 mId; + bool mVirtual; // virtual player + int mPriority; // tag for replacement + + KPlayer* mNetworkPlayer; // replacement player + + KGamePropertyHandler mProperties; + +// Playerdata + KGamePropertyQString mName; + KGamePropertyQString mGroup; +}; + +KPlayer::KPlayer() : QObject(0,0) +{ + init(); +} + +KPlayer::KPlayer(KGame* game) : QObject(0, 0) +{ + init(); + game->addPlayer(this); +} + +void KPlayer::init() +{ +// note that NO KGame object exists here! so we cannot use KGameProperty::send! + kdDebug(11001) << k_funcinfo << ": this=" << this << ", sizeof(this)="<mPriority=0; + // I guess we cannot translate the group otherwise no + // international conenctions are possible + + mUserId.registerData(KGamePropertyBase::IdUserId, this, i18n("UserId")); + mUserId.setLocal(0); + d->mGroup.registerData(KGamePropertyBase::IdGroup, this, i18n("Group")); + d->mGroup.setLocal(i18n("default")); + d->mName.registerData(KGamePropertyBase::IdName, this, i18n("Name")); + d->mName.setLocal(i18n("default")); + + mAsyncInput.registerData(KGamePropertyBase::IdAsyncInput, this, i18n("AsyncInput")); + mAsyncInput.setLocal(false); + mMyTurn.registerData(KGamePropertyBase::IdTurn, this, i18n("myTurn")); + mMyTurn.setLocal(false); + mMyTurn.setEmittingSignal(true); + mMyTurn.setOptimized(false); +} + +KPlayer::~KPlayer() +{ + kdDebug(11001) << k_funcinfo << ": this=" << this <<", id=" << this->id() << endl; + + // Delete IODevices + KGameIO *input; + while((input=mInputList.first())) + { + delete input; + } + if (game()) + { + game()->playerDeleted(this); + } + +// note: mProperties does not use autoDelete or so - user must delete objects +// himself + d->mProperties.clear(); + delete d; +// kdDebug(11001) << k_funcinfo << " done" << endl; +} + +bool KPlayer::forwardMessage(QDataStream &msg,int msgid,Q_UINT32 receiver,Q_UINT32 sender) +{ + if (!isActive()) + { + return false; + } + if (!game()) + { + return false; + } + kdDebug(11001) << k_funcinfo << ": to game sender="<sendPlayerInput(msg,this,sender); + } + else + { + kdDebug(11001) << "direct playerInput" << endl; + return game()->systemPlayerInput(msg,this,sender); + } +} + +void KPlayer::setId(Q_UINT32 newid) +{ + // Needs to be after the sendProcess + d->mId=newid; +} + + +void KPlayer::setGroup(const QString& group) +{ d->mGroup = group; } + +const QString& KPlayer::group() const +{ return d->mGroup.value(); } + +void KPlayer::setName(const QString& name) +{ d->mName = name; } + +const QString& KPlayer::name() const +{ return d->mName.value(); } + +Q_UINT32 KPlayer::id() const +{ return d->mId; } + +KGamePropertyHandler * KPlayer::dataHandler() +{ return &d->mProperties; } + +void KPlayer::setVirtual(bool v) +{ d->mVirtual = v; } + +bool KPlayer::isVirtual() const +{ return d->mVirtual;} + +void KPlayer::setNetworkPlayer(KPlayer* p) +{ d->mNetworkPlayer = p; } + +KPlayer* KPlayer::networkPlayer() const +{ return d->mNetworkPlayer; } + +int KPlayer::networkPriority() const +{ return d->mPriority; } + +void KPlayer::setNetworkPriority(int p) +{ d->mPriority = p; } + +bool KPlayer::addGameIO(KGameIO *input) +{ + if (!input) + { + return false; + } + mInputList.append(input); + input->initIO(this); // set player and init device + return true; +} + +// input=0, remove all +bool KPlayer::removeGameIO(KGameIO *targetinput,bool deleteit) +{ + kdDebug(11001) << k_funcinfo << ": " << targetinput << " delete=" << deleteit<< endl; + bool result=true; + if (!targetinput) // delete all + { + KGameIO *input; + while((input=mInputList.first())) + { + if (input) removeGameIO(input,deleteit); + } + } + else + { +// kdDebug(11001) << "remove IO " << targetinput << endl; + if (deleteit) + { + delete targetinput; + } + else + { + targetinput->setPlayer(0); + result=mInputList.remove(targetinput); + } + } + return result; +} + +KGameIO * KPlayer::findRttiIO(int rtti) const +{ + QPtrListIterator it(mInputList); + while (it.current()) + { + if (it.current()->rtti() == rtti) + { + return it.current(); + } + ++it; + } + return 0; +} + +int KPlayer::calcIOValue() +{ + int value=0; + QPtrListIterator it(mInputList); + while (it.current()) + { + value|=it.current()->rtti(); + ++it; + } + return value; +} + +bool KPlayer::setTurn(bool b, bool exclusive) +{ + kdDebug(11001) << k_funcinfo << ": " << id() << " (" << this << ") to " << b << endl; + if (!isActive()) + { + return false; + } + + // if we get to do an exclusive turn all other players are disallowed + if (exclusive && b && game()) + { + KPlayer *player; + KGame::KGamePlayerList *list=game()->playerList(); + for ( player=list->first(); player != 0; player=list->next() ) + { + if (player==this) + { + continue; + } + player->setTurn(false,false); + } + } + + // Return if nothing changed + mMyTurn = b; + + return true; +} + +bool KPlayer::load(QDataStream &stream) +{ + Q_INT32 id,priority; + stream >> id >> priority; + setId(id); + setNetworkPriority(priority); + + // Load Player Data + //FIXME: maybe set all properties setEmitSignal(false) before? + d->mProperties.load(stream); + + Q_INT16 cookie; + stream >> cookie; + if (cookie==KPLAYER_LOAD_COOKIE) + { + kdDebug(11001) << " Player loaded propertly"<mProperties.save(stream); + + stream << (Q_INT16)KPLAYER_LOAD_COOKIE; + + //emit signalSave(stream); + return true; +} + + +void KPlayer::networkTransmission(QDataStream &stream,int msgid,Q_UINT32 sender) +{ + //kdDebug(11001) << k_funcinfo ": msgid=" << msgid << " sender=" << sender << " we are=" << id() << endl; + // PlayerProperties processed + bool issender; + if (game()) + { + issender=sender==game()->gameId(); + } + else + { + issender=true; + } + if (d->mProperties.processMessage(stream,msgid,issender)) + { + return ; + } + switch(msgid) + { + case KGameMessage::IdPlayerInput: + { + kdDebug(11001) << k_funcinfo << ": Got player move " + << "KPlayer (virtual) forwards it to the game object" << endl; + forwardInput(stream,false); + } + break; + default: + emit signalNetworkData(msgid - KGameMessage::IdUser, + ((QBuffer*)stream.device())->readAll(),sender,this); + kdDebug(11001) << k_funcinfo << ": " + << "User data msgid " << msgid << endl; + break; + } + +} + +KGamePropertyBase* KPlayer::findProperty(int id) const +{ + return d->mProperties.find(id); +} + +bool KPlayer::addProperty(KGamePropertyBase* data) +{ + return d->mProperties.addProperty(data); +} + +void KPlayer::sendProperty(int msgid, QDataStream& stream, bool* sent) +{ + if (game()) + { + bool s = game()->sendPlayerProperty(msgid, stream, id()); + if (s) + { + *sent = true; + } + } +} + +void KPlayer::emitSignal(KGamePropertyBase *me) +{ + // Notify KGameIO (Process) for a new turn + if (me->id()==KGamePropertyBase::IdTurn) + { + //kdDebug(11001) << k_funcinfo << ": for KGamePropertyBase::IdTurn " << endl; + QPtrListIterator it(mInputList); + while (it.current()) + { + it.current()->notifyTurn(mMyTurn.value()); + ++it; + } + } + emit signalPropertyChanged(me,this); +} + +// --------------------- DEBUG -------------------- +void KPlayer::Debug() +{ + kdDebug(11001) << "------------------- KPLAYER -----------------------" << endl; + kdDebug(11001) << "this: " << this << endl; + kdDebug(11001) << "rtti: " << rtti() << endl; + kdDebug(11001) << "id : " << id() << endl; + kdDebug(11001) << "Name : " << name() << endl; + kdDebug(11001) << "Group: " << group() << endl; + kdDebug(11001) << "Async: " << asyncInput() << endl; + kdDebug(11001) << "myTurn: " << myTurn() << endl; + kdDebug(11001) << "Virtual: " << isVirtual() << endl; + kdDebug(11001) << "Active: " << isActive() << endl; + kdDebug(11001) << "Priority:" << networkPriority() << endl; + kdDebug(11001) << "Game : " << game() << endl; + kdDebug(11001) << "#IOs: " << mInputList.count() << endl; + kdDebug(11001) << "---------------------------------------------------" << endl; +} + +#include "kplayer.moc" diff --git a/libkdegames/kgame/kplayer.h b/libkdegames/kgame/kplayer.h new file mode 100644 index 00000000..0e511ac3 --- /dev/null +++ b/libkdegames/kgame/kplayer.h @@ -0,0 +1,471 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KPLAYER_H_ +#define __KPLAYER_H_ + +#include +#include +#include + +#include "kgameproperty.h" +#include + +class KGame; +class KGameIO; +class KGamePropertyBase; +class KGamePropertyHandler; + +class KPlayerPrivate; + +/** + * @short Base class for a game player + * + * The KPlayer class is the central player object. It holds + * information about the player and is responsible for any + * input the player does. For this arbitrary many KGameIO + * modules can be plugged into it. Main features are: + * - Handling of IO devices + * - load/save (mostly handled by KGamePropertyHandler) + * - Turn handling (turn based, asynchronous) + * + * A KPlayer depends on a KGame object. Call KGame::addPlayer() to plug + * a KPlayer into a KGame object. Note that you cannot do much with a + * KPlayer object before it has been plugged into a KGame. This is because + * most properties of KPlayer are KGameProperty which need to send messages + * through a KGame object to be changed. + * + * A KGameIO represents the input methods of a player and you should make all + * player inputs through it. So call something like playerInput->move(4); + * instead which should call KGameIO::sendInput() to actually move. This way + * you gain a *very* big advantage: you can exchange a KGameIO whenever you + * want! You can e.g. remove the KGameIO of a local (human) player and just + * replace it by a computerIO on the fly! So from that point on all playerInputs + * are done by the computerIO instead of the human player. You also can replace + * all network players by computer players when the network connection is broken + * or a player wants to quit. + * So remember: use KGameIO whenever possible! A KPlayer should just + * contain all data of the player (KGameIO must not!) and several common + * functions which are shared by all of your KGameIOs. + * + */ +class KDE_EXPORT KPlayer : public QObject +{ + Q_OBJECT + +public: + typedef QPtrList KGameIOList; + + // KPlayer(KGame *,KGameIO * input=0); + /** + * Create a new player object. It will be automatically + * deleted if the game it belongs to is deleted. + */ + KPlayer(); + + /** + * Create a new player object. It will be automatically + * deleted if the game it belongs to is deleted. This constructor + * automatically adds the player to the game using KGame::addPlayer() + */ + KPlayer(KGame* game); + + virtual ~KPlayer(); + + /** + * The idendification of the player. Overwrite this in + * classes inherting KPlayer to run time identify them. + * + * @return 0 for default KPlayer. + */ + virtual int rtti() const {return 0;} + + /** + * Gives debug output of the game status + */ + void Debug(); + + // properties + /** + * Returns a list of input devices + * + * @return list of devices + */ + KGameIOList *ioList() {return &mInputList;} + + /** + * sets the game the player belongs to. This + * is usually automatically done when adding a + * player + * + * @param game the game + */ + void setGame(KGame *game) {mGame=game;} + + /** + * Query to which game the player belongs to + * + * @return the game + */ + KGame *game() const {return mGame;} + + /** + * Set whether this player can make turns/input + * all the time (true) or only when it is its + * turn (false) as it is used in turn based games + * + * @param a async=true turn based=false + */ + void setAsyncInput(bool a) {mAsyncInput = a;} + + /** + * Query whether this player does asynchronous + * input + * + * @return true/false + */ + bool asyncInput() const {return mAsyncInput.value();} + + /** + * Is this player a virtual player, ie is it + * created by mirroring a real player from another + * network game. This mirroring is done autmatically + * as soon as a network connection is build and it affects + * all players regardless what type + * + * @return true/false + */ + bool isVirtual() const; + + /** + * @internal + * Sets whether this player is virtual. This is internally + * called + * + * @param v virtual true/false + */ + void setVirtual(bool v); + + /** + * Is this player an active player. An player is usually + * inactivated if it is replaced by a network connection. + * But this could also be called manually + * + * @return true/false + */ + bool isActive() const {return mActive;} + + /** + * Set an player as active (true) or inactive (false) + * + * @param v true=active, false=inactive + */ + void setActive(bool v) {mActive=v;} + + /** + * Returns the id of the player + * + * @return the player id + */ + Q_UINT32 id() const; + + /* Set the players id. This is done automatically by + * the game object when adding a new player! + * + * @param i the player id + */ + void setId(Q_UINT32 i); + + /** + * Returns the user defined id of the player + * This value can be used arbitrary by you to + * have some user idendification for your player, + * e.g. 0 for a white chess player, 1 for a black + * one. This value is more reliable than the player + * id whcih can even change when you make a network + * connection. + * + * @return the user defined player id + */ + int userId() const {return mUserId.value();} + + /* Set the user defined players id. + * + * @param i the user defined player id + */ + void setUserId(int i) {mUserId = i;} + + /** + * Returns whether this player can be replaced by a network + * connection player. The name of this function can be + * improved ;-) If you do not overwrite the function to + * select what players shall play in a network the KGame + * does an automatic selection based on the networkPriority + * This is not a terrible important function at the moment. + * + * @return true/false + */ + int networkPriority() const; + + /** + * Set whether this player can be replaced by a network + * player. There are to possible games. The first type + * of game has arbitrary many players. As soon as a network + * players connects the game runs with more players (not tagged + * situation). The other type is e.g. games like chess which + * require a constant player number. In a network game situation + * you would tag one or both players of all participants. As + * soon as the connect the tagged player will then be replaced + * by the network partner and it is then controlled over the network. + * On connection loss the old situation is automatically restored. + * + * The name of this function can be improved;-) + * + * @param b should this player be tagged + */ + void setNetworkPriority(int b); + + /** + * Returns the player which got inactivated to allow + * this player to be set up via network. Mostly internal + * function + */ + KPlayer *networkPlayer() const; + + /** + * Sets this network player replacement. Internal stuff + */ + void setNetworkPlayer(KPlayer *p); + + // A name and group the player belongs to + /** + * A group the player belongs to. This + * Can be set arbitrary by you. + */ + void setGroup(const QString& group); + + /** + * Query the group the player belongs to. + */ + virtual const QString& group() const; + + /** + * Sets the name of the player. + * This can be chosen arbitrary. + * @param name The player's name + */ + void setName(const QString& name); + + /** + * @return The name of the player. + */ + virtual const QString& name() const; + + + // set devices + /** + * Adds an IO device for the player. Possible KGameIO devices + * can either be taken from the existing ones or be self written. + * Existing are e.g. Keyboard, Mouse, Computerplayer + * + * @param input the inut device + * @return true if ok + */ + bool addGameIO(KGameIO *input); + + /** + * remove (and delete) a game IO device + * + * The remove IO(s) is/are deleted by default. If + * you do not want this set the parameter deleteit to false + * + * @param input the device to be removed or 0 for all devices + * @param deleteit true (default) to delete the device otherwisse just remove it + * @return true on ok + */ + bool removeGameIO(KGameIO *input=0,bool deleteit=true); + + /** + * Finds the KGameIO devies with the given rtti code. + * E.g. find the mouse or network device + * + * @param rtti the rtti code to be searched for + * @return the KGameIO device + */ + KGameIO *findRttiIO(int rtti) const; + + /** + * Checks whether this player has a IO device of the + * given rtti type + * + * @param rtti the rtti typed to be checked for + * @return true if it exists + */ + bool hasRtti(int rtti) const {return findRttiIO(rtti)!=0;} + + // Message exchange + /** + * Forwards input to the game object..internal use only + * + * This method is used by KGameIO::sendInput(). Use that function + * instead to send player inputs! + * + * This function forwards a player input (see KGameIO classes) to the + * game object, see KGame, either to KGame::sendPlayerInput() (if + * transmit=true, ie the message has just been created) or to + * KGame::playerInput() (if player=false, ie the message *was* sent through + * KGame::sendPlayerInput). + */ + virtual bool forwardInput(QDataStream &msg,bool transmit=true, Q_UINT32 sender=0); + + /** + * Forwards Message to the game object..internal use only + */ + virtual bool forwardMessage(QDataStream &msg,int msgid,Q_UINT32 receiver=0,Q_UINT32 sender=0); + + // Game logic + /** + * is it my turn to go + * + * @return true/false + */ + bool myTurn() const {return mMyTurn.value();} + + /** + * Sets whether this player is the next to turn. + * If exclusive is given all other players are set + * to setTurn(false) and only this player can move + * + * @param b true/false + * @param exclusive true (default)/ false + * @return should be void + */ + bool setTurn(bool b,bool exclusive=true); + + + // load/save + /** + * Load a saved player, from file OR network. By default all + * KGameProperty objects in the dataHandler of this player are loaded + * and saved when using load or save. If you need to save/load more + * you have to replace this function (and save). You will probably + * still want to call the default implementation additionally! + * + * @param stream a data stream where you can stream the player from + * + * @return true? + */ + virtual bool load(QDataStream &stream); + + /** + * Save a player to a file OR to network. See also load + * + * @param stream a data stream to load the player from + * + * @return true? + */ + virtual bool save(QDataStream &stream); + + /** + * Receives a message + * @param msgid The kind of the message. See messages.txt for further + * information + * @param stream The message itself + * @param sender + **/ + void networkTransmission(QDataStream &stream,int msgid,Q_UINT32 sender); + + /** + * Searches for a property of the player given its id. + * @param id The id of the property + * @return The property with the specified id + **/ + KGamePropertyBase* findProperty(int id) const; + + /** + * Adds a property to a player. You would add all + * your player specific game data as KGameProperty and + * they are automatically saved and exchanged over network. + * + * @param data The property to be added. Must have an unique id! + * @return false if the given id is not valid (ie another property owns + * the id) or true if the property could be added successfully + **/ + bool addProperty(KGamePropertyBase* data); + + /** + * Calculates a checksum over the IO devices. Can be used to + * restore the IO handlers. The value returned is the 'or'ed + * value of the KGameIO rtti's. + * this is itnernally used for saving and restorign a player. + */ + int calcIOValue(); + + /** + * @return the property handler + */ + KGamePropertyHandler* dataHandler(); + +signals: + /** + * The player object got a message which was targeted + * at it but has no default method to process it. This + * means probably a user message. Connecting to this signal + * allowed to process it. + */ + void signalNetworkData(int msgid, const QByteArray& buffer, Q_UINT32 sender, KPlayer *me); + + /** + * This signal is emmited if a player property changes its value and + * the property is set to notify this change. This is an + * important signal as you should base the actions on a reaction + * to this property changes. + */ + void signalPropertyChanged(KGamePropertyBase *property,KPlayer *me); + +protected slots: + /** + * Called by KGameProperty only! Internal function! + **/ + void sendProperty(int msgid, QDataStream& stream, bool* sent); + /** + * Called by KGameProperty only! Internal function! + **/ + void emitSignal(KGamePropertyBase *me); + + +private: + void init(); + +private: + KGame *mGame; + bool mActive; // active player + KGameIOList mInputList; + + // GameProperty // AB: I think we can't move them to KPlayerPrivate - inline + // makes sense here + KGamePropertyBool mAsyncInput; // async input allowed + KGamePropertyBool mMyTurn; // Is it my turn to play (only useful if not async)? + KGamePropertyInt mUserId; // a user defined id + + KPlayerPrivate* d; +}; + +#endif diff --git a/libkdegames/kgame/libkdegames.html b/libkdegames/kgame/libkdegames.html new file mode 100644 index 00000000..66206c47 --- /dev/null +++ b/libkdegames/kgame/libkdegames.html @@ -0,0 +1,187 @@ + + + Documentation for libkdegames + + + + +

Documentation for the classes in libkdegames

+ +

Design Principles

+ The library kdegames contains a collection of classes that can be used + to develop games using the KDE environment very easily. There are a few + principles that were used when developing the library:

+ +

    +
  • usable for a big variety of games
    + The class KGame provides many features that are needed in many games. + It can be used for board games, card games, maze games, simulation games, strategy games and + many more.
    + It does not (yet) include special features for realtime games, but can be used there, too. +
  • features one-player and multi-player games
    + A game developed with this library can easily support any number of simultaneous players. + So use it for one-player games (like Tetris or KSame), for two-player games (like TicTacToe + or chess) or for games with an arbitrary number of players. +
  • computer players can easily be developed
    + The class KPlayer represents an abstract player in a game. This can be a + human player that gets the input from the mouse or the keyboard. Or it can be a computer + player that makes moves by random or with artificial intelligence. All this can be achieved + subclassing KPlayer. +
  • support for network games transparently
    + The class KGame contains lots of features for network game support. Developing + a network game using a TCP/IP connection is very easy this way. But the default + case is still the local game. So the user should not need to connect to the internet + or to select a game server to play a game. +
  • support for turn based and for asynchronous games
    + You can use this library for turn based games, when only one player can make a move at a time + (like most bord games or card games), but also for asynchronous games, when every player can + make a move any time (like many action games). +
+ +

The central game class: KGame

+ + When you want to develop your own KDE game using the KDE games library, you most likely want + to use the KGame class. There are two possible ways to extend it to your own needs: + Create a subclass and overwrite the virtual methods, or simply create an instance of KGame + and connect to the appropriate signals.

+ + <<more code about KGame, an easy example>> + + +

Network games and related classes

+ + One of the main principles in the design of KGame was to make network games possible + with a minimum of effort for the game developer.

+ + A network game is a game with usually several players, that are on different computers. These + computers are usually connected to the internet, and all the moves a player does are exchanged + over this network.

+ + The exchange of moves and other information is done using the class KMessageServer. + An object of this class is a server that waits for connections. Someone who wants to take part + in the game has to connect to this server - usually using an internet socket connection. He does + this by creating a KMessageClient object. This object connects to the message server.

+ + The transfer of data is realised by subclasses of the abstract class KMessageIO. One object + of this class is created on the client side, one on the server side. Different types of networks can + be supported by creating new subclasses of KMessageIO. There are already two subclasses of KMessageIO: + KMessageSocket uses a internet socket connection to transfer the data from the message client + to the message server or vice versa. KMessageDirect can be used if both the message server and + the message client are within the same process. The data blocks are copied directly to the other side, + so the transfer is faster and needs no network bandwidth.

+ + A typical network game situation could look like this:

+ +

+ + Here, three KGame object (called message clients) are connected to the KMessageServer object. One + is in the same process, so it uses KMessageDirect. The other two use KMessageSocket, so an internet + socket connection is used. KGame doesn't talk directly to the message server, but uses a KMessageClient + object instead. One of the KMessageClient objects is the admin of the message server. This client has some + priviledges. It may e.g. kill the connection to other message clients, or limit the number of clients + that may connect.

+ + The KGame objects are by default all equal. So the usual approach will be that every KGame object will + store the complete status of the game, and any change or move will be broadcasted to the other KGame + objects, so that they all change the status in identical ways. Sometimes it may be necessary (or just + easier to implement) that one KGame object is a game server (i.e. he is repsonsible for everything, + he coordinates the complete game and stores the game status), whereas the other KGame objects are + only dumb stubs that are used to contact the game server. You can implement both approaches + using the message server structure. If you need to elect the KGame object that shall be + the game server, you may e.g. use the one that has the KMessageClient that is the admin of the message + server. (Of course this is only a suggestion, you can use other approaches.)

+ + The main principle when developing the message server/client structure was, that the message server + doesn't have any idea of the game and its rules that is played. The message server only forwards + messages from one message client to the others without interpreting or manipulating the data. So always + keep in mind that the message server is not a game server! It does not store any data about + the game status. It is only a server for network connections and message broadcasting, not + for game purposes. The reason for this principle is, that any game can be played using a + KMessageServer on any computer. The computer being the message server doesn't need to know anything + about the game that is played on it. So you don't have to install new versions of the game there. Only + the clients need to be game specific.

+ + Usually you don't need to create KMessageServer or KMessageClient objects in your game, + since KGame does this for you. There are three different scenarios fo network games that are + all supported in KGame:

+ + Scenario 1: local game

+ + The local game should always be the default state a game should be in. To avoid having this scenario + as a special case, KGame automatically creates a KMessageServer object and a KMessageClient + object. So every change and every move is sent to the message server and is returned to the KGame + object before it is processed. Since the connection between the message client and the message server + uses KMessageDirect the data transfer is very fast and wont hurt in most cases.

+ +

+ + This is the default situation right after creating the KGame object.

+ + Scenario 2: network game, started by one player

+ + If one user is bored of playing alone, he can open his game for connections from the outside world. + He listens to a TCP/IP socket port (i.e. a number between 0 and 65535). Other players can create + KGame objects of their own and connect to this port. They need to know the IP address of that computer + and the port number. This situation will have this structure: + +

+ + The first player has to do something like:

+ +

+      KGame *myGame = new KGame ();
+      // wait for connections on port 12345
+      myGame->offerConnections (12345);
+    
+ + And the other players have to do something like:

+ +

+      KGame *myGame = new KGame ();
+      // connect to the message server
+      myGame->connectToServer ("theServer.theDomain.com", 12345);
+    
+ + This automatically removes the message server in these KGame objects and connects to the given + one instead.

+ + Scenario 3: network game, using a stand alone message server

+ + Sometimes it is not possible to let the message server run on one of the players computer. If e.g. all + the players have their computer in a local network that uses masquerading to contact the internet, + other computers cannot connect to them since the computer doesn't have a IP address to the outside + world. Then the only way to play a network game is to have a standalone KMessageServer object on + another server computer (somthing like "games.kde.org" e.g.). Since the KMessageServer isn't game + specific at all, every game can be played using it. There doesn't have to be any special software + installed on that server computer, only the program creating a KMessageServer object.

+ + This scenario has some more advantages: The message server can be a well known meeting point to + start a game. This way one could play games against other players you never knew before. Furthermore + the game is stopped brutally when the program that contains the message server in scenario 2 is + quitted. (Migration of message servers is not yet implemented, but may be in the future.) Using a + stand alone message server, the players may enter and leave the game as they want. + +

+ + To create this scenario, a special KMessageServer program has to be started on the computer + that shall be the stand alone message server:

+ +

+      % kmessageserver -port=12345
+    
+ + The other games that want to connect have to do this (supposed the stand alone message server + has the IP address "games.kde.org"):

+ +

+      KGame *myGame = new KGame ();
+      // connect to the message server
+      myGame->connectToServer ("games.kde.org", 12345);
+    
+ + + + + + + \ No newline at end of file diff --git a/libkdegames/kgame/messages.txt b/libkdegames/kgame/messages.txt new file mode 100644 index 00000000..151196d5 --- /dev/null +++ b/libkdegames/kgame/messages.txt @@ -0,0 +1,93 @@ +Message formats of libkdegames: +------------------------------- + +There are two different communication layers, using their own protocols: + + - the message layer (KMessageIO, KMessageServer, KMessageClient) + + This is used to send messages from one client (i.e. KGame object) + to an other one, to a group of other clients, or to all the clients + connected to the KMessageServer. The messages are arbitrary blocks + of data, of an arbitrary length. + This layer is an underlying protocol that isn't game specific at all. + You shouldn't need to know the message format of the packets. If you + want to extend the protocol, have a look into KMessageServer API + reference for a complete list of message types. + + - the game layer (KGame, KGameIO, KPlayer) + + This layer uses the message layer to send its specific message packets + between the objects of the game. + This layer contains the game specific messages (e.g. addPlayer, setupGame). + The rest of this file describes this layer. + + +Game Layer Messages: +-------------------- + + Application Cookie 16 Bit + Version 8 Bit + MsgId 16 Bit + SenderId 16 Bit + ReceiverId 16 Bit + Userdata + +The format of the messages is used internally and there is usually no reason why +you have to know what it is used for. But as usually != always here are some +comments on the format. Note that KGame is under development and the content of +this file could be obsolete. Please result the sourcecode for up-to-date +information. +Application Cookie is used to identify the application. This prevents a +chess game from talking to a poker game. +Version is the version of KNetworkGame, sender and receiver must be of the same +version. + library note: this could be a limitation, as KGame should be backward + compatible. Maybe change to version must be >= KNETWORKGAME or something less + restrictive +MsgId specifies the kind of the message data (see below). +SenderId is the id of the KGame/KPlayer object which sent the message, it is +coded like this: the lower 10 bits specify a player and the upper bit +represent the game id shifted by 10 bits. So we get +Id=playerId | (gameId<<10); +ReceiverId is the id of the receiver of the message. It can be either +a player, a game or a broadcast. For a broadcast the Id is set to 0 +in the other cases the coding is as with the senderId +Userdata is the data of the user (wow ;-)) + + +MsgId UserData +--------------------------------------------------------- +IdMessage user defined + +IdSetupGame Q_INT32 isServer + Q_INT32 maxPlayers + Q_INT32 newid (id for the new game) + Q_INT32 cntR (virtual player nunmber) + Q_INT32 cntT (tagged player number) + TODO: Changed + +IdContinueSetup: TODO + +IdSendPlayer Q_INT32 omit how many tagged players for replacement + TODO: Changed + +IdGameSave Save(msg)->Load(msg) + +IdAddPlayer rtti + gameid() of the owner + player->Save(msg) -> player->Load(msg) + +IdRemovePlayer Q_INT16 playerid + +IdError Q_INT32 errorcode + QString errortext + +IdGameStatus Q_INT32 status + +IdPlayerProperty Q_INT16 propertyId + user defined -> the property + +IdGameProperty Q_INT16 propertyId + user defined -> the property + +IdPlayerInput user defined diff --git a/libkdegames/kgame/scenario0.png b/libkdegames/kgame/scenario0.png new file mode 100644 index 00000000..4de99b52 Binary files /dev/null and b/libkdegames/kgame/scenario0.png differ diff --git a/libkdegames/kgame/scenario1.png b/libkdegames/kgame/scenario1.png new file mode 100644 index 00000000..74af4f6f Binary files /dev/null and b/libkdegames/kgame/scenario1.png differ diff --git a/libkdegames/kgame/scenario2.png b/libkdegames/kgame/scenario2.png new file mode 100644 index 00000000..14ea0a3c Binary files /dev/null and b/libkdegames/kgame/scenario2.png differ diff --git a/libkdegames/kgamelcd.cpp b/libkdegames/kgamelcd.cpp new file mode 100644 index 00000000..65c436a5 --- /dev/null +++ b/libkdegames/kgamelcd.cpp @@ -0,0 +1,250 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001,2002,2003 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kgamelcd.h" +#include "kgamelcd.moc" + +#include +#include +#include + +#include + + +//----------------------------------------------------------------------------- +KGameLCD::KGameLCD(uint nbDigits, QWidget *parent, const char *name) + : QLCDNumber(nbDigits, parent, name), _htime(800) +{ + const QPalette &p = palette(); + _fgColor = p.color(QPalette::Active, QColorGroup::Foreground); + _hlColor = p.color(QPalette::Active, QColorGroup::HighlightedText); + + _timer = new QTimer(this); + connect(_timer, SIGNAL(timeout()), SLOT(timeout())); + + setFrameStyle(Panel | Plain); + setSegmentStyle(Flat); + + displayInt(0); +} + +KGameLCD::~KGameLCD() +{} + +void KGameLCD::setDefaultBackgroundColor(const QColor &color) +{ + QPalette p = palette(); + p.setColor(QColorGroup::Background, color); + setPalette(p); +} + +void KGameLCD::setDefaultColor(const QColor &color) +{ + _fgColor = color; + QPalette p = palette(); + p.setColor(QColorGroup::Foreground, color); + setPalette(p); +} + +void KGameLCD::setHighlightColor(const QColor &color) +{ + _hlColor = color; +} + +void KGameLCD::setLeadingString(const QString &s) +{ + _lead = s; + displayInt(0); +} + +void KGameLCD::setHighlightTime(uint time) +{ + _htime = time; +} + +void KGameLCD::resetColor() +{ + setColor(QColor()); +} + +void KGameLCD::setColor(const QColor &color) +{ + const QColor &c = (color.isValid() ? color : _fgColor); + QPalette p = palette(); + p.setColor(QColorGroup::Foreground, c); + setPalette(p); +} + +void KGameLCD::displayInt(int v) +{ + int n = numDigits() - _lead.length(); + display(_lead + QString::number(v).rightJustify(n)); +} + +void KGameLCD::highlight() +{ + highlight(true); + _timer->start(_htime, true); +} + +void KGameLCD::highlight(bool light) +{ + if (light) setColor(_hlColor); + else resetColor(); +} + +//----------------------------------------------------------------------------- +KGameLCDClock::KGameLCDClock(QWidget *parent, const char *name) +: KGameLCD(5, parent, name) +{ + _timerClock = new QTimer(this); + connect(_timerClock, SIGNAL(timeout()), SLOT(timeoutClock())); +} + +KGameLCDClock::~KGameLCDClock() +{} + +void KGameLCDClock::timeoutClock() +{ + // waiting an hour does not restart timer + if ( _min==59 && _sec==59 ) return; + _sec++; + if (_sec==60) { + _min++; + _sec = 0; + } + showTime(); +} + +QString KGameLCDClock::pretty() const +{ + QString sec = QString::number(_sec).rightJustify(2, '0', true); + QString min = QString::number(_min).rightJustify(2, '0', true); + return min + ':' + sec; +} + +void KGameLCDClock::showTime() +{ + display(pretty()); +} + +void KGameLCDClock::reset() +{ + _timerClock->stop(); + _sec = 0; + _min = 0; + showTime(); +} + +void KGameLCDClock::start() +{ + _timerClock->start(1000); // 1 second +} + +void KGameLCDClock::stop() +{ + _timerClock->stop(); +} + +uint KGameLCDClock::seconds() const +{ + return _min*60 + _sec; +} + +void KGameLCDClock::setTime(uint sec) +{ + Q_ASSERT( sec<3600 ); + _sec = sec % 60; + _min = sec / 60; + showTime(); +} + +void KGameLCDClock::setTime(const QString &s) +{ + Q_ASSERT( s.length()==5 && s[2]==':' ); + uint min = kMin(s.section(':', 0, 0).toUInt(), uint(59)); + uint sec = kMin(s.section(':', 1, 1).toUInt(), uint(59)); + setTime(sec + min*60); +} + + +//----------------------------------------------------------------------------- +class KGameLCDList::KGameLCDListPrivate +{ +public: + QValueVector _leadings; +}; + +KGameLCDList::KGameLCDList(const QString &title, QWidget *parent, + const char *name) + : QWidget(parent, name) +{ + init(title); +} + +KGameLCDList::KGameLCDList(QWidget *parent, const char *name) + : QWidget(parent, name) +{ + init(QString::null); +} + +KGameLCDList::~KGameLCDList() +{ + delete d; +} + +void KGameLCDList::init(const QString &title) +{ + d = new KGameLCDListPrivate; + + QGridLayout *top = new QGridLayout(this, 1, 2, 5); + top->setColStretch(1, 1); + + _title = new QLabel(title, this); + _title->setAlignment(AlignCenter); + top->addMultiCellWidget(_title, 0, 0, 0, 1, AlignCenter); +} + +void KGameLCDList::append(QLCDNumber *lcd) +{ + append(QString::null, lcd); +} + +void KGameLCDList::append(const QString &leading, QLCDNumber *lcd) +{ + uint i = size(); + QLabel *label = 0; + if ( !leading.isEmpty() ) { + label = new QLabel(leading, this); + static_cast(layout())->addWidget(label, i+1, 0); + } + d->_leadings.push_back(label); + _lcds.push_back(lcd); + static_cast(layout())->addWidget(lcd, i+1, 1); +} + +void KGameLCDList::clear() +{ + for (uint i=0; i<_lcds.size(); i++) { + delete d->_leadings[i]; + delete _lcds[i]; + } + d->_leadings.clear(); + _lcds.clear(); +} diff --git a/libkdegames/kgamelcd.h b/libkdegames/kgamelcd.h new file mode 100644 index 00000000..3e6ad33c --- /dev/null +++ b/libkdegames/kgamelcd.h @@ -0,0 +1,249 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001,2002,2003 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KGAMELCD_H +#define __KGAMELCD_H + +#include +#include +#include + +class QLabel; +class QTimer; + +//----------------------------------------------------------------------------- +/** + * This class is a visually enhanced @ref QLCDNumber: + *
    + *
  • It can show an additional string before the integer being + * displayed.
  • + *
  • Its foreground and background colors can easily be modified.
  • + *
  • It can be highlighted for a short time.
  • + *
+ * + * @since 3.2 + */ +class KDE_EXPORT KGameLCD : public QLCDNumber +{ + Q_OBJECT +public: + KGameLCD(uint nbDigits, QWidget *parent = 0, const char *name = 0); + + ~KGameLCD(); + + /** + * Set the default background color. + */ + void setDefaultBackgroundColor(const QColor &color); + + /** + * Set the default foreground color. + */ + void setDefaultColor(const QColor &color); + + /** + * Set highlight color. + */ + void setHighlightColor(const QColor &color); + + /** + * Set the string that will be displayed before the integer number to be + * displayed. By default this string is null. + */ + void setLeadingString(const QString &s); + + /** + * Set the highlight duration in milliseconds. The default value is + * 800 milliseconds. + */ + void setHighlightTime(uint time); + + /** + * Reset the foreground color to the default one. + */ + void resetColor(); + + /** + * Set the foreground color. + */ + void setColor(const QColor &color); + +public slots: + /** + * Highlight the LCD with the QColorGourp::HighlightedText color + * for a small time (setHighlightTime). + */ + void highlight(); + + /** + * Display the given integer with the (optionnal) leading string. + * + * Note: we cannot use display(int) since QLCDNumber::display is + * not virtual... And you cannot use QLCDNumber::intValue() to retrieve + * the given value. + */ + void displayInt(int value); + +private slots: + void timeout() { highlight(false); } + +private: + QColor _fgColor, _hlColor; + QString _lead; + uint _htime; + QTimer *_timer; + + class KGameLCDPrivate; + KGameLCDPrivate *d; + + void highlight(bool light); + +}; + +//----------------------------------------------------------------------------- +/** + * This class is a digital clock widget. It has a maximum duration of + * 3599 seconds (one hour) and it gets updated every second. + * + * @since 3.2 + */ +class KDE_EXPORT KGameLCDClock : public KGameLCD +{ + Q_OBJECT +public: + KGameLCDClock(QWidget *parent = 0, const char *name = 0); + + ~KGameLCDClock(); + + /** + * @return the total number of seconds elapsed. + */ + uint seconds() const; + + /** + * @return the time as a string to be displayed: "mm:ss". + */ + QString pretty() const; + + /** + * Set the time. + */ + void setTime(uint seconds); + + /** + * Set the time (format should be "mm:ss"). + */ + void setTime(const QString &s); + +public slots: + /** + * Stop the clock and reset it to zero. + */ + virtual void reset(); + + /** + * Stop the clock but do not reset it to zero. + */ + virtual void stop(); + + /** + * Start the clock from the current time. + */ + virtual void start(); + +protected slots: + virtual void timeoutClock(); + +private: + QTimer *_timerClock; + uint _sec, _min; + + class KGameLCDClockPrivate; + KGameLCDClockPrivate *d; + + void showTime(); +}; + +//----------------------------------------------------------------------------- +/** + * This widget holds a list of @ref QLCDNumber arranged in a vertical layout. + * It also shows a label at the top of the list. + * + * @since 3.2 + */ +class KDE_EXPORT KGameLCDList : public QWidget +{ + Q_OBJECT +public: + /** + * Constructor. + * + * @param title is the content of the top label. + * @param parent passed to the QWidget constructor + * @param name passed to the QWidget constructor + */ + KGameLCDList(const QString &title, + QWidget *parent = 0, const char *name = 0); + KGameLCDList(QWidget *parent = 0, const char *name = 0); + + ~KGameLCDList(); + + /** + * Append a QLCDNumber at the bottom of the list. + * The QLCDNumber should have the KGameLCDList as parent. + */ + void append(QLCDNumber *lcd); + + /** + * Append a QLCDNumber at the bottom of the list. + * The QLCDNumber should have the KGameLCDList as parent. + */ + void append(const QString &leading, QLCDNumber *lcd); + + /** + * Delete all @ref QLCDNumber and clear the list. + */ + void clear(); + + /** + * @return the title label. + */ + QLabel *title() const { return _title; } + + /** + * @return the QLCDNumber at index @param i + */ + QLCDNumber *lcd(uint i) const { return _lcds[i]; } + + /** + * @return the number of QLCDNumber in the list. + */ + uint size() const { return _lcds.size(); } + +private: + QLabel *_title; + QValueVector _lcds; + + class KGameLCDListPrivate; + KGameLCDListPrivate *d; + + void init(const QString &title); +}; + +#endif diff --git a/libkdegames/kgamemisc.cpp b/libkdegames/kgamemisc.cpp new file mode 100644 index 00000000..bfedd11c --- /dev/null +++ b/libkdegames/kgamemisc.cpp @@ -0,0 +1,62 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ + +#include + +#include +#include + +#include "kgamemisc.h" + +class KGameMiscPrivate +{ +public: + KGameMiscPrivate() + { + } + +}; + +KGameMisc::KGameMisc() +{ +// not yet used +// d = new KGamePrivate; +} + +KGameMisc::~KGameMisc() +{ + // don't forget to delete it as soon as it is used! +// delete d; +} + +QString KGameMisc::randomName()// do we need i18n? I think yes +{ + QStringList names = QStringList::split( QChar(' '), + i18n( "A list of language typical names ( for games ), separated by spaces", + "Adam Alex Andreas Andrew Bart Ben Bernd Bill " + "Chris Chuck Daniel Don Duncan Ed Emily Eric " + "Gary Greg Harry Ian Jean Jeff Jan Kai Keith Ken " + "Kirk Marc Mike Neil Paul Rik Robert Sam Sean " + "Thomas Tim Walter" ) ); + KRandomSequence random; + return *names.at( random.getLong( names.count() ) ); +} diff --git a/libkdegames/kgamemisc.h b/libkdegames/kgamemisc.h new file mode 100644 index 00000000..526bb0ae --- /dev/null +++ b/libkdegames/kgamemisc.h @@ -0,0 +1,45 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/* + $Id$ +*/ +#ifndef __KGAMEMISC_H__ +#define __KGAMEMISC_H__ + +#include +#include +class KGameMiscPrivate; +/** + * This class contains several (usually static) functions I really did not know + * a class for. If you know a class for any of these member s please drop one of + * the above copyright holders a mail (or just kde-games-devel@kde.org) + **/ +class KDE_EXPORT KGameMisc +{ +public: + KGameMisc(); + ~KGameMisc(); + + static QString randomName(); + +private: + KGameMiscPrivate* d; +}; + +#endif diff --git a/libkdegames/kgameprogress.cpp b/libkdegames/kgameprogress.cpp new file mode 100644 index 00000000..861dd454 --- /dev/null +++ b/libkdegames/kgameprogress.cpp @@ -0,0 +1,345 @@ +/* This file is part of the KDE libraries + Copyright (C) 1996 Martynas Kunigelis + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/** + * KGameProgress -- a progress indicator widget for KDE. + */ + +#include +#include +#include +#include +#include + +#include "kgameprogress.h" + +#include + +KGameProgress::KGameProgress(QWidget *parent, const char *name) + : QFrame(parent, name), + QRangeControl(0, 100, 1, 10, 0), + orient(Horizontal) +{ + initialize(); +} + +KGameProgress::KGameProgress(Orientation orientation, QWidget *parent, const char *name) + : QFrame(parent, name), + QRangeControl(0, 100, 1, 10, 0), + orient(orientation) +{ + initialize(); +} + +KGameProgress::KGameProgress(int minValue, int maxValue, int value, + Orientation orientation, QWidget *parent, const char *name) + : QFrame(parent, name), + QRangeControl(minValue, maxValue, 1, 10, value), + orient(orientation) +{ + initialize(); +} + +KGameProgress::~KGameProgress() +{ + delete bar_pixmap; +} + +void KGameProgress::advance(int offset) +{ + setValue(value() + offset); +} + +void KGameProgress::initialize() +{ + format_ = "%p%"; + use_supplied_bar_color = false; + bar_pixmap = 0; + bar_style = Solid; + text_enabled = TRUE; + setBackgroundMode( PaletteBackground ); + connect(kapp, SIGNAL(appearanceChanged()), this, SLOT(paletteChange())); + paletteChange(); +} + +void KGameProgress::paletteChange() +{ + QPalette p = kapp->palette(); + const QColorGroup &colorGroup = p.active(); + if (!use_supplied_bar_color) + bar_color = colorGroup.highlight(); + bar_text_color = colorGroup.highlightedText(); + text_color = colorGroup.text(); + setPalette(p); + + adjustStyle(); +} + + +void KGameProgress::setBarPixmap(const QPixmap &pixmap) +{ + if (pixmap.isNull()) + return; + if (bar_pixmap) + delete bar_pixmap; + + bar_pixmap = new QPixmap(pixmap); +} + +void KGameProgress::setBarColor(const QColor &color) +{ + bar_color = color; + use_supplied_bar_color = true; + if (bar_pixmap) { + delete bar_pixmap; + bar_pixmap = 0; + } +} + +void KGameProgress::setBarStyle(BarStyle style) +{ + if (bar_style != style) { + bar_style = style; + update(); + } +} + +void KGameProgress::setOrientation(Orientation orientation) +{ + if (orient != orientation) { + orient = orientation; + update(); + } +} + +void KGameProgress::setValue(int value) +{ + QRangeControl::setValue(value); +} + +void KGameProgress::setTextEnabled(bool enable) +{ + text_enabled = enable; +} + +const QColor & KGameProgress::barColor() const +{ + return bar_color; +} + +const QPixmap * KGameProgress::barPixmap() const +{ + return bar_pixmap; +} + +bool KGameProgress::textEnabled() const +{ + return text_enabled; +} + +QSize KGameProgress::sizeHint() const +{ + QSize s( size() ); + + if(orientation() == KGameProgress::Vertical) { + s.setWidth(24); + } else { + s.setHeight(24); + } + + return s; +} + +QSize KGameProgress::minimumSizeHint() const +{ + return sizeHint(); +} + +QSizePolicy KGameProgress::sizePolicy() const +{ + if ( orientation()==KGameProgress::Vertical ) + return QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding ); + else + return QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); +} + +KGameProgress::Orientation KGameProgress::orientation() const +{ + return orient; +} + +KGameProgress::BarStyle KGameProgress::barStyle() const +{ + return bar_style; +} + +int KGameProgress::recalcValue(int range) +{ + int abs_value = value() - minValue(); + int abs_range = maxValue() - minValue(); + return abs_range ? range * abs_value / abs_range : 0; +} + +void KGameProgress::valueChange() +{ + repaint(contentsRect(), FALSE); + emit percentageChanged(recalcValue(100)); +} + +void KGameProgress::rangeChange() +{ + repaint(contentsRect(), FALSE); + emit percentageChanged(recalcValue(100)); +} + +void KGameProgress::styleChange(QStyle&) +{ + adjustStyle(); +} + +void KGameProgress::adjustStyle() +{ + switch (style().styleHint(QStyle::SH_GUIStyle)) { + case WindowsStyle: + setFrameStyle(QFrame::WinPanel | QFrame::Sunken); + break; + case MotifStyle: + default: + setFrameStyle(QFrame::Panel | QFrame::Sunken); + setLineWidth( 2 ); + break; + } + update(); +} + +void KGameProgress::paletteChange( const QPalette &p ) +{ + // This never gets called for global color changes + // because we call setPalette() ourselves. + QFrame::paletteChange(p); +} + +void KGameProgress::drawText(QPainter *p) +{ + QRect r(contentsRect()); + //QColor c(bar_color.rgb() ^ backgroundColor().rgb()); + + // Rik: Replace the tags '%p', '%v' and '%m' with the current percentage, + // the current value and the maximum value respectively. + QString s(format_); + + s.replace(QRegExp(QString::fromLatin1("%p")), QString::number(recalcValue(100))); + s.replace(QRegExp(QString::fromLatin1("%v")), QString::number(value())); + s.replace(QRegExp(QString::fromLatin1("%m")), QString::number(maxValue())); + + p->setPen(text_color); + QFont font = p->font(); + font.setBold(true); + p->setFont(font); + //p->setRasterOp(XorROP); + p->drawText(r, AlignCenter, s); + p->setClipRegion( fr ); + p->setPen(bar_text_color); + p->drawText(r, AlignCenter, s); +} + +void KGameProgress::drawContents(QPainter *p) +{ + QRect cr = contentsRect(), er = cr; + fr = cr; + QBrush fb(bar_color), eb(backgroundColor()); + + if (bar_pixmap) + fb.setPixmap(*bar_pixmap); + + if (backgroundPixmap()) + eb.setPixmap(*backgroundPixmap()); + + switch (bar_style) { + case Solid: + if (orient == Horizontal) { + fr.setWidth(recalcValue(cr.width())); + er.setLeft(fr.right() + 1); + } else { + fr.setTop(cr.bottom() - recalcValue(cr.height())); + er.setBottom(fr.top() - 1); + } + + p->setBrushOrigin(cr.topLeft()); + p->fillRect(fr, fb); + + p->fillRect(er, eb); + + break; + + case Blocked: + const int margin = 2; + int max, num, dx, dy; + if (orient == Horizontal) { + fr.setHeight(cr.height() - 2 * margin); + fr.setWidth((int)(0.67 * fr.height())); + fr.moveTopLeft(QPoint(cr.left() + margin, cr.top() + margin)); + dx = fr.width() + margin; + dy = 0; + max = (cr.width() - margin) / (fr.width() + margin) + 1; + num = recalcValue(max); + } else { + fr.setWidth(cr.width() - 2 * margin); + fr.setHeight((int)(0.67 * fr.width())); + fr.moveBottomLeft(QPoint(cr.left() + margin, cr.bottom() - margin)); + dx = 0; + dy = - (fr.height() + margin); + max = (cr.height() - margin) / (fr.height() + margin) + 1; + num = recalcValue(max); + } + p->setClipRect(cr.x() + margin, cr.y() + margin, + cr.width() - margin, cr.height() - margin); + for (int i = 0; i < num; i++) { + p->setBrushOrigin(fr.topLeft()); + p->fillRect(fr, fb); + fr.moveBy(dx, dy); + } + + if (num != max) { + if (orient == Horizontal) + er.setLeft(fr.right() + 1); + else + er.setBottom(fr.bottom() + 1); + if (!er.isNull()) { + p->setBrushOrigin(cr.topLeft()); + p->fillRect(er, eb); + } + } + + break; + } + + if (text_enabled && bar_style != Blocked) + drawText(p); +} + +void KGameProgress::setFormat(const QString & format) +{ + format_ = format; +} + +QString KGameProgress::format() const +{ + return format_; +} + +#include "kgameprogress.moc" diff --git a/libkdegames/kgameprogress.h b/libkdegames/kgameprogress.h new file mode 100644 index 00000000..d6a353ac --- /dev/null +++ b/libkdegames/kgameprogress.h @@ -0,0 +1,255 @@ +/* This file is part of the KDE libraries + Copyright (C) 1996 Martynas Kunigelis + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/***************************************************************************** +* * +* KGameProgress -- progress indicator widget for KDE by Martynas Kunigelis * +* * +*****************************************************************************/ + +#ifndef _KPROGRES_H +#define _KPROGRES_H "$Id$" + +#include +#include +#include +/** + * @short A progress indicator widget. + * + * KGameProgress is derived from QFrame and QRangeControl, so + * you can use all the methods from those classes. The only difference + * is that setValue() is now made a slot, so you can connect + * stuff to it. + * + * None of the constructors take line step and page step as arguments, + * so by default they're set to 1 and 10 respectively. + * + * The Blocked style ignores the textEnabled() setting and displays + * no text, since it looks truly ugly (and for other reasons). Signal + * percentageChanged() is emitted whenever the value changes so you + * can set up a different widget to display the current percentage complete + * and connect the signal to it. + * + * @author Martynas Kunigelis + * @version $Id$ + */ +class KDE_EXPORT KGameProgress : public QFrame, public QRangeControl +{ + Q_OBJECT + Q_ENUMS( BarStyle ) + Q_PROPERTY( int value READ value WRITE setValue) + Q_PROPERTY( BarStyle barStyle READ barStyle WRITE setBarStyle ) + Q_PROPERTY( QColor barColor READ barColor WRITE setBarColor ) + Q_PROPERTY( QPixmap barPixmap READ barPixmap WRITE setBarPixmap ) + Q_PROPERTY( Orientation orientation READ orientation WRITE setOrientation ) + Q_PROPERTY( bool textEnabled READ textEnabled WRITE setTextEnabled ) + +public: + /** + * Possible values for bar style. + * + * @p Solid means one continuous progress bar, @p Blocked means a + * progress bar made up of several blocks. + */ + enum BarStyle { Solid, Blocked }; + + /** + * Construct a horizontal progress bar. + */ + KGameProgress(QWidget *parent=0, const char *name=0); + + /** + * Construct a progress bar with orientation @p orient. + */ + KGameProgress(Orientation orient, QWidget *parent=0, const char *name=0); + + /** + * Construct a progress bar with minimum, maximum and initial values. + */ + KGameProgress(int minValue, int maxValue, int value, Orientation, + QWidget *parent=0, const char *name=0); + + /** + * Destruct the progress bar. + */ + ~KGameProgress(); + + /** + * Set the progress bar style. + * + * Allowed values are @p Solid and @p Blocked. + */ + void setBarStyle(BarStyle style); + + /** + * Set the color of the progress bar. + */ + void setBarColor(const QColor &); + + /** + * Set a pixmap to be shown in the progress bar. + */ + void setBarPixmap(const QPixmap &); + + /** + * Set the orientation of the progress bar. + * + * Allowed values are @p Horizontal and @p Vertical. + */ + void setOrientation(Orientation); + + /** + * If this is set to @p true, the progress text will be displayed. + * + */ + void setTextEnabled(bool); + + /** + * Retrieve the bar style. + * + * @see setBarStyle() + */ + BarStyle barStyle() const; + + /** + * Retrieve the bar color. + * @see setBarColor() + */ + const QColor &barColor() const; + + /** + * Retrieve the bar pixmap. + * + * @see setBarPixmap() + */ + const QPixmap *barPixmap() const; + + /** + * Retrive the current status + * + * @see setValue() + */ + int value() const { return QRangeControl::value(); } + /** + * Retrive the orientation of the progress bar. + * + * @see setOrientation() + */ + Orientation orientation() const; + + /** + * Returns @p true if progress text will be displayed, + * @p false otherwise. + * + * @see setFormat() + */ + bool textEnabled() const; + + /** + */ + virtual QSize sizeHint() const; + + /** + */ + virtual QSize minimumSizeHint() const; + + /** + */ + virtual QSizePolicy sizePolicy() const; + + /** + * Retrieve the current format for printing status text. + * @see setFormat() + */ + QString format() const; + +public slots: + + /** + * Set the format of the text to use to display status. + * + * The default format is "%p%" (which looks like "42%".) + * + * @param format %p is replaced by percentage done, %v is replaced by actual + * value, %m is replaced by the maximum value. + */ + void setFormat(const QString & format); + + /** + * Set the current value of the progress bar to @p value. + * + * This must be a number in the range 0..100. + */ + void setValue(int value); + + /** + * Advance the progress bar by @p prog. + * + * This method is + * provided for convenience and is equivalent with + * setValue(value()+prog). + */ + void advance(int prog); + +signals: + /** + * Emitted when the state of the progress bar changes. + */ + void percentageChanged(int); + +protected: + /** + */ + void valueChange(); + /** + */ + void rangeChange(); + /** + */ + void styleChange( QStyle& ); + /** + */ + void paletteChange( const QPalette & ); + /** + */ + void drawContents( QPainter * ); + +private slots: + void paletteChange(); + +private: + QPixmap *bar_pixmap; + bool use_supplied_bar_color; + QColor bar_color; + QColor bar_text_color; + QColor text_color; + QRect fr; + BarStyle bar_style; + Orientation orient; + bool text_enabled; + QString format_; + void initialize(); + int recalcValue(int); + void drawText(QPainter *); + void adjustStyle(); + + class KGameProgressPrivate; + KGameProgressPrivate *d; +}; + + +#endif diff --git a/libkdegames/kgrid2d.h b/libkdegames/kgrid2d.h new file mode 100644 index 00000000..f9dfd8dd --- /dev/null +++ b/libkdegames/kgrid2d.h @@ -0,0 +1,520 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001-02 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KGRID2D_H_ +#define __KGRID2D_H_ + +#include + +#include +#include +#include + +#include + + +//----------------------------------------------------------------------------- +namespace KGrid2D +{ + /** + * This type represents coordinates on a bidimensionnal grid. + * @since 3.2 + */ + typedef QPair Coord; + + /** + * This type represents a list of @ref Coord. + * @since 3.2 + */ + typedef QValueList CoordList; +} + +inline KGrid2D::Coord +operator +(const KGrid2D::Coord &c1, const KGrid2D::Coord &c2) { + return KGrid2D::Coord(c1.first + c2.first, c1.second + c2.second); +} + +inline KGrid2D::Coord +operator -(const KGrid2D::Coord &c1, const KGrid2D::Coord &c2) { + return KGrid2D::Coord(c1.first - c2.first, c1.second - c2.second); +} + +/** + * @return the maximum of both coordinates. + * @since 3.2 + */ +inline KGrid2D::Coord +maximum(const KGrid2D::Coord &c1, const KGrid2D::Coord &c2) { + return KGrid2D::Coord(kMax(c1.first, c2.first), kMax(c1.second, c2.second)); +} +/** + * @return the minimum of both coordinates. + * @since 3.2 + */ +inline KGrid2D::Coord +minimum(const KGrid2D::Coord &c1, const KGrid2D::Coord &c2) { + return KGrid2D::Coord(kMin(c1.first, c2.first), kMin(c1.second, c2.second)); +} + +inline QTextStream &operator <<(QTextStream &s, const KGrid2D::Coord &c) { + return s << '(' << c.second << ", " << c.first << ')'; +} + +inline QTextStream &operator <<(QTextStream &s, const KGrid2D::CoordList &list) +{ + for(KGrid2D::CoordList::const_iterator i=list.begin(); i!=list.end(); ++i) + s << *i; + return s; +} + +//----------------------------------------------------------------------------- +namespace KGrid2D +{ +/** + * This template class represents a generic bidimensionnal grid. Each node + * contains an element of the template type. + * + * @since 3.2 + */ +template +class Generic +{ + public: + /** + * Constructor. + */ + Generic(uint width = 0, uint height = 0) { + resize(width, height); + } + + virtual ~Generic() {} + + /** + * Resize the grid. + */ + void resize(uint width, uint height) { + _width = width; + _height = height; + _vector.resize(width*height); + } + + /** + * Fill the nodes with the given value. + */ + void fill(const Type &value) { + for (uint i=0; i<_vector.count(); i++) _vector[i] = value; + } + + /** + * @return the width. + */ + uint width() const { return _width; } + /** + * @return the height. + */ + uint height() const { return _height; } + /** + * @return the number of nodes (ie width*height). + */ + uint size() const { return _width*_height; } + + /** + * @return the linear index for the given coordinate. + */ + uint index(const Coord &c) const { + return c.first + c.second*_width; + } + + /** + * @return the coordinate corresponding to the linear index. + */ + Coord coord(uint index) const { + return Coord(index % _width, index / _width); + } + + /** + * @return the value at the given coordinate. + */ + const Type &at(const Coord &c) const { return _vector[index(c)]; } + /** + * @return the value at the given coordinate. + */ + Type &at(const Coord &c) { return _vector[index(c)]; } + /** + * @return the value at the given coordinate. + */ + const Type &operator [](const Coord &c) const { return _vector[index(c)]; } + /** + * @return the value at the given coordinate. + */ + Type &operator [](const Coord &c) { return _vector[index(c)]; } + + /** + * @return the value at the given linear index. + */ + const Type &at(uint index) const { return _vector[index]; } + /** + * @return the value at the given linear index. + */ + Type &at(uint index) { return _vector[index]; } + /** + * @return the value at the given linear index. + */ + const Type &operator [](uint index) const { return _vector[index]; } + /** + * @return the value at the given linear index. + */ + Type &operator [](uint index) { return _vector[index]; } + + /** + * @return if the given coordinate is inside the grid. + */ + bool inside(const Coord &c) const { + return ( c.first>=0 && c.first<(int)_width + && c.second>=0 && c.second<(int)_height ); + } + + /** + * Bound the given coordinate with the grid dimensions. + */ + void bound(Coord &c) const { + c.first = kMax(kMin(c.first, (int)_width-1), 0); + c.second = kMax(kMin(c.second, (int)_height-1), 0); + } + + protected: + uint _width, _height; + QValueVector _vector; +}; +} + +template +QDataStream &operator <<(QDataStream &s, const KGrid2D::Generic &m) { + s << (Q_UINT32)m.width() << (Q_UINT32)m.height(); + for (uint i=0; i +QDataStream &operator >>(QDataStream &s, KGrid2D::Generic &m) { + Q_UINT32 w, h; + s >> w >> h; + m.resize(w, h); + for (uint i=0; i> m[i]; + return s; +} + + +namespace KGrid2D +{ + +//----------------------------------------------------------------------------- +/** + * This class contains static methods to manipulate coordinates for a + * square bidimensionnal grid. + * + * @since 3.2 + */ +class SquareBase +{ + public: + /** + * Identify the eight neighbours. + */ + enum Neighbour { Left=0, Right, Up, Down, LeftUp, LeftDown, + RightUp, RightDown, Nb_Neighbour }; + + /** + * @return the trigonometric angle in radians for the given neighbour. + */ + static double angle(Neighbour n) { + switch (n) { + case Left: return M_PI; + case Right: return 0; + case Up: return M_PI_2; + case Down: return -M_PI_2; + case LeftUp: return 3.0*M_PI_4; + case LeftDown: return -3.0*M_PI_4; + case RightUp: return M_PI_4; + case RightDown: return -M_PI_4; + case Nb_Neighbour: Q_ASSERT(false); + } + return 0; + } + + /** + * @return the opposed neighbour. + */ + static Neighbour opposed(Neighbour n) { + switch (n) { + case Left: return Right; + case Right: return Left; + case Up: return Down; + case Down: return Up; + case LeftUp: return RightDown; + case LeftDown: return RightUp; + case RightUp: return LeftDown; + case RightDown: return LeftUp; + case Nb_Neighbour: Q_ASSERT(false); + } + return Nb_Neighbour; + } + + /** + * @return true if the neighbour is a direct one (ie is one of the four + * nearest). + */ + static bool isDirect(Neighbour n) { return n +class Square : public Generic, public SquareBase +{ + public: + /** + * Constructor. + */ + Square(uint width = 0, uint height = 0) + : Generic(width, height) {} + + /** + * @return the neighbours of coordinate @param c + * to the given set of coordinates + * @param c the coordinate to use as the reference point + * @param insideOnly only add coordinates that are inside the grid. + * @param directOnly only add the four nearest neighbours. + */ + CoordList neighbours(const Coord &c, bool insideOnly = true, + bool directOnly = false) const { + CoordList neighbours; + for (uint i=0; i<(directOnly ? LeftUp : Nb_Neighbour); i++) { + Coord n = neighbour(c, (Neighbour)i); + if ( insideOnly && !Generic::inside(n) ) continue; + neighbours.append(n); + } + return neighbours; + } + + /** + * @return the "projection" of the given coordinate on the grid edges. + * + * @param c the coordinate to use as the reference point + * @param n the direction of projection. + */ + Coord toEdge(const Coord &c, Neighbour n) const { + switch (n) { + case Left: return Coord(0, c.second); + case Right: return Coord(Generic::width()-1, c.second); + case Up: return Coord(c.first, 0); + case Down: return Coord(c.first, Generic::height()-1); + case LeftUp: return Coord(0, 0); + case LeftDown: return Coord(0, Generic::height()-1); + case RightUp: return Coord(Generic::width()-1, 0); + case RightDown: return Coord(Generic::width()-1, Generic::height()-1); + case Nb_Neighbour: Q_ASSERT(false); + } + return c; + } +}; + +//----------------------------------------------------------------------------- +/** + * This class contains static methods to manipulate coordinates on an + * hexagonal grid where hexagons form horizontal lines: + *
+ * (0,0)   (0,1)   (0,2)
+ *     (1,0)   (1,1)   (1,2)
+ * (2,0)   (2,1)   (2,2)
+ * 
+ * + * @since 3.2 + */ +class HexagonalBase +{ + public: + /** + * Identify the six neighbours. + */ + enum Neighbour { Left = 0, Right, LeftUp, LeftDown, + RightUp, RightDown, Nb_Neighbour }; + + /** + * @return the trigonometric angle in radians for the given neighbour. + */ + static double angle(Neighbour n) { + switch (n) { + case Left: return M_PI; + case Right: return 0; + case LeftUp: return 2.0*M_PI/3; + case LeftDown: return -2.0*M_PI/3; + case RightUp: return M_PI/3; + case RightDown: return -M_PI/3; + case Nb_Neighbour: Q_ASSERT(false); + } + return 0; + } + + /** + * @return the opposed neighbour. + */ + static Neighbour opposed(Neighbour n) { + switch (n) { + case Left: return Right; + case Right: return Left; + case LeftUp: return RightDown; + case LeftDown: return RightUp; + case RightUp: return LeftDown; + case RightDown: return LeftUp; + case Nb_Neighbour: Q_ASSERT(false); + } + return Nb_Neighbour; + } + + /** + * @return the neighbour of the given coordinate. + */ + static Coord neighbour(const Coord &c, Neighbour n) { + bool oddRow = c.second%2; + switch (n) { + case Left: return c + Coord(-1, 0); + case Right: return c + Coord( 1, 0); + case LeftUp: return c + (oddRow ? Coord( 0, -1) : Coord(-1, -1)); + case LeftDown: return c + (oddRow ? Coord( 0, 1) : Coord(-1, 1)); + case RightUp: return c + (oddRow ? Coord( 1, -1) : Coord( 0, -1)); + case RightDown: return c + (oddRow ? Coord( 1, 1) : Coord( 0, 1)); + case Nb_Neighbour: Q_ASSERT(false); + } + return c; + } + + /** + * @return the distance between the two coordinates in term of hexagons. + */ + static uint distance(const Coord &c1, const Coord &c2) { + return kAbs(c1.first - c2.first) + kAbs(c1.second - c2.second) + + (c1.first==c2.first || c1.second==c2.second ? 0 : -1); + } +}; + +/** + * This template implements a hexagonal grid + * where hexagons form horizontal lines: + *
+ * (0,0)   (0,1)   (0,2)
+ *     (1,0)   (1,1)   (1,2)
+ * (2,0)   (2,1)   (2,2)
+ * 
+ * + * @ since 3.2 + */ +template +class Hexagonal : public Generic, public HexagonalBase +{ + public: + /** + * Constructor. + */ + Hexagonal(uint width = 0, uint height = 0) + : Generic(width, height) {} + + /** + * @return the neighbours of coordinate @param c + * to the given set of coordinates + * @param c the coordiante to use as the reference point + * @param insideOnly only add coordinates that are inside the grid. + */ + CoordList neighbours(const Coord &c, bool insideOnly = true) const { + CoordList neighbours; + for (uint i=0; i::inside(n) ) continue; + neighbours.append(n); + } + return neighbours; + } + + + /** + * @return the neighbours at distance @param distance of coordinate + * @param c the coordinate to use as the reference point + * @param distance distance to the neighbour (1 means at contact). + * @param insideOnly only add coordinates that are inside the grid. + * @param all returns all neighbours at distance equal and less than + * @param distance (the original coordinate is not included). + */ + CoordList neighbours(const Coord &c, uint distance, bool all, + bool insideOnly = true) const { + // brute force algorithm -- you're welcome to make it more efficient :) + CoordList ring; + if ( distance==0 ) return ring; + ring = neighbours(c, insideOnly); + if ( distance==1 ) return ring; + CoordList center; + center.append(c); + for (uint i=1; i +#include +#include +#include +#include + + +KStdGameAction::KStdGameAction() +{} + +KStdGameAction::~KStdGameAction() +{} + +KAction *KStdGameAction::action(StdGameAction act_enum, const QObject *recvr, + const char *slot, KActionCollection *parent, + const char *name) +{ + return create( act_enum, name, recvr, slot, parent ); +} + +const char* KStdGameAction::stdName(StdGameAction act_enum) +{ + return name(act_enum); +} + +struct KStdGameActionInfo +{ + KStdGameAction::StdGameAction id; + KStdAccel::StdAccel globalAccel; // if we reuse a global accel + int shortcut; // specific shortcut (NH: should be configurable) + const char* psName; + const char* psLabel; + const char* psWhatsThis; + const char* psIconName; +}; + +const KStdGameActionInfo g_rgActionInfo[] = { +// "game" menu + { KStdGameAction::New, KStdAccel::New, 0, "game_new", I18N_NOOP2("new game", "&New"), 0, "filenew" }, + { KStdGameAction::Load, KStdAccel::Open, 0, "game_load", I18N_NOOP("&Load..."), 0, "fileopen" }, + { KStdGameAction::LoadRecent, KStdAccel::AccelNone, 0, "game_load_recent", I18N_NOOP("Load &Recent"), 0, 0 }, + { KStdGameAction::Restart, KStdAccel::Reload, 0, "game_restart", I18N_NOOP("Restart &Game"), 0, "reload" }, + { KStdGameAction::Save, KStdAccel::Save, 0, "game_save", I18N_NOOP("&Save"), 0, "filesave" }, + { KStdGameAction::SaveAs, KStdAccel::AccelNone, 0, "game_save_as", I18N_NOOP("Save &As..."), 0, "filesaveas" }, + { KStdGameAction::End, KStdAccel::End, 0, "game_end", I18N_NOOP("&End Game"), 0, "fileclose" }, + { KStdGameAction::Pause, KStdAccel::AccelNone, Qt::Key_P, "game_pause", I18N_NOOP("Pa&use"), 0, "player_pause" }, + { KStdGameAction::Highscores, KStdAccel::AccelNone, Qt::CTRL+Qt::Key_H, "game_highscores", I18N_NOOP("Show &Highscores"), 0, "highscore" }, + { KStdGameAction::Print, KStdAccel::Print, 0, "game_print", I18N_NOOP("&Print..."), 0, "fileprint" }, + { KStdGameAction::Quit, KStdAccel::Quit, 0, "game_quit", I18N_NOOP("&Quit"), 0, "exit" }, +// "move" menu + { KStdGameAction::Repeat, KStdAccel::AccelNone, 0, "move_repeat", I18N_NOOP("Repeat"), 0, 0 }, + { KStdGameAction::Undo, KStdAccel::Undo, 0, "move_undo", I18N_NOOP("Und&o"), 0, "undo" }, + { KStdGameAction::Redo, KStdAccel::Redo, 0, "move_redo", I18N_NOOP("Re&do"), 0, "redo" }, + { KStdGameAction::Roll, KStdAccel::AccelNone, Qt::CTRL+Qt::Key_R, "move_roll", I18N_NOOP("&Roll Dice"), 0, "roll" }, + { KStdGameAction::EndTurn, KStdAccel::AccelNone, 0, "move_end_turn", I18N_NOOP("End Turn"), 0, "endturn" }, + { KStdGameAction::Hint, KStdAccel::AccelNone, Qt::Key_H, "move_hint", I18N_NOOP("&Hint"), 0, "idea" }, + { KStdGameAction::Demo, KStdAccel::AccelNone, Qt::Key_D, "move_demo", I18N_NOOP("&Demo"), 0, "1rightarrow" }, + { KStdGameAction::Solve, KStdAccel::AccelNone, 0, "move_solve", I18N_NOOP("&Solve"), 0, "wizard" }, +// "settings" menu + { KStdGameAction::ChooseGameType, KStdAccel::AccelNone, 0, "options_choose_game_type", I18N_NOOP("Choose Game &Type"), 0, 0 }, + { KStdGameAction::Carddecks, KStdAccel::AccelNone, 0, "options_configure_carddecks", I18N_NOOP("Configure &Carddecks..."), 0, 0 }, + { KStdGameAction::ConfigureHighscores, KStdAccel::AccelNone, 0, "options_configure_highscores", I18N_NOOP("Configure &Highscores..."), 0, 0 }, + + { KStdGameAction::ActionNone, KStdAccel::AccelNone, 0, 0, 0, 0, 0 } +}; + +static const KStdGameActionInfo* infoPtr( KStdGameAction::StdGameAction id ) +{ + for (uint i = 0; g_rgActionInfo[i].id!=KStdGameAction::ActionNone; i++) { + if( g_rgActionInfo[i].id == id ) + return &g_rgActionInfo[i]; + } + return 0; +} + + +KAction* KStdGameAction::create(StdGameAction id, const char *name, + const QObject *recvr, const char *slot, + KActionCollection* parent ) +{ + KAction* pAction = 0; + const KStdGameActionInfo* pInfo = infoPtr( id ); + kdDebug(125) << "KStdGameAction::create( " << id << "=" << (pInfo ? pInfo->psName : (const char*)0) << ", " << parent << ", " << name << " )" << endl; + if( pInfo ) { + QString sLabel = i18n(pInfo->psLabel); + KShortcut cut = (pInfo->globalAccel==KStdAccel::AccelNone + ? KShortcut(pInfo->shortcut) + : KStdAccel::shortcut(pInfo->globalAccel)); + const char *n = name ? name : pInfo->psName; + switch( id ) { + case LoadRecent: + pAction = + new KRecentFilesAction(sLabel, cut, recvr, slot, parent, n); + break; + case Pause: + case Demo: + pAction = new KToggleAction( sLabel, pInfo->psIconName, cut, + recvr, slot, parent, n); + break; + case ChooseGameType: + pAction = new KSelectAction( sLabel, pInfo->psIconName, cut, + recvr, slot, parent, n); + break; + default: + pAction = new KAction( sLabel, pInfo->psIconName, cut, + recvr, slot, parent, n); + break; + } + } + return pAction; +} + +const char* KStdGameAction::name( StdGameAction id ) +{ + const KStdGameActionInfo* pInfo = infoPtr( id ); + return (pInfo) ? pInfo->psName : 0; +} + +KAction *KStdGameAction::gameNew(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(New, name, recvr, slot, parent); } +KAction *KStdGameAction::load(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Load, name, recvr, slot, parent); } +KRecentFilesAction *KStdGameAction::loadRecent(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return static_cast(KStdGameAction::create(LoadRecent, name, recvr, slot, parent)); } +KAction *KStdGameAction::save(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Save, name, recvr, slot, parent); } +KAction *KStdGameAction::saveAs(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(SaveAs, name, recvr, slot, parent); } +KAction *KStdGameAction::end(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(End, name, recvr, slot, parent); } +KToggleAction *KStdGameAction::pause(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return static_cast(KStdGameAction::create(Pause, name, recvr, slot, parent)); } +KAction *KStdGameAction::highscores(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Highscores, name, recvr, slot, parent); } +KAction *KStdGameAction::print(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Print, name, recvr, slot, parent); } +KAction *KStdGameAction::quit(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Quit, name, recvr, slot, parent); } + +KAction *KStdGameAction::repeat(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Repeat, name, recvr, slot, parent); } +KAction *KStdGameAction::undo(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Undo, name, recvr, slot, parent); } + +KAction *KStdGameAction::redo(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Redo, name, recvr, slot, parent); } + +KAction *KStdGameAction::roll(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Roll, name, recvr, slot, parent); } +KAction *KStdGameAction::endTurn(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(EndTurn, name, recvr, slot, parent); } + +KAction *KStdGameAction::carddecks(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Carddecks, name, recvr, slot, parent); } +KAction *KStdGameAction::configureHighscores(const QObject*recvr, const char *slot, + KActionCollection *parent, const char *name) +{ return KStdGameAction::create(ConfigureHighscores, name, recvr, slot, parent); } +KAction *KStdGameAction::hint(const QObject*recvr, const char *slot, + KActionCollection *parent, const char *name) +{ return KStdGameAction::create(Hint, name, recvr, slot, parent); } +KToggleAction *KStdGameAction::demo(const QObject*recvr, const char *slot, + KActionCollection *parent, const char *name) +{ return static_cast(KStdGameAction::create(Demo, name, recvr, slot, parent)); } +KAction *KStdGameAction::solve(const QObject*recvr, const char *slot, + KActionCollection *parent, const char *name) +{ return KStdGameAction::create(Solve, name, recvr, slot, parent); } +KSelectAction *KStdGameAction::chooseGameType(const QObject*recvr, const char *slot, + KActionCollection *parent, const char *name) +{ return static_cast(KStdGameAction::create(ChooseGameType, name, recvr, slot, parent)); } +KAction *KStdGameAction::restart(const QObject*recvr, const char *slot, + KActionCollection *parent, const char *name) +{ return KStdGameAction::create(Restart, name, recvr, slot, parent); } diff --git a/libkdegames/kstdgameaction.h b/libkdegames/kstdgameaction.h new file mode 100644 index 00000000..a38082af --- /dev/null +++ b/libkdegames/kstdgameaction.h @@ -0,0 +1,261 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +// this class was shamelessy stolen from kdelibs/kdeui/kstdction.[cpp|h] and +// after that just edited for our needs +#ifndef KSTDGAMEACTION_H +#define KSTDGAMEACTION_H + +class KAction; +class KToggleAction; +class QObject; +class KActionCollection; +class KRecentFilesAction; +class KSelectAction; +#include + +//----------------------------------------------------------------------------- +/** + * Replacement for KStdAction for KDE Games + * + * This class is an extension to the usual KStdAction class which provides + * easy access to often used KDE actions + * + * Games often use different menu entries than other programs, e.g. games use + * the menu "game" instead of "file". This class provides the entries which + * differ from the usual KStdAction entries. + * + * @see KStdAction + * + * @author Andreas Beckermann + */ +// #### KDE4: transform in namespace +class KDE_EXPORT KStdGameAction +{ +public: + /** + * The standard menubar and toolbar actions. + **/ + enum StdGameAction { + // Game menu + New=1, Load, LoadRecent, Save, SaveAs, End, Pause, Highscores, + Print, Quit, + // Move menu + Repeat, Undo, Redo, Roll, EndTurn, + // Settings menu + Carddecks, + ChooseGameType, // @since 3.2 + ConfigureHighscores, // @since 3.2 + + Restart, // @since 3.2 + Hint, // @since 3.2 + Demo, // @since 3.2 + Solve, // @since 3.2 + ActionNone // @since 3.2 + }; + + KStdGameAction(); + ~KStdGameAction(); + + /** + * Creates an action corresponding to the + * KStdAction::StdAction enum. + * @since 3.2 + */ + static KAction* create( StdGameAction id, const char *name, + const QObject *recvr, const char *slot, + KActionCollection* parent ); + + /** + * @since 3.2 + */ + static KAction* create( StdGameAction id, + const QObject *recvr, const char *slot, + KActionCollection* parent ) + { return create( id, 0, recvr, slot, parent ); } + + + /** + * Retrieve the action corresponding to the + * KStdGameAction::StdGameAction enum. + * @deprecated + */ + static KAction *action(StdGameAction act_enum, const QObject *recvr = 0, + const char *slot = 0, KActionCollection *parent = 0, + const char *name = 0L ); + + /** + * This will return the internal name of a given standard action. + * @since 3.2 + */ + static const char* name( StdGameAction id ); + + /** + * This will return the internal name of a given standard action. + * @deprecated + */ + static const char* stdName(StdGameAction act_enum); + + /** + * Start a new game + **/ + static KAction *gameNew(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Load a previousely saved game + */ + static KAction *load(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Load a recently loaded game. + */ + static KRecentFilesAction *loadRecent(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Save the current game. + */ + static KAction *save(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Save the current game under a different filename. + */ + static KAction *saveAs(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Pause the game + **/ + static KToggleAction *pause(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Show the highscores. + */ + static KAction *highscores(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + + /** + * End the current game, but do not quit the program. Think of a "close" + * entry. + */ + static KAction *end(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Print the current screen? Game? Whatever - hardly used in games but there + * is at least one example (ktuberling) + */ + static KAction *print(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Quit the game. + */ + static KAction *quit(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + + + /** + * Repeat the last move. + **/ + static KAction *repeat(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Undo the last move + **/ + static KAction *undo(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Redo the last move (which has been undone) + **/ + static KAction *redo(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Roll die or dice + **/ + static KAction *roll(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * End the current turn (not the game). Usually to let the next player + * start + **/ + static KAction *endTurn(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + + /** + * Display configure carddecks dialog. + */ + static KAction *carddecks(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Display configure highscores dialog. + * @since 3.2 + */ + static KAction *configureHighscores(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Give an advice/hint. + * @since 3.2 + */ + static KAction *hint(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Show a demo. + * @since 3.2 + */ + static KToggleAction *demo(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Solve the game. + * @since 3.2 + */ + static KAction *solve(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Choose game type. + * @since 3.2 + */ + static KSelectAction *chooseGameType(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Restart game. + * @since 3.2 + */ + static KAction *restart(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + +}; + +#endif diff --git a/libkdegames/pics/Makefile.am b/libkdegames/pics/Makefile.am new file mode 100644 index 00000000..8f94956a --- /dev/null +++ b/libkdegames/pics/Makefile.am @@ -0,0 +1,4 @@ +picsdir = $(kde_datadir)/kdegames/pics +pics_DATA = star.png + +KDE_ICON = action-roll action-highscore action-endturn diff --git a/libkdegames/pics/cr16-action-endturn.png b/libkdegames/pics/cr16-action-endturn.png new file mode 100644 index 00000000..00051f46 Binary files /dev/null and b/libkdegames/pics/cr16-action-endturn.png differ diff --git a/libkdegames/pics/cr16-action-highscore.png b/libkdegames/pics/cr16-action-highscore.png new file mode 100644 index 00000000..8eb54762 Binary files /dev/null and b/libkdegames/pics/cr16-action-highscore.png differ diff --git a/libkdegames/pics/cr16-action-roll.png b/libkdegames/pics/cr16-action-roll.png new file mode 100644 index 00000000..e41d572a Binary files /dev/null and b/libkdegames/pics/cr16-action-roll.png differ diff --git a/libkdegames/pics/cr22-action-roll.png b/libkdegames/pics/cr22-action-roll.png new file mode 100644 index 00000000..bdf85a8b Binary files /dev/null and b/libkdegames/pics/cr22-action-roll.png differ diff --git a/libkdegames/pics/cr32-action-endturn.png b/libkdegames/pics/cr32-action-endturn.png new file mode 100644 index 00000000..fc6d82bc Binary files /dev/null and b/libkdegames/pics/cr32-action-endturn.png differ diff --git a/libkdegames/pics/cr32-action-highscore.png b/libkdegames/pics/cr32-action-highscore.png new file mode 100644 index 00000000..ad797080 Binary files /dev/null and b/libkdegames/pics/cr32-action-highscore.png differ diff --git a/libkdegames/pics/cr32-action-roll.png b/libkdegames/pics/cr32-action-roll.png new file mode 100644 index 00000000..8452f4a8 Binary files /dev/null and b/libkdegames/pics/cr32-action-roll.png differ diff --git a/libkdegames/pics/star.png b/libkdegames/pics/star.png new file mode 100644 index 00000000..ea88d160 Binary files /dev/null and b/libkdegames/pics/star.png differ diff --git a/libksirtet/CHANGELOG b/libksirtet/CHANGELOG new file mode 100644 index 00000000..d7e56fd2 --- /dev/null +++ b/libksirtet/CHANGELOG @@ -0,0 +1 @@ +see "ksirtet" CHANGELOG diff --git a/libksirtet/DESIGN b/libksirtet/DESIGN new file mode 100644 index 00000000..f09b8539 --- /dev/null +++ b/libksirtet/DESIGN @@ -0,0 +1,57 @@ +'lo, time to explain how some things are designed in the generic class +of these games (ksirtet, kfouleggs and klickety) + +NB: You should read this file with a fixed font editor ... + +the following set of functions is implemented in the hierarchy of classes : +GenericTetris -> BaseBoard -> Board -> specific Board class for each game + +=============================================================================== +action 'Piece Drop Down' activated + | +pieceDropped() <-| + | | +oneLineDown() -> by timer -| + +=============================================================================== +if oneLineDown() make the piece touch the ground + | +pieceDropped() + | +_beforeGlue() <-| + | | +beforeGlue() -> by timer -| // here is implemented the bump effect + | (when done) +gluePiece() + | +_afterGlue() <-| + | | +afterGlue() -> by timer -| // here kfouleggs remove holes + | (when done) +needRemoving ? no -> _afterAfterRemove() + | +_beforeRemove() <-| + | | +beforeRemove() -> by timer -| // here blocks to be removed are highlighted + | (when done) +remove() + | +_afterRemove() <-| + | | +afterRemove() -> by timer -| // here is animated the fall of piece in + | // the holes leaved by removed blocks + | if needs removing again -> _beforeRemove() + | +_afterAfterRemove() ? no gift pending -> afterAfterRemove() + | +putGift() + | +_afterGift() <-| + | | +afterGift() -> by timer -| // here kfouleggs make the gift block to land + | +_afterAfterRemove() + | +afterAfterRemove() ? gameOver -> gameOver() + | +newPiece() diff --git a/libksirtet/LICENSE b/libksirtet/LICENSE new file mode 100644 index 00000000..d2793d2f --- /dev/null +++ b/libksirtet/LICENSE @@ -0,0 +1,22 @@ +LIBKSIRTET +---------- +Copyright (c) 1995 Eirik ENG +Copyright (c) 1996-2004 Nicolas HADACEK (hadacek@kde.org) + + +This file is part of the KDE ksirtet library +Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License version 2 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public License +along with this library; see the file COPYING.LIB. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. diff --git a/libksirtet/Makefile.am b/libksirtet/Makefile.am new file mode 100644 index 00000000..14913f02 --- /dev/null +++ b/libksirtet/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = base lib common + +messages: + $(XGETTEXT) base/*.cpp lib/*.cpp common/*.cpp -o $(podir)/libksirtet.pot diff --git a/libksirtet/README b/libksirtet/README new file mode 100644 index 00000000..f6f9b187 --- /dev/null +++ b/libksirtet/README @@ -0,0 +1,16 @@ +LIBKSIRTET: a set of libraries for use in tetris-alike games +------------------------------------------------------------ +Copyright (c) 1995 Eirik ENG +Copyright (c) 1996-2004 Nicolas HADACEK (hadacek@kde.org) +Distributed under the GNU Library General Public License + + +These libraries provides almost all functionnalities to make +a tetris-alike games. Moreover it gives multiplayers functionality +and a framework for "artificial intelligence" players. + +The basis of the core code (gtetris) has been written by Eirik Eng as +an example for the Qt distribution (see "README.gtetris"). + + +Enjoy ! diff --git a/libksirtet/README.gtetris b/libksirtet/README.gtetris new file mode 100644 index 00000000..88d6f314 --- /dev/null +++ b/libksirtet/README.gtetris @@ -0,0 +1,21 @@ + Oslo, May 14 1995 + +This code was written about 1 and a half year ago to test the very +first Qt skeleton. It was in fact the first Qt application and was +written at a time when Qt only had pushbutton widgets and the drawing +engine was limited to drawing text in a single font and drawing lines +(no kidding, those were the only two drawing operations). In fact the +tetris project doubled the number of widgets in Qt by introducing +the QLCDNumber widget. The whole application was written in 5 evenings +and 1 weekend and is not very well documented. The tetris engine is +implemented in the GenericTetris class and was first made on my +good old 10 MHZ AT where I made a DOS text-mode Tetris (using about +50 lines) to test it out, it was later moved to a SUN Sparc 10, where it +met Qt. GenericTetris is totally independent of Qt, feel free to use +it in any way you like. (see the file gtetris.h for a brief description +of how to use it) + +Enjoy! + + Eirik Eng + ----- diff --git a/libksirtet/TODO b/libksirtet/TODO new file mode 100644 index 00000000..abe0c12d --- /dev/null +++ b/libksirtet/TODO @@ -0,0 +1,15 @@ +GLOBAL: + * options : different types of tiles ? + * background image + more animations + * key profiles (?) + * autopause if focus is lost (cf kmines) + * set the keyboard repeat rate (possible ?) or better do not use the + repeat... + * use KGame + + +KNOWN BUGS: + * occasional crash in multiplayer wizard / with keybinding changes. + * infinite loop with out-of-bound call to GArray::at + sometimes when creating several boards (human vs AI/human). + * does not update immediately second board when changing background. diff --git a/libksirtet/base/Makefile.am b/libksirtet/base/Makefile.am new file mode 100644 index 00000000..bb8b2365 --- /dev/null +++ b/libksirtet/base/Makefile.am @@ -0,0 +1,16 @@ +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) + +# Don't compile with hidden symbols since we are a library. +if disable_VISIBILITY +KDE_CXXFLAGS = -fvisibility=default +endif + +noinst_LTLIBRARIES = libksirtetbase.la +libksirtetbase_la_LDFLAGS = $(all_libraries) + +noinst_HEADERS = kzoommainwindow.h piece.h gtetris.h factory.h highscores.h \ + board.h settings.h field.h inter.h main.h +libksirtetbase_la_SOURCES = kzoommainwindow.cpp main.cpp field.cpp piece.cpp highscores.cpp \ + factory.cpp gtetris.cpp board.cpp settings.cpp \ + inter.cpp baseprefs.kcfgc +METASOURCES = AUTO diff --git a/libksirtet/base/README b/libksirtet/base/README new file mode 100644 index 00000000..a16060b1 --- /dev/null +++ b/libksirtet/base/README @@ -0,0 +1 @@ +This directory contains code shared between ksirtet, kfouleggs and klickety. diff --git a/libksirtet/base/baseprefs.kcfgc b/libksirtet/base/baseprefs.kcfgc new file mode 100644 index 00000000..41c852af --- /dev/null +++ b/libksirtet/base/baseprefs.kcfgc @@ -0,0 +1,7 @@ +# Code generation options for kconfig_compiler +File=libksirtet1.kcfg +#IncludeFiles=defines.h +ClassName=BasePrefs +Singleton=true +#CustomAdditions=true +Mutators=MenubarVisible,BlockSize diff --git a/libksirtet/base/board.cpp b/libksirtet/base/board.cpp new file mode 100644 index 00000000..257e72c3 --- /dev/null +++ b/libksirtet/base/board.cpp @@ -0,0 +1,429 @@ +#include "board.h" +#include "board.moc" + +#include +#include +#include + +#include "piece.h" +#include "factory.h" +#include "baseprefs.h" + +using namespace KGrid2D; + +//----------------------------------------------------------------------------- +FixedCanvasView::FixedCanvasView(QWidget *parent, const char *name) + : QCanvasView(parent, name, WNoAutoErase) +{} + +QSize FixedCanvasView::sizeHint() const +{ + if ( canvas()==0 ) return QSize(); + return canvas()->size() + 2 * QSize(frameWidth(), frameWidth()); +} + +void FixedCanvasView::adjustSize() +{ + setFixedSize(sizeHint()); +} + +//----------------------------------------------------------------------------- +const BaseBoard::DirectionData BaseBoard::DIRECTION_DATA[Nb_Direction] = { + { SquareBase::Left, Left }, + { SquareBase::Right, Right }, + { SquareBase::Down, Up }, + { SquareBase::Up, Down } +}; + +BaseBoard::BaseBoard(bool graphic, QWidget *parent) +: FixedCanvasView(parent, "board"), + GenericTetris(bfactory->bbi.width, bfactory->bbi.height, + bfactory->bbi.withPieces, graphic), + state(GameOver), timer(this), sequences(0), main(0), _next(0), + _arcade(false) +{ + if (graphic) { + setVScrollBarMode(AlwaysOff); + setHScrollBarMode(AlwaysOff); + setFrameStyle( QFrame::Panel | QFrame::Sunken ); + + sequences = new SequenceArray; + main = new BlockInfo(*sequences); + setCanvas(main); + if (bfactory->bbi.withPieces) + _next = new BlockInfo(*sequences); + setBlockInfo(main, _next); + + connect(&timer, SIGNAL(timeout()), SLOT(timeout())); + + Piece::info().loadColors(); + KZoomMainWindow::addWidget(this); + } +} + +void BaseBoard::copy(const GenericTetris &g) +{ + GenericTetris::copy(g); + state = static_cast(g).state; +} + +void BaseBoard::settingsChanged() +{ + Q_ASSERT( graphic() ); + Piece::info().loadColors(); +} + +void BaseBoard::adjustSize() +{ + int size = BasePrefs::blockSize(); + + sequences->setBlockSize(size); + main->resize(matrix().width() * size, matrix().height() * size); + for (uint i=0; iresize(c.first * size, c.second * size); + _nextPiece->moveCenter(); + } + + FixedCanvasView::adjustSize(); +} + +BaseBoard::~BaseBoard() +{ + if ( graphic() ) { + setBlockInfo(0, 0); // destruct all sprites before deleting canvas + delete _next; + delete main; + delete sequences; + } +} + +void BaseBoard::init(bool arcade) +{ + _arcade = arcade; + _arcadeStageDone = false; +} + +void BaseBoard::start(const GTInitData &data) +{ + Q_ASSERT( graphic() ); + if ( !_arcadeStageDone || _arcadeStage==bfactory->bbi.nbArcadeStages ) + _arcadeStage = 0; + _arcadeStageDone = false; + state = Normal; + GenericTetris::start(data); // NB: the timer is started by updateLevel ! + if (_arcade) arcadePrepare(); +} + +void BaseBoard::stop() +{ + timer.stop(); + state = GameOver; +} + +void BaseBoard::pause() +{ + Q_ASSERT( graphic() ); + timer.stop(); + _oldState = state; + state = Paused; + showBoard(false); +} + +void BaseBoard::gameOver() +{ + stop(); + emit gameOverSignal(); +} + +void BaseBoard::showCanvas(QCanvas *c, bool show) +{ + QCanvasItemList l = c->allItems(); + QCanvasItemList::Iterator it; + for (it=l.begin(); it!=l.end(); ++it) { + if (show) (*it)->show(); + else (*it)->hide(); + } + c->update(); +} + +void BaseBoard::showBoard(bool show) +{ + showCanvas(main, show); +} + +void BaseBoard::unpause() +{ + Q_ASSERT( graphic() ); + showBoard(true); + state = _oldState; + startTimer(); +} + +void BaseBoard::updateRemoved(uint newRemoved) +{ + GenericTetris::updateRemoved(newRemoved); + emit removedUpdated(); +} + +void BaseBoard::updateScore(uint newScore) +{ + GenericTetris::updateScore(newScore); + emit scoreUpdated(); +} + +int BaseBoard::firstColumnBlock(uint col) const +{ + for (int j=firstClearLine()-1; j>=0; j--) { + Coord c(col, j); + if ( matrix()[c]!=0 ) return j; + } + return -1; +} + +//----------------------------------------------------------------------------- +void BaseBoard::_beforeRemove(bool first) +{ + if ( graphic() ) { + state = ( beforeRemove(first) ? BeforeRemove : Normal ); + if ( state==BeforeRemove ) { + startTimer(); + return; + } + } + remove(); + _afterRemove(true); +} + +void BaseBoard::remove() +{ + for (uint j=0; jupdate(); + KNotifyClient::event(winId(), "removed", i18n("Blocks removed")); + } +} + +bool BaseBoard::doFall(bool doAll, bool first, bool lineByLine) +{ + Q_ASSERT( !lineByLine || !doAll ); + + if ( !doAll ) { + if (first) loop = 0; + else loop++; + } + bool final = (doAll || lineByLine + || loop==bfactory->bbi.nbFallStages); + + for (uint i=0; i heights(matrix().height()); + for (uint j=1; j=arcadeTodo() ) { + _arcadeStage++; + _arcadeStageDone = true; + gameOver(); + return; + } + if ( !afterAfterRemove() ) gameOver(); + else if ( graphic() ) startTimer(); +} + +bool BaseBoard::timeout() +{ + Q_ASSERT( graphic() ); + if ( state==GameOver ) return true; + switch (state) { + case BeforeRemove: _beforeRemove(FALSE); break; + case AfterRemove: _afterRemove(FALSE); break; + default: return false; + } + main->update(); + return true; +} + +bool BaseBoard::startTimer() +{ + Q_ASSERT( graphic() ); + if ( state==GameOver ) return true; + switch (state) { + case BeforeRemove: + timer.start(bfactory->bbi.beforeRemoveTime, true); + break; + case AfterRemove: + timer.start(bfactory->bbi.afterRemoveTime, true); + break; + default: + return false; + } + return true; +} + +bool BaseBoard::beforeRemove(bool first) +{ + if (first) loop = 0; + else loop++; + + for (uint j=0; jtoggleLight(); + } + + return ( loop!=bfactory->bbi.nbToggles ); +} + + +//----------------------------------------------------------------------------- +void BaseBoard::partialBlockFall(const Coord &src, const Coord &dest) +{ + Q_ASSERT( loopbbi.nbFallStages ); + + float c = float(loop+1) / bfactory->bbi.nbFallStages * BasePrefs::blockSize(); + int xdec = dest.first - src.first; + int ydec = src.second - dest.second; + QPoint p(int(xdec * c), int(ydec * c)); + partialMoveBlock(src, p); +} + +uint BaseBoard::findGroup(Square &field, const Coord &c) const +{ + uint nb = 0; + _findGroup(field, c, nb, false); + return nb; +} + +void BaseBoard::setGroup(Square &field, const Coord &c, uint nb) const +{ + _findGroup(field, c, nb, true); +} + +void BaseBoard::_findGroup(Square &field, const Coord &c, + uint &nb, bool set) const +{ + if (!set) nb++; + field[c] = (set ? (int)nb : -1); + uint value = matrix()[c]->value(); + CoordList n = matrix().neighbours(c, true, true); + for (CoordList::const_iterator i = n.begin(); i!=n.end(); ++i) + blockInGroup(field, *i, value, nb, set); +} + +void BaseBoard::blockInGroup(Square &field, const Coord &c, uint value, + uint &nb, bool set) const +{ + if ( matrix()[c]==0 ) return; + if ( matrix()[c]->value()!=value ) return; + if ( field[c]!=(set ? -1 : 0) ) return; + _findGroup(field, c, nb, set); +} + +QMemArray BaseBoard::findGroups(Square &field, uint minSize, + bool exitAtFirstFound) const +{ + field.fill(0); + QMemArray groups; + for (uint j=0; jisGarbage() ) continue; + if ( field[c]!=0 ) continue; + uint nb = findGroup(field, c); + setGroup(field, c, nb); + if ( nb>=minSize ) { + uint s = groups.size(); + groups.resize(s+1); + groups[s] = nb; + if (exitAtFirstFound) return groups; + } + } + return groups; +} + +uint BaseBoard::drawCode(const Coord &c) const +{ + uint v = matrix()[c]->value(); + uint code = 0; + for (uint i=0; ivalue()!=v ) continue; + code |= DIRECTION_DATA[i].direction; + } + return code; +} + +void BaseBoard::computeNeighbours() +{ + for (uint j=0; jisGarbage() ) continue; + matrix()[c]->sprite()->setFrame( drawCode(c) ); + } +} diff --git a/libksirtet/base/board.h b/libksirtet/base/board.h new file mode 100644 index 00000000..443c6532 --- /dev/null +++ b/libksirtet/base/board.h @@ -0,0 +1,134 @@ +#ifndef BASE_BOARD_H +#define BASE_BOARD_H + +#include +#include + +#include "gtetris.h" + +#include + +class SequenceArray; +class BlockInfo; + +//----------------------------------------------------------------------------- +class KDE_EXPORT FixedCanvasView : public QCanvasView +{ + Q_OBJECT +public: + FixedCanvasView(QWidget *parent = 0, const char *name = 0); + + virtual QSize sizeHint() const; + +public slots: + virtual void adjustSize(); +}; + +//----------------------------------------------------------------------------- +class KDE_EXPORT BaseBoard : public FixedCanvasView, public GenericTetris +{ + Q_OBJECT + public: + enum Direction { Left = 1, Right = 2, Up = 4, Down = 8, Nb_Direction = 4 }; + private: + struct DirectionData { + KGrid2D::SquareBase::Neighbour neighbour; + Direction direction; + }; + static const DirectionData DIRECTION_DATA[Nb_Direction]; + + public: + BaseBoard(bool graphic, QWidget *parent); + virtual ~BaseBoard(); + void copy(const GenericTetris &); + + void init(bool arcade); + virtual void start(const GTInitData &); + virtual void pause(); + virtual void unpause(); + virtual void stop(); + bool isGameOver() const { return state==GameOver; } + bool isPaused() const { return state==Paused; } + + bool isArcade() const { return _arcade; } + uint arcadeStage() const { return _arcadeStage; } + bool arcadeStageDone() const { return _arcadeStageDone; } + virtual uint arcadeTodo() const { return 0; } + virtual uint arcadeDone() const { return 0; } + + virtual void settingsChanged(); + BlockInfo *next() const { return _next; } + + int firstColumnBlock(uint column) const; + + public slots: + virtual void adjustSize(); + + protected slots: + virtual bool timeout(); // return true if treated + + signals: + void updatePieceConfigSignal(); + void removedUpdated(); + void scoreUpdated(); + void gameOverSignal(); + + protected: + virtual bool beforeRemove(bool first); + void _beforeRemove(bool first); + enum AfterRemoveResult { Done, NeedAfterRemove, NeedRemoving }; + virtual AfterRemoveResult afterRemove(bool doAll, bool first); + void _afterAfterRemove(); + virtual bool afterAfterRemove() = 0; + virtual bool startTimer(); // return true if treated + virtual bool toBeRemoved(const KGrid2D::Coord &) const = 0; + virtual void remove(); + virtual bool toFall(const KGrid2D::Coord &) const = 0;//height>0 when called + virtual bool doFall(bool doAll, bool first, bool lineByLine); + virtual void gameOver(); + virtual void arcadePrepare() {} + + uint drawCode(const KGrid2D::Coord &) const; + void computeNeighbours(); + void partialBlockFall(const KGrid2D::Coord &src, const KGrid2D::Coord &dest); + + // return the sizes of the groups (>=minSize) + QMemArray findGroups(KGrid2D::Square &field, uint minSize, + bool exitAtFirstFound = false) const; + // find group size and put -1 in the corresponding blocks (these blocks + // should be 0 at start) + uint findGroup(KGrid2D::Square &field, const KGrid2D::Coord &) const; + // set the size of the group in the blocks (these blocks should be -1 + // at start ie you should have called findGroup() before) + void setGroup(KGrid2D::Square &field, const KGrid2D::Coord &c, + uint nb) const; + + void updateRemoved(uint newRemoved); + void updateScore(uint newScore); + + virtual void showBoard(bool show); + void showCanvas(QCanvas *c, bool show); + + enum BoardState { GameOver, Normal, Paused, + DropDown, BeforeGlue, AfterGlue, BeforeRemove, + AfterRemove, AfterGift }; + BoardState state, _oldState; + QTimer timer; + SequenceArray *sequences; + BlockInfo *main, *_next; + uint loop; + + private: + bool _arcade, _arcadeStageDone; + uint _arcadeStage; + + void _afterRemove(bool first); + void updatePieceConfig() { emit updatePieceConfigSignal(); } + + void _findGroup(KGrid2D::Square &field, const KGrid2D::Coord &c, + uint &nb, bool set) const; + void blockInGroup(KGrid2D::Square &field, const KGrid2D::Coord &c, + uint value, uint &nb, bool ser) const; +}; + +#endif diff --git a/libksirtet/base/factory.cpp b/libksirtet/base/factory.cpp new file mode 100644 index 00000000..55850f0b --- /dev/null +++ b/libksirtet/base/factory.cpp @@ -0,0 +1,52 @@ +#include "factory.h" + +#include +#include +#include +#include +#include + +#include "settings.h" + + +BaseFactory *BaseFactory::_self = 0; + +BaseFactory::BaseFactory(const MainData &md, const BaseBoardInfo &bi) + : mainData(md), bbi(bi) +{ + Q_ASSERT( _self==0 ); + _self = this; + _aboutData = + new KAboutData(md.appName, md.trName, md.longVersion, md.description, + KAboutData::License_GPL, + "(c) 1995, Eirik Eng\n(c) 1996-2004, Nicolas Hadacek", + 0, md.homepage); + _aboutData->addAuthor("Nicolas Hadacek", 0, "hadacek@kde.org"); + _aboutData->addCredit("Eirik Eng", I18N_NOOP("Core engine")); +} + +void BaseFactory::init(int argc, char **argv) +{ + KCmdLineArgs::init(argc, argv, _aboutData); + (void)new KApplication; + KGlobal::locale()->insertCatalogue("libkdegames"); + KGlobal::locale()->insertCatalogue("libksirtet"); +} + +BaseFactory::~BaseFactory() +{ + delete kapp; + delete _aboutData; + Q_ASSERT(_self); + _self = 0; +} + +QWidget *BaseFactory::createAppearanceConfig() +{ + return new BaseAppearanceConfig; +} + +QWidget *BaseFactory::createColorConfig() +{ + return new ColorConfig; +} diff --git a/libksirtet/base/factory.h b/libksirtet/base/factory.h new file mode 100644 index 00000000..b542205e --- /dev/null +++ b/libksirtet/base/factory.h @@ -0,0 +1,60 @@ +#ifndef BASE_FACTORY_H +#define BASE_FACTORY_H + +#include + +#include + +struct MainData { + const char *appName, *trName, *description, *homepage, *removedLabel, + *version, *longVersion; +}; + +struct BaseBoardInfo { + uint width, height; + bool withPieces; + + uint beforeRemoveTime, afterRemoveTime; + uint nbToggles, nbFallStages; + + uint nbArcadeStages; + + const uint *histogram; + uint histogramSize; + bool scoreBound; +}; + +class BaseBoard; +class BaseInterface; +class QWidget; +class KAboutData; + +#define bfactory BaseFactory::self() + +class KDE_EXPORT BaseFactory +{ + public: + BaseFactory(const MainData &, const BaseBoardInfo &); + virtual ~BaseFactory(); + void init(int argc, char **argv); + + static BaseFactory *self() { return _self; } + + const MainData &mainData; + const BaseBoardInfo &bbi; + + virtual BaseBoard *createBoard(bool graphic, QWidget *parent) = 0; + virtual BaseInterface *createInterface(QWidget *parent) = 0; + + virtual QWidget *createAppearanceConfig(); + virtual QWidget *createColorConfig(); + virtual QWidget *createGameConfig() { return 0; } + + protected: + KAboutData *_aboutData; + + private: + static BaseFactory *_self; +}; + +#endif diff --git a/libksirtet/base/field.cpp b/libksirtet/base/field.cpp new file mode 100644 index 00000000..53f6220a --- /dev/null +++ b/libksirtet/base/field.cpp @@ -0,0 +1,162 @@ +#include "field.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "factory.h" +#include "board.h" +#include "baseprefs.h" + + +const char *BaseField::BUTTON_TEXTS[NB_BUTTON_TYPE] = { + I18N_NOOP("Start"), I18N_NOOP("Resume"), I18N_NOOP("Proceed") +}; + +BaseField::BaseField(QWidget *w) + : _widget(w), _boardLayout(0), _label(0), _button(0) +{ + top = new QGridLayout(w, 3, 5, 10); + + lcds = new QGridLayout(7, 1, 5); + top->addLayout(lcds, 1, 0); + lcds->setRowStretch(1, 0); + + board = bfactory->createBoard(true, w); + _boardRootPixmap = new KCanvasRootPixmap(board); + _boardRootPixmap->start(); + top->addWidget(board, 1, 2); +} + +void BaseField::init(bool AI, bool multiplayer, bool server, bool first, + const QString &name) +{ + _flags.AI = AI; + _flags.multiplayer = multiplayer; + _flags.server = server; + _flags.first = first; + QString text = (AI ? i18n("%1\n(AI player)").arg(name) + : (multiplayer ? i18n("%1\n(Human player)").arg(name) + : QString::null)); + if ( first && !server ) text += i18n("\nWaiting for server"); + setMessage(text, (first && server ? StartButton : NoButton)); + showScore->resetColor(); + board->init(false); +} + +void BaseField::setArcade() +{ + board->init(true); + setMessage(i18n("Stage #1"), StartButton); +} + +bool BaseField::isArcade() const +{ + return board->isArcade(); +} + +void BaseField::setMessage(const QString &label, ButtonType type) +{ + delete _label; + _label = 0; + delete _button; + _button = 0; + delete _boardLayout; + _boardLayout = 0; + + if ( label.isEmpty() && type==NoButton ) { + _widget->setFocus(); + return; + } + + _boardLayout = new QVBoxLayout(board); + _boardLayout->addStretch(3); + if ( !label.isEmpty() ) { + QString str = (isArcade() ? i18n("Arcade game") + '\n' + : QString::null) + label; + _label = new QLabel(str, board); + _label->setAlignment(Qt::AlignCenter); + _label->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + _boardLayout->addWidget(_label, 0, Qt::AlignCenter); + _label->show(); + } + _boardLayout->addStretch(1); + if ( type!=NoButton ) { + _button = new QPushButton(i18n(BUTTON_TEXTS[type]), board); + _button->setFocus(); + const char *slot = (type==ResumeButton ? SLOT(pause()) + : SLOT(start())); + _button->connect(_button, SIGNAL(clicked()), + _widget->parent(), slot); + _boardLayout->addWidget(_button, 0, Qt::AlignCenter); + _button->show(); + } + _boardLayout->addStretch(3); +} + +void BaseField::start(const GTInitData &data) +{ + _firstScore = KExtHighscore::firstScore(); + _lastScore = KExtHighscore::lastScore(); + hideMessage(); + board->start(data); +} + +void BaseField::pause(bool pause) +{ + if (pause) { + board->pause(); + setMessage(i18n("Game paused"), ResumeButton); + } else { + board->unpause(); + hideMessage(); + } +} + +void BaseField::stop(bool gameover) +{ + board->stop(); + ButtonType button = StartButton; + QString msg = (gameover ? i18n("Game over") : QString::null); + if ( board->isArcade() && board->arcadeStageDone() ) { + if ( board->arcadeStage()==bfactory->bbi.nbArcadeStages ) + msg = i18n("The End"); + else { + msg = i18n("Stage #%1 done").arg(board->arcadeStage()); + button = ProceedButton; + } + } + setMessage(msg, button); +} + +void BaseField::gameOver(const KExtHighscore::Score &score, QWidget *parent) +{ + KNotifyClient::event(parent->winId(), "game over", i18n("Game Over")); + KExtHighscore::submitScore(score, parent); +} + +void BaseField::scoreUpdated() +{ + showScore->display( (int)board->score() ); + if (_flags.multiplayer) return; + + QColor color; + if ( _firstScoresetColor(color); +} + +void BaseField::settingsChanged() +{ + QColor color = BasePrefs::fadeColor(); + double s = BasePrefs::fadeIntensity(); + _boardRootPixmap->setFadeEffect(s, color); + board->canvas()->setBackgroundColor(color); + board->settingsChanged(); +} diff --git a/libksirtet/base/field.h b/libksirtet/base/field.h new file mode 100644 index 00000000..d006a052 --- /dev/null +++ b/libksirtet/base/field.h @@ -0,0 +1,66 @@ +#ifndef BASE_FIELD_H +#define BASE_FIELD_H + +#include + +#include + +class QVBoxLayout; +class QGridLayout; +class KGameLCD; +class KGameLCDList; +class BaseBoard; +class QLabel; +class QButton; +class GTInitData; +class KCanvasRootPixmap; + +class KDE_EXPORT BaseField +{ + public: + BaseField(QWidget *widget); + virtual ~BaseField() {} + + virtual KExtHighscore::Score currentScore() const = 0; + static void gameOver(const KExtHighscore::Score &, QWidget *parent); + + virtual void setArcade(); + bool isArcade() const; + + protected: + QGridLayout *top, *lcds; + KGameLCD *showScore; + KGameLCDList *removedList, *scoreList; + BaseBoard *board; + + virtual void scoreUpdated(); + virtual void init(bool AI, bool multiplayer, bool server, bool first, + const QString &name); + virtual void start(const GTInitData &); + virtual void pause(bool pause); + virtual void stop(bool gameover); + virtual void settingsChanged(); + + private: + QWidget *_widget; + struct Flags { + bool AI, multiplayer, server, first; + }; + Flags _flags; + uint _arcadeStage; + QVBoxLayout *_boardLayout; + QLabel *_label; + QButton *_button; + KCanvasRootPixmap *_boardRootPixmap; + KExtHighscore::Score _firstScore, _lastScore; + + enum ButtonType { StartButton = 0, ResumeButton, ProceedButton, + NB_BUTTON_TYPE, NoButton = NB_BUTTON_TYPE }; + static const char *BUTTON_TEXTS[NB_BUTTON_TYPE]; + + bool hasButton() const { return _flags.server && _flags.first; } + void setMessage(const QString &label, ButtonType); + void hideMessage() { setMessage(QString::null, NB_BUTTON_TYPE); } +}; + +#endif diff --git a/libksirtet/base/gtetris.cpp b/libksirtet/base/gtetris.cpp new file mode 100644 index 00000000..2141f9ef --- /dev/null +++ b/libksirtet/base/gtetris.cpp @@ -0,0 +1,241 @@ +#include "gtetris.h" + +#include "piece.h" + + +using namespace KGrid2D; + +GenericTetris::GenericTetris(uint width, uint height, bool withPieces, + bool graphic) + : _nextPiece(0), _currentPiece(0), _score(0), _nbRemoved(0), + _nbClearLines(height), _main(0), + _graphic(graphic), _matrix(width, height) +{ + if (withPieces) { + _nextPiece = new Piece; + _currentPiece = new Piece; + } + _matrix.fill(0); +} + +void GenericTetris::copy(const GenericTetris &g) +{ + Q_ASSERT(_currentPiece); + // copy to non graphic + _score = g._score; + _level = g._level; + _nbRemoved = g._nbRemoved; + _nbClearLines = g._nbClearLines; + _currentPos = g._currentPos; + _nextPiece->copy(g._nextPiece); + _currentPiece->copy(g._currentPiece); + for (uint i=0; i<_matrix.size(); i++) { + Coord c = _matrix.coord(i); + delete _matrix[c]; + if ( g._matrix[c] ) _matrix[c] = new Block(g._matrix[c]->value()); + else _matrix[c] = 0; + } +} + +void GenericTetris::clear() +{ + _currentPos = Coord(0, -1); + for (uint i=0; i<_matrix.size(); i++) removeBlock(_matrix.coord(i)); + computeInfos(); +} + +GenericTetris::~GenericTetris() +{ + // everything should already be done by setBlockInfo(0, 0); +} + +void GenericTetris::setBlockInfo(BlockInfo *main, BlockInfo *next) +{ + Q_ASSERT( _graphic ); + if (main) { + _main = main; + if (_currentPiece) { + Q_ASSERT(next); + _nextPiece->setBlockInfo(next); + _currentPiece->setBlockInfo(main); + } + } else { // before destruction + clear(); + delete _currentPiece; + delete _nextPiece; + } +} + +void GenericTetris::start(const GTInitData &data) +{ + Q_ASSERT( _graphic ); + _random.setSeed(data.seed); + _initLevel = data.initLevel; + updateScore(0); + updateLevel(_initLevel); + updateRemoved(0); + clear(); + if (_nextPiece) { + _nextPiece->setRandomSequence(&_random); + _nextPiece->generateNext(); + newPiece(); + } +} + +void GenericTetris::dropDown() +{ + uint dropHeight = moveTo(Coord(0, -_currentPos.second)); + pieceDropped(dropHeight); +} + +void GenericTetris::oneLineDown() +{ + if ( moveTo(Coord(0, -1))==0 ) pieceDropped(0); +} + +bool GenericTetris::newPiece() +{ + Q_ASSERT(_currentPiece); + Coord min = _nextPiece->min(); + _currentPos.second = _matrix.height() - 1 + min.second; + _currentPos.first = (_matrix.width() - _nextPiece->size().first)/2 + - min.first; + if ( !canPosition(_currentPos, _nextPiece)) { + _currentPos.second = -1; + return false; + } + _currentPiece->copy(_nextPiece); + if (_graphic) { + _currentPiece->move(toPoint(_currentPos)); + _currentPiece->show(true); + updatePieceConfig(); + } + _nextPiece->generateNext(); + if (_graphic) { + _nextPiece->moveCenter(); + _nextPiece->show(true); + updateNextPiece(); + } + return true; +} + +bool GenericTetris::canPosition(const Coord &pos, const Piece *piece) const +{ + for(uint k=0; knbBlocks(); k++) { + Coord c(piece->pos(k, pos)); + if ( !_matrix.inside(c) || _matrix[c]!=0 ) + return false; // outside or something in the way + } + return true; +} + +uint GenericTetris::moveTo(const Coord &dec) +{ + Q_ASSERT(_currentPiece); + Q_ASSERT(dec.first==0 || dec.second==0); + + Coord newPos = _currentPos; + Coord d(0, 0); + uint n, i; + + if (dec.first) { + d.first = (dec.first<0 ? -1 : 1); + n = kAbs(dec.first); + } else { + d.second = (dec.second<0 ? -1 : 1); + n = kAbs(dec.second); + } + + for (i=0; imove(toPoint(newPos)); + updatePieceConfig(); + } + } + return i; +} + +bool GenericTetris::rotate(bool left) +{ + Q_ASSERT(_currentPiece); + + Piece tmp; + tmp.copy(_currentPiece); + QPoint p(0, 0); + tmp.rotate(left, p); + if ( canPosition(_currentPos, &tmp) ) { + if (_graphic) p = toPoint(_currentPos); + _currentPiece->rotate(left, p); + if (_graphic) updatePieceConfig(); + return true; + } + return false; +} + +void GenericTetris::computeInfos() +{ + _nbClearLines = 0; + for (uint j=_matrix.height(); j>0; j--) { + for (uint i=0; i<_matrix.width(); i++) + if ( _matrix[Coord(i, j-1)]!=0 ) return; + _nbClearLines++; + } +} + +void GenericTetris::setBlock(const Coord &c, Block *b) +{ + Q_ASSERT( b && _matrix[c]==0 ); + _matrix[c] = b; + if (_graphic) { + QPoint p = toPoint(c); + b->sprite()->move(p.x(), p.y()); + } +} + +void GenericTetris::removeBlock(const Coord &c) +{ + delete _matrix[c]; + _matrix[c] = 0; +} + +void GenericTetris::moveBlock(const Coord &src, const Coord &dest) +{ + Q_ASSERT( _matrix[dest]==0 ); + if ( _matrix[src] ) { + setBlock(dest, _matrix[src]); + _matrix[src] = 0; + } +} + +QPoint GenericTetris::toPoint(const Coord &c) const +{ + return _main->toPoint(Coord(c.first, _matrix.height() - 1 - c.second)); +} + +void GenericTetris::gluePiece() +{ + Q_ASSERT(_currentPiece); + + for(uint k=0; k<_currentPiece->nbBlocks(); k++) + setBlock(_currentPiece->pos(k, _currentPos), + _currentPiece->takeBlock(k)); + computeInfos(); +} + +void GenericTetris::bumpCurrentPiece(int dec) +{ + Q_ASSERT( _graphic && _currentPiece ); + _currentPiece->move(toPoint(_currentPos) + QPoint(0, dec)); +} + +void GenericTetris::partialMoveBlock(const Coord &c, const QPoint &dec) +{ + Q_ASSERT( _graphic && _matrix[c]!=0 ); + QPoint p = toPoint(c) + dec; + _matrix[c]->sprite()->move(p.x(), p.y()); +} diff --git a/libksirtet/base/gtetris.h b/libksirtet/base/gtetris.h new file mode 100644 index 00000000..48aefb9d --- /dev/null +++ b/libksirtet/base/gtetris.h @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Definition of GenericTetris, a generic class for implementing Tetris. +** +** Author : Eirik Eng +** Created : 940126 +** Modified by Nicolas Hadacek +** +** Copyright (C) 1994 by Eirik Eng. All rights reserved. +** +**--------------------------------------------------------------------------- +** +** The GenericTetris class is an abstract class that can be used to implement +** the well known game of Tetris. It is totally independent of any hardware +** platform or user interface mechanism. It has no notion of time, so you +** have to supply the timer ticks (or heartbeat) of the game. Apart from +** that this is a complete Tetris implementation. +** +** In the following it is assumed that the reader is familiar with the game +** of Tetris. +** +** The class operates on a grid of squares (referred to as the "board" below), +** where each of the different types of pieces in Tetris covers 4 squares. The +** width and height of the board can be specified in the constructor (default +** is 10x22). The coordinate (0,0) is at the TOP LEFT corner. The class +** assumes that it has total control over the board and uses this to optimize +** drawing of the board. If you need to update parts of the board +** (e.g. if you are using a window system), use the updateBoard() function. +** +** An implementation of the game must subclass from TetrisBoard and must +** implement these abstract functions: +** +** virtual void drawSquare(int x,int y,int value) +** +** This function is called when a square needs to be drawn +** on the Tetris board. A value of 0 means that the square +** should be erased. Values of 1 to 7 indicate the different +** types of pieces. (Thus a minimal implementation can +** draw 0 in one way and 1-7 in a second way). The x and y +** values are coordinates on the Tetris board (see above). +** +** virtual void gameOver() +** +** This function is called when it is impossible to put a new +** piece at the top of the board. +** +** To get a working minimal implementation of Tetris the following functions +** must be called from the user interface: +** +** void startGame() +** +** Clears the board and starts a new game. +** +** void moveLeft() +** void moveRight() +** void rotateLeft() +** void rotateRight() +** +** The standard Tetris controls for moving and rotating the +** falling pieces. +** +** void dropDown(); +** +** Another Tetris control, drops the falling piece and calls the +** virtual function pieceDropped(), whose default implementation is +** to create a new block which appears at the top of the board. +** +** void oneLineDown(); +** +** This is where you supply the timer ticks, or heartbeat, of the +** game. This function moves the current falling piece one line down +** on the board, or, if that cannot be done, calls the virtual +** function pieceDropped() (see dropDown() above). The time between +** each call to this function directly affects the difficulty of the +** game. If you want to pause the game, simply block calls to the +** user control functions above and stop calling this function (you +** might want to call hideBoard() also). +** +** And that's it! There are several other public functions you can call +** and virtual functions you can overload to modify or extend the game. +** +** Do whatever you want with this code (i.e. the files gtetris.h, +** gtetris.cpp, tpiece.h and tpiece.cpp). It is basically a weekend hack +** and it would bring joy to my heart if anyone in any way would find +** it useful. +** +** Nostalgia, comments and/or praise can be sent to: Eirik.Eng@troll.no +** +****************************************************************************/ + +#ifndef GTETRIS_H +#define GTETRIS_H + +#include + +#include +#include + +#include + +class Piece; +class BlockInfo; +class Block; + +struct GTInitData { + int seed; + uint initLevel; +}; + +class KDE_EXPORT GenericTetris +{ + public: + GenericTetris(uint width, uint height, bool withPieces, bool graphic); + virtual ~GenericTetris(); + virtual void copy(const GenericTetris &); + + void setBlockInfo(BlockInfo *main, BlockInfo *next); + virtual void start(const GTInitData &); + + uint moveLeft(int steps = 1) { return moveTo(KGrid2D::Coord(-steps, 0)); } + uint moveRight(int steps = 1) { return moveTo(KGrid2D::Coord(steps, 0)); } + bool rotateLeft() { return rotate(true); } + bool rotateRight() { return rotate(false); } + virtual void oneLineDown(); + virtual void dropDown(); + + KRandomSequence &randomSequence() { return _random; } + uint score() const { return _score; } + uint level() const { return _level; } + uint nbClearLines() const { return _nbClearLines; } + uint nbRemoved() const { return _nbRemoved; } + bool graphic() const { return _graphic; } + uint firstClearLine() const { return _matrix.height() - _nbClearLines; } + const KGrid2D::Coord ¤tPos() const { return _currentPos; } + const Piece *nextPiece() const { return _nextPiece; } + const Piece *currentPiece() const { return _currentPiece; } + const KGrid2D::Square &matrix() const { return _matrix; } + + protected: + Piece *_nextPiece, *_currentPiece; + + virtual void pieceDropped(uint /*dropHeight*/) {} + virtual bool newPiece(); // return false if cannot place new piece + virtual void gluePiece(); + virtual void computeInfos(); + + virtual void updateRemoved(uint newNbRemoved) { _nbRemoved = newNbRemoved;} + virtual void updateScore(uint newScore) { _score = newScore; } + virtual void updateLevel(uint newLevel) { _level = newLevel; } + + void setBlock(const KGrid2D::Coord &, Block *); + virtual void removeBlock(const KGrid2D::Coord &); + void moveBlock(const KGrid2D::Coord &src, const KGrid2D::Coord &dest); + + virtual void updateNextPiece() {} + virtual void updatePieceConfig() {} + void bumpCurrentPiece(int dec); + void partialMoveBlock(const KGrid2D::Coord &, const QPoint &dec); + + private: + QPoint toPoint(const KGrid2D::Coord &) const; + uint moveTo(const KGrid2D::Coord &dec); + bool rotate(bool left); + void clear(); + bool canPosition(const KGrid2D::Coord &newPos, const Piece *newPiece) const; + + GenericTetris(const GenericTetris &); // disabled + + uint _score, _level, _nbRemoved; + uint _nbClearLines, _initLevel; + KGrid2D::Coord _currentPos; + BlockInfo *_main; + bool _graphic; + KGrid2D::Square _matrix; + KRandomSequence _random; +}; + +#endif diff --git a/libksirtet/base/highscores.cpp b/libksirtet/base/highscores.cpp new file mode 100644 index 00000000..2b3596d7 --- /dev/null +++ b/libksirtet/base/highscores.cpp @@ -0,0 +1,19 @@ +#include "highscores.h" + +#include + +#include "factory.h" + + +using namespace KExtHighscore; + +BaseHighscores::BaseHighscores() +{ + setWWHighscores(KURL( bfactory->mainData.homepage ), bfactory->mainData.version); + const BaseBoardInfo &bi = bfactory->bbi; + if ( bi.histogramSize!=0 ) { + QMemArray a; + a.duplicate(bi.histogram, bi.histogramSize); + setScoreHistogram(a, bi.scoreBound ? ScoreBound : ScoreNotBound); + } +} diff --git a/libksirtet/base/highscores.h b/libksirtet/base/highscores.h new file mode 100644 index 00000000..b3e7b99e --- /dev/null +++ b/libksirtet/base/highscores.h @@ -0,0 +1,13 @@ +#ifndef BASE_HIGHSCORES_H +#define BASE_HIGHSCORES_H + +#include +#include + +class KDE_EXPORT BaseHighscores : public KExtHighscore::Manager +{ + public: + BaseHighscores(); +}; + +#endif diff --git a/libksirtet/base/inter.cpp b/libksirtet/base/inter.cpp new file mode 100644 index 00000000..4f40b63f --- /dev/null +++ b/libksirtet/base/inter.cpp @@ -0,0 +1,15 @@ +#include "inter.h" + +#include + + +void BaseInterface::showHighscores(QWidget *parent) +{ + if ( !_isPaused() ) _pause(); + _showHighscores(parent); +} + +void BaseInterface::_showHighscores(QWidget *parent) +{ + KExtHighscore::show(parent); +} diff --git a/libksirtet/base/inter.h b/libksirtet/base/inter.h new file mode 100644 index 00000000..446a77d3 --- /dev/null +++ b/libksirtet/base/inter.h @@ -0,0 +1,23 @@ +#ifndef BASE_INTER_H +#define BASE_INTER_H + +class QWidget; + + +class BaseInterface +{ +public: + BaseInterface() {} + virtual ~BaseInterface() {} + + virtual void _start() = 0; + virtual void _pause() = 0; + virtual bool _isPaused() const = 0; + + void showHighscores(QWidget *parent); + +protected: + virtual void _showHighscores(QWidget *parent); +}; + +#endif diff --git a/libksirtet/base/kzoommainwindow.cpp b/libksirtet/base/kzoommainwindow.cpp new file mode 100644 index 00000000..115d5175 --- /dev/null +++ b/libksirtet/base/kzoommainwindow.cpp @@ -0,0 +1,115 @@ +/* + This file is part of the KDE games library + Copyright (C) 2004 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kzoommainwindow.h" +#include "kzoommainwindow.moc" + +#include +#include +#include +#include + +KZoomMainWindow::KZoomMainWindow(uint min, uint max, uint step, const char *name) + : KMainWindow(0, name), _zoomStep(step), _minZoom(min), _maxZoom(max) +{ + installEventFilter(this); + + _zoomInAction = KStdAction::zoomIn(this, SLOT(zoomIn()), actionCollection()); + _zoomOutAction = + KStdAction::zoomOut(this, SLOT(zoomOut()), actionCollection()); + _menu = + KStdAction::showMenubar(this, SLOT(toggleMenubar()), actionCollection()); +} + +void KZoomMainWindow::init(const char *popupName) +{ + // zoom + setZoom(readZoomSetting()); + + // menubar + _menu->setChecked( menubarVisibleSetting() ); + toggleMenubar(); + + // context popup + if (popupName) { + QPopupMenu *popup = + static_cast(factory()->container(popupName, this)); + Q_ASSERT(popup); + if (popup) KContextMenuManager::insert(this, popup); + } +} + +void KZoomMainWindow::addWidget(QWidget *widget) +{ + widget->adjustSize(); + QWidget *tlw = widget->topLevelWidget(); + KZoomMainWindow *zm = + static_cast(tlw->qt_cast("KZoomMainWindow")); + Q_ASSERT(zm); + zm->_widgets.append(widget); + connect(widget, SIGNAL(destroyed()), zm, SLOT(widgetDestroyed())); +} + +void KZoomMainWindow::widgetDestroyed() +{ + _widgets.remove(static_cast(sender())); +} + +bool KZoomMainWindow::eventFilter(QObject *o, QEvent *e) +{ + if ( e->type()==QEvent::LayoutHint ) + setFixedSize(minimumSize()); // because K/QMainWindow + // does not manage fixed central widget + // with hidden menubar... + return KMainWindow::eventFilter(o, e); +} + +void KZoomMainWindow::setZoom(uint zoom) +{ + _zoom = zoom; + writeZoomSetting(_zoom); + QPtrListIterator it(_widgets); + for (; it.current(); ++it) + (*it)->adjustSize();; + _zoomOutAction->setEnabled( _zoom>_minZoom ); + _zoomInAction->setEnabled( _zoom<_maxZoom ); +} + +void KZoomMainWindow::zoomIn() +{ + setZoom(_zoom + _zoomStep); +} + +void KZoomMainWindow::zoomOut() +{ + Q_ASSERT( _zoom>=_zoomStep ); + setZoom(_zoom - _zoomStep); +} + +void KZoomMainWindow::toggleMenubar() +{ + if ( _menu->isChecked() ) menuBar()->show(); + else menuBar()->hide(); +} + +bool KZoomMainWindow::queryExit() +{ + writeMenubarVisibleSetting(_menu->isChecked()); + return KMainWindow::queryExit(); +} diff --git a/libksirtet/base/kzoommainwindow.h b/libksirtet/base/kzoommainwindow.h new file mode 100644 index 00000000..14f780fb --- /dev/null +++ b/libksirtet/base/kzoommainwindow.h @@ -0,0 +1,128 @@ +/* + This file is part of the KDE games library + Copyright (C) 2004 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KZOOMMAINWINDOW_H +#define KZOOMMAINWINDOW_H + +#include +#include + + +class KToggleAction; + +/** + * KZoomMainWindow is a main window of fixed size. Its size can be + * modified with the "zoom in"/"zoom out" actions. + * + * It manages one or several widgets: their adjustSize() method is + * called whenever the zoom level is changed. + * The usual implementation for those widget is to redefine adjustSize() + * with code like: + * /code + * setFixedSize(newsize); + * /endcode + * + * This class also has a "show/hide menubar" action and allows the use + * of a context popup menu (useful to restore the menubar when hidden). + */ +class KDE_EXPORT KZoomMainWindow : public KMainWindow +{ + Q_OBJECT +public: + /** Constructor. */ + KZoomMainWindow(uint minZoom, uint maxZoom, uint zoomStep, + const char *name = 0); + + /** Add a widget to be managed i.e. the adjustSize() method of the + * widget is called whenever the zoom is changed. + * This function assumes that the topLevelWidget() is the KZoomMainWindow. + */ + static void addWidget(QWidget *widget); + + uint zoom() const { return _zoom; } + +public slots: + void zoomIn(); + void zoomOut(); + void toggleMenubar(); + +protected: + /** You need to call this after the createGUI or setupGUI method + * is called. + * @param popupName is the name of the context popup menu as defined in + * the ui.rc file. + */ + void init(const char *popupName = 0); + + virtual void setZoom(uint zoom); + virtual bool eventFilter(QObject *o, QEvent *e); + virtual bool queryExit(); + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::setZoom(zoom); + * Settings::writeConfig(); + * /endcode + */ + virtual void writeZoomSetting(uint zoom) = 0; + + /** Youneed to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * return Settings::zoom(); + * /endcode + */ + virtual uint readZoomSetting() const = 0; + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::setMenubarVisible(visible); + * Settings::writeConfig(); + * /endcode + */ + virtual void writeMenubarVisibleSetting(bool visible) = 0; + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::menubarVisible(); + * /endcode + */ + virtual bool menubarVisibleSetting() const = 0; + +private slots: + void widgetDestroyed(); + +private: + uint _zoom, _zoomStep, _minZoom, _maxZoom; + QPtrList _widgets; + KAction *_zoomInAction, *_zoomOutAction; + KToggleAction *_menu; + + class KZoomMainWindowPrivate; + KZoomMainWindowPrivate *d; +}; + +#endif diff --git a/libksirtet/base/libksirtet1.kcfg b/libksirtet/base/libksirtet1.kcfg new file mode 100644 index 00000000..684de57f --- /dev/null +++ b/libksirtet/base/libksirtet1.kcfg @@ -0,0 +1,38 @@ + + + factory.h + piece.h + + + + 4 + 100 + 15 + + + + 1.0 + + + + black + + + + true + + + + true + + + + + + Piece::info().defaultColor($(Number)) + + + diff --git a/libksirtet/base/main.cpp b/libksirtet/base/main.cpp new file mode 100644 index 00000000..4b5a4160 --- /dev/null +++ b/libksirtet/base/main.cpp @@ -0,0 +1,131 @@ +#include "main.h" +#include "main.moc" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inter.h" +#include "factory.h" +#include "settings.h" +#include "baseprefs.h" + +BaseMainWindow::BaseMainWindow() + : KZoomMainWindow(4, 100, 1, "main_window") +{ + KNotifyClient::startDaemon(); + + // File & Popup + KStdGameAction::gameNew(this, SLOT(start()), actionCollection()); + _pause = KStdGameAction::pause(this, SLOT(pause()), actionCollection()); + _pause->setEnabled(false); + KStdGameAction::highscores(this, SLOT(showHighscores()), + actionCollection()); + KStdGameAction::quit(qApp, SLOT(quit()), actionCollection()); + + // Settings + KStdAction::preferences(this, SLOT(configureSettings()), + actionCollection()); + KStdAction::keyBindings(this, SLOT(configureKeys()), actionCollection()); + KStdAction::configureNotifications(this, SLOT(configureNotifications()), + actionCollection()); + KStdGameAction::configureHighscores(this, SLOT(configureHighscores()), + actionCollection()); + + _inter = bfactory->createInterface(this); +} + +void BaseMainWindow::buildGUI(QWidget *widget) +{ + createGUI(); + setCentralWidget(widget); + init("popup"); +} + +BaseMainWindow::~BaseMainWindow() +{ + delete _inter; +} + +void BaseMainWindow::showHighscores() +{ + _inter->showHighscores(this); +} + +void BaseMainWindow::start() +{ + _inter->_start(); +} + +void BaseMainWindow::pause() +{ + _inter->_pause(); +} + +void BaseMainWindow::configureHighscores() +{ + KExtHighscore::configure(this); +} + +void BaseMainWindow::configureSettings() +{ + if ( !_inter->_isPaused() ) _inter->_pause(); + if ( KConfigDialog::showDialog("settings") ) return; + + KConfigDialog *dialog = new KConfigDialog(this, "settings", BasePrefs::self() ); + QWidget *w = bfactory->createGameConfig(); + if (w) dialog->addPage(w, i18n("Game"), "package_system"); + w = bfactory->createAppearanceConfig(); + if (w) dialog->addPage(w, i18n("Appearance"), "style"); + w = bfactory->createColorConfig(); + if (w) dialog->addPage(w, i18n("Colors"), "colorize"); +// dialog->addPage(new BackgroundConfigWidget, i18n("Background"), "background"); + addConfig(dialog); + connect(dialog, SIGNAL(settingsChanged()), SIGNAL(settingsChanged())); + dialog->show(); +} + +void BaseMainWindow::configureKeys() +{ + KKeyDialog d(true, this); + addKeys(d); + d.insert(actionCollection()); + d.configure(false); + actionCollection()->writeShortcutSettings(); + saveKeys(); +} + +void BaseMainWindow::configureNotifications() +{ + KNotifyDialog::configure(this); +} + +void BaseMainWindow::writeZoomSetting(uint zoom) +{ + BasePrefs::setBlockSize(zoom); + BasePrefs::writeConfig(); +} + +uint BaseMainWindow::readZoomSetting() const +{ + return BasePrefs::blockSize(); +} + +void BaseMainWindow::writeMenubarVisibleSetting(bool visible) +{ + BasePrefs::setMenubarVisible(visible); + BasePrefs::writeConfig(); +} + +bool BaseMainWindow::menubarVisibleSetting() const +{ + return BasePrefs::menubarVisible(); +} diff --git a/libksirtet/base/main.h b/libksirtet/base/main.h new file mode 100644 index 00000000..d2e108aa --- /dev/null +++ b/libksirtet/base/main.h @@ -0,0 +1,49 @@ +#ifndef BASE_MAIN_H +#define BASE_MAIN_H + +#include "kzoommainwindow.h" + +#include + +class BaseInterface; +class KToggleAction; +class KKeyDialog; +class KConfigDialog; + +class KDE_EXPORT BaseMainWindow : public KZoomMainWindow +{ + Q_OBJECT +public: + BaseMainWindow(); + virtual ~BaseMainWindow(); + +signals: + void settingsChanged(); + +private slots: + void start(); + void pause(); + void showHighscores(); + void configureSettings(); + void configureKeys(); + void configureHighscores(); + void configureNotifications(); + +protected: + BaseInterface *_inter; + + void buildGUI(QWidget *widget); + virtual void addConfig(KConfigDialog *) {} + virtual void addKeys(KKeyDialog &) {} + virtual void saveKeys() {} + + virtual void writeZoomSetting(uint zoom); + virtual uint readZoomSetting() const; + virtual void writeMenubarVisibleSetting(bool visible); + virtual bool menubarVisibleSetting() const; + +private: + KToggleAction *_pause; +}; + +#endif diff --git a/libksirtet/base/piece.cpp b/libksirtet/base/piece.cpp new file mode 100644 index 00000000..25aed934 --- /dev/null +++ b/libksirtet/base/piece.cpp @@ -0,0 +1,274 @@ +#include "piece.h" + +#include +#include + +#include "baseprefs.h" + +using namespace KGrid2D; + +QPoint operator *(const Coord &c, int i) +{ + return QPoint(c.first * i, c.second * i); +} + +//----------------------------------------------------------------------------- +GPieceInfo::GPieceInfo() +{ + Piece::setPieceInfo(this); +} + +QPixmap *GPieceInfo::pixmap(uint blockSize, uint blockType, uint blockMode, + bool lighted) const +{ + QPixmap *pixmap = new QPixmap(blockSize, blockSize); + draw(pixmap, blockType, blockMode, lighted); + setMask(pixmap, blockMode); + return pixmap; +} + +Coord GPieceInfo::maxSize() const +{ + Coord min, max; + Coord size(0, 0); + for (uint n=0; ngetLong( nbTypes() ); +} + +uint GPieceInfo::generateGarbageBlockType(KRandomSequence *random) const +{ + return nbNormalBlockTypes() + random->getLong( nbGarbageBlockTypes() ); +} + +void GPieceInfo::loadColors() +{ + _colors.resize(nbColors()); + for (uint i=0; i<_colors.size(); i++) + _colors[i] = BasePrefs::color(i); +} + + +//----------------------------------------------------------------------------- +SequenceArray::SequenceArray() +: _size(0) +{ + const GPieceInfo &pinfo = Piece::info(); + fill(0, pinfo.nbNormalBlockTypes() + pinfo.nbGarbageBlockTypes()); +} + +void SequenceArray::setBlockSize(uint bsize) +{ + _size = bsize; + const GPieceInfo &pinfo = Piece::info(); + QPtrList pixmaps; + pixmaps.setAutoDelete(TRUE); + QPtrList points; + points.setAutoDelete(TRUE); + uint nm = pinfo.nbBlockModes(); + for (uint i=0; isetImage(k*nm + j, new QCanvasPixmap(*pi, *po)); + delete po; + delete pi; + } else { + points.append(po); + pixmaps.append(pi); + } + } + if ( at(i)==0 ) { + at(i) = new QCanvasPixmapArray(pixmaps, points); + pixmaps.clear(); + points.clear(); + } + } +} + +SequenceArray::~SequenceArray() +{ + for (uint i=0; isequences()[value]; + if (_sprite) _sprite->setSequence(seq); + else { + _sprite = new QCanvasSprite(seq, binfo); + _sprite->setZ(0); + } + } +} + +void Block::toggleLight() +{ + const GPieceInfo &pinfo = Piece::info(); + uint f = _sprite->frame() + pinfo.nbBlockModes() + * (_sprite->frame()>=(int)pinfo.nbBlockModes() ? -1 : 1); + _sprite->setFrame(f); +} + +bool Block::isGarbage() const +{ + return Piece::info().isGarbage(_value); +} + + +//----------------------------------------------------------------------------- +GPieceInfo *Piece::_info = 0; + +Piece::Piece() + : _binfo(0), _i(0), _j(0) +{ + _blocks.setAutoDelete(true); +} + +void Piece::rotate(bool left, const QPoint &p) +{ + if (left) { + if ( _rotation==0 ) _rotation = 3; + else _rotation--; + } else { + if ( _rotation==3 ) _rotation = 0; + else _rotation++; + } + + uint form = _info->form(_type); + _i = _info->i(form, _rotation); + _j = _info->j(form, _rotation); + if (_binfo) move(p); +} + +Coord Piece::min() const +{ + if ( _i==0 || _j==0 ) return Coord(0, 0); + Coord min = coord(0); + for(uint k=1; k<_info->nbBlocks(); k++) + min = minimum(min, coord(k)); + return min; +} + +Coord Piece::max() const +{ + if ( _i==0 || _j==0 ) return Coord(0, 0); + Coord max = coord(0); + for(uint k=1; k<_info->nbBlocks(); k++) + max = maximum(max, coord(k)); + return max; +} + +void Piece::copy(const Piece *p) +{ + if ( p->_blocks.size()!=0 ) { + _blocks.resize(p->_blocks.size()); + for (uint k=0; k<_blocks.size(); k++) { + if ( _blocks[k]==0 ) _blocks.insert(k, new Block); + _blocks[k]->setValue(p->_blocks[k]->value(), _binfo); + } + } + _type = p->_type; + _random = p->_random; + _rotation = p->_rotation; + _i = p->_i; + _j = p->_j; +} + +void Piece::generateNext(int type) +{ + if ( _blocks.size()==0 ) { + _blocks.resize(_info->nbBlocks()); + for (uint k=0; k<_blocks.size(); k++) _blocks.insert(k, new Block); + } + _type = (type==-1 ? _info->generateType(_random) : (uint)type ); + _rotation = 0; + + uint form = _info->form(_type); + _i = _info->i(form, _rotation); + _j = _info->j(form, _rotation); + + for (uint k=0; k<_blocks.size(); k++) + _blocks[k]->setValue(_info->value(_type, k), _binfo); +} + +void Piece::moveCenter() +{ + uint s = _binfo->sequences().blockSize(); + QPoint p = QPoint(_binfo->width(), _binfo->height()) - size() * s; + move(p/2 - min() * s); +} + +Coord Piece::pos(uint k, const Coord &pos) const +{ + return Coord(pos.first + coord(k).first, pos.second - coord(k).second); +} + +void Piece::move(const QPoint &p) +{ + for (uint k=0; k<_blocks.size(); k++) moveBlock(k, p); +} + +void Piece::moveBlock(uint k, const QPoint &p) +{ + QPoint po = p + _binfo->toPoint(coord(k)); + _blocks[k]->sprite()->move(po.x(), po.y()); +} + +Block *Piece::garbageBlock() const +{ + Block *b = new Block; + b->setValue(_info->generateGarbageBlockType(_random), _binfo); + return b; +} + +Block *Piece::takeBlock(uint k) +{ + Block *b = _blocks.take(k); + _blocks.insert(k, new Block); + return b; +} + +void Piece::show(bool show) +{ + for (uint k=0; k<_blocks.size(); k++) { + if (show) _blocks[k]->sprite()->show(); + else _blocks[k]->sprite()->hide(); + } +} diff --git a/libksirtet/base/piece.h b/libksirtet/base/piece.h new file mode 100644 index 00000000..4c0486a8 --- /dev/null +++ b/libksirtet/base/piece.h @@ -0,0 +1,155 @@ +#ifndef BASE_PIECE_H +#define BASE_PIECE_H + +#include +#include + +#include + + +class KRandomSequence; + +//----------------------------------------------------------------------------- +class GPieceInfo +{ + public: + GPieceInfo(); + virtual ~GPieceInfo() {} + + virtual uint nbBlocks() const = 0; // nb of blocks in a piece + virtual uint nbTypes() const = 0; // nb of combin. of types in piece + virtual uint nbForms() const = 0; // nb of geometrical form of piece + + virtual const int *i(uint form, uint rotation) const = 0; + virtual const int *j(uint form, uint rotation) const = 0; + virtual uint value(uint type, uint n) const = 0; + virtual uint form(uint type) const = 0; + virtual uint nbConfigurations(uint type) const = 0; + uint generateType(KRandomSequence *) const; + + KGrid2D::Coord maxSize() const; + + QPixmap *pixmap(uint blockSize, uint blockType, uint blockMode, + bool lighted) const; + + virtual uint nbNormalBlockTypes() const = 0; + virtual uint nbGarbageBlockTypes() const = 0; + virtual uint nbBlockModes() const = 0; // nb of modes per block + bool isGarbage(uint type) const { return type>=nbNormalBlockTypes(); } + uint generateGarbageBlockType(KRandomSequence *) const; + + virtual uint nbColors() const = 0; + virtual QString colorLabel(uint i) const = 0; + QCString colorKey(uint i) const; + virtual QColor defaultColor(uint i) const = 0; + void loadColors(); + + protected: + QColor color(uint i) const { return _colors[i]; } + + virtual void draw(QPixmap *, uint blockType, uint blockMode, + bool lighted) const = 0; + virtual void setMask(QPixmap *, uint /*blockMode*/) const {} + + private: + QValueVector _colors; +}; + +class SequenceArray : public QMemArray +{ + public: + SequenceArray(); + ~SequenceArray(); + + void setBlockSize(uint size); + uint blockSize() const { return _size; } + + private: + uint _size; +}; + +//----------------------------------------------------------------------------- +class BlockInfo : public QCanvas +{ + public: + BlockInfo(const SequenceArray &); + const SequenceArray &sequences() const { return _sequences; } + + QPoint toPoint(const KGrid2D::Coord &) const; + + private: + const SequenceArray &_sequences; +}; + +//----------------------------------------------------------------------------- +class Block +{ + public: + Block(uint value = 0); + ~Block(); + + void setValue(uint, BlockInfo *); + uint value() const { return _value; } + bool isGarbage() const; + void toggleLight(); + QCanvasSprite *sprite() const { return _sprite; } + + private: + uint _value; + QCanvasSprite *_sprite; + + Block(const Block &); // disabled + Block &operator =(const Block &); // disabled +}; + +//----------------------------------------------------------------------------- +class Piece +{ + public: + Piece(); + + void copy(const Piece *); + void setBlockInfo(BlockInfo *bi) { _binfo = bi; } + static void setPieceInfo(GPieceInfo *pi) { _info = pi; } + static GPieceInfo &info() { return *_info; } + + uint type() const { return _type; } + uint nbBlocks() const { return _blocks.size(); } + uint nbConfigurations() const { return _info->nbConfigurations(_type); } + + int value(uint k) const { return _blocks[k]->value(); } + KGrid2D::Coord pos(uint k, const KGrid2D::Coord &) const; + + KGrid2D::Coord min() const; + KGrid2D::Coord max() const; + KGrid2D::Coord size() const { return max() - min() + KGrid2D::Coord(1, 1); } + + void generateNext(int type = -1); + void rotate(bool left, const QPoint &); + void move(const QPoint &); + void moveCenter(); + void show(bool show); + + void setRandomSequence(KRandomSequence *random) { _random = random; } + + Block *garbageBlock() const; + Block *takeBlock(uint k); + + private: + QPtrVector _blocks; + uint _type; + KRandomSequence *_random; + static GPieceInfo *_info; + BlockInfo *_binfo; + uint _rotation; + int const *_i; + int const *_j; + + Piece(const Piece &); // disabled + Piece &operator =(const Piece &); // disabled + + KGrid2D::Coord coord(uint k) const { return KGrid2D::Coord(_i[k], _j[k]); } + void moveBlock(uint k, const QPoint &); +}; + +#endif diff --git a/libksirtet/base/settings.cpp b/libksirtet/base/settings.cpp new file mode 100644 index 00000000..1ea3b16a --- /dev/null +++ b/libksirtet/base/settings.cpp @@ -0,0 +1,84 @@ +#include "settings.h" +#include "settings.moc" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "piece.h" +#include "factory.h" + + +//----------------------------------------------------------------------------- +BaseAppearanceConfig::BaseAppearanceConfig() + : QWidget(0, "appearance_config") +{ + QVBoxLayout *top = new QVBoxLayout(this); + + // upper part + _main = new QWidget(this); + top->addWidget(_main); + _grid = new QGridLayout(_main, 3, 2, 0, KDialog::spacingHint()); + _grid->setColStretch(1, 1); + + QCheckBox *chb = + new QCheckBox(i18n("Enable animations"), _main, "kcfg_AnimationsEnabled"); + _grid->addMultiCellWidget(chb, 2, 2, 0, 1); + + top->addSpacing(KDialog::spacingHint()); + + // lower part + QHGroupBox *gbox = new QHGroupBox(i18n("Background"), this); + top->addWidget(gbox); + QWidget *widget = new QWidget(gbox); + QGridLayout *grid = + new QGridLayout(widget, 2, 3, 0, KDialog::spacingHint()); + grid->setColStretch(2, 1); + QLabel *label = new QLabel(i18n("Color:"), widget); + grid->addWidget(label, 0, 0); + KColorButton *cob = new KColorButton(widget, "kcfg_FadeColor"); + cob->setFixedWidth(100); + grid->addWidget(cob, 0, 1); + label = new QLabel(i18n("Opacity:"), widget); + grid->addWidget(label, 1, 0); + KDoubleNumInput *dn = new KDoubleNumInput(widget, "kcfg_FadeIntensity"); + dn->setRange(0.0, 1.0, 0.01); + grid->addMultiCellWidget(dn, 1, 1, 1, 2); + + top->addStretch(1); +} + +//----------------------------------------------------------------------------- +ColorConfig::ColorConfig() + : QWidget(0, "color_config") +{ + const GPieceInfo &info = Piece::info(); + QVBoxLayout *top = new QVBoxLayout(this); + uint nb = info.nbColors(); + QGridLayout *grid = new QGridLayout(top, nb+1, 3, KDialog::spacingHint()); + grid->setColStretch(2, 1); + for (uint i=0; iaddWidget(label, i, 0); + KColorButton *cob = new KColorButton(this, colorKey(i)); + cob->setFixedWidth(100); + grid->addWidget(cob, i, 1); + } + grid->setRowStretch(nb, 1); +} + +QCString ColorConfig::colorKey(uint i) +{ + QCString s; + s.setNum(i); + return "kcfg_Color" + s; +} + diff --git a/libksirtet/base/settings.h b/libksirtet/base/settings.h new file mode 100644 index 00000000..c64bfd5b --- /dev/null +++ b/libksirtet/base/settings.h @@ -0,0 +1,33 @@ +#ifndef BASE_SETTINGS_H +#define BASE_SETTINGS_H + +#include +#include + +class QGridLayout; + + +//----------------------------------------------------------------------------- +class BaseAppearanceConfig : public QWidget +{ + Q_OBJECT +public: + BaseAppearanceConfig(); + +protected: + QWidget *_main; + QGridLayout *_grid; +}; + +//----------------------------------------------------------------------------- +class ColorConfig : public QWidget +{ + Q_OBJECT +public: + ColorConfig(); + +private: + static QCString colorKey(uint i); +}; + +#endif diff --git a/libksirtet/common/Makefile.am b/libksirtet/common/Makefile.am new file mode 100644 index 00000000..1c11265d --- /dev/null +++ b/libksirtet/common/Makefile.am @@ -0,0 +1,28 @@ +INCLUDES = -I$(top_builddir)/libksirtet -I$(top_srcdir)/libksirtet -I$(srcdir)/../base -I$(top_srcdir)/libkdegames/highscore -I$(top_srcdir)/libkdegames $(all_includes) + +# Don't compile with hidden symbols since we are a library. +if disable_VISIBILITY +KDE_CXXFLAGS = -fvisibility=default +endif + +noinst_LTLIBRARIES = libksirtetcommon.la +libksirtetcommon_la_LDFLAGS = $(all_libraries) -no-undefined +libksirtetcommon_la_DEPENDENCIES = $(LIB_KDEGAMES_DEP) $(top_builddir)/libksirtet/lib/libksirtetmultiplayers.la $(top_builddir)/libksirtet/base/libksirtetbase.la +libksirtetcommon_la_LIBADD = $(LIB_KDEGAMES) $(top_builddir)/libksirtet/lib/libksirtetmultiplayers.la $(top_builddir)/libksirtet/base/libksirtetbase.la + +noinst_HEADERS = types.h factory.h misc_ui.h highscores.h \ + board.h ai.h field.h settings.h inter.h main.h +libksirtetcommon_la_SOURCES = types.cpp factory.cpp misc_ui.cpp \ + highscores.cpp \ + board.cpp ai.cpp field.cpp settings.cpp \ + inter.cpp main.cpp commonprefs.kcfgc +METASOURCES = misc_ui.moc board.moc ai.moc field.moc \ + settings.moc inter.moc main.moc + +ai.lo: ../base/baseprefs.h +board.lo: ../base/baseprefs.h +commonprefs.lo: ../base/baseprefs.h +field.lo: ../base/baseprefs.h +inter.lo: ../base/baseprefs.h +misc_ui.lo: ../base/baseprefs.h + diff --git a/libksirtet/common/README b/libksirtet/common/README new file mode 100644 index 00000000..03ffa387 --- /dev/null +++ b/libksirtet/common/README @@ -0,0 +1 @@ +This directory contains code shared between ksirtet and kfouleggs. diff --git a/libksirtet/common/ai.cpp b/libksirtet/common/ai.cpp new file mode 100644 index 00000000..bc1c6722 --- /dev/null +++ b/libksirtet/common/ai.cpp @@ -0,0 +1,356 @@ +#include "ai.h" +#include "ai.moc" + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "commonprefs.h" +#include "board.h" +#include "base/piece.h" +#include "base/factory.h" + + +//----------------------------------------------------------------------------- +AIPiece::AIPiece() + : _current(0) +{} + +AIPiece::~AIPiece() +{ + delete _current; +} + +void AIPiece::init(const Piece *piece, Board *b) +{ + _piece = piece; + _board = b; + if ( _current==0 ) _current = new Piece; + reset(); +} + +void AIPiece::reset() +{ + curPos = 0; + curRot = 0; + if (_piece) _current->copy(_piece); +// else _current->generateNext(0); + nbRot = _current->nbConfigurations() - 1; + nbPos = _board->matrix().width() - _current->size().first + 1; +} + +bool AIPiece::increment() +{ + curPos++; + if ( curPos==nbPos ) { + if ( curRot==nbRot ) { +// if ( _piece || _current->type()==Piece::info().nbTypes() ) { + reset(); + return false; +// } +// _current->generateNext(_current->type()+1); +// nbRot = _current->nbConfigurations() - 1; +// curRot = 0; + } + _current->rotate(true, QPoint(0, 0)); + nbPos = _board->matrix().width() - _current->size().first + 1; + curRot++; + curPos = 0; + } + return true; +} + +bool AIPiece::place() +{ + if ( curRot==3 ) { + if ( !_board->rotateRight() ) return false; + } else for (uint i=0; irotateLeft() ) return false; + curDec = curPos - _board->currentPos().first - _current->min().first; + if ( curDec!=0 && _board->moveRight(curDec)!=(uint)kAbs(curDec) ) + return false; + _board->dropDown(); + return !_board->isGameOver(); +} + +//----------------------------------------------------------------------------- +const AI::Data AI::LastData = { 0, 0, 0, false, 0 }; + +AI::AI(uint tTime, uint oTime, const Data *DATA) + : timer(this), thinkTime(tTime), orderTime(oTime), stopped(false), + board(0) +{ + connect(&timer, SIGNAL(timeout()), SLOT(timeout())); + + for (uint i=0; DATA[i].name; i++) { + Element element; + element.coefficient = 0.; + element.trigger = 0; + element.data = &DATA[i]; + _elements.append(element); + } + settingsChanged(); +} + +void AI::resizePieces(uint size) +{ + uint oldSize = pieces.size(); + for (uint i=size; icopy(*main); +} + +void AI::launch(Board *m) +{ + main = m; + if ( board==0 ) + board = static_cast(bfactory->createBoard(false, 0)); + + pieces[0]->init(main->currentPiece(), board); // current + if ( pieces.size()>=2 ) pieces[1]->init(main->nextPiece(), board); // next + + state = Thinking; + hasBestPoints = false; + startTimer(); +} + +void AI::stop() +{ + timer.stop(); + stopped = true; +} + +void AI::start() +{ + if (stopped) { + startTimer(); + stopped = false; + } +} + +void AI::startTimer() +{ + switch (state) { + case Thinking: timer.start(thinkTime, true); break; + case GivingOrders: timer.start(orderTime, true); break; + } +} + +void AI::timeout() +{ + switch (state) { + case Thinking: + if ( think() ) state = GivingOrders; + break; + case GivingOrders: + if ( emitOrder() ) return; + break; + } + + startTimer(); +} + +bool AI::emitOrder() +{ + if ( bestRot==3 ) { + bestRot = 0; + main->pRotateRight(); + } else if (bestRot) { + bestRot--; + main->pRotateLeft(); + } else if ( bestDec>0 ) { + bestDec--; + main->pMoveRight(); + } else if ( bestDec<0 ) { + bestDec++; + main->pMoveLeft(); + } else { + main->pDropDownStart(); + return true; + } + return false; +} + +bool AI::think() +{ + initThink(); + bool moveOk = true; + for (uint i=0; iplace() ) { + moveOk = false; + break; + } + if (moveOk) { + double p = points(); + if ( !hasBestPoints || p>bestPoints + || (p==hasBestPoints && random.getBool()) ) { + hasBestPoints = true; + bestPoints = p; + bestDec = pieces[0]->dec(); + bestRot = pieces[0]->rot(); + } + } + + for (uint i=pieces.size(); i>0; i--) + if ( pieces[i-1]->increment() ) return false; + return true; +} + +double AI::points() const +{ + double pts = 0; + for (uint i=0; i<_elements.size(); i++) { + if ( _elements[i].coefficient==0.0 ) continue; + double v = _elements[i].data->function(*main, *board); + if ( _elements[i].data->triggered && qRound(v)<_elements[i].trigger ) + continue; + pts += _elements[i].coefficient * v; + } + return pts; +} + +void AI::settingsChanged() +{ + int d = CommonPrefs::thinkingDepth(); + resizePieces(d); + for (uint i=0; i<_elements.size(); i++) { + const Data &data = *_elements[i].data; + _elements[i].coefficient = AIConfig::coefficient(data); + if (data.triggered) _elements[i].trigger = AIConfig::trigger(data); + } + if ( timer.isActive() ) launch(main); +} + +double AI::nbOccupiedLines(const Board &, const Board ¤t) +{ + return current.matrix().height() - current.nbClearLines(); +} + +double AI::nbHoles(const Board &, const Board ¤t) +{ + uint nb = 0; + for (uint i=0; i=0; j--) { + KGrid2D::Coord c(i, j); + if ( current.matrix()[c]==0 ) nb++; + } + } + return nb; +} + +double AI::peakToPeak(const Board &, const Board ¤t) +{ + int min = current.matrix().height()-1; + for (uint i=0; i &elements) + : QWidget(0, "ai config") +{ + QGridLayout *top = new QGridLayout(this, 3, 2, KDialog::marginHint(), + KDialog::spacingHint()); + + QLabel *label = new QLabel(i18n("Thinking depth:"), this); + top->addWidget(label, 0, 0); + KIntNumInput *in = new KIntNumInput(this, "kcfg_ThinkingDepth"); + in->setRange(minThinkingDepth, maxThinkingDepth); + top->addWidget(in, 0, 1); + + top->addRowSpacing(1, KDialog::spacingHint()); + + QGrid *grid = new QGrid(2, this); + top->addMultiCellWidget(grid, 2, 2, 0, 1); + for (uint i=0; isetFrameStyle(QFrame::Panel | QFrame::Plain); + + QVBox *vb = new QVBox(grid); + if (data.whatsthis) QWhatsThis::add(vb, i18n(data.whatsthis)); + vb->setMargin(KDialog::spacingHint()); + vb->setSpacing(KDialog::spacingHint()); + vb->setFrameStyle(QFrame::Panel | QFrame::Plain); + if (data.triggered) { + KIntNumInput *trig = new KIntNumInput(vb, triggerKey(data.name)); + trig->setRange(0, 10, 1, true); + } + KDoubleNumInput *coeff = new KDoubleNumInput(vb, coefficientKey(data.name)); + coeff->setRange(0.0, 1.0, 1.0, true); + } +} + +QCString AIConfig::triggerKey(const char *name) +{ + return "kcfg_Trigger_" + QCString(name); +} + +QCString AIConfig::coefficientKey(const char *name) +{ + return "kcfg_Coefficient_" + QCString(name); +} + +double AIConfig::coefficient(const AI::Data &data) +{ + KConfigSkeletonItem *item = CommonPrefs::self()->findItem( QString("Coefficient_%1").arg(data.name) ); + assert(item); + return item->property().toDouble(); +} + +int AIConfig::trigger(const AI::Data &data) +{ + KConfigSkeletonItem *item = CommonPrefs::self()->findItem( QString("Trigger_%1").arg(data.name) ); + assert(item); + return item->property().toInt(); +} diff --git a/libksirtet/common/ai.h b/libksirtet/common/ai.h new file mode 100644 index 00000000..da298abc --- /dev/null +++ b/libksirtet/common/ai.h @@ -0,0 +1,122 @@ +#ifndef COMMON_AI_H +#define COMMON_AI_H + +#include +#include + +#include +#include +#include +#include "lib/libksirtet_export.h" + +class Board; +class Piece; + + +//----------------------------------------------------------------------------- +class LIBKSIRTET_EXPORT AIPiece +{ + public: + AIPiece(); + ~AIPiece(); + + void init(const Piece *p, Board *b); + bool place(); + bool increment(); + + int dec() const { return curDec; } + uint rot() const { return curRot; } + + private: + uint nbPos, nbRot, curPos, curRot; + int curDec; + const Piece *_piece; + Piece *_current; + Board *_board; + + void reset(); +}; + +//----------------------------------------------------------------------------- +class LIBKSIRTET_EXPORT AI : public QObject +{ + Q_OBJECT + public: + struct Data { + const char *name, *label, *whatsthis; + bool triggered; + double (*function)(const Board &, const Board &); + }; + static const Data LastData; + + AI(uint thinkTime, uint orderTime, const Data *DATA); + virtual ~AI(); + + void launch(Board *main); + void stop(); + void start(); + + class Element { + public: + const Data *data; + double coefficient; + int trigger; + }; + const QValueVector &elements() const { return _elements; } + + void settingsChanged(); + + private slots: + void timeout(); + + protected: + virtual void initThink(); + + static double nbOccupiedLines(const Board &, const Board &); + static double nbHoles(const Board &, const Board &); + static double nbSpaces(const Board &, const Board &); + static double peakToPeak(const Board &, const Board &); + static double mean(const Board &, const Board &); + static double nbRemoved(const Board &, const Board &); + + private: + bool think(); + void startTimer(); + bool emitOrder(); + double points() const; + void resizePieces(uint size); + + QTimer timer; + enum ThinkState { Thinking, GivingOrders }; + ThinkState state; + uint thinkTime, orderTime; + bool stopped; + QMemArray pieces; + QValueVector _elements; + Board *main, *board; + KRandomSequence random; + + bool hasBestPoints; + double bestPoints; + int bestDec; + uint bestRot; +}; + +//----------------------------------------------------------------------------- +class LIBKSIRTET_EXPORT AIConfig : public QWidget +{ + Q_OBJECT + public: + AIConfig(const QValueVector &elements); + + static double coefficient(const AI::Data &data); + static int trigger(const AI::Data &data); + + private: + static QCString triggerKey(const char *name); + static QCString coefficientKey(const char *name); + + static const uint minThinkingDepth, maxThinkingDepth; +}; + +#endif diff --git a/libksirtet/common/board.cpp b/libksirtet/common/board.cpp new file mode 100644 index 00000000..f5f011a6 --- /dev/null +++ b/libksirtet/common/board.cpp @@ -0,0 +1,286 @@ +#include "board.h" +#include "board.moc" + +#include +#include + +#include "factory.h" +#include "base/piece.h" +#include "misc_ui.h" +#include "ai.h" +#include "commonprefs.h" + + +Board::Board(bool graphic, GiftPool *gp, QWidget *parent) + : BaseBoard(graphic, parent), + _giftPool(gp), aiEngine(0) +{} + +Board::~Board() +{ + delete aiEngine; +} + +void Board::setType(bool _ai) +{ + Q_ASSERT( graphic() ); + if (_ai) { + if ( aiEngine==0 ) aiEngine = cfactory->createAI(); + } else { + delete aiEngine; + aiEngine = 0; + } +} + +void Board::start(const GTInitData &data) +{ + randomGarbage.setSeed(data.seed); + _giftPool->reset(); + BaseBoard::start(data); +} + +void Board::stop() +{ + BaseBoard::stop(); + if (aiEngine) aiEngine->stop(); +} + +void Board::showBoard(bool show) +{ + BaseBoard::showBoard(show); + showCanvas(_next, show); +} + +void Board::unpause() +{ + BaseBoard::unpause(); + if (aiEngine) aiEngine->start(); // eventually restart thinking +} + +void Board::updateLevel() +{ + uint nb = cfactory->cbi.nbRemovedToLevel; + if ( nbRemoved()>=level()*nb ) updateLevel(level()+1); +} + +void Board::updateLevel(uint newLevel) +{ + BaseBoard::updateLevel(newLevel); + emit levelUpdated(); + if ( graphic() ) startTimer(); +} + +void Board::settingsChanged() +{ + BaseBoard::settingsChanged(); + if (aiEngine) aiEngine->settingsChanged(); +} + +/*****************************************************************************/ +void Board::pMoveLeft() +{ + if ( state!=Normal ) return; + moveLeft(); + main->update(); +} + +void Board::pMoveRight() +{ + if ( state!=Normal ) return; + moveRight(); + main->update(); +} + +void Board::pMoveLeftTotal() +{ + if ( state!=Normal ) return; + moveLeft(bfactory->bbi.width); + main->update(); +} + +void Board::pMoveRightTotal() +{ + if ( state!=Normal ) return; + moveRight(bfactory->bbi.width); + main->update(); +} + +void Board::pOneLineDown() +{ + if ( state!=Normal ) return; + oneLineDown(); + main->update(); +} + +void Board::pDropDownStart() +{ + if ( state!=Normal ) return; + _dropHeight = 0; + oneLineDown(); + if ( state==Normal ) { + state = DropDown; + startTimer(); + } + main->update(); +} + +void Board::pDropDownStop() +{ + if ( state!=DropDown || CommonPrefs::directDropDownEnabled() ) return; + state = Normal; + startTimer(); + main->update(); +} + +void Board::pRotateLeft() +{ + if ( state!=Normal ) return; + rotateLeft(); + main->update(); +} + +void Board::pRotateRight() +{ + if ( state!=Normal ) return; + rotateRight(); + main->update(); +} + +void Board::pieceDropped(uint dropHeight) +{ + if ( state==DropDown ) state = Normal; + else _dropHeight = dropHeight; + _beforeGlue(true); +} + +void Board::_beforeGlue(bool first) +{ + if ( graphic() ) { + state = (beforeGlue(_dropHeight>=1, first) ? BeforeGlue : Normal); + if ( state==BeforeGlue ) { + startTimer(); + return; + } + } + gluePiece(); +} + +void Board::gluePiece() +{ + BaseBoard::gluePiece(); + _afterGlue(true); + if ( graphic() ) KNotifyClient::event(winId(), "glued", i18n("Piece glued")); +} + +void Board::_afterGlue(bool first) +{ + bool b = afterGlue(!graphic(), first); + if ( graphic() ) { + state = (b ? AfterGlue : Normal); + if ( state==AfterGlue ) { + startTimer(); + return; + } + } + + updateScore(score() + _dropHeight); + if ( needRemoving() ) _beforeRemove(true); + else _afterAfterRemove(); +} + +bool Board::afterAfterRemove() +{ + // checkGift + if ( graphic() && _giftPool->pending() ) { + if ( putGift(_giftPool->take()) ) { + computeInfos(); + _afterGift(true); + return true; + } else return false; + } + return newPiece(); +} + +void Board::_afterGift(bool first) +{ + Q_ASSERT( graphic() ); + state = (afterGift(first) ? AfterGift : Normal); + if ( state==AfterGift ) startTimer(); + else afterAfterRemove(); +} + +bool Board::newPiece() +{ + Q_ASSERT( !graphic() || state==Normal ); + if ( !BaseBoard::newPiece() ) return false; + if ( graphic() ) { + main->update(); + _next->update(); + if (aiEngine) aiEngine->launch(this); + // else : a human player can think by himself ... + } + return true; +} + +bool Board::timeout() +{ + if ( BaseBoard::timeout() ) return true; + + switch (state) { + case DropDown: _dropHeight++; + case Normal: oneLineDown(); break; + case BeforeGlue: _beforeGlue(false); break; + case AfterGlue: _afterGlue(false); break; + case AfterGift: _afterGift(false); break; + default: return false; + } + main->update(); + return true; +} + +uint Board::normalTime() const +{ + return cfactory->cbi.baseTime / (1 + level()); +} + +bool Board::startTimer() +{ + if ( BaseBoard::startTimer() ) return true; + + switch (state) { + case Normal: + timer.start(normalTime()); + break; + case DropDown: + timer.start(cfactory->cbi.dropDownTime); + break; + case BeforeGlue: + timer.start(cfactory->cbi.beforeGlueTime, true); + break; + case AfterGlue: + timer.start(cfactory->cbi.afterGlueTime, true); + break; + case AfterGift: + timer.start(cfactory->cbi.afterGiftTime, true); + break; + default: + return false; + } + return true; +} + + +//----------------------------------------------------------------------------- +bool Board::beforeGlue(bool bump, bool first) +{ + if ( !bump ) return false; + if (first) { + loop = 0; + return true; + } else loop++; + + float dec = BasePrefs::blockSize() * (loop+1) * -0.1; + if ( BasePrefs::animationsEnabled() ) bumpCurrentPiece((int)dec); + + return ( loop!=cfactory->cbi.nbBumpStages ); +} diff --git a/libksirtet/common/board.h b/libksirtet/common/board.h new file mode 100644 index 00000000..97c37c17 --- /dev/null +++ b/libksirtet/common/board.h @@ -0,0 +1,75 @@ +#ifndef COMMON_BOARD_H +#define COMMON_BOARD_H + +#include "base/board.h" + +#include "lib/libksirtet_export.h" + +class GiftPool; +class AI; + +class LIBKSIRTET_EXPORT Board : public BaseBoard +{ + Q_OBJECT + public: + Board(bool graphic, GiftPool *, QWidget *parent); + virtual ~Board(); + + void setType(bool computer); + virtual void start(const GTInitData &); + void unpause(); + void stop(); + + virtual uint gift() = 0; + virtual bool needRemoving() = 0; + + GiftPool *giftPool() const { return _giftPool; } + + public slots: + void pMoveLeft(); + void pMoveRight(); + void pDropDownStart(); + void pDropDownStop(); + void pOneLineDown(); + void pRotateLeft(); + void pRotateRight(); + void pMoveLeftTotal(); + void pMoveRightTotal(); + + private slots: + bool timeout(); + + signals: + void levelUpdated(); + + protected: + KRandomSequence randomGarbage; + + virtual bool beforeGlue(bool bump, bool first); + virtual void gluePiece(); + virtual bool afterGlue(bool /*doAll*/, bool /*first*/) { return false; } + virtual bool afterGift(bool /*first*/) { return false; } + virtual bool putGift(uint) = 0; + + virtual uint normalTime() const; + void updateLevel(); + + void settingsChanged(); + + private: + uint _dropHeight; + GiftPool *_giftPool; + AI *aiEngine; + + bool newPiece(); + void pieceDropped(uint dropHeight); + void _afterGift(bool first); + void _beforeGlue(bool first); + void _afterGlue(bool first); + bool afterAfterRemove(); + bool startTimer(); + void showBoard(bool show); + void updateLevel(uint newLevel); +}; + +#endif diff --git a/libksirtet/common/commonprefs.kcfgc b/libksirtet/common/commonprefs.kcfgc new file mode 100644 index 00000000..8e41b16f --- /dev/null +++ b/libksirtet/common/commonprefs.kcfgc @@ -0,0 +1,9 @@ +# Code generation options for kconfig_compiler +File=libksirtet2.kcfg +IncludeFiles=base/baseprefs.h +ClassName=CommonPrefs +Inherits=BasePrefs +Singleton=true +#Mutators=true +#CustomAdditions=true +Mutators=false diff --git a/libksirtet/common/factory.cpp b/libksirtet/common/factory.cpp new file mode 100644 index 00000000..1b239a82 --- /dev/null +++ b/libksirtet/common/factory.cpp @@ -0,0 +1,28 @@ +#include "factory.h" + +#include "ai.h" +#include "settings.h" + + +CommonFactory::CommonFactory(const MainData &md, const BaseBoardInfo &bbi, + const CommonBoardInfo &ci) + : BaseFactory(md, bbi), cbi(ci) +{} + +QWidget *CommonFactory::createAppearanceConfig() +{ + return new AppearanceConfig; +} + +QWidget *CommonFactory::createGameConfig() +{ + return new GameConfig; +} + +QWidget *CommonFactory::createAIConfig() +{ + AI *ai = createAI(); + QWidget *cw = new AIConfig(ai->elements()); + delete ai; + return cw; +} diff --git a/libksirtet/common/factory.h b/libksirtet/common/factory.h new file mode 100644 index 00000000..c0b40d66 --- /dev/null +++ b/libksirtet/common/factory.h @@ -0,0 +1,36 @@ +#ifndef COMMON_FACTORY_H +#define COMMON_FACTORY_H + +#include "base/factory.h" + +#include "lib/libksirtet_export.h" + +struct CommonBoardInfo { + uint baseTime, dropDownTime, beforeGlueTime, afterGlueTime; + uint afterGiftTime, nbBumpStages; + uint nbRemovedToLevel; + uint nbGiftLeds, maxGiftsToSend, giftShowerTimeout, giftPoolTimeout; +}; + +class BaseField; +class AI; + +#define cfactory static_cast(BaseFactory::self()) + +class LIBKSIRTET_EXPORT CommonFactory : public BaseFactory +{ + public: + CommonFactory(const MainData &, const BaseBoardInfo &, + const CommonBoardInfo &); + + const CommonBoardInfo &cbi; + + virtual BaseField *createField(QWidget *parent) = 0; + virtual AI *createAI() = 0; + + QWidget *createAIConfig(); + virtual QWidget *createAppearanceConfig(); + virtual QWidget *createGameConfig(); +}; + +#endif diff --git a/libksirtet/common/field.cpp b/libksirtet/common/field.cpp new file mode 100644 index 00000000..2d67062e --- /dev/null +++ b/libksirtet/common/field.cpp @@ -0,0 +1,243 @@ +#include "field.h" +#include "field.moc" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "base/baseprefs.h" +#include "factory.h" +#include "highscores.h" +#include "misc_ui.h" +#include "board.h" +#include "commonprefs.h" + + +Field::Field(QWidget *parent) +: MPSimpleBoard(parent), BaseField(this) +{ +// column 1 + // score LCD + scoreList = new KGameLCDList(this); + showScore = new KGameLCD(6, scoreList); + scoreList->append(showScore); + showTime = new KGameLCDClock(scoreList); + scoreList->append(showTime); + lcds->addWidget(scoreList, 1, 0); + lcds->setRowStretch(2, 1); + + // removed LCD + removedList = new KGameLCDList(i18n(bfactory->mainData.removedLabel), this); + lcds->addWidget(removedList, 3, 0); + lcds->setRowStretch(4, 1); + + // level progress + levelLabel = new QLabel(this); + levelLabel->setAlignment(AlignCenter); + lcds->addWidget(levelLabel, 5, 0); + toLevel = new KProgress(this); + toLevel->setTextEnabled(true); + toLevel->setFormat("1"); + QWhatsThis::add(toLevel, i18n("Display the progress to complete the current level or stage.")); + lcds->addWidget(toLevel, 6, 0); + lcds->setRowStretch(7, 1); + +// column 2 + // previous player height + prevHeight = new PlayerProgress(board, this, "prev_progress"); + QWhatsThis::add(prevHeight, i18n("Previous player's height")); + top->addWidget(prevHeight, 1, 1, AlignHCenter); + +// column 3 + // pending gift shower + Board *b = static_cast(board); + top->addWidget(b->giftPool(), 0, 2, AlignCenter); + + // shadow piece + shadow = new Shadow(board, this); + QWhatsThis::add(shadow, i18n("Shadow of the current piece")); + top->addWidget(shadow, 2, 2); + +// column 4 + // next player height + nextHeight = new PlayerProgress(board, this, "next_progress"); + QWhatsThis::add(nextHeight, i18n("Next player's height")); + top->addWidget(nextHeight, 1, 3, AlignHCenter); + +// column 5 + // next piece shower + QVBoxLayout *vbl = new QVBoxLayout(10); + top->addLayout(vbl, 1, 4); + vbl->addStretch(1); + + labShowNext = new QLabel(i18n("Next Tile"), this); + labShowNext->setAlignment(AlignCenter); + vbl->addWidget(labShowNext, 0); + showNext = new ShowNextPiece(board, this); + _snRootPixmap = new KCanvasRootPixmap(showNext); + _snRootPixmap->start(); + vbl->addWidget(showNext, 0); + vbl->addStretch(4); + + connect(board, SIGNAL(scoreUpdated()), SLOT(scoreUpdatedSlot())); + connect(board, SIGNAL(levelUpdated()), SLOT(levelUpdated())); + connect(board, SIGNAL(removedUpdated()), SLOT(removedUpdated())); + + initVariableGUI(); +} + +void Field::levelUpdated() +{ + toLevel->setFormat(QString::number(board->level())); + // necessary to update string ... + int p = toLevel->progress(); + toLevel->setProgress(p+1); + toLevel->setProgress(p); +} + +void Field::removedUpdated() +{ + uint nb = cfactory->cbi.nbRemovedToLevel; + toLevel->setProgress(isArcade() ? board->arcadeDone() + : board->nbRemoved() % nb); +} + +void Field::showOpponents(bool show) +{ + Board *b = static_cast(board); + if (show) { + prevHeight->show(); + nextHeight->show(); + b->giftPool()->show(); + } else { + prevHeight->hide(); + nextHeight->hide(); + b->giftPool()->hide(); + } +} + +void Field::settingsChanged() +{ + BaseField::settingsChanged(); + QColor color = BasePrefs::fadeColor(); + double s = BasePrefs::fadeIntensity(); + _snRootPixmap->setFadeEffect(s, color); + showNext->canvas()->setBackgroundColor(color); + bool b = CommonPrefs::showNextPiece(); + if (b) { + showNext->show(); + labShowNext->show(); + } else { + showNext->hide(); + labShowNext->hide(); + } + b = CommonPrefs::showPieceShadow(); + if (b) shadow->show(); + else shadow->hide(); +} + +void Field::_init(bool AI, bool multiplayer, bool server, bool first, + const QString &name) +{ + BaseField::init(AI, multiplayer, server, first, name); + showOpponents(multiplayer); + static_cast(board)->setType(AI); +} + +void Field::_initFlag(QDataStream &s) +{ + ServerInitData sid; + s >> sid; + GTInitData data; + data.seed = sid.seed; + data.initLevel = sid.initLevel; + + shadow->setDisplay(true); + toLevel->setValue(0); + showTime->reset(); + showTime->start(); + + BaseField::start(data); + initVariableGUI(); +} + +void Field::initVariableGUI() +{ + if ( board->isArcade() ) { + scoreList->title()->setText(i18n("Elapsed time")); + showScore->hide(); + showTime->show(); + QWhatsThis::add(scoreList, i18n("Display the elapsed time.")); + levelLabel->setText(i18n("Stage")); + toLevel->setTotalSteps( board->arcadeTodo() ); + } else { + scoreList->title()->setText(i18n("Score")); + showScore->show(); + showTime->hide(); + QWhatsThis::add(scoreList, i18n("Display the current score.
It turns blue if it is a highscore and red if it is the best local score.
")); + levelLabel->setText(i18n("Level")); + toLevel->setTotalSteps(cfactory->cbi.nbRemovedToLevel); + } +} + +void Field::_playFlag(QDataStream &s) +{ + ServerPlayData spd; + s >> spd; + prevHeight->setValue(spd.prevHeight); + nextHeight->setValue(spd.nextHeight); + if (spd.gift) + static_cast(board)->giftPool()->put(spd.gift); +} + +void Field::_pauseFlag(bool p) +{ + pause(p); + shadow->setDisplay(!p); + if (p) showTime->stop(); + else showTime->start(); +} + +void Field::_stopFlag(bool gameover) +{ + BaseField::stop(gameover); + showTime->stop(); +} + +void Field::_dataOut(QDataStream &s) +{ + _cpd.height = board->firstClearLine(); + _cpd.end = static_cast(board)->isGameOver(); + _cpd.gift = static_cast(board)->gift(); + s << _cpd; +} + +KExtHighscore::Score Field::currentScore() const +{ + KExtHighscore::Score score(_cpd.end ? KExtHighscore::Lost : KExtHighscore::Won); + score.setScore(board->score()); + score.setData("level", board->level()); + score.setData("removed", board->nbRemoved()); + return score; +} + +void Field::_gameOverDataOut(QDataStream &s) +{ + s << currentScore(); +} + +void Field::moveLeft() { static_cast(board)->pMoveLeft(); } +void Field::moveRight() { static_cast(board)->pMoveRight(); } +void Field::dropDownStart() { static_cast(board)->pDropDownStart(); } +void Field::dropDownStop() { static_cast(board)->pDropDownStop(); } +void Field::oneLineDown() { static_cast(board)->pOneLineDown(); } +void Field::rotateLeft() { static_cast(board)->pRotateLeft(); } +void Field::rotateRight() { static_cast(board)->pRotateRight(); } +void Field::moveLeftTotal() { static_cast(board)->pMoveLeftTotal(); } +void Field::moveRightTotal() { static_cast(board)->pMoveRightTotal();} diff --git a/libksirtet/common/field.h b/libksirtet/common/field.h new file mode 100644 index 00000000..8179539b --- /dev/null +++ b/libksirtet/common/field.h @@ -0,0 +1,68 @@ +#ifndef COMMON_FIELD_H +#define COMMON_FIELD_H + +#include "lib/mp_simple_board.h" +#include "base/field.h" +#include "types.h" + +#include "lib/libksirtet_export.h" + + +class ShowNextPiece; +class GiftShower; +class Shadow; +class KProgress; +class KGameProgress; +class KGameLCDClock; + +class LIBKSIRTET_EXPORT Field : public MPSimpleBoard, public BaseField +{ + Q_OBJECT + public: + Field(QWidget *parent); + + public slots: + void moveLeft(); + void moveRight(); + void dropDownStart(); + void dropDownStop(); + void oneLineDown(); + void rotateLeft(); + void rotateRight(); + void moveLeftTotal(); + void moveRightTotal(); + + virtual void settingsChanged(); + + protected slots: + void scoreUpdatedSlot() { scoreUpdated(); } + virtual void levelUpdated(); + virtual void removedUpdated(); + + private: + KGameLCDClock *showTime; + ShowNextPiece *showNext; + KProgress *toLevel; + QLabel *labShowNext, *levelLabel; + KGameProgress *prevHeight, *nextHeight; + Shadow *shadow; + KCanvasRootPixmap *_snRootPixmap; + ClientPlayData _cpd; + + void _init(bool AI, bool multiplayer, bool server, bool first, + const QString &name); + void showOpponents(bool show); + void initVariableGUI(); + + void _initFlag(QDataStream &); + void _playFlag(QDataStream &); + void _pauseFlag(bool pause); + void _stopFlag(bool gameover); + void _dataOut(QDataStream &); + void _gameOverDataOut(QDataStream &); + void _initDataOut(QDataStream &) {} + + KExtHighscore::Score currentScore() const; +}; + +#endif diff --git a/libksirtet/common/highscores.cpp b/libksirtet/common/highscores.cpp new file mode 100644 index 00000000..799a31b9 --- /dev/null +++ b/libksirtet/common/highscores.cpp @@ -0,0 +1,60 @@ +#include "highscores.h" + +#include +#include +#include + +#include "base/factory.h" + + +using namespace KExtHighscore; + +CommonHighscores::CommonHighscores() +{ + Item *item = new Item((uint)1, i18n("Level"), Qt::AlignRight); + addScoreItem("level", item); + item = new Item((uint)0, i18n(bfactory->mainData.removedLabel), + Qt::AlignRight); + addScoreItem("removed", item); +} + +void CommonHighscores::convertLegacy(uint) +{ + KConfigGroupSaver cg(kapp->config(), "High Scores"); + for (uint i=0; i<10; i++) { + QString name + = cg.config()->readEntry(QString("name%1").arg(i), QString::null); + if ( name.isNull() ) break; + if ( name.isEmpty() ) name = i18n("anonymous"); + uint score + = cg.config()->readUnsignedNumEntry(QString("score%1").arg(i), 0); + uint level + = cg.config()->readUnsignedNumEntry(QString("level%1").arg(i), 1); + Score s(Won); + s.setScore(score); + s.setData("name", name); + s.setData("level", level); + submitLegacyScore(s); + } +} + +bool CommonHighscores::isStrictlyLess(const Score &s1, const Score &s2) const +{ + uint l1 = s1.data("level").toUInt(); + uint r1 = s1.data("removed").toUInt(); + uint l2 = s2.data("level").toUInt(); + uint r2 = s2.data("removed").toUInt(); + + if ( s1.score()==s2.score() ) { + if ( l1==l2 ) return r1 +#include +#include + +#include "factory.h" +#include "field.h" +#include "commonprefs.h" +#include "main.h" + + +const ActionData Interface::ACTION_DATA[Nb_Actions] = { + { I18N_NOOP("Move Left"), "move left", SLOT(moveLeft()), 0 }, + { I18N_NOOP("Move Right"), "move right", SLOT(moveRight()), 0 }, + { I18N_NOOP("Drop Down"), "drop down", SLOT(dropDownStart()), + SLOT(dropDownStop()) }, + { I18N_NOOP("One Line Down"), "one line down", SLOT(oneLineDown()), 0 }, + { I18N_NOOP("Rotate Left"), "rotate left", SLOT(rotateLeft()), 0 }, + { I18N_NOOP("Rotate Right"), "rotate right", SLOT(rotateRight()), 0 }, + { I18N_NOOP("Move to Left Column"), "move left total", + SLOT(moveLeftTotal()), 0 }, + { I18N_NOOP("Move to Right Column"), "move right total", + SLOT(moveRightTotal()), 0 } +}; + +const int Interface::KEYCODE_ONE[Nb_Actions] = { + Key_Left, Key_Right, Key_Down, Key_Shift, Key_Up, Key_Return, + CTRL+Key_Left, CTRL+Key_Right +}; +const int Interface::KEYCODE_TWO[Nb_Actions] = { + Key_F, Key_G, Key_D, Key_Space, Key_E, Key_C, SHIFT+Key_F, SHIFT+Key_G +}; + +Interface::Interface(const MPGameInfo &gi, QWidget *parent) + : MPSimpleInterface(gi, Nb_Actions, ACTION_DATA, parent) +{ + setDefaultKeycodes(1, 0, KEYCODE_ONE); + setDefaultKeycodes(2, 0, KEYCODE_TWO); + setDefaultKeycodes(2, 1, KEYCODE_ONE); +} + +MPBoard *Interface::newBoard(uint i) +{ + Field *f = static_cast(cfactory->createField(this)); + f->settingsChanged(); + connect(this, SIGNAL(settingsChanged()), f, SLOT(settingsChanged())); + if ( i==0 ) _firstField = f; + return f; +} + +void Interface::normalGame() +{ + singleHuman(); +} + +void Interface::arcadeGame() +{ + singleHuman(); + _firstField->setArcade(); +} + +void Interface::_init() +{ + if ( !server() ) return; + _data.resize(nbPlayers()); + _scores.setPlayerCount( nbPlayers() ); + for (uint i=0; i> _data[i]; + if (_data[i].end) end = true; + } + return end; +} + +void Interface::_sendPlayData() +{ + ServerPlayData sd; + for(uint i=0; iisArcade() ) { + _score.setType(KExtHighscore::Won); + BaseField::gameOver(_score, this); + } +} + +uint Interface::prev(uint i) const +{ + if ( i==0 ) return nbPlayers()-1; + else return i-1; +} + +uint Interface::next(uint i) const +{ + if ( i==(nbPlayers()-1) ) return 0; + else return i+1; +} + +// server only +void Interface::_treatInit() +{ + ServerInitData sid; + sid.seed = kapp->random(); + sid.initLevel = CommonPrefs::initialGameLevel(); + for (uint i=0; i1 ); + for (uint i=0; i> _score; + if (multiplayers) _scores.addScore(i, _score); + } + if (multiplayers) s << _scores; + // else no need to send anything +} + +// client only +void Interface::_readGameOverData(QDataStream &s) +{ + s >> _scores; +} diff --git a/libksirtet/common/inter.h b/libksirtet/common/inter.h new file mode 100644 index 00000000..e38c2ed5 --- /dev/null +++ b/libksirtet/common/inter.h @@ -0,0 +1,62 @@ +#ifndef COMMON_INTER_H +#define COMMON_INTER_H + +#include +#include "lib/libksirtet_export.h" + +#include "lib/mp_simple_interface.h" +#include "base/inter.h" +#include "types.h" +#include "board.h" + + +class Field; + +class LIBKSIRTET_EXPORT Interface : public MPSimpleInterface, public BaseInterface +{ + Q_OBJECT +public: + Interface(const MPGameInfo &, QWidget *parent); + +signals: + void settingsChanged(); + +public slots: + void normalGame(); + void arcadeGame(); + void settingsChangedSlot() { emit settingsChanged(); } + +protected: + void _showHighscores(QWidget *parent); + +private: + QMemArray _data; + KExtHighscore::Score _score; + KExtHighscore::MultiplayerScores _scores; + Field *_firstField; + + enum Action { Nb_Actions = 8 }; + static const ActionData ACTION_DATA[Nb_Actions]; + static const int KEYCODE_ONE[Nb_Actions]; + static const int KEYCODE_TWO[Nb_Actions]; + + MPBoard *newBoard(uint); + void setInitData(uint player, ServerInitData &); + uint prev(uint i) const; + uint next(uint i) const; + + void _readGameOverData(QDataStream &s); + void _sendGameOverData(QDataStream &s); + void _firstInit() {} + void _treatInit(); + void _init(); + void _showGameOverData(); + bool _readPlayData(); + void _sendPlayData(); + + void _start() { MPSimpleInterface::start(); } + void _pause() { MPSimpleInterface::pause(); } + bool _isPaused() const { return MPSimpleInterface::isPaused(); } +}; + +#endif diff --git a/libksirtet/common/libksirtet2.kcfg b/libksirtet/common/libksirtet2.kcfg new file mode 100644 index 00000000..27ee72e3 --- /dev/null +++ b/libksirtet/common/libksirtet2.kcfg @@ -0,0 +1,36 @@ + + + + + + true + + + + true + + + + false + + + + 1 + 1 + 20 + + + + false + + + + + + 2 + + + diff --git a/libksirtet/common/main.cpp b/libksirtet/common/main.cpp new file mode 100644 index 00000000..004b86ca --- /dev/null +++ b/libksirtet/common/main.cpp @@ -0,0 +1,60 @@ +#include "main.h" +#include "main.moc" + +#include +#include +#include +#include +#include + +#include "inter.h" +#include "factory.h" + +void MainWindow::addConfig(KConfigDialog *dialog) +{ + QWidget *w = cfactory->createAIConfig(); + if (w) dialog->addPage(w, i18n("A.I."), "personal"); +} + +void MainWindow::init() +{ + Interface *inter = static_cast(_inter); + inter->normalGame(); + setFocusPolicy(StrongFocus); + + // Modes + bool ama = ( bfactory->bbi.nbArcadeStages!=0 ); + QString s = (ama ? i18n("&Single Human (Normal)") : i18n("&Single Human")); + (void)new KAction(s, 0, inter, SLOT(normalGame()), + actionCollection(), "mp_single_human"); + if (ama) (void)new KAction(i18n("&Single Human (Arcade)"), 0, + inter, SLOT(arcadeGame()), + actionCollection(), "mp_arcade"); + (void)new KAction(i18n("Human vs &Human"), 0, inter, SLOT(humanVsHuman()), + actionCollection(), "mp_human_vs_human"); + (void)new KAction(i18n("Human vs &Computer"), 0, + inter, SLOT(humanVsComputer()), + actionCollection(), "mp_human_vs_computer"); + (void)new KAction(i18n("&More..."), 0, inter, SLOT(dialog()), + actionCollection(), "mp_more"); + + buildGUI(inter); + connect(this, SIGNAL(settingsChanged()), + inter, SLOT(settingsChangedSlot())); +} + +void MainWindow::addKeys(KKeyDialog &d) +{ + static_cast(_inter)->addKeys(d); +} + +void MainWindow::saveKeys() +{ + static_cast(_inter)->saveKeys(); +} + +void MainWindow::focusInEvent(QFocusEvent *e) +{ + static_cast(_inter)->setFocus(); + BaseMainWindow::focusInEvent(e); +} diff --git a/libksirtet/common/main.h b/libksirtet/common/main.h new file mode 100644 index 00000000..d22b7273 --- /dev/null +++ b/libksirtet/common/main.h @@ -0,0 +1,22 @@ +#ifndef COMMON_MAIN_H +#define COMMON_MAIN_H + +#include "base/main.h" + +#include "lib/libksirtet_export.h" + +class LIBKSIRTET_EXPORT MainWindow : public BaseMainWindow +{ + Q_OBJECT +public: + MainWindow() {} + +protected: + void init(); + void addConfig(KConfigDialog *); + void addKeys(KKeyDialog &); + void saveKeys(); + virtual void focusInEvent(QFocusEvent *e); +}; + +#endif diff --git a/libksirtet/common/misc_ui.cpp b/libksirtet/common/misc_ui.cpp new file mode 100644 index 00000000..45cfb440 --- /dev/null +++ b/libksirtet/common/misc_ui.cpp @@ -0,0 +1,194 @@ +#include "misc_ui.h" +#include "misc_ui.moc" + +#include + +#include "base/piece.h" +#include "base/board.h" +#include "base/baseprefs.h" +#include "base/kzoommainwindow.h" +#include "factory.h" + +const uint GIFT_SHOWER_TIMEOUT = 800; +const uint GIFT_POOL_TIMEOUT = 2000; + +const uint SHADOW_HEIGHT = 10; + +const uint GI_WIDTH = 15; +const uint GI_HEIGHT = 11; +const uint ARROW_HEIGHT = 3; +const uint ARROW_WIDTH = 7; + +const uint LED_WIDTH = 15; +const uint LED_HEIGHT = 15; +const uint LED_SPACING = 5; + +/*****************************************************************************/ +ShowNextPiece::ShowNextPiece(BaseBoard *board, QWidget *parent) + : FixedCanvasView(parent, "show_next_piece") +{ + setCanvas(board->next()); + setFrameStyle(QFrame::Panel | QFrame::Sunken); + KZoomMainWindow::addWidget(this); +} + +/*****************************************************************************/ +Shadow::Shadow(BaseBoard *board, QWidget *parent) + : QWidget(parent, "shadow"), _xOffset(board->frameWidth()), + _board(board), _show(false) +{ + KZoomMainWindow::addWidget(this); + connect(board, SIGNAL(updatePieceConfigSignal()), SLOT(update())); +} + +QSize Shadow::sizeHint() const +{ + return QSize(_xOffset + _board->matrix().width() * BasePrefs::blockSize(), + SHADOW_HEIGHT); +} + +QSizePolicy Shadow::sizePolicy() const +{ + return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); +} + +void Shadow::setDisplay(bool show) +{ + _show = show; + update(); +} + +void Shadow::paintEvent(QPaintEvent *) +{ + if ( !_show ) return; + + const Piece *piece = _board->currentPiece(); + uint pf = piece->min().first + _board->currentPos().first; + uint pl = pf + piece->size().first - 1; + + QPainter p(this); + p.setBrush(black); + p.setPen(black); + for (uint i=pf; i<=pl; i++) { + QRect r(_xOffset + i * BasePrefs::blockSize() + 1 , 0, + BasePrefs::blockSize() - 2, SHADOW_HEIGHT); + p.drawRect(r); + } +} + + +/*****************************************************************************/ +class Led : public QWidget +{ + public: + Led(const QColor &c, QWidget *parent) + : QWidget(parent), col(c), _on(FALSE) {} + + QSizePolicy sizePolicy() const + { return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } + QSize sizeHint() const { return QSize(LED_WIDTH, LED_HEIGHT); } + + void on() { if (!_on) { _on = TRUE; repaint(); } } + void off() { if (_on) {_on = FALSE; repaint(); } } + void setColor(const QColor &c) { if (c!=col) { col = c; repaint(); } } + + protected: + void paintEvent(QPaintEvent *) { + QPainter p(this); + p.setBrush((_on ? col.light() : col.dark())); + p.setPen(black); + p.drawEllipse(0, 0, width(), height()); + } + + private: + QColor col; + bool _on; +}; + +GiftPool::GiftPool(QWidget *parent) + : QHBox(parent, "gift_pool"), nb(0), ready(false) +{ + setSpacing(LED_SPACING); + leds.resize(cfactory->cbi.nbGiftLeds); + for (uint i=0; isizeHint() : QSize()); + return QSize((s.width()+LED_SPACING)*leds.size()-LED_SPACING, s.height()); +} + +QSizePolicy GiftPool::sizePolicy() const +{ + return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); +} + +void GiftPool::put(uint n) +{ + if ( n==0 ) return; + if ( nb==0 && !ready ) + QTimer::singleShot(cfactory->cbi.giftPoolTimeout, + this, SLOT(timeout())); + uint e = QMIN(nb+n, leds.size()); + for (uint i=nb; ion(); + uint f = QMIN(nb+n-e, leds.size()); + for (uint i=0; isetColor(red); + nb += n; +} + +uint GiftPool::take() +{ + Q_ASSERT(ready); + for (uint i=0; isetColor(yellow); + leds[i]->off(); + } + uint max = cfactory->cbi.maxGiftsToSend; + if ( nb>max ) { + uint p = nb - max; + nb = 0; + put(p); + return max; + } else { + ready = false; + uint t = nb; + nb = 0; + return t; + } +} + +void GiftPool::reset() +{ + killTimers(); + ready = false; + nb = 0; + for (uint i=0; isetColor(yellow); + leds[i]->off(); + } +} + +//----------------------------------------------------------------------------- +PlayerProgress::PlayerProgress(BaseBoard *board, QWidget *parent, + const char *name) + : KGameProgress(0, board->matrix().height(), 0, KGameProgress::Vertical, + parent, name), _board(board) +{ + setBackgroundColor(lightGray); + setTextEnabled(false); + setBarColor(blue); + KZoomMainWindow::addWidget(this); +} + +QSize PlayerProgress::sizeHint() const +{ + return QSize(10, _board->matrix().height() * BasePrefs::blockSize()) + + 2 * QSize(frameWidth(), frameWidth()); +} + +QSizePolicy PlayerProgress::sizePolicy() const +{ + return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); +} diff --git a/libksirtet/common/misc_ui.h b/libksirtet/common/misc_ui.h new file mode 100644 index 00000000..3a89acaa --- /dev/null +++ b/libksirtet/common/misc_ui.h @@ -0,0 +1,81 @@ +#ifndef COMMON_MISC_UI_H +#define COMMON_MISC_UI_H + +#include +#include +#include + +#include +#include "lib/libksirtet_export.h" + +#include "base/board.h" + +/*****************************************************************************/ +class LIBKSIRTET_EXPORT ShowNextPiece : public FixedCanvasView +{ + Q_OBJECT + public: + ShowNextPiece(BaseBoard *, QWidget *parent); +}; + +/*****************************************************************************/ +class LIBKSIRTET_EXPORT Shadow : public QWidget +{ + Q_OBJECT + public: + Shadow(BaseBoard *, QWidget *parent); + + virtual QSize sizeHint() const; + virtual QSizePolicy sizePolicy() const; + void setDisplay(bool show); + + private: + int _xOffset; + const BaseBoard *_board; + bool _show; + + void paintEvent(QPaintEvent *); +}; + +/*****************************************************************************/ +class Led; + +class LIBKSIRTET_EXPORT GiftPool : public QHBox +{ + Q_OBJECT + public: + GiftPool(QWidget *parent); + + virtual QSize sizeHint() const; + virtual QSizePolicy sizePolicy() const; + + void reset(); + void put(uint); + uint take(); + bool pending() const { return ready; } + + private slots: + void timeout() { ready = true; } + + private: + QPtrVector leds; + uint _timeout, nb; + bool ready; +}; + + +/*****************************************************************************/ +class LIBKSIRTET_EXPORT PlayerProgress : public KGameProgress +{ + Q_OBJECT +public: + PlayerProgress(BaseBoard *board, QWidget *parent = 0, const char *name = 0); + + virtual QSize sizeHint() const; + virtual QSizePolicy sizePolicy() const; + +private: + BaseBoard *_board; +}; + +#endif diff --git a/libksirtet/common/settings.cpp b/libksirtet/common/settings.cpp new file mode 100644 index 00000000..902d1e56 --- /dev/null +++ b/libksirtet/common/settings.cpp @@ -0,0 +1,54 @@ +#include "settings.h" +#include "settings.moc" + +#include +#include +#include +#include + +#include +#include +#include +#include + + +//----------------------------------------------------------------------------- +AppearanceConfig::AppearanceConfig() +{ + int row = _grid->numRows(); + int col = _grid->numCols(); + + QCheckBox *cb = new QCheckBox(i18n("Show piece's shadow"), _main, "kcfg_ShowPieceShadow"); + _grid->addMultiCellWidget(cb, row, row, 0, col-1); + + cb = new QCheckBox(i18n("Show next piece"), _main, "kcfg_ShowNextPiece"); + _grid->addMultiCellWidget(cb, row+1, row+1, 0, col-1); + + cb = new QCheckBox(i18n("Show detailed \"removed lines\" field"), _main, "kcfg_ShowDetailedRemoved"); + _grid->addMultiCellWidget(cb, row+2, row+2, 0, col-1); +} + +//----------------------------------------------------------------------------- +GameConfig::GameConfig() + : QWidget(0, "game config") +{ + QVBoxLayout *top = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint()); + + _grid = new QGridLayout(top, 3, 2); + _grid->setColStretch(1, 1); + + QLabel *label = new QLabel(i18n("Initial level:"), this); + _grid->addWidget(label, 0, 0); + KIntNumInput *in = new KIntNumInput(this, "kcfg_InitialGameLevel"); + in->setRange(1, 20, 1, true); + _grid->addWidget(in, 0, 1); + + _grid->addRowSpacing(1, KDialog::spacingHint()); + + QCheckBox *cb = new QCheckBox(i18n("Direct drop down"), this, "kcfg_DirectDropDownEnabled"); + QWhatsThis::add(cb, i18n("Drop down is not stopped when drop down key is released.")); + _grid->addMultiCellWidget(cb, 2, 2, 0, 1); + + top->addStretch(1); +} + diff --git a/libksirtet/common/settings.h b/libksirtet/common/settings.h new file mode 100644 index 00000000..ceda13a1 --- /dev/null +++ b/libksirtet/common/settings.h @@ -0,0 +1,28 @@ +#ifndef COMMON_SETTINGS_H +#define COMMON_SETTINGS_H + +#include "base/settings.h" + +#include "lib/libksirtet_export.h" + + +//----------------------------------------------------------------------------- +class LIBKSIRTET_EXPORT AppearanceConfig : public BaseAppearanceConfig +{ + Q_OBJECT +public: + AppearanceConfig(); +}; + +//----------------------------------------------------------------------------- +class LIBKSIRTET_EXPORT GameConfig : public QWidget +{ + Q_OBJECT +public: + GameConfig(); + +protected: + QGridLayout *_grid; +}; + +#endif diff --git a/libksirtet/common/types.cpp b/libksirtet/common/types.cpp new file mode 100644 index 00000000..05d91db3 --- /dev/null +++ b/libksirtet/common/types.cpp @@ -0,0 +1,16 @@ +#include "types.h" + +LIBKSIRTET_EXPORT QDataStream &operator <<(QDataStream &s, const ClientPlayData &d) + { s << d.height << d.gift << d.end; return s; } +LIBKSIRTET_EXPORT QDataStream &operator >>(QDataStream &s, ClientPlayData &d) + { s >> d.height >> d.gift >> d.end; return s; } + +LIBKSIRTET_EXPORT QDataStream &operator <<(QDataStream &s, const ServerPlayData &d) + { s << d.prevHeight << d.nextHeight << d.gift; return s; } +LIBKSIRTET_EXPORT QDataStream &operator >>(QDataStream &s, ServerPlayData &d) + { s >> d.prevHeight >> d.nextHeight >> d.gift; return s; } + +LIBKSIRTET_EXPORT QDataStream &operator <<(QDataStream &s, const ServerInitData &d) +{ s << d.initLevel << d.seed << d.nextName << d.prevName << d.name; return s; } +LIBKSIRTET_EXPORT QDataStream &operator >>(QDataStream &s, ServerInitData &d) +{ s >> d.initLevel >> d.seed >> d.nextName >> d.prevName >> d.name; return s; } diff --git a/libksirtet/common/types.h b/libksirtet/common/types.h new file mode 100644 index 00000000..8a30d276 --- /dev/null +++ b/libksirtet/common/types.h @@ -0,0 +1,26 @@ +#ifndef COMMON_TYPES_H +#define COMMON_TYPES_H + +#include + +#include "lib/libksirtet_export.h" + + +struct ClientPlayData { Q_UINT8 height, gift, end; }; +LIBKSIRTET_EXPORT QDataStream &operator <<(QDataStream &s, const ClientPlayData &d); +LIBKSIRTET_EXPORT QDataStream &operator >>(QDataStream &s, ClientPlayData &d); + +struct ServerPlayData { Q_UINT8 prevHeight, nextHeight, gift; }; +LIBKSIRTET_EXPORT QDataStream &operator <<(QDataStream &s, const ServerPlayData &d); +LIBKSIRTET_EXPORT QDataStream &operator >>(QDataStream &s, ServerPlayData &d); + +class ServerInitData +{ + public: + QString prevName, nextName, name; + Q_UINT32 initLevel, seed; +}; +LIBKSIRTET_EXPORT QDataStream &operator <<(QDataStream &s, const ServerInitData &d); +LIBKSIRTET_EXPORT QDataStream &operator >>(QDataStream &s, ServerInitData &d); + +#endif diff --git a/libksirtet/configure.in.in b/libksirtet/configure.in.in new file mode 100644 index 00000000..fef48401 --- /dev/null +++ b/libksirtet/configure.in.in @@ -0,0 +1,5 @@ +AC_CHECK_HEADERS(fcntl.h sys/time.h unistd.h sys/select.h sys/filio.h) + +dnl libksirtet is completely not prepared for visibility support, check for it being enabled +dnl so we can disable it in the Makefile.am. +AM_CONDITIONAL(disable_VISIBILITY, test "$HAVE_GCC_VISIBILITY" = "1") diff --git a/libksirtet/lib/CHANGELOG b/libksirtet/lib/CHANGELOG new file mode 100644 index 00000000..84c03edb --- /dev/null +++ b/libksirtet/lib/CHANGELOG @@ -0,0 +1,39 @@ +0.1.8 (11 April 2001) + * better Player/Meeting "choose boxes" + * use KExtendedSocket (get rid of custom utilities) + and hopefully fix network game + +0.1.7 + * resize handle removed from statusbar of netmeeting dialog + +0.1.6 + * fixed a bug in key configuration + * players name access improved + * some internal cleanups + use of KDialogBase + +0.1.5 + * many bug fixes for network game + * added a framework for simple multiplayers games + * Wizard-like configuration + * enhanced keys configuration + +0.1.4 + * all data transport is now done via QDataStream (no more bitorder/storage + problems). + * QDataStream use make things easier from the user point of view (at least + I think so). + * big cleaning : the library restricts itself to data transport between + boards and to game configuration. The library doesn't want to and doesn't + have to manage things like game pause or gameover of a specific player ... + all those things must be done by the game programmer. + +0.1.3 + * ported to QT 2.0 (hard way : now we send QString over the network :) + +0.1.2 + * finally THE bug has been found (eight months later) ! + so network game seems stable. + * lots of bug fixes + +0.1.1 + * first code (June 1998 : pause in dvplt) diff --git a/libksirtet/lib/LICENSE b/libksirtet/lib/LICENSE new file mode 100644 index 00000000..6afe98e6 --- /dev/null +++ b/libksirtet/lib/LICENSE @@ -0,0 +1,18 @@ +kdemultiplayers library +----------------------- +Copyright (c) 1998-2001 Nicolas HADACEK (hadacek@kde.org) + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Library General Public License as published +by the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Library General Public License for more details. + +You should have received a copy of the GNU Library General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. diff --git a/libksirtet/lib/Makefile.am b/libksirtet/lib/Makefile.am new file mode 100644 index 00000000..890ac458 --- /dev/null +++ b/libksirtet/lib/Makefile.am @@ -0,0 +1,21 @@ +INCLUDES = $(all_includes) + +# Don't compile with hidden symbols since we are a library. +if disable_VISIBILITY +KDE_CXXFLAGS = -fvisibility=default +endif + +noinst_LTLIBRARIES = libksirtetmultiplayers.la + +noinst_HEADERS = defines.h types.h miscui.h wizard.h pline.h meeting.h \ + socket.h smanager.h internal.h keys.h mp_board.h mp_option.h \ + mp_interface.h mp_simple_types.h mp_simple_board.h \ + mp_simple_interface.h + +libksirtetmultiplayers_la_SOURCES = miscui.cpp types.cpp defines.cpp \ + socket.cpp smanager.cpp pline.cpp \ + wizard.cpp \ + meeting.cpp keys.cpp mp_interface.cpp internal.cpp \ + mp_simple_types.cpp mp_simple_board.cpp mp_simple_interface.cpp + +METASOURCES = AUTO diff --git a/libksirtet/lib/README b/libksirtet/lib/README new file mode 100644 index 00000000..442c9c30 --- /dev/null +++ b/libksirtet/lib/README @@ -0,0 +1,63 @@ +kdemultiplayers library +----------------------- +Copyright (c) 1998-2000 Nicolas HADACEK (hadacek@kde.org) +Distributed under the GNU Library General Public License + +Introduction +------------ +The "kdemultiplayers" library is an attempt to address the realization of +multiplayer games localy on a computer (the players have each a "board" on +the screen and they use the same keyboard) or/and by network (between several +computers). The implementation is not so simple but this library gives +a basic API which allows a lot of flexibility. + +Note: The headers which defines the API have names beginning with "mp". + +The library provides three services : + * a set of dialog widgets to allow the players to configure/create/join + a game. + * management of all the sending/receiving data between + player boards so you as a game programmer will *not* have to deal with + sockets or network programming. + * a framework for simple multiplayers game (for instance ksirtet) with a + very simple API ("mp_simple" headers). + +Semantics +--------- +"player" : an individual which take part in the game (it can also be an AI). +"AI" : artificial intelligence (a game that is played by a program). +"board" : there is one board per player ; it is the widget where the player + acts in the game. +"host" : a computer which hosts one or several players. +"server" : the host that has created the game. +"client" : a host that is not the server. + + +Basic description of the API +---------------------------- +From the game programmer point of view there should be no difference between +a local multiplayers game (one host with several players) and a network game +(several hosts with one or more players). + +Each player has a board which is a widget inherited from the class "LocalBoard" +(see "mp_board.h"). + +On each host there must be a class inherited from "MPInterface" +which manages local boards and the data transport with other hosts. +(see "mp_interface.h"). + +During the game at given intervals of time, the server asks data from clients +and from its local boards. After treating this data, the server dispatches back +results to all boards. + +General advice +-------------- +There should be no blocking operation or long computation as it will freeze +the user front-end and will also block the multiplayers data exchanges +which operate by timer signals. + +There are general rules to avoid such blocking : + * answer to X/QT events such as keyboard/mouse/window events with + short methods. + * use timer(s) to make things evolve in time or to break long + computation in shorter parts. diff --git a/libksirtet/lib/TODO b/libksirtet/lib/TODO new file mode 100644 index 00000000..54a924bc --- /dev/null +++ b/libksirtet/lib/TODO @@ -0,0 +1,18 @@ +* change to an event-driven data exchange framework (it currently uses a timer + on the server side) -> we probably need a way to ensure clients are not + dead (?) + +* grid/row layout of boards +* better dialogs ... +* check the 64bit fix in "types.h" + +* user help (add help button to wizard) +* tooltips !! +* API documentation + +* heavy test of network game +* test option widget in netmeeting (--> needs fixing in NetMeeting class) +* test of options for players and computers + +* keys configuration : probably need to change KKeyDialog (the msgbox for + duplicate keys is so annoying ...) + unused key finder diff --git a/libksirtet/lib/defines.cpp b/libksirtet/lib/defines.cpp new file mode 100644 index 00000000..7c897dd9 --- /dev/null +++ b/libksirtet/lib/defines.cpp @@ -0,0 +1,24 @@ +#include "defines.h" + +#include + +void errorBox(const QString &msg1, const QString &msg2, QWidget *parent) +{ + QString str; + if ( msg2.isNull() ) str = msg1; + else str = i18n("%1:\n%2").arg(msg1).arg(msg2); + KMessageBox::error(parent, str); +} + +QString socketError(const KExtendedSocket *socket) +{ + return KExtendedSocket::strError(socket->status(), socket->systemError()); +} + +bool checkSocket(int res, const KExtendedSocket *socket, + const QString &msg, QWidget *parent) +{ + if ( res==0 ) return false; + errorBox(msg, socketError(socket), parent); + return true; +} diff --git a/libksirtet/lib/defines.h b/libksirtet/lib/defines.h new file mode 100644 index 00000000..9015c6b1 --- /dev/null +++ b/libksirtet/lib/defines.h @@ -0,0 +1,35 @@ +#ifndef DEFINES_H +#define DEFINES_H + +#include +#include + +// constants +#define TALKER_MAX_LENGTH 35 +#define NAME_MAX_LENGTH 15 + +// config keys +#define MP_GROUP "Multi-Players" +#define MP_GAMETYPE "Game type" +#define MP_PLAYER_NAME "Player name #%1" +#define MP_PLAYER_TYPE "Player type #%1" +#define MP_SERVER_ADDRESS "Server address" +#define MP_PORT "Port" + +void errorBox(const QString &msg1, const QString &msg2, QWidget *parent); +QString socketError(const KExtendedSocket *socket); +bool checkSocket(int res, const KExtendedSocket *, + const QString &msg, QWidget *parent); + +#define R_ERROR_BOX(msg1, msg2) { \ + errorBox(msg1, msg2, this); \ + return; \ +} + +template +bool XOR(Type a, Type b) +{ + return ( (!a && b) || (a && !b) ); +} + +#endif // DEFINES_H diff --git a/libksirtet/lib/internal.cpp b/libksirtet/lib/internal.cpp new file mode 100644 index 00000000..c7b69f9e --- /dev/null +++ b/libksirtet/lib/internal.cpp @@ -0,0 +1,278 @@ +#include "internal.h" +#include "internal.moc" + +#include +#include +#include "mp_interface.h" + +#define DATA_ERROR(i) { dataError(i); return; } +#define READ_ERROR(i) { readError(i); return; } +#define WRITE_ERROR(i) { writeError(i); return; } +#define BROKE_ERROR(i) { brokeError(i); return; } +#define LAG_ERROR { lagError(); return; } + + +//----------------------------------------------------------------------------- +void Local::dataError(uint i) +{ + qWarning("MP : Invalid data from board #%i", i); +} + +void Local::readData(bool inverse) +{ + // read data from local boards + for (uint i=0; idataOut(ios[i]->writing); + if (inverse) ios[i]->writingToReading(); + } +} + +void Local::writeData(bool inverse) +{ + // write data to local boards + for (uint i=0; iwritingToReading(); + boards[i].ptr->dataIn(ios[i]->reading); + if ( !ios[i]->reading.readOk() ) DATA_ERROR(i); + } +} + +void Local::treatData() +{ + readData(TRUE); + interface->treatData(); + // check reading stream + for (uint i=0; ireading.readOk() ) DATA_ERROR(i); + writeData(TRUE); +} + +//----------------------------------------------------------------------------- +Server::Server(uint _interval) +: interval(_interval) +{ + timer.start(interval); +} + +void Server::congestion() +{ + qWarning("MP : congestion occurred !!"); +} + +void Server::serverTimeout() +{ + ctimer.start(2*interval, TRUE); + timeout(); +} + +//----------------------------------------------------------------------------- +Network::Network(MPInterface *_interface, + QValueList &_boards, + const QPtrList &rhd) +: Local(_interface, _boards) +{ + RemoteData rd; + QPtrListIterator it(rhd); + for (; it.current(); ++it) { + rd.socket = it.current()->socket; + rd.socket->notifier()->setEnabled(TRUE); + connect(rd.socket->notifier(), SIGNAL(activated(int)), + SLOT(notifier(int))); + uint nb = it.current()->bds.count(); + Q_ASSERT( nb>=1 ); + rd.array = new BufferArray(nb); + for (uint k=0; kbds.count(); k++) + rd.names += it.current()->bds[k].name; + remotes += rd; + } +} + +Network::~Network() +{ + for (uint i=0; isize(); + return nb; +} + +QString Network::playerName(uint i) const +{ + uint l = Local::nbPlayers(); + if ( isize(); + if ( i<(l+ll) ) return remotes[k].names[i-l]; + l += ll; + } + return QString::null; // dummy +} + +IOBuffer *Network::ioBuffer(uint i) const +{ + if ( isize() ) return (*remotes[k].array)[i]; + i -= remotes[k].array->size(); + } + Q_ASSERT(FALSE); + return 0; +} + +void Network::readError(uint i) +{ + disconnectHost(i, i18n("Unable to read socket")); +} + +void Network::writeError(uint i) +{ + disconnectHost(i, i18n("Unable to write to socket")); +} + +void Network::brokeError(uint i) +{ + disconnectHost(i, i18n("Link broken")); +} + +void Network::disconnectHost(uint i, const QString &msg) +{ + delete remotes[i].socket; + delete remotes[i].array; + remotes.remove(remotes.at(i)); + interface->hostDisconnected(i, msg); +} + +//----------------------------------------------------------------------------- +LocalServer::LocalServer(MPInterface *_interface, + QValueList &_boards, + uint _interval) +: Local(_interface, _boards), Server(_interval) +{ + connect(&timer, SIGNAL(timeout()), SLOT(timeoutSlot())); + connect(&ctimer, SIGNAL(timeout()), SLOT(congestionTimeoutSlot())); + serverTimeout(); +} + +//----------------------------------------------------------------------------- +NetworkServer::NetworkServer(MPInterface *_interface, + QValueList &_boards, + const QPtrList &rhd, uint _interval) +: Network(_interface, _boards, rhd), Server(_interval), + nbReceived(remotes.count()) +{ + connect(&timer, SIGNAL(timeout()), SLOT(timeoutSlot())); + connect(&ctimer, SIGNAL(timeout()), SLOT(congestionTimeoutSlot())); + // to catch unexpected data + for (uint i=0; iwritingStream() << MF_Ask; +// debug("SERVER : send ask flag"); + if ( !remotes[i].socket->write() ) WRITE_ERROR(i); + } +} + +void NetworkServer::notifier(int fd) +{ + uint i; + for (i=0; ifd()==fd ) break; + Q_ASSERT( iread() ) { + case -1: READ_ERROR(i); + case 0: BROKE_ERROR(i); + } + + remotes[i].received = TRUE; + nbReceived++; + + ReadingStream &s = remotes[i].socket->readingStream(); +// debug("SERVER : notifier + read (fd=%i i=%i size=%i)", fd, i, +// s.size()); + s >> *remotes[i].array; + if ( !s.readOk() ) DATA_ERROR(i); + + // all data from clients received + if ( nbReceived==remotes.count() ) treatData(); +} + +void NetworkServer::writeData(bool inverse) +{ + Local::writeData(inverse); + for (uint i=0; iwritingStream(); + s << MF_Data; + s << *remotes[i].array; + s.writeRawBytes(globalStream()->buffer().data(), + globalStream()->size()); +// debug("SERVER : write data (size= 1 + %i + %i=%i)", +// globalStream()->size(), s.size()-globalStream()->size()-1, +// s.size()); + if ( !remotes[i].socket->write() ) WRITE_ERROR(i); + } + globalStream()->clear(); +} + +void NetworkServer::lagError() +{ + for (uint i=0; iread() ) { + case -1: READ_ERROR(0); + case 0: BROKE_ERROR(0); + } + ReadingStream &s = remotes[0].socket->readingStream(); + MetaFlag mf; + s >> mf; + if ( !s.readOk() ) DATA_ERROR(0); +// debug("CLIENT : reading stream (size=%i flag=%i)", s.size(), +// (int)mf); + switch(mf) { + case MF_Ask: + // write data from local boards to server socket (cleaning + // of writing stream is done in write()) + readData(FALSE); + remotes[0].socket->writingStream() << ios; +// debug("CLIENT : send ios (size=%i)", +// remotes[0].socket->writingStream().size()); + if ( !remotes[0].socket->write() ) WRITE_ERROR(0); + break; + case MF_Data: + // read data from server to interface & local boards +// debug("CLIENT : before receive ios (at=%i)", s.device()->at()); + s >> ios; +// debug("CLIENT : after receive ios (at=%i)", s.device()->at()); + interface->dataFromServer(s); +// debug("CLIENT : after dataFromServer (at=%i)", s.device()->at()); + if ( !s.readOk() ) DATA_ERROR(0); + writeData(FALSE); + break; + default: DATA_ERROR(0); + } + if ( !s.atEnd() ) qWarning("CLIENT : remaining data"); +} diff --git a/libksirtet/lib/internal.h b/libksirtet/lib/internal.h new file mode 100644 index 00000000..4dece16a --- /dev/null +++ b/libksirtet/lib/internal.h @@ -0,0 +1,152 @@ +#ifndef INTERNAL_H +#define INTERNAL_H + +#include +#include + +#include "socket.h" +#include "mp_interface.h" + +class MPBoard; +class RemoteHostData; + +//----------------------------------------------------------------------------- +class Local +{ + public: + Local(MPInterface *_interface, QValueList &_boards) + : interface(_interface), ios(_boards.count()), boards(_boards) {} + virtual ~Local() {} + + virtual uint nbPlayers() const { return boards.count(); } + virtual QString playerName(uint i) const { return boards[i].name; } + virtual IOBuffer *ioBuffer(uint i) const { return ios[i]; } + virtual void writeData(bool inverse); + virtual WritingStream *globalStream() { return 0; } + + protected: + MPInterface *interface; + BufferArray ios; + + void dataError(uint i); + void readData(bool inverse); + void treatData(); + + private: + QValueList boards; +}; + +//----------------------------------------------------------------------------- +class Server +{ + public: + Server(uint _interval); + virtual ~Server() {} + + protected: + WritingStream stream; + QTimer timer, ctimer; + + virtual void timeout() = 0; + void serverTimeout(); + void congestion(); + + private: + uint interval; +}; + +//----------------------------------------------------------------------------- +class Network : public QObject, public Local +{ + Q_OBJECT + + public: + Network(MPInterface *_interface, QValueList &_boards, + const QPtrList &rhd); + virtual ~Network(); + + virtual uint nbPlayers() const; + QString playerName(uint i) const; + IOBuffer *ioBuffer(uint i) const; + + protected slots: + virtual void notifier(int fd) = 0; + + protected: + class RemoteData { + public: + RemoteData() {} + Socket *socket; + BufferArray *array; + bool received; + QStringList names; + }; + QValueList remotes; + + void readError(uint i); + void writeError(uint i); + void brokeError(uint i); + void disconnectHost(uint i, const QString &msg); +}; + +//----------------------------------------------------------------------------- +class LocalServer : public QObject, public Local, public Server +{ + Q_OBJECT + + public: + LocalServer(MPInterface *_interface, + QValueList &_boards, uint _interval); + + WritingStream *globalStream() { return &stream; } + + private slots: + void timeoutSlot() { serverTimeout(); } + void congestionTimeoutSlot() { congestion(); } + + private: + void timeout() { treatData(); } +}; + +//----------------------------------------------------------------------------- +class NetworkServer : public Network, public Server +{ + Q_OBJECT + + public: + NetworkServer(MPInterface *_interface, + QValueList &_boards, + const QPtrList &rhd, uint _interval); + + void writeData(bool inverse); + WritingStream *globalStream() { return &stream; } + + private slots: + void timeoutSlot() { serverTimeout(); } + void congestionTimeoutSlot() { congestion(); } + void notifier(int fd); + + private: + uint nbReceived; + + void lagError(); + void timeout(); +}; + +//----------------------------------------------------------------------------- +class Client : public Network +{ + Q_OBJECT + + public: + Client(MPInterface *_interface, QValueList &_boards, + const QPtrList &rhd) + : Network(_interface, _boards, rhd) {} + + uint nbPlayers() const { return Local::nbPlayers(); } + + private slots: + void notifier(int fd); +}; + +#endif // INTERNAL_H diff --git a/libksirtet/lib/keys.cpp b/libksirtet/lib/keys.cpp new file mode 100644 index 00000000..879f0bfa --- /dev/null +++ b/libksirtet/lib/keys.cpp @@ -0,0 +1,104 @@ +#include "keys.h" +#include "keys.moc" + +#include +#include +#include + + +KeyData::KeyData(uint maxNb, uint nbActions, const ActionData *data, + QObject *parent) + : QObject(parent), _maxNb(maxNb) +{ + _data.duplicate(data, nbActions); + + for (uint n=0; nsetEnabled(false); + if ( slot==0 ) { + SpecialData data; + data.enabled = false; + data.pressed = new QSignal(this); + data.pressed->connect(receiver, _data[k].slot); + data.released = new QSignal(this); + data.released->connect(receiver, _data[k].slotRelease); + _specActions[a] = data; + } + } + _cols[index]->readShortcutSettings(group()); +} + +void KeyData::setEnabled(uint index, bool enabled) +{ + for (uint k=0; k<_cols[index]->count(); k++) { + QMap::Iterator it = + _specActions.find(_cols[index]->action(k)); + if ( it==_specActions.end() ) + _cols[index]->action(k)->setEnabled(enabled); + else (*it).enabled = enabled; + } +} + +void KeyData::addKeys(KKeyDialog &d) +{ + for (uint i=0; i<_cols.size(); i++) + d.insert(_cols[i], i18n("Shortcuts for player #%1/%2").arg(i+1) + .arg(_cols.size())); +} + +void KeyData::save() +{ + for (uint i=0; i<_cols.size(); i++) + _cols[i]->writeShortcutSettings(group()); +} + +void KeyData::keyEvent(QKeyEvent *e, bool pressed) +{ + if ( e->isAutoRepeat() ) return; + + KKey key(e); + QMap::Iterator it = _specActions.begin(); + for(; it!=_specActions.end(); ++it) { + if ( !it.data().enabled ) continue; + if ( !it.key()->shortcut().contains(key) ) continue; + if (pressed) it.data().pressed->activate(); + else it.data().released->activate(); + } + e->ignore(); +} diff --git a/libksirtet/lib/keys.h b/libksirtet/lib/keys.h new file mode 100644 index 00000000..07c08419 --- /dev/null +++ b/libksirtet/lib/keys.h @@ -0,0 +1,42 @@ +#ifndef KEYS_H +#define KEYS_H + +#include +#include + +#include "mp_interface.h" + + +class KeyData : public QObject +{ + Q_OBJECT + public: + KeyData(uint maxNb, uint nbActions, const ActionData *, + QObject *parent); + void setKeycodes(uint nb, uint i, const int *keycodes); + + void setCurrentNb(uint nb); + void clear(); + void createActionCollection(uint index, QWidget *receiver); + void setEnabled(uint index, bool enabled); + void addKeys(KKeyDialog &); + void save(); + + void keyEvent(QKeyEvent *e, bool pressed); + + private: + uint _maxNb; + QMemArray _data; + QMap > > _keycodes; + QMemArray _cols; + struct SpecialData { + bool enabled; + QSignal *pressed, *released; + }; + QMap _specActions; + + QString group() const + { return QString("Keys (%1 humans)").arg(_cols.size()); } +}; + +#endif // KEYS_H diff --git a/libksirtet/lib/libksirtet_export.h b/libksirtet/lib/libksirtet_export.h new file mode 100644 index 00000000..819e0029 --- /dev/null +++ b/libksirtet/lib/libksirtet_export.h @@ -0,0 +1,35 @@ +/* + This file is part of libkexif project + Copyright (c) 2005 Laurent Montel + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _LIBKEXIF_EXPORT_H +#define _LIBKEXIF_EXPORT_H + +#ifdef KDEMACROS_USABLE +#include +#endif + +#ifdef KDE_EXPORT +#define LIBKSIRTET_EXPORT KDE_EXPORT +#else +#define LIBKSIRTET_EXPORT +#endif + +#endif /* _LIBKEXIF_EXPORT_H */ + diff --git a/libksirtet/lib/meeting.cpp b/libksirtet/lib/meeting.cpp new file mode 100644 index 00000000..2cb6e285 --- /dev/null +++ b/libksirtet/lib/meeting.cpp @@ -0,0 +1,575 @@ +#include "meeting.h" + +#include +#include + +#include +#include + +#include "defines.h" +#include "mp_option.h" + +#define LIST_INTERVAL 0 + + +NetMeeting::NetMeeting(const cId &_id, Socket *socket, + MPOptionWidget *option, + bool _server, QWidget *parent, const char * name) +: KDialogBase(Plain, i18n("Network Meeting"), + (_server ? Ok|Cancel|Help : Cancel|Help), + (_server ? Ok : Cancel), parent, name), + server(_server), ow(option), id(_id), socketRemoved(FALSE) +{ + sm.append(socket, SocketManager::ReadWrite); + sm[0]->notifier()->setEnabled(TRUE); + +/* top layout */ + QVBoxLayout *top = new QVBoxLayout(plainPage(), spacingHint()); + top->setResizeMode(QLayout::Fixed); + + // server line + spl = new MeetingLine(server, server, true, plainPage()); + top->addWidget(spl); + + // widget list + wl = new WidgetList(LIST_INTERVAL, plainPage()); + wl->hide(); + top->addWidget(wl); + + labWait = new QLabel(i18n("Waiting for clients"), plainPage()); + labWait->setAlignment(AlignCenter); + top->addWidget(labWait); + + // options widget +// if (ow) top->addWidget(ow); #### FIXME + + // status bar + status = new QStatusBar(plainPage()); + status->setSizeGripEnabled(false); + top->addWidget(status); + + // buttons + enableButtonSeparator(TRUE); + if (server) { + setButtonOK(i18n("Start Game")); + enableButtonOK(FALSE); + } + setButtonCancel(server ? i18n("Abort") : i18n("Quit")); + enableButton(Help, FALSE); +} + +NetMeeting::~NetMeeting() +{} + +void NetMeeting::appendLine(const MeetingLineData &pld, bool server) +{ + MeetingLine *pl; + pl = new MeetingLine(pld.own, server, false, wl); + if (pld.own) connect(pl, SIGNAL(textChanged(const QString &)), + SLOT(textChanged(const QString &))); + else message(i18n("A new client has just arrived (#%1)") + .arg(wl->size()+1)); + pl->setData(pld.ed); + connect(pl, SIGNAL(typeChanged(MeetingCheckBox::Type)), + SLOT(typeChanged(MeetingCheckBox::Type))); + wl->append(pl); + waiting(); +} + +void NetMeeting::removeLine(uint i) +{ + wl->remove(i); + waiting(); +} + +void NetMeeting::waiting() +{ + if ( wl->size() ) { + labWait->hide(); + wl->show(); + } else { + labWait->show(); + wl->hide(); + } + if (server) enableButtonOK(ready()); +} + +void NetMeeting::setType(const TypeInfo &ti) +{ + if ( ti.i==0 ) spl->setType(ti.type); // in fact should not append + else { + wl->widget(ti.i-1)->setType(ti.type); + if (server) enableButtonOK(ready()); + } +} + +void NetMeeting::setText(const TextInfo &ti) +{ + if ( ti.i==0 ) spl->setText(ti.text); + else wl->widget(ti.i-1)->setText(ti.text); +} + +bool NetMeeting::ready() const +{ + int nbReady = 0; + for(uint k=0; ksize(); k++) { + switch ( wl->widget(k)->type() ) { + case MeetingCheckBox::Ready : nbReady++; break; + case MeetingCheckBox::NotReady : return FALSE; + default : break; + } + } + return ( nbReady!=0 ); +} + +void NetMeeting::cleanReject(const QString &str) +{ + sm.clean(); // remove the sockets immediately to avoid possible further mess + if ( !str.isEmpty() ) + KMessageBox::information(this, str, caption()); + KDialogBase::reject(); +} + +#define WRITE(i) if ( !sm[i]->write() ) { writeError(i); return; } +#define CHECK_READ(i) if ( !sm[i]->readingStream().readOk() ) { dataError(i); return; } + +// Read incoming data +void NetMeeting::readNotifier(int fd) +{ + int i = sm.find(fd); + Q_ASSERT( i!=-1 ); + switch ( sm[i]->read() ) { + case -1: readError(i); break; + case 0: brokeError(i); break; + default: readData(i); + } +} + +void NetMeeting::readData(uint i) +{ + // get message type + MeetingMsgFlag mt; + sm[i]->readingStream() >> mt; + CHECK_READ(i); + switch (mt) { + case EndFlag: endFlag(i); break; + case NewFlag: newFlag(i); break; + case Mod_TextFlag: modTextFlag(i); break; + case Mod_TypeFlag: modTypeFlag(i); break; + case IdFlag: idFlag(i); break; + case DelFlag: delFlag(i); break; + case Mod_OptFlag: modOptFlag(i); break; + case PlayFlag: playFlag(i); break; + default: dataError(i); + } + + if (socketRemoved) socketRemoved = FALSE; + else if ( !sm[i]->readingStream().atEnd() ) + readData(i); // more pending data +} + +void NetMeeting::readError(uint i) + { netError(i, i18n("Error reading data from")); } +void NetMeeting::dataError(uint i) + { netError(i, i18n("Unknown data from")); } +void NetMeeting::writeError(uint i) + { netError(i, i18n("Error writing to")); } +void NetMeeting::brokeError(uint i) + { netError(i, i18n("Link broken or empty data from")); } + +bool NetMeeting::checkState(uint i, PlayerState s) +{ + bool ok = ( players[i]==s ); + if (!ok) dataError(i); + return ok; +} + +bool NetMeeting::checkAndSetState(uint i, PlayerState os, PlayerState ns) +{ + bool ok = checkState(i, os); + if (ok) players[i] = ns; + return ok; +} + +void NetMeeting::reject() +{ + // send an End flag + sm.commonWritingStream() << EndFlag; + writeToAll(); + + cleanReject(); +} + +void NetMeeting::accept() +{ + KDialogBase::accept(); +} + +void NetMeeting::message(const QString &str) +{ + status->message(str, 3000); +} + +/** ServerNetMeeting *********************************************************/ +ServerNetMeeting::ServerNetMeeting(const cId &id, + const RemoteHostData &r, MPOptionWidget *option, + QPtrList &arhd, QWidget *parent, const char * name) +: NetMeeting(id, r.socket, option, TRUE, parent, name), rhd(arhd) +{ + connect(sm[0]->notifier(), SIGNAL(activated(int)), SLOT(newHost(int))); + players.append(Accepted); // server + + // set server line + ExtData ed(r.bds, "", MeetingCheckBox::Ready); + spl->setData(ed); + connect(spl, SIGNAL(textChanged(const QString &)), + SLOT(textChanged(const QString &))); + + // options signal + if (ow) connect(ow, SIGNAL(changed()), SLOT(optionsChanged())); +} + +void ServerNetMeeting::writeToAll(uint i) +{ + for (uint k=1; kaccept(s); + if ( res!=0 ) { + message(i18n("Failed to accept incoming client:\n%1") + .arg(socketError(s))); + return; + } + players.append(NewPlayer); + Socket *socket = new Socket(s, true); + uint i = sm.append(socket, SocketManager::ReadWrite); + connect(sm[i]->notifier(), SIGNAL(activated(int)), + SLOT(readNotifier(int))); + sm[i]->notifier()->setEnabled(TRUE); +} + +void ServerNetMeeting::idFlag(uint i) +{ + bool b = checkAndSetState(i, NewPlayer, IdChecked); + Q_ASSERT(b); + + // get client id + cId clientId; + sm[i]->readingStream() >> clientId; + CHECK_READ(i); + + // compare id + id.check(clientId); + + // send result to client + Stream &s = sm[i]->writingStream(); + s << IdFlag << id; + WRITE(i); + + // if not accepted : remove socket and player from list + if ( !id.accepted() ) + disconnectHost(i, i18n("Client rejected for incompatible ID")); +} + +void ServerNetMeeting::endFlag(uint i) +{ + disconnectHost(i, i18n("Client #%1 has left").arg(i)); +} + +void ServerNetMeeting::newFlag(uint i) +{ + checkAndSetState(i, IdChecked, Accepted); + + // get line infos from new client (GameData struct) + MeetingLineData pld; + sm[i]->readingStream() >> pld.ed.bds; + CHECK_READ(i); + + // complete the MeetingLineData struct with initial values + pld.own = FALSE; // client line + pld.ed.type = MeetingCheckBox::NotReady; // not ready by default + pld.ed.text = ""; // empty line to begin with + appendLine(pld, TRUE); + + // send to the new client already present lines including its own + // (New flag + MeetingLineData struct) + spl->data(pld.ed); + sm[i]->writingStream() << NewFlag << pld.ed; + for(uint k=1; kwidget(k-1)->data(pld.ed); + pld.own = ( k==i ); + sm[i]->writingStream() << NewFlag << pld; + } + WRITE(i); + + // send to all other clients the new line (New flag + MeetingLineData struct) + wl->widget(i-1)->data(pld.ed); + pld.own = FALSE; + sm.commonWritingStream() << NewFlag << pld; + writeToAll(i); +} + +void ServerNetMeeting::modTextFlag(uint i) +{ + checkState(i-1, Accepted); + + // the client i has just sent a new text (QString) + TextInfo ti; + sm[i]->readingStream() >> ti.text; + CHECK_READ(i); + ti.i = i; + setText(ti); + + // send it to all other clients (Mod_Text flag + TextInfo struct) + sm.commonWritingStream() << Mod_TextFlag << ti; + writeToAll(i); +} + +void ServerNetMeeting::modTypeFlag(uint i) +{ + checkState(i-1, Accepted); + + // a client has just sent a new TCB type (TCB type) + TypeInfo ti; + sm[i]->readingStream() >> ti.type; + CHECK_READ(i); + ti.i = i; + setType(ti); + + // send it to all other clients (Mod_Type flag + TypeInfo struct) + sm.commonWritingStream() << Mod_TypeFlag << ti; + writeToAll(i); +} + +void ServerNetMeeting::textChanged(const QString &text) +{ + // server line text changed : send to every clients (Mod_Text flag + TextInfo struct) + TextInfo ti; ti.i = 0; ti.text = text; + sm.commonWritingStream() << Mod_TextFlag << ti; + writeToAll(); +} + +void ServerNetMeeting::typeChanged(MeetingCheckBox::Type type) +{ + Q_ASSERT( sender()!=spl ); // server TCB not modifiable + // the server has changed a client TCB + + // find the changed TCB index + TypeInfo ty; + ty.type = type; + for (ty.i=0; ty.isize(); ty.i++) + if ( sender()==wl->widget(ty.i) ) break; + ty.i++; + + // TCB change : send to every clients (Mod_Type flag + TypeInfo struct) + sm.commonWritingStream() << Mod_TypeFlag << ty; + writeToAll(); + if (server) enableButtonOK(ready()); +} + +void ServerNetMeeting::accept() +{ + Q_ASSERT( ready() && rhd.count()==0 ); + + // stop receiving data from clients (will be buffered by OS) + for (uint k=0; knotifier()); + sm.remove(0, true); + + // check which client will play and fill RemoteHostData array + ExtData ed; + bool willPlay; + for (uint k=1; kwidget(k-1)->data(ed); + if ( ed.type==MeetingCheckBox::Ready ) { + willPlay = TRUE; + RemoteHostData *r = new RemoteHostData; + r->socket = sm[0]; + r->bds = ed.bds; + rhd.append(r); + } + + // send play message to client (Play flag + // + bool [accepted/rejected]) + sm[0]->writingStream() << PlayFlag << (Q_UINT8)willPlay; + // if write failed and the client is not playing : silently + // put it aside ... + if ( !sm[0]->write() && willPlay ) { + cleanReject(i18n("Unable to write to client #%1 at game " + "beginning.")); + return; + } + } + + sm[0]->notifier()->setEnabled(false); + sm.remove(0, !willPlay); + } + + NetMeeting::accept(); +} + +void ServerNetMeeting::optionsChanged() +{ + sm.commonWritingStream() << Mod_OptFlag; + ow->dataOut( sm.commonWritingStream() ); + writeToAll(); +} + +/** ClientNetMeeting *********************************************************/ +ClientNetMeeting::ClientNetMeeting(const cId &id, + const RemoteHostData &rhd, MPOptionWidget *option, + QWidget *parent, const char * name) +: NetMeeting(id, rhd.socket, option, FALSE, parent, name), bds(rhd.bds) +{ + connect(sm[0]->notifier(), SIGNAL(activated(int)), + SLOT(readNotifier(int))); + players.append(NewPlayer); // server player + + // Send id to server (Id flag + Id struct) + sm.commonWritingStream() << IdFlag << id; + writeToAll(); // what happens if there is a message box appearing before exec() call ?? +} + +void ClientNetMeeting::netError(uint, const QString &str) +{ + cleanReject(i18n("%1 server: aborting connection.").arg(str)); +} + +void ClientNetMeeting::writeToAll(uint) +{ + if ( !sm.writeCommon(0) ) writeError(0); + sm.commonWritingStream().clear(); +} + +void ClientNetMeeting::idFlag(uint) +{ + checkAndSetState(0, NewPlayer, IdChecked); + + // read Id result (Id flag + Id struct) + cId serverId; + sm[0]->readingStream() >> serverId; + CHECK_READ(0); + + // check result + if ( !serverId.accepted() ) cleanReject(serverId.errorMessage(id)); + else { + // send client info (New flag + GameData struct) + sm.commonWritingStream() << NewFlag << bds; + writeToAll(); + } +} + +void ClientNetMeeting::newFlag(uint) +{ + if ( players[0]==IdChecked ) { + ExtData ed; + sm[0]->readingStream() >> ed; + spl->setData(ed); + players[0] = Accepted; + } else { + MeetingLineData pld; + sm[0]->readingStream() >> pld; + appendLine(pld, FALSE); + } + CHECK_READ(0); +} + +void ClientNetMeeting::modTextFlag(uint) +{ + // receive new text from server (TextInfo struct) + TextInfo ti; + sm[0]->readingStream() >> ti; + CHECK_READ(0); + setText(ti); +} + +void ClientNetMeeting::modTypeFlag(uint) +{ + // receive new type from server (TypeInfo struct) + TypeInfo ti; + sm[0]->readingStream() >> ti; + CHECK_READ(0); + setType(ti); +} + +void ClientNetMeeting::delFlag(uint) +{ + // receive client number (uint) + uint k; + sm[0]->readingStream() >> k; + CHECK_READ(0); + removeLine(k-1); + message(i18n("Client %1 has left").arg(k)); +} + +void ClientNetMeeting::textChanged(const QString &text) +{ + // text changed : send to server (Mod_Text flag + QString) + sm.commonWritingStream() << Mod_TextFlag << text; + writeToAll(); +} + +void ClientNetMeeting::typeChanged(MeetingCheckBox::Type type) +{ + // type changed : send to server (Mod_Type flag + TCB) + sm.commonWritingStream() << Mod_TypeFlag << type; + writeToAll(); +} + +void ClientNetMeeting::playFlag(uint) +{ + // receive accept or reject (bool) + Q_UINT8 i; + sm[0]->readingStream() >> i; + CHECK_READ(0); + sm[0]->notifier()->setEnabled(false); + sm.remove(0, i==0); + socketRemoved = true; + if (i) accept(); + else cleanReject(i18n("The game has begun without you\n" + "(You have been excluded by the server).")); +} + +void ClientNetMeeting::modOptFlag(uint) +{ + // read new option data + ow->dataIn( sm[0]->readingStream() ); + CHECK_READ(0); +} + +void ClientNetMeeting::endFlag(uint) +{ + // abort from server + cleanReject(i18n("The server has aborted the game.")); +} +#include "meeting.moc" diff --git a/libksirtet/lib/meeting.h b/libksirtet/lib/meeting.h new file mode 100644 index 00000000..cb534404 --- /dev/null +++ b/libksirtet/lib/meeting.h @@ -0,0 +1,137 @@ +#ifndef MEETING_H +#define MEETING_H + +#include +#include +#include "smanager.h" +#include "pline.h" +#include "types.h" + +class MPOptionWidget; + +/** Internal class : net meeting. */ +class NetMeeting : public KDialogBase +{ + Q_OBJECT + + public: + // "gameName" and "gameId" are QByteArray because they are + // used for ID comparing between games. + NetMeeting(const cId &id, Socket *, MPOptionWidget *option, bool server, + QWidget *parent = 0, const char * name = 0); + virtual ~NetMeeting(); + + protected slots: + void readNotifier(int socket); + virtual void textChanged(const QString &) = 0; + virtual void typeChanged(MeetingCheckBox::Type) = 0; + virtual void reject(); + virtual void accept(); + + protected: + enum PlayerState { NewPlayer, IdChecked, Accepted }; + QValueList players; + bool server; + MeetingLine *spl; + WidgetList *wl; + SocketManager sm; + MPOptionWidget *ow; + cId id; + bool socketRemoved; + + void appendLine(const MeetingLineData &pld, bool server); + void removeLine(uint i); + void setType(const TypeInfo &ti); + void setText(const TextInfo &ti); + + void cleanReject(const QString &str = QString::null); + bool checkState(uint i, PlayerState s); + bool checkAndSetState(uint i, PlayerState os, PlayerState ns); + bool ready() const; + + virtual void idFlag(uint i) { dataError(i); } + virtual void newFlag(uint i) { dataError(i); } + virtual void endFlag(uint i) { dataError(i); } + virtual void modTypeFlag(uint i) { dataError(i); } + virtual void modTextFlag(uint i) { dataError(i); } + virtual void delFlag(uint i) { dataError(i); } + virtual void modOptFlag(uint i) { dataError(i); } + virtual void playFlag(uint i) { dataError(i); } + + virtual void netError(uint i, const QString &str) = 0; + virtual void writeToAll(uint i=0) = 0; + void readError(uint i); + void writeError(uint i); + void dataError(uint i); + void brokeError(uint i); + void message(const QString &str); + + private: + QLabel *labWait; + QStatusBar *status; + + void waiting(); + void readData(uint i); +}; + +class ServerNetMeeting : public NetMeeting +{ + Q_OBJECT + + public: + ServerNetMeeting(const cId &id, + const RemoteHostData &rhd, MPOptionWidget *options, + QPtrList &arhd, + QWidget *parent = 0, const char * name = 0); + + private slots: + void newHost(int); + void textChanged(const QString &text); + void typeChanged(MeetingCheckBox::Type); + void accept(); + void optionsChanged(); + + private: + QPtrList &rhd; + + void idFlag(uint i); + void newFlag(uint i); + void endFlag(uint i); + void modTypeFlag(uint i); + void modTextFlag(uint i); + + void netError(uint i, const QString &str); + void writeToAll(uint i = 0); + void disconnectHost(uint i, const QString &str); +}; + +class ClientNetMeeting : public NetMeeting +{ + Q_OBJECT + + public: + ClientNetMeeting(const cId &id, + const RemoteHostData &rhd, MPOptionWidget *options, + QWidget *parent = 0, const char * name = 0); + + private slots: + void textChanged(const QString &text); + void typeChanged(MeetingCheckBox::Type); + + private: + QValueList bds; + + void idFlag(uint); + void newFlag(uint); + void endFlag(uint); + void delFlag(uint); + void modTypeFlag(uint); + void modTextFlag(uint); + void modOptFlag(uint); + void playFlag(uint); + + void writeToAll(uint i=0); + void netError(uint, const QString &str); +}; + +#endif // MEETING_H diff --git a/libksirtet/lib/miscui.cpp b/libksirtet/lib/miscui.cpp new file mode 100644 index 00000000..28f00914 --- /dev/null +++ b/libksirtet/lib/miscui.cpp @@ -0,0 +1,58 @@ +#include "miscui.h" +#include "miscui.moc" + +#include + +#include + + +//----------------------------------------------------------------------------- +MeetingCheckBox::MeetingCheckBox(Type type, bool owner, bool server, + QWidget *parent) + : QWidget(parent, "meeting_check_box") +{ + QVBoxLayout *vbox = new QVBoxLayout(this); + + _ready = new QCheckBox(i18n("Ready"), this); + vbox->addWidget(_ready); + _ready->setEnabled(owner); + connect(_ready, SIGNAL(clicked()), SLOT(changedSlot())); + + _excluded = new QCheckBox(i18n("Excluded"), this); + vbox->addWidget(_excluded); + _excluded->setEnabled(server); + connect(_excluded, SIGNAL(clicked()), SLOT(changedSlot())); + + setType(type); +} + +void MeetingCheckBox::setType(Type type) +{ + _ready->setChecked( type==Ready ); + _excluded->setChecked( type==Excluded ); +} + +MeetingCheckBox::Type MeetingCheckBox::type() const +{ + if ( _excluded->isChecked() ) return Excluded; + if ( _ready->isChecked() ) return Ready; + return NotReady; +} + +void MeetingCheckBox::changedSlot() +{ + emit changed(type()); +} + +//----------------------------------------------------------------------------- +PlayerComboBox::PlayerComboBox(Type type, bool canBeEmpty, bool acceptAI, + QWidget *parent) + : QComboBox(parent, "player_combo_box") +{ + insertItem(i18n("Human")); + if (acceptAI) insertItem(i18n("AI")); + if (canBeEmpty) insertItem(i18n("None")); + setCurrentItem(type); + + connect(this, SIGNAL(activated(int)), SIGNAL(changed(int))); +} diff --git a/libksirtet/lib/miscui.h b/libksirtet/lib/miscui.h new file mode 100644 index 00000000..7de5dfbc --- /dev/null +++ b/libksirtet/lib/miscui.h @@ -0,0 +1,43 @@ +#ifndef MISCUI_H +#define MISCUI_H + +#include +#include + + +//----------------------------------------------------------------------------- +class MeetingCheckBox : public QWidget +{ + Q_OBJECT + public: + enum Type { Ready, NotReady, Excluded }; + MeetingCheckBox(Type, bool owner, bool server, QWidget *parent); + + void setType(Type); + Type type() const; + + signals: + void changed(int); + + private slots: + void changedSlot(); + + private: + QCheckBox *_ready, *_excluded; +}; + +//----------------------------------------------------------------------------- +class PlayerComboBox : public QComboBox +{ + Q_OBJECT + public: + enum Type { Human = 0, AI, None }; + PlayerComboBox(Type, bool canBeNone, bool acceptAI, QWidget *parent); + + Type type() const { return (Type)currentItem(); } + + signals: + void changed(int); +}; + +#endif // MISCUI_H diff --git a/libksirtet/lib/mp_board.h b/libksirtet/lib/mp_board.h new file mode 100644 index 00000000..fbd1e01f --- /dev/null +++ b/libksirtet/lib/mp_board.h @@ -0,0 +1,51 @@ +#ifndef MP_BOARD_H +#define MP_BOARD_H + +#include + +/** + * The MP_Board class is the base widget from which each individual + * board should inheritate ; you must implement its virtual methods. + */ +class MPBoard : public QWidget +{ + Q_OBJECT + + public: + MPBoard(QWidget *parent, const char *name=0) + : QWidget(parent, name) {} + virtual ~MPBoard() {} + + /** + * This method is called once at the board creation. + * @param AI is TRUE if the player is not human. + * @param multiplayers is TRUE if the game is not a single player game. + * @param first is TRUE if this board is the first local one. + */ + virtual void init(bool AI, bool multiplayers, bool server, bool first, + const QString &name) = 0; + + /** + * Put data on the stream. + * + * This method is the communication way out. The data given here will + * be the only information that will go to the server. + */ + virtual void dataOut(QDataStream &) = 0; + + /** + * Get data from the stream. + * + * This method is the communication way in. The data given here will be + * the only information that you will receive from the server. + */ + virtual void dataIn(QDataStream &) = 0; + + signals: + /** + * Call this signal to enable/disable the keys associated with a board. + */ + void enableKeys(bool); +}; + +#endif // MP_BOARD_H diff --git a/libksirtet/lib/mp_interface.cpp b/libksirtet/lib/mp_interface.cpp new file mode 100644 index 00000000..61742562 --- /dev/null +++ b/libksirtet/lib/mp_interface.cpp @@ -0,0 +1,274 @@ +#include "mp_interface.h" +#include "mp_interface.moc" + +#include +#include + +#include +#include +#include +#include + +#include "defines.h" +#include "types.h" +#include "meeting.h" +#include "internal.h" +#include "keys.h" +#include "wizard.h" + +/*****************************************************************************/ +/* Constructor & Destructor */ +/*****************************************************************************/ +MPInterface::MPInterface(const MPGameInfo &_gameInfo, + uint nbActions, const ActionData *data, + QWidget *parent, const char *name) +: QWidget(parent, name), internal(0), gameInfo(_gameInfo), nbLocalHumans(0) +{ + Q_ASSERT( gameInfo.maxNbLocalPlayers>=1 ); + + hbl = new QHBoxLayout(this, 0, 5); + hbl->setResizeMode( QLayout::Fixed ); + + _keyData = new KeyData(gameInfo.maxNbLocalPlayers, nbActions, data, this); +} + +MPInterface::~MPInterface() +{ + delete internal; +} + +/*****************************************************************************/ +/* Game creation */ +/*****************************************************************************/ +void MPInterface::clear() +{ + if (internal) { + stop(); + delete internal; + internal = 0; + _keyData->clear(); + } +} + +void MPInterface::dialog() +{ + clear(); + + // configuration wizard + ConnectionData cd; + MPWizard wiz(gameInfo, cd, this); + if ( wiz.exec()==QDialog::Rejected ) { + singleHuman(); // create a single game + return; + } + + // net meeting + QPtrList rhd; + rhd.setAutoDelete(TRUE); + if (cd.network) { + cId id(kapp->name(), gameInfo.gameId); + MPOptionWidget *ow = newOptionWidget(); + NetMeeting *nm; + if (cd.server) nm = new ServerNetMeeting(id, cd.rhd, ow, rhd, this); + else nm = new ClientNetMeeting(id, cd.rhd, ow, this); + int res = nm->exec(); + if (ow) { + if (res) ow->saveData(); + delete ow; + } + delete nm; + if (!res) { + singleHuman(); + return; + } + } + + createLocalGame(cd); + if (cd.server) createServerGame(rhd); + else createClientGame(cd.rhd); +} + +void MPInterface::specialLocalGame(uint nbHumans, uint nbAIs) +{ + clear(); + + ConnectionData cd; + BoardData bd; + PlayerComboBox::Type t; + KConfigGroupSaver cg(kapp->config(), MP_GROUP); + for (uint i=0; i<(nbHumans+nbAIs); i++) { + bd.type = (ireadNumEntry(QString(MP_PLAYER_TYPE).arg(i), + PlayerComboBox::None); + if ( bd.type==t ) + bd.name = cg.config()->readEntry(QString(MP_PLAYER_NAME).arg(i), + QString::null); + if ( bd.name.isNull() ) + bd.name = (i rhd; + createServerGame(rhd); +} + +void MPInterface::createServerGame(const QPtrList &rhd) +{ + internal = (rhd.count() + ? (Local *)new NetworkServer(this, boards, rhd, gameInfo.interval) + : (Local *)new LocalServer(this, boards, gameInfo.interval)); + init(); +} + +void MPInterface::createClientGame(const RemoteHostData &rhd) +{ + QPtrList r; + r.append((RemoteHostData *)&rhd); + internal = new Client(this, boards, r); + init(); +} + +void MPInterface::createLocalGame(const ConnectionData &cd) +{ + _server = cd.server; + nbLocalHumans = 0; + for (uint i=0; iaddWidget(d.ptr); + d.ptr->show(); + connect(d.ptr, SIGNAL(enableKeys(bool)), SLOT(enableKeys(bool))); + boards += d; + } + + // init local boards + _keyData->setCurrentNb(nbLocalHumans); + int k = 0; + for (uint i=0; icreateActionCollection(k, boards[i].ptr); + k++; + } + boards[i].name = cd.rhd.bds[i].name; + boards[i].ptr->init(!h, cd.network || boards.count()>1, _server, i==0, + cd.rhd.bds[i].name); + } +} + +/*****************************************************************************/ +/* Key management */ +/*****************************************************************************/ +void MPInterface::setDefaultKeycodes(uint nb, uint i, const int *def) +{ + _keyData->setKeycodes(nb, i, def); +} + +void MPInterface::addKeys(KKeyDialog &d) +{ + _keyData->addKeys(d); +} + +void MPInterface::saveKeys() +{ + _keyData->save(); +} + +void MPInterface::enableKeys(bool enable) +{ + if ( nbLocalHumans==0 ) return; + // find the sending board + uint i; + for (i=0; isetEnabled(hi, enable); +} + +void MPInterface::keyPressEvent(QKeyEvent *e) +{ + _keyData->keyEvent(e, true); +} + +void MPInterface::keyReleaseEvent(QKeyEvent *e) +{ + _keyData->keyEvent(e, false); +} + +/*****************************************************************************/ +/* Misc. functions */ +/*****************************************************************************/ +uint MPInterface::nbPlayers() const +{ + return internal->nbPlayers(); +} + +QString MPInterface::playerName(uint i) const +{ + Q_ASSERT(_server); + return internal->playerName(i); +} + +QDataStream &MPInterface::readingStream(uint i) const +{ + Q_ASSERT(_server); + return internal->ioBuffer(i)->reading; +} + +QDataStream &MPInterface::writingStream(uint i) const +{ + return internal->ioBuffer(i)->writing; +} + +QDataStream &MPInterface::dataToClientsStream() const +{ + Q_ASSERT(_server); + return *internal->globalStream(); +} + +void MPInterface::immediateWrite() +{ + internal->writeData(_server); +} + +void MPInterface::hostDisconnected(uint, const QString &msg) +{ + errorBox(msg, QString::null, this); + + if ( !disconnected ) { // to avoid multiple calls + disconnected = TRUE; + // the zero timer is used to be outside the "internal" class + QTimer::singleShot(0, this, SLOT(singleHumanSlot())); + } +} + +void MPInterface::singleHumanSlot() +{ + disconnected = FALSE; + singleHuman(); +} + +void MPInterface::paintEvent(QPaintEvent *e) +{ + QPainter p(this); + p.fillRect(e->rect(), darkGray); +} diff --git a/libksirtet/lib/mp_interface.h b/libksirtet/lib/mp_interface.h new file mode 100644 index 00000000..ad925cba --- /dev/null +++ b/libksirtet/lib/mp_interface.h @@ -0,0 +1,246 @@ +#ifndef MP_INTERFACE_H +#define MP_INTERFACE_H + +#include +#include +#include + +#include "mp_board.h" +#include "mp_option.h" + +class QHBoxLayout; +class Local; +class ConnectionData; +class RemoteHostData; +class KeyData; +class KeyCollection; +class KKeyDialog; +class KAction; + +struct ActionData { + const char *label, *name; + const char *slot, *slotRelease; // if slotRelease!=0 + // : use keyPress/ReleaseEvent mecanism +}; + +/** + * This structure contains information about the game + * configuration. + */ +typedef struct { + /** The game version id used for identification (e.g. "4"). + * You should change this id when the game is made incompatible + * with previous version. (changes in data for example). + */ + const char *gameId; + + /** Maximum number of local players. */ + uint maxNbLocalPlayers; + + /** Interval (in msec.) between data exchange. */ + uint interval; + + /** If there are built-in artificial intelligences that can play. */ + bool AIAllowed; + + /** Slot for player/AI additional configuration. These must be SLOTs which + * take an "int" as parameter. It must open a setting + * dialog for the corresponding local player/computer and save the + * new settings in the config file. It should probably create a group + * with the given number in its name. + * If such a pointer is set to 0 : it means there is no perticular + * setting. + */ + const char *humanSettingSlot, *AISettingSlot; +} MPGameInfo; + +/** + * The MPInterface class is useful for multiplayers game + * management. Each game is represented by a class you have inherited + * from the @ref MPBoard class. + * + * Multiplayers games can take place with several (humans or eventually + * AIs) players on the same computer (they use the same keyboard and have + * each a @ref MPBoard widget on the screen) or/and network players. + * + * This class is intended to do all the hard work of sending/receiving data + * between the players and to send the keyboard events to the right + * @ref MPBoard. So multiplayers game should be completely transparent + * from your point of view. + * + * Note : The data exchange is done in background with a timer calling at given + * intervals the read/write methods. Obviously this kind of things can be done + * easily with threads but I have no experience with thread programming + * and not all people have thread libraries and a thread-safe system. + */ +class MPInterface : public QWidget +{ + Q_OBJECT + + public: + /** Constructor which takes a MPGameInfo struct as parameter. + */ + MPInterface(const MPGameInfo &gameInfo, + uint nbActions, const ActionData *data, + QWidget *parent = 0, const char *name = 0); + virtual ~MPInterface(); + + public slots: + /** Create a single player game for a human being. + * Call @ref stop if a game is already created. */ + void singleHuman() { specialLocalGame(1, 0); } + /** Create a local game opposing two human beings. + * Call @ref stop if a game is already created. */ + void humanVsHuman() { specialLocalGame(2, 0); } + /** Create a local game opposing a human with an AI. + * Call @ref stop if a game is already created. */ + void humanVsComputer() { specialLocalGame(1, 1); } + + /** Open a dialog to create a multiplayer game. + * Call @ref stop if a game is already created. */ + void dialog(); + + public: + virtual void addKeys(KKeyDialog &); + void saveKeys(); + + /** Called when a new game is created. At this point + * the number of players is known. */ + virtual void init() {} + + /** Called just before a new game is created (called by + * singleHuman, humanVsHuman, humanVsComputer and dialog). */ + virtual void stop() {} + + /** Called when the start button of the netmeeting is pressed. */ + virtual void start() {} + + /** + * Set keys configuration for the given number of human players. + * The size of the array is the number of defined actions. + */ + void setDefaultKeycodes(uint nbHumans, uint human, const int *keycodes); + + /** + * @return the total number of players. + * (If called from client : return the number of local boards). + */ + uint nbPlayers() const; + + /** + * @return true if the interface is the server. + */ + bool server() const { return _server; } + + /** @return the player name. + Do not call from client ! + */ + QString playerName(uint i) const; + + /** + * Create a new @ref MPBoard. + * + * @param i is the game index that goes from 0 to the number of + * local players : it can be used to retrieve configuration settings. + */ + virtual MPBoard *newBoard(uint i) = 0; + + /** + * This method must read data from each client with method + * @ref readingStream, do the needed treatement + * (for instance which players has lost, which data to be resent, ...) and + * then write the useful data to each client with method + * @ref writingStream. + * + * NB: this method is also called for single player games but + * you probably only want to check for game over condition (it depends + * on game implementation that you really return data to the board). + */ + virtual void treatData() = 0; + + /** @return the reading stream for board #i. + * Do not call from client ! + */ + QDataStream &readingStream(uint i) const; + + /** @return the writing stream for board #i. + */ + QDataStream &writingStream(uint i) const; + + /** + * Read data sent from server to clients "MultiplayersInterface" + * (this data is not addressed to boards). + * These are meta data that are not directly used in game. + * It can be used to display "game over" infos for all + * local games. + * NB: the use of this method is optional. + */ + virtual void dataFromServer(QDataStream &) {} + + /** Used by the server to write meta data to clients. + * NB: the use of this method is optional. + * Do not call from client ! + */ + QDataStream &dataToClientsStream() const; + + /** Write immediately data to clients and local boards. + * It is unlike the normal exchange which is driven + * by the timer of the server. Be aware of possible + * interactions. + */ + void immediateWrite(); + + /** + * This method should be overload if an option widget is used in the + * the "netmeeting" dialog). By default a + * null pointer is returned and so no option widget is shown. + * The option widget must be inherited from the @ref MPOptionWidget class. + */ + virtual MPOptionWidget *newOptionWidget() const { return 0; } + + /** Called when a network error occurred or when a host gets disconnected. + * The default implementation displays a message and calls singleHumans() + * ie it stops the current game. By overloading this method, it is + * possible to continue the game at this point with the remaining players. + */ + virtual void hostDisconnected(uint i, const QString &msg); + + protected: + void paintEvent(QPaintEvent *); + void keyPressEvent(QKeyEvent *); + void keyReleaseEvent(QKeyEvent *); + + private slots: + void enableKeys(bool enable); + void singleHumanSlot(); + + public: + class Data { + public: + Data() {} + MPBoard *ptr; + int humanIndex; + QString name; + }; + + private: + Local *internal; + const MPGameInfo gameInfo; + QValueList boards; + uint nbLocalHumans; + QHBoxLayout *hbl; + bool _server, disconnected; + + KeyData *_keyData; + QMemArray _keyCol; + + void createServerGame(const QPtrList &); + void createClientGame(const RemoteHostData &); + void createLocalGame(const ConnectionData &); + void specialLocalGame(uint nbHumans, uint nbComputers); + + void clear(); + void initKeys(uint nbHumans); +}; + +#endif // MP_INTERFACE_H diff --git a/libksirtet/lib/mp_option.h b/libksirtet/lib/mp_option.h new file mode 100644 index 00000000..94c59107 --- /dev/null +++ b/libksirtet/lib/mp_option.h @@ -0,0 +1,74 @@ +#ifndef MP_OPTION_H +#define MP_OPTION_H + +#include + +/** + * The OptionWidget is a base widget for the option widget in the + * "netmeeting" dialog. This option widget is optional (!). + * + * For example you will have : + *
+ *  class MyOptionWidget : public OptionWidget { ... };
+ *  class MyMultiPlayerInterface : public MultiPlayerInterface { ... };
+ *   
+ *  OptionWidget *MyMultiPlayerInterface::newOptionWidget(bool server) const
+ *  { return new MyOptionWidget(server); };
+ * 
+ * + * The option widget must have two different behaviours for server and + * clients. The server is able to change the options but the clients are only + * able to see them. The library will catch the @ref #changed signal from + * the option widget and will send the changes to the clients. It uses the + * @ref #dataOut to obtain the data from the + * server option dialog and then on the client side, it sets the new data with + * the method @ref #dataIn. + * You must implement this three methods to have useful option widgets and be + * careful to emit the signal @ref #changed whenever the server widget is + * changed. In addition you'll need to implement the method @ref #saveData + * to save the configuration in the config file ; so that it will be available + * for the initialisation of @ref LocalBoard. + * It seems a good idea that the widget have the same layout + * on both (server and client) sides but with the inner widgets all disabled + * on the client side. + */ +class MPOptionWidget : public QWidget +{ + Q_OBJECT + + public: + MPOptionWidget(bool Server, QWidget *parent = 0, const char *name = 0) + : QWidget(parent, name), server(Server) {} + virtual ~MPOptionWidget() {} + + bool isServer() const { return server; } + + /** + * This method is used on the client side to set the data coming from + * the server widget. + */ + virtual void dataIn(QDataStream &s) = 0; + + /** This method is used on the server side to get the data. */ + virtual void dataOut(QDataStream &s) const = 0; + + /** + * When the game will begin (ie when the "netmeeting" is over and the + * server has press the "start game" button) this method will be called + * (for clients and server). It must save the settings in the config + * file. + */ + virtual void saveData() = 0; + + signals: + /** + * This signal must be called each time options are changed + * (by the server). + */ + void changed(); + + private: + bool server; +}; + +#endif // MP_OPTION_H diff --git a/libksirtet/lib/mp_simple_board.cpp b/libksirtet/lib/mp_simple_board.cpp new file mode 100644 index 00000000..f032c488 --- /dev/null +++ b/libksirtet/lib/mp_simple_board.cpp @@ -0,0 +1,84 @@ +#include "mp_simple_board.h" +#include "mp_simple_board.moc" + + +void MPSimpleBoard::init(bool AI, bool multiplayers, bool server, bool first, + const QString &name) +{ + state = BS_Init; + _init(AI, multiplayers, server, first, name); +} + +void MPSimpleBoard::dataIn(QDataStream &s) +{ + if ( s.atEnd() ) return; // no data + + IO_Flag f; + s >> f; + switch ( f.value() ) { + case IO_Flag::Init: initFlag(s); break; + case IO_Flag::Play: playFlag(s); break; + case IO_Flag::Pause: pauseFlag(); break; + case IO_Flag::GameOver: gameOverFlag(); break; + case IO_Flag::Stop: stopFlag(); break; + } +} + +void MPSimpleBoard::initFlag(QDataStream &s) +{ + state = BS_Play; + emit enableKeys(true); + _initFlag(s); +} + +void MPSimpleBoard::playFlag(QDataStream &s) +{ + Q_ASSERT( state==BS_Play ); + _playFlag(s); +} + +void MPSimpleBoard::pauseFlag() +{ + Q_ASSERT( state==BS_Play || state==BS_Pause ); + bool p = ( state==BS_Pause ); + state = (p ? BS_Play : BS_Pause); + emit enableKeys(p); + _pauseFlag(!p); +} + +void MPSimpleBoard::gameOverFlag() +{ + Q_ASSERT( BS_Play ); + _stop(TRUE); + state = BS_Stop; +} + +void MPSimpleBoard::stopFlag() +{ + _stop(FALSE); + state = BS_Standby; +} + +void MPSimpleBoard::_stop(bool gameover) +{ + if ( state==BS_Pause ) _pauseFlag(FALSE); + emit enableKeys(false); + _stopFlag(gameover); +} + +void MPSimpleBoard::dataOut(QDataStream &s) +{ + switch (state) { + case BS_Init: + _initDataOut(s); + state = BS_Standby; + return; + case BS_Play: _dataOut(s); return; + case BS_Stop: + _gameOverDataOut(s); + state = BS_Standby; + return; + case BS_Pause: return; + case BS_Standby: return; + } +} diff --git a/libksirtet/lib/mp_simple_board.h b/libksirtet/lib/mp_simple_board.h new file mode 100644 index 00000000..2131feb2 --- /dev/null +++ b/libksirtet/lib/mp_simple_board.h @@ -0,0 +1,45 @@ +#ifndef MP_SIMPLE_BOARD_H +#define MP_SIMPLE_BOARD_H + +#include "mp_board.h" +#include "mp_simple_types.h" + +#include + +class KDE_EXPORT MPSimpleBoard : public MPBoard +{ + Q_OBJECT + + public: + MPSimpleBoard(QWidget *parent = 0, const char *name = 0) + : MPBoard(parent, name) {} + virtual ~MPSimpleBoard() {} + + void init(bool AI, bool multiplayers, bool server, bool first, + const QString &name); + void dataOut(QDataStream &s); + void dataIn(QDataStream &s); + + protected: + virtual void _init(bool AI, bool multiplayers, bool server, bool first, + const QString &name) = 0; + virtual void _initFlag(QDataStream &s) = 0; + virtual void _playFlag(QDataStream &s) = 0; + virtual void _pauseFlag(bool pause) = 0; + virtual void _stopFlag(bool gameover) = 0; + virtual void _dataOut(QDataStream &s) = 0; + virtual void _gameOverDataOut(QDataStream &s) = 0; + virtual void _initDataOut(QDataStream &s) = 0; + + private: + BoardState state; + + void initFlag(QDataStream &s); + void playFlag(QDataStream &s); + void pauseFlag(); + void stopFlag(); + void gameOverFlag(); + void _stop(bool button); +}; + +#endif // MP_SIMPLE_BOARD_H diff --git a/libksirtet/lib/mp_simple_interface.cpp b/libksirtet/lib/mp_simple_interface.cpp new file mode 100644 index 00000000..e75243a6 --- /dev/null +++ b/libksirtet/lib/mp_simple_interface.cpp @@ -0,0 +1,152 @@ +#include "mp_simple_interface.h" +#include "mp_simple_interface.moc" + +#include +#include +#include +#include +#include + + +#define PAUSE_ACTION \ + ((KToggleAction *)((KMainWindow *)topLevelWidget())->action("game_pause")) + +MPSimpleInterface::MPSimpleInterface(const MPGameInfo &gi, + uint nbActions, const ActionData *data, + QWidget *parent, const char *name) +: MPInterface(gi, nbActions, data, parent, name), state(SS_Standby) +{} + +void MPSimpleInterface::init() +{ + if ( server() ) { + state = SS_Standby; + first_init = TRUE; + } + _init(); +} + +void MPSimpleInterface::start() +{ + // WARNING : multiple calls can happen here (because button + // hiding is delayed) + state = SS_Init; +} + +void MPSimpleInterface::stop() +{ + state = SS_Standby; + SC_Flag f1(SC_Flag::Stop); + if ( server() ) dataToClientsStream() << f1; + IO_Flag f2(IO_Flag::Stop); + for (uint i=0; i> scf; + switch (scf.value()) { + case SC_Flag::Stop: + KMessageBox::information(this, i18n("Server has left game!")); + QTimer::singleShot(0, this, SLOT(singleHuman())); + return; + case SC_Flag::GameOver: + _readGameOverData(s); + _showGameOverData(); + return; + } +} + +void MPSimpleInterface::treatData() +{ + switch (state) { + case SS_Init: treatInit(); break; + case SS_Play: treatPlay(); break; + case SS_Pause: break; + case SS_Stop: treatStop(); break; + case SS_Standby: break; + case SS_PauseAsked: treatPause(TRUE); break; + case SS_UnpauseAsked: treatPause(FALSE); break; + } +} + +void MPSimpleInterface::treatInit() +{ + state = SS_Play; + + if (first_init) { + _firstInit(); + first_init = FALSE; + } + + IO_Flag f(IO_Flag::Init); + for (uint i=0; isetEnabled(true); + PAUSE_ACTION->setChecked(false); + + bool end = _readPlayData(); + if (end) { + state = SS_Stop; + IO_Flag f(IO_Flag::GameOver); + for (uint i=0; isetChecked(pause); +} + +void MPSimpleInterface::treatStop() +{ + state = SS_Standby; + + // read game over data + send them to all clients + QDataStream &s = dataToClientsStream(); + SC_Flag f(SC_Flag::GameOver); + s << f; + _sendGameOverData(s); + _showGameOverData(); + + PAUSE_ACTION->setEnabled(false); + PAUSE_ACTION->setChecked(false); +} diff --git a/libksirtet/lib/mp_simple_interface.h b/libksirtet/lib/mp_simple_interface.h new file mode 100644 index 00000000..84e6f444 --- /dev/null +++ b/libksirtet/lib/mp_simple_interface.h @@ -0,0 +1,48 @@ +#ifndef MP_SIMPLE_INTERFACE_H +#define MP_SIMPLE_INTERFACE_H + +#include "mp_interface.h" +#include "mp_simple_types.h" + +class MPSimpleInterface : public MPInterface +{ + Q_OBJECT + + public: + MPSimpleInterface(const MPGameInfo &gi, + uint nbActions, const ActionData *data, + QWidget *parent = 0, const char *name = 0); + + bool isPaused() const { return state==SS_Pause; } + + public slots: + void start(); + void pause(); + void addKeys(KKeyDialog &); + + protected: + virtual void _init() = 0; + virtual void _readGameOverData(QDataStream &s) = 0; + virtual void _sendGameOverData(QDataStream &s) = 0; + virtual void _showGameOverData() = 0; + virtual void _firstInit() = 0; + virtual void _treatInit() = 0; + virtual bool _readPlayData() = 0; + virtual void _sendPlayData() = 0; + + private: + ServerState state; + bool first_init; + + void treatData(); + void treatInit(); + void treatPlay(); + void treatPause(bool pause); + void treatStop(); + + void init(); + void stop(); + void dataFromServer(QDataStream &); +}; + +#endif // MP_SIMPLE_INTERFACE_H diff --git a/libksirtet/lib/mp_simple_types.cpp b/libksirtet/lib/mp_simple_types.cpp new file mode 100644 index 00000000..a470ce7c --- /dev/null +++ b/libksirtet/lib/mp_simple_types.cpp @@ -0,0 +1,6 @@ +#include "mp_simple_types.h" + +QDataStream &operator <<(QDataStream &s, const EnumClass &ec) + { s << (Q_UINT8)ec.f; return s; } +QDataStream &operator >>(QDataStream &s, EnumClass &ec) + { Q_UINT8 t; s >> t; ec.f = (int)t; return s; } diff --git a/libksirtet/lib/mp_simple_types.h b/libksirtet/lib/mp_simple_types.h new file mode 100644 index 00000000..2fd0c289 --- /dev/null +++ b/libksirtet/lib/mp_simple_types.h @@ -0,0 +1,36 @@ +#ifndef WF_TYPES_H +#define WF_TYPES_H + +#include + +class EnumClass +{ + public: + EnumClass(int _f) : f(_f) {} + int f; +}; +QDataStream &operator <<(QDataStream &s, const EnumClass &f); +QDataStream &operator >>(QDataStream &s, EnumClass &f); + +class IO_Flag : public EnumClass +{ + public: + enum IOF { Init = 0, Play, Pause, Stop, GameOver }; + IO_Flag(IOF f = Init) : EnumClass(f) {} + IOF value() const { return (IOF)f; } +}; + +enum ServerState { SS_Init, SS_Play, SS_Pause, SS_Stop, SS_Standby, + SS_PauseAsked, SS_UnpauseAsked }; + +class SC_Flag : public EnumClass +{ + public: + enum SC { Stop = 0, GameOver }; + SC_Flag(SC f = Stop) : EnumClass(f) {} + SC value() const { return (SC)f; } +}; + +enum BoardState { BS_Init, BS_Play, BS_Pause, BS_Stop, BS_Standby }; + +#endif diff --git a/libksirtet/lib/pline.cpp b/libksirtet/lib/pline.cpp new file mode 100644 index 00000000..41faf6ac --- /dev/null +++ b/libksirtet/lib/pline.cpp @@ -0,0 +1,147 @@ +#include "pline.h" +#include "pline.moc" + +#include +#include +#include +#include "defines.h" + +#define THIN_BORDER 4 + +MeetingLine::MeetingLine(bool isOwner, bool serverIsReader, bool serverLine, + QWidget *parent, const char *name) +: QFrame(parent, name) +{ + setFrameStyle(Panel | (serverLine ? Raised : Plain)); + + // Top layout + hbl = new QHBoxLayout(this, THIN_BORDER + frameWidth()); + + /* TriCheckBox */ + tcb = new MeetingCheckBox(MeetingCheckBox::Ready, isOwner, serverIsReader, + this); + if ( !XOR(isOwner, serverIsReader) ) tcb->setEnabled(FALSE); + else connect(tcb, SIGNAL(changed(int)), SLOT(_typeChanged(int))); + hbl->addWidget(tcb); + + /* Name */ + lname = new QLabel(" ", this); + lname->setAlignment(AlignCenter); + lname->setFrameStyle(QFrame::Panel | QFrame::Sunken); + lname->setLineWidth(2); + lname->setMidLineWidth(3); + QFont f = lname->font(); + f.setBold(TRUE); + lname->setFont(f); + lname->setFixedSize(lname->fontMetrics().maxWidth()*NAME_MAX_LENGTH, + lname->sizeHint().height()); + hbl->addWidget(lname); + hbl->addStretch(1); + + // Nb humans + labH = new QLabel(this); + hbl->addWidget(labH); + + // Nb AIs + labAI = new QLabel(this); + hbl->addWidget(labAI); + + // talker + qle = new QLineEdit(this); + qle->setMaxLength(TALKER_MAX_LENGTH); + qle->setFont( QFont("fixed", 12, QFont::Bold) ); + qle->setFixedSize(qle->fontMetrics().maxWidth()*TALKER_MAX_LENGTH, + qle->sizeHint().height()); + connect(qle, SIGNAL(textChanged(const QString &)), + SLOT(_textChanged(const QString &))); + qle->setEnabled(isOwner); + hbl->addWidget(qle); +} + +void MeetingLine::setData(const ExtData &ed) +{ + bds = ed.bds; + uint nbh = 0, nba = 0; + for (uint i=0; isetText(i18n("Hu=%1").arg(nbh)); + labAI->setText(i18n("AI=%1").arg(nba)); + lname->setText(bds[0].name); + setType(ed.type); + setText(ed.text); +} + +void MeetingLine::data(ExtData &ed) const +{ + ed.bds = bds; + ed.type = tcb->type(); + ed.text = text(); +} + +/*****************************************************************************/ +PlayerLine::PlayerLine(PlayerComboBox::Type type, const QString &txt, + bool humanSetting, bool AISetting, + bool canBeEmpty, bool acceptAI, + QWidget *parent, const char *name) +: QFrame(parent, name), hs(humanSetting), as(AISetting) +{ + setFrameStyle(Panel | Raised); + + // Top layout + QHBoxLayout *hbl; + hbl = new QHBoxLayout(this, THIN_BORDER + frameWidth()); + + /* CheckBox */ + pcb = new PlayerComboBox(type, canBeEmpty, acceptAI, this); + connect(pcb, SIGNAL(changed(int)), SLOT(typeChangedSlot(int))); + hbl->addWidget(pcb); + + /* Name */ + edit = new QLineEdit(txt, this); + edit->setMaxLength(NAME_MAX_LENGTH); + edit->setFixedSize(edit->fontMetrics().maxWidth()*(NAME_MAX_LENGTH+2), + edit->sizeHint().height()); + hbl->addWidget(edit); + + /* settings button */ + setting = new QPushButton(i18n("Settings"), this); + connect(setting, SIGNAL(clicked()), SLOT(setSlot())); + hbl->addWidget(setting); + + typeChangedSlot(type); +} + +void PlayerLine::typeChangedSlot(int t) +{ + edit->setEnabled(type()!=PlayerComboBox::None); + setting->setEnabled( (type()==PlayerComboBox::Human && hs) + || (type()==PlayerComboBox::AI && as) ); + emit typeChanged(t); +} + +void PlayerLine::setSlot() +{ + if ( type()==PlayerComboBox::Human ) emit setHuman(); + else emit setAI(); +} + +/*****************************************************************************/ +GWidgetList::GWidgetList(uint interval, QWidget *parent, const char * name) + : QWidget(parent, name), vbl(this, interval) +{ + widgets.setAutoDelete(TRUE); +} + +void GWidgetList::append(QWidget *wi) +{ + vbl.addWidget(wi); + wi->show(); + widgets.append(wi); +} + +void GWidgetList::remove(uint i) +{ + widgets.remove(i); +} diff --git a/libksirtet/lib/pline.h b/libksirtet/lib/pline.h new file mode 100644 index 00000000..2defd10a --- /dev/null +++ b/libksirtet/lib/pline.h @@ -0,0 +1,112 @@ +#ifndef PLINE_H +#define PLINE_H + +#include +#include +#include +#include +#include +#include + +#include "types.h" + +class QPushButton; + +/** Internal class : display a "player line" in netmeeting. */ +class MeetingLine : public QFrame +{ + Q_OBJECT + + public: + MeetingLine(bool isOwner, bool readerIsServer, bool serverLine, + QWidget *parent, const char *name = 0); + + MeetingCheckBox::Type type() const { return tcb->type(); } + void setType(MeetingCheckBox::Type type) { tcb->setType(type); } + void setText(const QString &text) { qle->setText(text); } + + void setData(const ExtData &ed); + void data(ExtData &ed) const; + QString text() const { return qle->text(); } + + signals: + void typeChanged(MeetingCheckBox::Type); + void textChanged(const QString &); + + private slots: + void _typeChanged(int t) + { emit typeChanged((MeetingCheckBox::Type)t); } + void _textChanged(const QString &text) { emit textChanged(text); } + + protected: + QHBoxLayout *hbl; + + private: + MeetingCheckBox *tcb; + QLabel *lname, *labH, *labAI; + QValueList bds; + QLineEdit *qle; +}; + +class PlayerLine : public QFrame +{ + Q_OBJECT + + public: + PlayerLine(PlayerComboBox::Type type, const QString &txt, + bool humanSetting, bool AISetting, + bool canBeEmpty, bool acceptAI, + QWidget *parent = 0, const char *name = 0); + + PlayerComboBox::Type type() const { return pcb->type(); } + QString name() const { return edit->text(); } + + signals: + void setHuman(); + void setAI(); + void typeChanged(int); + + private slots: + void setSlot(); + void typeChangedSlot(int); + + private: + PlayerComboBox *pcb; + QLineEdit *edit; + QPushButton *setting; + bool hs, as; +}; + +/** Internal class : scrolable list of widgets. */ +class GWidgetList : public QWidget +{ + Q_OBJECT + + public: + GWidgetList(uint interval, QWidget *parent = 0, const char * name = 0); + + void remove(uint i); + uint size() const { return widgets.count(); } + + protected: + /** The widget must be created with this widget as parent. */ + void append(QWidget *); + QWidget *widget(uint i) { return widgets.at(i); } + + private: + QPtrList widgets; + QVBoxLayout vbl; +}; + +template +class WidgetList : public GWidgetList +{ + public: + WidgetList(uint interval, QWidget *parent=0, const char *name=0) + : GWidgetList(interval, parent, name) {} + + void append(Type *w) { GWidgetList::append(w); } + Type *widget(uint i) { return (Type *)GWidgetList::widget(i); } +}; + +#endif // PLINE_H diff --git a/libksirtet/lib/smanager.cpp b/libksirtet/lib/smanager.cpp new file mode 100644 index 00000000..f6588546 --- /dev/null +++ b/libksirtet/lib/smanager.cpp @@ -0,0 +1,115 @@ +#include "smanager.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +SocketManager::SocketManager() +{ + max_fd = 0; + FD_ZERO(&read_set); + FD_ZERO(&write_set); + nbWriteable = 0; +} + +SocketManager::~SocketManager() +{ + clean(); +} + +void SocketManager::clean() +{ + for(uint i=0; ifd()==fd ) return i; + return -1; +} + +uint SocketManager::append(Socket *socket, SocketProperty sp) +{ + uint s = sockets.size(); + sockets.resize(s+1); + sockets[s] = socket; + + max_fd = QMAX(max_fd, socket->fd()); + + if ( sp==ReadWrite || sp==ReadOnly ) FD_SET(socket->fd(), &read_set); + if ( sp==ReadWrite || sp==WriteOnly ) { + nbWriteable++; + FD_SET(socket->fd(), &write_set); + } + + return s; +} + +void SocketManager::remove(uint i, bool del) +{ + Socket *so = sockets[i]; + + uint s = sockets.size()-1; + for(uint j=i; jfd()); + + int fd = so->fd(); + if ( FD_ISSET(fd, &read_set) ) FD_CLR(fd, &read_set); + if ( FD_ISSET(fd, &write_set) ) { + nbWriteable--; + FD_CLR(fd, &write_set); + } + + if (del) delete so; +} + +bool SocketManager::canWriteAll(uint sec, uint usec) +{ + write_tmp = write_set; + tv.tv_sec = sec; + tv.tv_usec = usec; + return ( select(max_fd+1, 0, &write_tmp, 0, &tv)==(int)nbWriteable ); +} + +bool SocketManager::canWrite(uint i, uint sec, uint usec) +{ + int fd = sockets[i]->fd(); + FD_ZERO(&write_tmp); + FD_SET(fd, &write_tmp); + tv.tv_sec = sec; + tv.tv_usec = usec; + return ( select(fd+1, 0, &write_tmp, 0, &tv)==1 ); +} + +bool SocketManager::checkPendingData(uint sec, uint usec) +{ + read_tmp = read_set; + tv.tv_sec = sec; + tv.tv_usec = usec; + return ( select(max_fd+1, &read_tmp, 0, 0, &tv)!=-1 ); +} + +bool SocketManager::dataPending(uint i) +{ + int fd = sockets[i]->fd(); + return FD_ISSET(fd, &read_tmp); +} + +bool SocketManager::writeCommon(uint i) +{ + return sockets[i]->write(writing.buffer()); +} diff --git a/libksirtet/lib/smanager.h b/libksirtet/lib/smanager.h new file mode 100644 index 00000000..a831b702 --- /dev/null +++ b/libksirtet/lib/smanager.h @@ -0,0 +1,88 @@ +#ifndef SMANAGER_H +#define SMANAGER_H + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#ifdef HAVE_SYS_SELECT_H +#include // Needed on some systems. +#endif + +#include + +#include "socket.h" + +/** + * The SocketManager class is useful to manage (rw, ro, wo) sockets. + * + * You must add the sockets you want to manage to this class before other + * operations. The sockets are stored in an array and other methods reference + * the sockets by their index in that array. + */ +class SocketManager +{ + public: + SocketManager(); + + /** Be aware that unremoved sockets will be closed there. */ + ~SocketManager(); + + /** Remove all sockets and close them. */ + void clean(); + + enum SocketProperty { ReadOnly, WriteOnly, ReadWrite }; + + /** @return the number of sockets. */ + uint size() const { return sockets.size(); } + + const Socket *operator[](uint i) const { return sockets[i]; } + Socket *operator [](uint i) { return sockets[i]; } + + /** @return the index of the socket (-1 if not present). */ + int find(int fd); + + /** + * Append a socket at the end of the array of sockets. + * @param sp determines if the socket will be used for ReadWrite, + * ReadOnly or WriteOnly operations. + * @return the index of the socket. + */ + uint append(Socket *, SocketProperty sp = ReadWrite); + + /** + * Remove the socket indexed i. Note that the following sockets in + * the array will have their index decremented by one. + * @param deleteSocket if true, the socket is deleted + */ + void remove(uint i, bool deleteSocket); + + /** @return TRUE if it is possible to write to all the writeable sockets. */ + bool canWriteAll(uint sec = 0, uint usec = 0); + + /** @return TRUE if it is possible to write to the specified socket. */ + bool canWrite(uint i, uint sec = 0, uint usec = 0); + + Stream &commonWritingStream() { return writing; } + bool writeCommon(uint i); // do not clear stream + + /** + * Check if there are pending data on at least one of the readeable + * socket. + */ + bool checkPendingData(uint sec = 0, uint usec = 0); + + bool dataPending(uint i); + + private: + QMemArray sockets; + + fd_set read_set, write_set, read_tmp, write_tmp; + struct timeval tv; + int max_fd; + uint nbWriteable; + + WritingStream writing; +}; + +#endif // SMANAGER_H diff --git a/libksirtet/lib/socket.cpp b/libksirtet/lib/socket.cpp new file mode 100644 index 00000000..9ef3ba3c --- /dev/null +++ b/libksirtet/lib/socket.cpp @@ -0,0 +1,80 @@ +#include "socket.h" + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_FILIO_H +#include // for FIONREAD +#endif + +Socket::Socket(KExtendedSocket *s, bool createNotifier, + QObject *parent, const char *name) +: _socket(s), _notifier(0) +{ + Q_ASSERT(s); + if (createNotifier) { + _notifier = new QSocketNotifier(s->fd(), QSocketNotifier::Read, + parent, name); + _notifier->setEnabled(FALSE); + } +} + +Socket::~Socket() +{ + delete _notifier; + delete _socket; +} + +bool Socket::write(const QByteArray &a) +{ + return ( _socket->writeBlock(a.data(), a.size())==(int)a.size() ); +} + +bool Socket::write() +{ + bool res = write(writing.buffer()); + writing.clear(); + return res; +} + +int Socket::pendingData() const +{ + int size = 0; + if ( ioctl(_socket->fd(), FIONREAD, (char *)&size)<0 ) return -1; + return size; +} + +int Socket::read() +{ + reading.clearRead(); + + int size = pendingData(); + if ( size==-1 ) return -1; + + reading.device()->close(); + int dec = reading.size(); + reading.buffer().resize(dec + size); + size = _socket->readBlock(reading.buffer().data() + dec, size); + if ( size==-1 ) reading.buffer().resize(dec); + reading.device()->open(IO_ReadOnly); + + return size; +} + +int Socket::accept(KExtendedSocket *&s) +{ + return _socket->accept(s); +} diff --git a/libksirtet/lib/socket.h b/libksirtet/lib/socket.h new file mode 100644 index 00000000..a2f47a63 --- /dev/null +++ b/libksirtet/lib/socket.h @@ -0,0 +1,65 @@ +#ifndef SOCKET_H +#define SOCKET_H + +#include + +#include + +#include "types.h" + + +class Socket +{ + public: + Socket(KExtendedSocket *, bool createNotifier = FALSE, + QObject *parent = 0, const char *name = 0); + + /** close the socket */ + ~Socket(); + + int fd() const { return _socket->fd(); } + + /** + * Accept a new socket. + */ + int accept(KExtendedSocket *&); + + /** + * @return the socket notifier associated with the socket + * (0 if none). + */ + QSocketNotifier *notifier() const { return _notifier; } + + /** + * Write data contained in the writing stream to the socket. + * It clears the stream. + * @return TRUE if all was written without error. + */ + bool write(); + bool write(const QByteArray &a); + + /** @return the QDataStream for writing. */ + WritingStream &writingStream() { return writing; } + + /** @return the size of pending data. */ + int pendingData() const; + + /** + * Read data from socket and append them to reading stream for the specified socket. + * The portion of the stream that has been read is cleared. + * @return the read size or -1 on error + */ + int read(); + + /** @return the reading stream. */ + ReadingStream &readingStream() { return reading; } + + private: + KExtendedSocket *_socket; + QSocketNotifier *_notifier; + + WritingStream writing; + ReadingStream reading; +}; + +#endif // SOCKET_H diff --git a/libksirtet/lib/types.cpp b/libksirtet/lib/types.cpp new file mode 100644 index 00000000..557fffff --- /dev/null +++ b/libksirtet/lib/types.cpp @@ -0,0 +1,254 @@ +#include "types.h" + +#include +#include "version.h" + +cId::cId(const QString &_gameName, const QString &_gameId) +: libId(MULTI_ID), gameName(_gameName), gameId(_gameId) +{} + +void cId::check(const cId &id) +{ + if ( libId!=id.libId ) state = LibIdClash; + else if ( gameName!=id.gameName ) state = GameNameClash; + else if ( gameId!=id.gameId ) state = GameIdClash; + else state = Accepted; +} + +QString cId::errorMessage(const cId &id) const +{ + QString str = i18n("\nServer: \"%1\"\nClient: \"%2\""); + + switch (state) { + case Accepted: return QString::null; + case LibIdClash: + return i18n("The MultiPlayer library of the server is incompatible") + + str.arg(libId).arg(id.libId); + case GameNameClash: + return i18n("Trying to connect a server for another game type") + + str.arg(gameName).arg(id.gameName); + case GameIdClash: + return i18n("The server game version is incompatible") + + str.arg(gameId).arg(id.gameId); + } + Q_ASSERT(0); + return QString::null; +} + +QDataStream &operator << (QDataStream &s, const cId &id) +{ + s << id.libId << id.gameName << id.gameId << (Q_UINT8)id.state; + return s; +} + +QDataStream &operator >> (QDataStream &s, cId &id) +{ + Q_UINT8 state; + s >> id.libId >> id.gameName >> id.gameId >> state; + id.state = (cId::State)state; + return s; +} + +//----------------------------------------------------------------------------- +QDataStream &operator << (QDataStream &s, const MeetingMsgFlag &f) +{ + s << (Q_UINT8)f; + return s; +} + +QDataStream &operator >> (QDataStream &s, MeetingMsgFlag &f) +{ + Q_UINT8 i; + s >> i; f = (MeetingMsgFlag)i; + return s; +} + +//----------------------------------------------------------------------------- +QDataStream &operator << (QDataStream &s, const TextInfo &ti) +{ + s << (Q_UINT32)ti.i << ti.text; + return s; +} + +QDataStream &operator >> (QDataStream &s, TextInfo &ti) +{ + Q_UINT32 i; + s >> i >> ti.text; ti.i = i; + return s; +} + +//----------------------------------------------------------------------------- +QDataStream &operator << (QDataStream &s, const MeetingCheckBox::Type &t) +{ + s << (Q_UINT8)t; + return s; +} + +QDataStream &operator >> (QDataStream &s, MeetingCheckBox::Type &t) +{ + Q_UINT8 i; + s >> i; t = (MeetingCheckBox::Type)i; + return s; +} + +//----------------------------------------------------------------------------- +QDataStream &operator << (QDataStream &s, const TypeInfo &ti) +{ + s << (Q_UINT32)ti.i << ti.type; + return s; +} + +QDataStream &operator >> (QDataStream &s, TypeInfo &ti) +{ + Q_UINT32 i; + s >> i >> ti.type; ti.i = i; + return s; +} + +//----------------------------------------------------------------------------- +QDataStream &operator << (QDataStream &s, const BoardData &bd) +{ + s << (Q_UINT8)bd.type << bd.name; + return s; +} + +QDataStream &operator >> (QDataStream &s, BoardData &bd) +{ + Q_UINT8 i; + s >> i >> bd.name; + bd.type = (PlayerComboBox::Type)i; + return s; +} + +//----------------------------------------------------------------------------- +QDataStream &operator << (QDataStream &s, const ExtData &ed) +{ + s << ed.bds << ed.text << ed.type; + return s; +} + +QDataStream &operator >> (QDataStream &s, ExtData &ed) +{ + s >> ed.bds >> ed.text >> ed.type; + return s; +} + +//----------------------------------------------------------------------------- +QDataStream &operator << (QDataStream &s, const MeetingLineData &pld) +{ + s << pld.ed << (Q_UINT8)pld.own; + return s; +} + +QDataStream &operator >> (QDataStream &s, MeetingLineData &pld) +{ + Q_UINT8 b; + s >> pld.ed >> b; pld.own = b; + return s; +} + +//----------------------------------------------------------------------------- +QDataStream &operator << (QDataStream &s, const MetaFlag &f) +{ + s << (Q_UINT8)f; + return s; +} + +QDataStream &operator >> (QDataStream &s, MetaFlag &f) +{ + Q_UINT8 i; + s >> i; f = (MetaFlag)i; + return s; +} + + +//----------------------------------------------------------------------------- +Stream::Stream(int _mode) +: mode(_mode) +{ + setDevice(&buf); + Q_ASSERT( _mode==IO_ReadOnly || _mode==IO_WriteOnly ); + buf.open(_mode); +} + +void Stream::clear() +{ + buf.close(); + buf.open(mode | IO_Truncate); +} + +void Stream::setArray(QByteArray a) +{ + buf.close(); + buf.setBuffer(a); + buf.open(mode); +} + +bool ReadingStream::readOk() +{ + return ( buf.status()==IO_Ok ); +} + +void ReadingStream::clearRead() +{ + int i = buf.at(); + if ( i==0 ) return; + buf.close(); + QByteArray a; + a.duplicate(buffer().data() + i, size() - i); + buf.setBuffer(a); + buf.open(IO_ReadOnly); +} + +//----------------------------------------------------------------------------- +void IOBuffer::writingToReading() +{ + // this should do the trick :) + reading.setArray(writing.buffer()); + QByteArray a; + writing.setArray(a); +} + +//----------------------------------------------------------------------------- +void BufferArray::clear(uint k) +{ + for (uint i=k; iwriting.buffer().data(), b[i]->writing.size()); +// debug("BUFFERARRAY : << (i=%i size=%i)", i, b[i]->writing.size()); + b[i]->writing.clear(); + } + return s; +} + +QDataStream &operator >>(QDataStream &s, BufferArray &b) +{ + uint size; + char *c; + for (uint i=0; ireading.setArray(a); +// debug("BUFFERARRAY : >> (i=%i c=%i size=%i s=%i)", +// i, (int)c, size, b[i]->reading.size()); + } + return s; +} diff --git a/libksirtet/lib/types.h b/libksirtet/lib/types.h new file mode 100644 index 00000000..2ccdcba0 --- /dev/null +++ b/libksirtet/lib/types.h @@ -0,0 +1,197 @@ +#ifndef MTYPES_H +#define MTYPES_H + +#include +#include +#include + +#include "miscui.h" + + +/** Internal class : used for client identification. */ +class cId +{ + public: + cId() {} + cId(const QString &gameName, const QString &gameId); + + enum State { Accepted, LibIdClash, GameNameClash, GameIdClash }; + void check(const cId &id); + bool accepted() const { return state==Accepted; } + QString errorMessage(const cId &id) const; + + friend QDataStream &operator << (QDataStream &s, const cId &id); + friend QDataStream &operator >> (QDataStream &s, cId &id); + + private: + QString libId, gameName, gameId; + State state; +}; +QDataStream &operator << (QDataStream &s, const cId &id); +QDataStream &operator >> (QDataStream &s, cId &id); + +/** Flags used for the netmeeting. */ +enum MeetingMsgFlag + { IdFlag = 0, EndFlag, NewFlag, DelFlag, Mod_TextFlag, Mod_TypeFlag, Mod_OptFlag, PlayFlag }; +QDataStream &operator << (QDataStream &s, const MeetingMsgFlag &f); +QDataStream &operator >> (QDataStream &s, MeetingMsgFlag &f); + +/** Internal class : used in netmeeting to transport text line. */ +class TextInfo +{ + public: + TextInfo() {} + + uint i; + QString text; +}; +QDataStream &operator << (QDataStream &s, const TextInfo &ti); +QDataStream &operator >> (QDataStream &s, TextInfo &ti); + +/** Internal class : used in netmeeting to transport readiness status. */ +typedef struct { + uint i; + MeetingCheckBox::Type type; +} TypeInfo; +QDataStream &operator << (QDataStream &s, const MeetingCheckBox::Type &t); +QDataStream &operator >> (QDataStream &s, MeetingCheckBox::Type &t); +QDataStream &operator << (QDataStream &s, const TypeInfo &ti); +QDataStream &operator >> (QDataStream &s, TypeInfo &ti); + +/* Internal class : store game data. */ +class BoardData +{ + public: + BoardData() {} + + QString name; + PlayerComboBox::Type type; +}; +QDataStream &operator <<(QDataStream &, const BoardData &); +QDataStream &operator >>(QDataStream &, BoardData &); + +/* Internal class : store extended game data (used in netmeeting). */ +class ExtData +{ + public: + ExtData() {} + ExtData(const QValueList &_bds, const QString &_text, + MeetingCheckBox::Type _type) + : bds(_bds), text(_text), type(_type) {} + + QValueList bds; + QString text; + MeetingCheckBox::Type type; +}; +QDataStream &operator << (QDataStream &s, const ExtData &ed); +QDataStream &operator >> (QDataStream &s, ExtData &ed); + +/* Internal class : store meeting line data (in netmeeting). */ +class MeetingLineData +{ + public: + MeetingLineData() {} + + ExtData ed; + bool own; +}; +QDataStream &operator << (QDataStream &s, const MeetingLineData &pld); +QDataStream &operator >> (QDataStream &s, MeetingLineData &pld); + +/* Internal class : store remote host data. */ +class Socket; + +class RemoteHostData +{ + public: + RemoteHostData() : socket(0) {} + + Socket *socket; + QValueList bds; +}; + +/* Internal class : store connection data (used by config. wizard). */ +class ConnectionData +{ + public: + ConnectionData() {} + + bool network, server; + RemoteHostData rhd; +}; + +/** Flags used for network communication. */ +enum MetaFlag { MF_Ask = 0, MF_Data }; +QDataStream &operator << (QDataStream &s, const MetaFlag &f); +QDataStream &operator >> (QDataStream &s, MetaFlag &f); + +/** Internal class : encapsulate read/write QBuffer. */ +class Stream : public QDataStream +{ + public: + Stream(int mode); + + void clear(); + void setArray(QByteArray a); + + QByteArray buffer() const { return buf.buffer(); } + uint size() const { return buf.buffer().size(); } + + protected: + QBuffer buf; + + private: + int mode; +}; + +/** Internal class : encapsulate write QBuffer. */ +class WritingStream : public Stream +{ + public: + WritingStream() : Stream(IO_WriteOnly) {} +}; + +/** Internal class : encapsulate read QBuffer. */ +class ReadingStream : public Stream +{ + public: + ReadingStream() : Stream(IO_ReadOnly) {} + + bool readOk(); + void clearRead(); +}; + +/** Internal class : include a @ref ReadingStream and a @ref WritingStream. */ +class IOBuffer +{ + public: + IOBuffer() {} + + void writingToReading(); + + ReadingStream reading; + WritingStream writing; +}; + +/** Internal class : array of @ref IOBuffer. */ +class BufferArray +{ + public: + BufferArray() {} + BufferArray(uint nb) { resize(nb); } + ~BufferArray(); + + void resize(uint nb); + + uint size() const { return a.size(); } + IOBuffer *operator [](uint i) const { return a[i]; } + + private: + QMemArray a; + + void clear(uint nb); +}; +QDataStream &operator <<(QDataStream &s, const BufferArray &b); +QDataStream &operator >>(QDataStream &s, BufferArray &b); + +#endif // MTYPES_H diff --git a/libksirtet/lib/version.h b/libksirtet/lib/version.h new file mode 100644 index 00000000..49dc7b6b --- /dev/null +++ b/libksirtet/lib/version.h @@ -0,0 +1,6 @@ +#define MULTI_VERSION "0.1.8" +#define MULTI_LONG_VERSION "0.1.8 (11 April 2001)" +#define MULTI_COPYLEFT "(c) 1998-2001, Nicolas Hadacek" + +#define MULTI_ID "003" // should be increased when incompatible + // changes are made. diff --git a/libksirtet/lib/wizard.cpp b/libksirtet/lib/wizard.cpp new file mode 100644 index 00000000..30d5a89d --- /dev/null +++ b/libksirtet/lib/wizard.cpp @@ -0,0 +1,229 @@ +#include "wizard.h" +#include "wizard.moc" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "types.h" +#include "defines.h" +#include "socket.h" + +#ifdef __bsdi__ +#define IPPORT_USERRESERVED IPPORT_DYNAMIC +#endif +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__sgi) +#define IPPORT_USERRESERVED IPPORT_RESERVED +#endif +#define MIN_USER_PORT (unsigned short int)IPPORT_USERRESERVED +#define MAX_USER_PORT 65535 + +MPWizard::MPWizard(const MPGameInfo &gi, ConnectionData &_cd, + QWidget *parent, const char *name) +: KWizard(parent, name, TRUE), cd(_cd) +{ +// setupTypePage(); // #### REMOVE NETWORK GAMES UNTIL FIXED + type = Local; + setupLocalPage(gi); +} + +//----------------------------------------------------------------------------- +void MPWizard::setupTypePage() +{ + KConfigGroupSaver cg(kapp->config(), MP_GROUP); + + typePage = new QVBox(this); + typePage->setMargin(KDialogBase::marginHint()); + + QVButtonGroup *vbg = new QVButtonGroup(typePage); + connect(vbg, SIGNAL(clicked(int)), SLOT(typeChanged(int))); + QRadioButton *b; + b = new QRadioButton(i18n("Create a local game"), vbg); + b = new QRadioButton(i18n("Create a network game"), vbg); + b = new QRadioButton(i18n("Join a network game"), vbg); + type = (Type)cg.config()->readNumEntry(MP_GAMETYPE, 0); + if ( type<0 || type>2 ) type = Local; + vbg->setButton(type); + + typePage->setSpacing(KDialogBase::spacingHint()); + net = new QVGroupBox(i18n("Network Settings"), typePage); + QGrid *grid = new QGrid(2, net); + lserver = new QLabel(" ", grid); + grid->setSpacing(KDialogBase::spacingHint()); + eserver = new QLineEdit(grid); + (void)new QLabel(i18n("Port:"), grid); + int p = cg.config()->readNumEntry(MP_PORT, (uint)MIN_USER_PORT); + eport = new KIntNumInput(p, grid); + eport->setRange(MIN_USER_PORT, MAX_USER_PORT, 1, false); + + addPage(typePage, i18n("Choose Game Type")); + setHelpEnabled(typePage, FALSE); + typeChanged(type); +} + +//----------------------------------------------------------------------------- +void MPWizard::setupLocalPage(const MPGameInfo &gi) +{ + localPage = new QVBox(this); + localPage->setMargin(KDialogBase::marginHint()); + + wl = new WidgetList(5, localPage); + QSignalMapper *husm = new QSignalMapper(this); + if (gi.humanSettingSlot) connect(husm, SIGNAL(mapped(int)), + gi.humanSettingSlot); + QSignalMapper *aism = new QSignalMapper(this); + if (gi.AISettingSlot) connect(aism, SIGNAL(mapped(int)), gi.AISettingSlot); + + KConfigGroupSaver cg(kapp->config(), MP_GROUP); + QString n; + PlayerComboBox::Type type; + PlayerLine *pl; + Q_ASSERT( gi.maxNbLocalPlayers>0 ); + for (uint i=0; ireadNumEntry(QString(MP_PLAYER_TYPE).arg(i), + (i==0 ? PlayerComboBox::Human : PlayerComboBox::None)); + n = cg.config()->readEntry(QString(MP_PLAYER_NAME).arg(i), + i18n("Player #%1").arg(i)); + + pl = new PlayerLine(type, n, gi.humanSettingSlot, gi.AISettingSlot, + i!=0, gi.AIAllowed, wl); + connect(pl, SIGNAL(typeChanged(int)), SLOT(lineTypeChanged(int))); + husm->setMapping(pl, i); + connect(pl, SIGNAL(setHuman()), husm, SLOT(map())); + aism->setMapping(pl, i); + connect(pl, SIGNAL(setAI()), aism, SLOT(map())); + wl->append(pl); + } + + ((QVBox *)localPage)->setSpacing(KDialogBase::spacingHint()); + +// keys = new QPushButton(i18n("Configure Keys..."), localPage); +// connect(keys, SIGNAL(clicked()), SLOT(configureKeysSlot())); + + addPage(localPage, i18n("Local Player's Settings")); + setHelpEnabled(localPage, FALSE); + lineTypeChanged(0); +} + +QString MPWizard::name(uint i) const +{ + QString s = wl->widget(i)->name(); + if ( s.length()==0 ) s = i18n("Player #%1").arg(i); + return s; +} + +void MPWizard::typeChanged(int t) +{ + type = (Type)t; + + QString str; + if ( type!=Client ) { + str = "localhost"; + lserver->setText(i18n("Hostname:")); + } else { + KConfigGroupSaver cg(kapp->config(), MP_GROUP); + str = cg.config()->readEntry(MP_SERVER_ADDRESS, + i18n("the.server.address")); + lserver->setText(i18n("Server address:")); + } + eserver->setText(str); + eserver->setEnabled(type==Client); + eport->setEnabled(type!=Local); + net->setEnabled(type!=Local); +} + +void MPWizard::lineTypeChanged(int) +{ + bool b = FALSE; + for (uint i=0; isize(); i++) + if ( wl->widget(i)->type()==PlayerComboBox::Human ) { + b = TRUE; + break; + } +// keys->setEnabled(b); +} + +void MPWizard::accept() +{ + KConfigGroupSaver cg(kapp->config(), MP_GROUP); + + cd.network = ( type!=Local ); + cd.server = ( type!=Client ); + + if (cd.network) { + //********************************************************** + // create socket + int flags = KExtendedSocket::inetSocket + | KExtendedSocket::streamSocket; + if (cd.server) flags |= KExtendedSocket::passiveSocket; + QString host = QFile::encodeName(eserver->text()); + KExtendedSocket *socket + = new KExtendedSocket(host, eport->value(), flags); + + // do lookup + int res = socket->lookup(); + if ( checkSocket(res, socket, i18n("Error looking up for \"%1\"") + .arg(host), this) ) { + delete socket; + return; + } + + // connect (client) or listen (server) + res = (cd.server ? socket->listen() : socket->connect()); + if ( checkSocket(res, socket, i18n("Error opening socket"), this) ) { + delete socket; + return; + } + + cd.rhd.socket = new Socket(socket, true); + + if ( !cd.server ) + cg.config()->writeEntry(MP_SERVER_ADDRESS, eserver->text()); + cg.config()->writeEntry(MP_PORT, eport->value()); + } + + BoardData bd; + for (uint i=0; isize(); i++) { + if ( wl->widget(i)->type()==PlayerComboBox::None ) continue; + bd.name = name(i); + bd.type = wl->widget(i)->type(); + cd.rhd.bds += bd; + } + + cg.config()->writeEntry(MP_GAMETYPE, (int)type); + for (uint i=0; isize(); i++) { + cg.config()->writeEntry(QString(MP_PLAYER_TYPE).arg(i), + (int)wl->widget(i)->type()); + cg.config()->writeEntry(QString(MP_PLAYER_NAME).arg(i), name(i)); + } + + KWizard::accept(); +} + +void MPWizard::showPage(QWidget *page) +{ + if ( page==localPage ) setFinishEnabled(localPage, TRUE); + KWizard::showPage(page); +} + +void MPWizard::configureKeysSlot() +{ + uint nb = 0; + for (uint i=0; isize(); i++) + if ( wl->widget(i)->type()==PlayerComboBox::Human ) nb++; + emit configureKeys(nb); +} diff --git a/libksirtet/lib/wizard.h b/libksirtet/lib/wizard.h new file mode 100644 index 00000000..29287508 --- /dev/null +++ b/libksirtet/lib/wizard.h @@ -0,0 +1,57 @@ +#ifndef WIZARD_H +#define WIZARD_H + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "pline.h" +#include "mp_interface.h" + +class ConnectionData; + +class MPWizard : public KWizard +{ + Q_OBJECT + + public: + MPWizard(const MPGameInfo &gi, ConnectionData &cd, + QWidget *parent = 0, const char *name = 0); + + void showPage(QWidget *page); + + signals: + void configureKeys(uint); + + protected slots: + void accept(); + + private slots: + void typeChanged(int t); + void lineTypeChanged(int); + void configureKeysSlot(); + + private: + ConnectionData &cd; + enum Type { Local, Server, Client }; + Type type; + QVBox *typePage, *localPage; + WidgetList *wl; + QLabel *lserver; + QLineEdit *eserver; + KIntNumInput *eport; + QVGroupBox *net; +// QPushButton *keys; + + void setupTypePage(); + void setupLocalPage(const MPGameInfo &gi); + QString name(uint i) const; +}; + +#endif // WIZARD_H diff --git a/lskat/AUTHORS b/lskat/AUTHORS new file mode 100644 index 00000000..944b9a7d --- /dev/null +++ b/lskat/AUTHORS @@ -0,0 +1 @@ +Martin Heni diff --git a/lskat/COPYING b/lskat/COPYING new file mode 100644 index 00000000..54754ab4 --- /dev/null +++ b/lskat/COPYING @@ -0,0 +1,341 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/lskat/Makefile.am b/lskat/Makefile.am new file mode 100644 index 00000000..efb7b82c --- /dev/null +++ b/lskat/Makefile.am @@ -0,0 +1,11 @@ + +SUBDIRS = lskat grafix lskatproc + +####### kdevelop will overwrite this part!!! (end)############ +# not a GNU package. You can remove this line, if +# have all needed files, that a GNU package needs +AUTOMAKE_OPTIONS = foreign + +KDE_ICON = lskat + +xdg_apps_DATA = lskat.desktop diff --git a/lskat/README b/lskat/README new file mode 100644 index 00000000..08a01e00 --- /dev/null +++ b/lskat/README @@ -0,0 +1,33 @@ +GENERAL NOTE: + + This is a small KDE 2.0 game, I wrote to practise + programming in KDE. + It should work quite stable. But if you find any bugs + please contact me. + It would be also nice to give me some general feedback + about the program, so that it can be approved or it + can be considered in other programs. + (Email: martin@heni-online.de) + +INSTALLATION: + +# unpack the archive +tar xzf lskat-0.9.tar.gz + +# build the package +cd lskat-0.9 +./configure +# or if KDE is in /opt/kde2 +./configure --prefix=/opt/kde2 +make + +# become superuser for installing +su -c 'make install' + +DECKS: + + You can use up to 6 own carddecks. Simply put into + the grafichs directory resp the lskatpic.taz archive + BMP graphics of the size 72x96, named deck1.bmp .. + deck6.bmp + diff --git a/lskat/TODO b/lskat/TODO new file mode 100644 index 00000000..7c90f740 --- /dev/null +++ b/lskat/TODO @@ -0,0 +1,3 @@ +- Convert BMP graphics to PNG (done) +- Checke QT QString-char * casting + (Its a bit of a QCString/QString mess right now) diff --git a/lskat/grafix/Makefile.am b/lskat/grafix/Makefile.am new file mode 100644 index 00000000..c99fec81 --- /dev/null +++ b/lskat/grafix/Makefile.am @@ -0,0 +1,8 @@ +grafix_DATA = background.png deck1.png deck2.png deck3.png deck4.png \ +t1.png t2.png t3.png \ +t4.png t5.png tback.png type1.png type2.png type3.png + +grafixdir = $(kde_datadir)/lskat/grafix + +EXTRA_DIST = $(grafix_DATA) + diff --git a/lskat/grafix/background.png b/lskat/grafix/background.png new file mode 100644 index 00000000..7c7546a2 Binary files /dev/null and b/lskat/grafix/background.png differ diff --git a/lskat/grafix/deck1.png b/lskat/grafix/deck1.png new file mode 100644 index 00000000..70eb9049 Binary files /dev/null and b/lskat/grafix/deck1.png differ diff --git a/lskat/grafix/deck2.png b/lskat/grafix/deck2.png new file mode 100644 index 00000000..a17f390b Binary files /dev/null and b/lskat/grafix/deck2.png differ diff --git a/lskat/grafix/deck3.png b/lskat/grafix/deck3.png new file mode 100644 index 00000000..c04eccd4 Binary files /dev/null and b/lskat/grafix/deck3.png differ diff --git a/lskat/grafix/deck4.png b/lskat/grafix/deck4.png new file mode 100644 index 00000000..44e9ef91 Binary files /dev/null and b/lskat/grafix/deck4.png differ diff --git a/lskat/grafix/t1.png b/lskat/grafix/t1.png new file mode 100644 index 00000000..1722b1a7 Binary files /dev/null and b/lskat/grafix/t1.png differ diff --git a/lskat/grafix/t2.png b/lskat/grafix/t2.png new file mode 100644 index 00000000..a5b11f7a Binary files /dev/null and b/lskat/grafix/t2.png differ diff --git a/lskat/grafix/t3.png b/lskat/grafix/t3.png new file mode 100644 index 00000000..6a1b10fc Binary files /dev/null and b/lskat/grafix/t3.png differ diff --git a/lskat/grafix/t4.png b/lskat/grafix/t4.png new file mode 100644 index 00000000..21079f7e Binary files /dev/null and b/lskat/grafix/t4.png differ diff --git a/lskat/grafix/t5.png b/lskat/grafix/t5.png new file mode 100644 index 00000000..2cc08d8c Binary files /dev/null and b/lskat/grafix/t5.png differ diff --git a/lskat/grafix/tback.png b/lskat/grafix/tback.png new file mode 100644 index 00000000..84132ad6 Binary files /dev/null and b/lskat/grafix/tback.png differ diff --git a/lskat/grafix/type1.png b/lskat/grafix/type1.png new file mode 100644 index 00000000..d6714c7d Binary files /dev/null and b/lskat/grafix/type1.png differ diff --git a/lskat/grafix/type2.png b/lskat/grafix/type2.png new file mode 100644 index 00000000..1f646df1 Binary files /dev/null and b/lskat/grafix/type2.png differ diff --git a/lskat/grafix/type3.png b/lskat/grafix/type3.png new file mode 100644 index 00000000..66b109d4 Binary files /dev/null and b/lskat/grafix/type3.png differ diff --git a/lskat/hi128-app-lskat.png b/lskat/hi128-app-lskat.png new file mode 100644 index 00000000..48c94d8b Binary files /dev/null and b/lskat/hi128-app-lskat.png differ diff --git a/lskat/hi16-app-lskat.png b/lskat/hi16-app-lskat.png new file mode 100644 index 00000000..de69f4cf Binary files /dev/null and b/lskat/hi16-app-lskat.png differ diff --git a/lskat/hi22-app-lskat.png b/lskat/hi22-app-lskat.png new file mode 100644 index 00000000..f811b52f Binary files /dev/null and b/lskat/hi22-app-lskat.png differ diff --git a/lskat/hi32-app-lskat.png b/lskat/hi32-app-lskat.png new file mode 100644 index 00000000..7a605d44 Binary files /dev/null and b/lskat/hi32-app-lskat.png differ diff --git a/lskat/hi48-app-lskat.png b/lskat/hi48-app-lskat.png new file mode 100644 index 00000000..259608d1 Binary files /dev/null and b/lskat/hi48-app-lskat.png differ diff --git a/lskat/hi64-app-lskat.png b/lskat/hi64-app-lskat.png new file mode 100644 index 00000000..00abe3bf Binary files /dev/null and b/lskat/hi64-app-lskat.png differ diff --git a/lskat/lskat.desktop b/lskat/lskat.desktop new file mode 100644 index 00000000..40044788 --- /dev/null +++ b/lskat/lskat.desktop @@ -0,0 +1,108 @@ +[Desktop Entry] +GenericName=Card Game +GenericName[af]=Kaart Speletjie +GenericName[ar]=لعبة الورق +GenericName[be]=ÐšÐ°Ñ€Ñ‚Ð°Ñ‡Ð½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ +GenericName[bg]=Игра на карти +GenericName[bn]=তাস খেলা +GenericName[br]=C'hoari kartennoù +GenericName[bs]=Igra s kartama +GenericName[ca]=Joc de cartes +GenericName[cs]=Karty +GenericName[cy]=Gêm Cerdiau +GenericName[da]=Kortspil +GenericName[de]=Kartenspiel +GenericName[el]=Παιχνίδι καÏτών +GenericName[eo]=Kartludo +GenericName[es]=Juego de cartas +GenericName[et]=Kaardimäng +GenericName[eu]=Karta-jokoa +GenericName[fa]=بازی ورق +GenericName[fi]=Korttipeli +GenericName[fo]=Kortspøl +GenericName[fr]=Jeu de cartes +GenericName[ga]=Cluiche Cártaí +GenericName[gl]=Xogo de cartas +GenericName[he]=משחק ×§×œ×¤×™× +GenericName[hi]=ताश के खेल +GenericName[hr]=KartaÅ¡ka igra +GenericName[hu]=Kártyajáték +GenericName[is]=Spil +GenericName[it]=Gioco di carte +GenericName[ja]=カードゲーム +GenericName[km]=ល្បែង​បៀ +GenericName[ko]=ì¹´ë“œ ë†€ì´ +GenericName[lt]=Kortų žaidimas +GenericName[lv]=KÄrÅ¡u SpÄ“le +GenericName[mk]=Игра Ñо карти +GenericName[mt]=Logħba tal-karti +GenericName[nb]=Kortspill +GenericName[nds]=Koortenspeel +GenericName[ne]=कारà¥à¤¡ खेल +GenericName[nl]=Kaartspel +GenericName[nn]=Kortspel +GenericName[nso]=Papadi ya Dikarata +GenericName[pa]=ਤਾਸ਼ ਖੇਡ +GenericName[pl]=Gra karciana +GenericName[pt]=Jogo de Cartas +GenericName[pt_BR]=Jogo de Cartas +GenericName[ro]=Un joc de cărÅ£i +GenericName[ru]=Лейтенант Скат +GenericName[rw]=Umukino Ikarika +GenericName[se]=Goartaspeallu +GenericName[sk]=Kartová hra +GenericName[sl]=Igra s kartami +GenericName[sr]=Игра Ñа картама +GenericName[sr@Latn]=Igra sa kartama +GenericName[sv]=Kortspel +GenericName[ta]=அடà¯à®Ÿà¯ˆ விளையாடà¯à®Ÿà¯ +GenericName[tg]=Бозии Кортҳо +GenericName[th]=เà¸à¸¡à¹„พ่ +GenericName[tr]=Ä°skambil Oyunu +GenericName[uk]=Карти +GenericName[uz]=Qarta oÊ»yini +GenericName[uz@cyrillic]=Қарта ўйини +GenericName[ven]=Garatha ya Mutambo +GenericName[vi]=Game thẻ +GenericName[wa]=CwÃ¥rdjeu +GenericName[xh]=Ikhadi lomdlalo +GenericName[zh_CN]=ç‰Œç±»æ¸¸æˆ +GenericName[zh_TW]=紙牌éŠæˆ² +GenericName[zu]=Umdlalo wamakhadi +Exec=lskat +Icon=lskat +Name=Lieutenant Skat +Name[bg]=Лейтенант Скат +Name[ca]=Tinent Skat +Name[cs]=PoruÄík Skat +Name[da]=Lieutnant Skat +Name[de]=Offiziersskat +Name[el]=Υπολοχαγός Skat +Name[eo]=LeÅ­tenanta skato +Name[es]=Teniente Skat +Name[et]=Leitnant Skat +Name[eu]=Skat tenientea +Name[fi]=Luutnantti Skat +Name[hr]=VojniÄki Skat +Name[hu]=Skat hadnagy +Name[is]=Hermaðurinn Skat +Name[it]=Tenente Skat +Name[km]=Lieutnant Skat +Name[lt]=Leitenantas Skatas +Name[nds]=Buernskat +Name[ne]=लिउटिनानà¥à¤Ÿ सà¥à¤•à¤¾à¤Ÿ +Name[nl]=Luitenant Skat +Name[nn]=Løytnant Skat +Name[pl]=Skat porucznika +Name[pt]=Tenente Skat +Name[pt_BR]=Tenente Skat +Name[sl]=PoroÄnik Skat +Name[sv]=Officersskat +Name[uk]=Лейтенант Скет +Name[wa]=Lieutnant Skat +Terminal=false +Type=Application +DocPath=lskat/index.html +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;CardGame; diff --git a/lskat/lskat.kdevprj b/lskat/lskat.kdevprj new file mode 100644 index 00000000..bbba872e --- /dev/null +++ b/lskat/lskat.kdevprj @@ -0,0 +1,286 @@ +[AUTHORS] +dist=true +install=false +install_location= +type=DATA +[COPYING] +dist=true +install=false +install_location= +type=DATA +[ChangeLog] +dist=true +install=false +install_location= +type=DATA +[Config for BinMakefileAm] +bin_program=lskat +cxxflags=-O0 -g3 -Wall +ldadd=-lkfile -lkdeui -lkdecore -lqt -lXext -lX11 +ldflags= +[General] +author=Martin Heni +email=martin@heni-online.de +kdevprj_version=1.0beta2 +lfv_open_groups= +makefiles=Makefile.am,lskat/Makefile.am,lskat/docs/Makefile.am,lskat/docs/en/Makefile.am,po/Makefile.am,lskatproc/Makefile +project_name=LSkat +project_type=normal_kde +sub_dir=lskat/ +version=0.9 +version_control=CVS +[INSTALL] +dist=true +install=false +install_location= +type=DATA +[LFV Groups] +Dialogs=*.kdevdlg +GNU=AUTHORS,COPYING,ChangeLog,INSTALL,README,TODO,NEWS +Headers=*.h,*.hxx,*.hpp,*.H +Others=* +Sources=*.cpp,*.c,*.cc,*.C,*.cxx,*.ec,*.ecpp,*.lxx,*.l++,*.ll,*.l +Translations=*.po +groups=Headers,Sources,Dialogs,Translations,GNU,Others +[Makefile.am] +files=lskat.kdevprj,AUTHORS,COPYING,ChangeLog,INSTALL,README,TODO,lskat.lsm +sub_dirs=lskat,po +type=normal +[README] +dist=true +install=false +install_location= +type=DATA +[TODO] +dist=true +install=false +install_location= +type=DATA +[lskat.kdevprj] +dist=true +install=false +install_location= +type=DATA +[lskat.lsm] +dist=true +install=false +install_location= +type=DATA +[lskat/KChildConnect.cpp] +dist=true +install=false +install_location= +type=SOURCE +[lskat/KChildConnect.h] +dist=true +install=false +install_location= +type=HEADER +[lskat/KConnectEntry.cpp] +dist=true +install=false +install_location= +type=SOURCE +[lskat/KConnectEntry.h] +dist=true +install=false +install_location= +type=HEADER +[lskat/KConnectTypes.h] +dist=true +install=false +install_location= +type=HEADER +[lskat/KEInput.cpp] +dist=true +install=false +install_location= +type=SOURCE +[lskat/KEInput.h] +dist=true +install=false +install_location= +type=HEADER +[lskat/KEMessage.cpp] +dist=true +install=false +install_location= +type=SOURCE +[lskat/KEMessage.h] +dist=true +install=false +install_location= +type=HEADER +[lskat/KInputChildProcess.cpp] +dist=true +install=false +install_location= +type=SOURCE +[lskat/KInputChildProcess.h] +dist=true +install=false +install_location= +type=HEADER +[lskat/KInteractiveConnect.cpp] +dist=true +install=false +install_location= +type=SOURCE +[lskat/KInteractiveConnect.h] +dist=true +install=false +install_location= +type=HEADER +[lskat/KMessageEntry.cpp] +dist=true +install=false +install_location= +type=SOURCE +[lskat/KMessageEntry.h] +dist=true +install=false +install_location= +type=HEADER +[lskat/KProcessConnect.cpp] +dist=true +install=false +install_location= +type=SOURCE +[lskat/KProcessConnect.h] +dist=true +install=false +install_location= +type=HEADER +[lskat/KRSocket.cpp] +dist=true +install=false +install_location= +type=SOURCE +[lskat/KRSocket.h] +dist=true +install=false +install_location= +type=HEADER +[lskat/KRemoteConnect.cpp] +dist=true +install=false +install_location= +type=SOURCE +[lskat/KRemoteConnect.h] +dist=true +install=false +install_location= +type=HEADER +[lskat/Makefile.am] +files=lskat/main.cpp,lskat/lskat.cpp,lskat/lskat.h,lskat/lskatdoc.cpp,lskat/lskatdoc.h,lskat/lskatview.cpp,lskat/lskatview.h,lskat/resource.h,lskat/lskat.desktop,lskat/lskat.xpm,lskat/mini-lskat.xpm,lskat/KChildConnect.cpp,lskat/KChildConnect.h,lskat/KConnectEntry.cpp,lskat/KConnectEntry.h,lskat/KConnectTypes.h,lskat/KEInput.cpp,lskat/KEInput.h,lskat/KEMessage.cpp,lskat/KEMessage.h,lskat/KInputChildProcess.cpp,lskat/KInputChildProcess.h,lskat/KInteractiveConnect.cpp,lskat/KInteractiveConnect.h,lskat/KMessageEntry.cpp,lskat/KMessageEntry.h,lskat/KProcessConnect.cpp,lskat/KProcessConnect.h,lskat/KRSocket.cpp,lskat/KRSocket.h,lskat/KRemoteConnect.cpp,lskat/KRemoteConnect.h,lskat/namedlg.cpp,lskat/namedlg.h,lskat/networkdlg.cpp,lskat/networkdlg.h,lskat/aboutdlg.cpp,lskat/aboutdlg.h,lskat/aboutdlgdata.cpp,lskat/msgdlg.cpp,lskat/msgdlg.h +sub_dirs= +type=prog_main +[lskat/aboutdlg.cpp] +dist=true +install=false +install_location= +type=SOURCE +[lskat/aboutdlg.h] +dist=true +install=false +install_location= +type=HEADER +[lskat/aboutdlgdata.cpp] +dist=true +install=false +install_location= +type=SOURCE +[lskat/docs/Makefile.am] +sub_dirs= +type=normal +[lskat/docs/en/Makefile.am] +sub_dirs= +type=normal +[lskat/lskat.cpp] +dist=true +install=false +install_location= +type=SOURCE +[lskat/lskat.desktop] +dist=true +install=true +install_location=$$(kde_appsdir)/Applications/lskat.desktop +type=DATA +[lskat/lskat.h] +dist=true +install=false +install_location= +type=HEADER +[lskat/lskat.xpm] +dist=true +install=true +install_location=$$(kde_icondir)/lskat.xpm +type=DATA +[lskat/lskatdoc.cpp] +dist=true +install=false +install_location= +type=SOURCE +[lskat/lskatdoc.h] +dist=true +install=false +install_location= +type=HEADER +[lskat/lskatview.cpp] +dist=true +install=false +install_location= +type=SOURCE +[lskat/lskatview.h] +dist=true +install=false +install_location= +type=HEADER +[lskat/main.cpp] +dist=true +install=false +install_location= +type=SOURCE +[lskat/mini-lskat.xpm] +dist=true +install=true +install_location=$$(kde_minidir)/lskat.xpm +type=DATA +[lskat/msgdlg.cpp] +dist=true +install=false +install_location= +type=SOURCE +[lskat/msgdlg.h] +dist=true +install=false +install_location= +type=HEADER +[lskat/namedlg.cpp] +dist=true +install=false +install_location= +type=SOURCE +[lskat/namedlg.h] +dist=true +install=false +install_location= +type=HEADER +[lskat/networkdlg.cpp] +dist=true +install=false +install_location= +type=SOURCE +[lskat/networkdlg.h] +dist=true +install=false +install_location= +type=HEADER +[lskat/resource.h] +dist=true +install=false +install_location= +type=HEADER +[po/Makefile.am] +sub_dirs= +type=po diff --git a/lskat/lskat.lsm b/lskat/lskat.lsm new file mode 100644 index 00000000..388ff010 --- /dev/null +++ b/lskat/lskat.lsm @@ -0,0 +1,14 @@ +Begin3 +Title: LSkat +Version: 0.9 +Entered-date: +Description: A small cardgame for the KDE 2 desktop +Keywords: skat, card, cardgame,game,kde +Author: Martin Heni +Maintained-by: Martin Heni +Primary-site: +Home-page: http://www.heni-online.de/linux +Original-site: +Platforms: Linux and other Unices +Copying-policy: GNU Public License +End diff --git a/lskat/lskat/KChildConnect.cpp b/lskat/lskat/KChildConnect.cpp new file mode 100644 index 00000000..61931693 --- /dev/null +++ b/lskat/lskat/KChildConnect.cpp @@ -0,0 +1,124 @@ +/*************************************************************************** + KChildConnect.cpp - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include +#include "KChildConnect.h" + +#include "KChildConnect.moc" + +KChildConnect::KChildConnect() + : QObject(0,0) +{ + input_pending=false; + inputbuffer=""; +} + +KChildConnect::~KChildConnect() +{ +} + +KR_STATUS KChildConnect::QueryStatus() +{ + return KR_OK; +} + +// Communication with process +bool KChildConnect::SendMsg(KEMessage *msg) +{ + QString sendstring=msg->ToString(); + // Debug only + if (msg->HasKey(QCString("KLogSendMsg"))) + { + char *p; + int size; + FILE *fp; + msg->GetData(QCString("KLogSendMsg"),p,size); + if (p && (fp=fopen(p,"a")) ) + { + fprintf(fp,"------------------------------------\n"); + fprintf(fp, "%s", sendstring.utf8().data()); + fclose(fp); + } + } + // end debug only + return Send(sendstring); +} + +// Send string to parent +bool KChildConnect::Send(QString str) +{ + if (!str || str.length()<1) return true; // no need to send crap + printf("%s",str.latin1()); + fflush(stdout); + return true; +} + +void KChildConnect::Receive(QString input) +{ + // Cut out CR + int len,pos; + QString tmp; + + + // Call us recursive until there are no CR left + len=KEMESSAGE_CR.length(); + pos=input.find(KEMESSAGE_CR); + if (pos>0) + { + tmp=input.left(pos); + if (tmp.length()>0) Receive(tmp); // CR free + input=input.right(input.length()-pos-len); + if (input.length()>0) Receive(input); + return ; + } + +// printf(" ---> KChildConnect::Receive: '%s'\n",(const char *)input); + if (input==(KEMESSAGE_HEAD) && !input_pending) + { + input_pending=true; + inputcache.clear(); + return ; + } + if (!input_pending) return ; // ignore + if (input!=(KEMESSAGE_TAIL)) + { + inputcache.append(input.latin1()); + return; + } + input_pending=0; + + KEMessage *msg=new KEMessage; + char *it; + for (it=inputcache.first();it!=0;it=inputcache.next()) + { + msg->AddString(QCString(it)); + } + +// printf("+- CHILDprocess:: GOT MESSAGE::Emmiting slotReceiveMsg\n"); + emit signalReceiveMsg(msg,ID); + + delete msg; +} + +void KChildConnect::SetID(int id) +{ + ID=id; +} +int KChildConnect::QueryID() +{ + return ID; +} + diff --git a/lskat/lskat/KChildConnect.h b/lskat/lskat/KChildConnect.h new file mode 100644 index 00000000..d90c62ca --- /dev/null +++ b/lskat/lskat/KChildConnect.h @@ -0,0 +1,53 @@ +/*************************************************************************** + KChildConnect.h - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef _KCHILDCONNECT_H_ +#define _KCHILDCONNECT_H_ + +#include +#include +#include "KEMessage.h" + + +class KChildConnect: public QObject +{ + Q_OBJECT + + protected: + QStrList inputcache; + bool input_pending; + QString inputbuffer; + int ID; + + public: + KChildConnect(); + ~KChildConnect(); + void Receive(QString input); + int QueryID(); + void SetID(int id); + + virtual bool SendMsg(KEMessage *msg); + virtual bool Send(QString str); + virtual KR_STATUS QueryStatus(); + + public slots: + + + signals: + void signalReceiveMsg(KEMessage *msg,int id); +}; + +#endif diff --git a/lskat/lskat/KConnectEntry.cpp b/lskat/lskat/KConnectEntry.cpp new file mode 100644 index 00000000..870b8cd2 --- /dev/null +++ b/lskat/lskat/KConnectEntry.cpp @@ -0,0 +1,165 @@ +/*************************************************************************** + KConnectEntry.cpp - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +/*************************************************************************** + FILENAME| - description + ------------------- + begin : Tue Apr 4 2000 + copyright : (C) |1995-2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include +#include +#include +#include "KConnectEntry.h" + +KG_INPUTTYPE KConnectEntry::QueryType() +{ + return type; +} + +KRemoteConnect *KConnectEntry::QueryRemoteConnect() +{ + return connect.r; +} + +KProcessConnect *KConnectEntry::QueryProcessConnect() +{ + return connect.p; +} + +KInteractiveConnect *KConnectEntry::QueryInteractiveConnect() +{ + return connect.i; +} + +KR_STATUS KConnectEntry::QueryStatus() +{ + switch(type) + { + case KG_INPUTTYPE_INTERACTIVE: + return connect.i->QueryStatus(); + break; + case KG_INPUTTYPE_REMOTE: + return connect.r->QueryStatus(); + break; + case KG_INPUTTYPE_PROCESS: + return connect.p->QueryStatus(); + break; + default: + return KR_INVALID; + break; + } +} +bool KConnectEntry::SendMsg(KEMessage *msg) +{ + switch(type) + { + case KG_INPUTTYPE_INTERACTIVE: + return connect.i->SendMsg(msg); + break; + case KG_INPUTTYPE_REMOTE: + return connect.r->SendMsg(msg); + break; + case KG_INPUTTYPE_PROCESS: + return connect.p->SendMsg(msg); + break; + default: + return false; + break; + } +} + +bool KConnectEntry::Exit() +{ + bool result; + result=FALSE; + switch(type) + { + case KG_INPUTTYPE_INTERACTIVE: + result=connect.i->Exit(); + delete connect.i; + break; + case KG_INPUTTYPE_REMOTE: + result=connect.r->Exit(); + delete connect.r; + break; + case KG_INPUTTYPE_PROCESS: + result=connect.p->Exit(); + delete connect.p; + break; + default: result=FALSE; + break; + } + return result; +} + +bool KConnectEntry::Init(KG_INPUTTYPE stype,int id,KEMessage *msg) +{ + bool result; + type=stype; + ID=id; + switch(stype) + { + case KG_INPUTTYPE_INTERACTIVE: + connect.i=new KInteractiveConnect; + result=connect.i->Init(id,msg); + break; + case KG_INPUTTYPE_REMOTE: + connect.r=new KRemoteConnect; + result=connect.r->Init(id,msg); + break; + case KG_INPUTTYPE_PROCESS: + connect.p=new KProcessConnect; + result=connect.p->Init(id,msg); + break; + default: result=FALSE; + break; + } + return result; +} +KConnectEntry::KConnectEntry() +{ + type=(KG_INPUTTYPE)0; + connect.r=0; +} + +KConnectEntry::~KConnectEntry() +{ +// printf("DESTRUCTING KCONNECTENTRY\n"); + Exit(); +} + +KConnectEntry::KConnectEntry(KConnectEntry &msg) +{ + *this=msg; +} +KConnectEntry &KConnectEntry::operator=(KConnectEntry &entry) +{ + // printf("&KConnectEntry::operator=:: I am not sure whether this is really a good idea...\n"); + Init(entry.QueryType(),entry.ID); + + return *this; +} diff --git a/lskat/lskat/KConnectEntry.h b/lskat/lskat/KConnectEntry.h new file mode 100644 index 00000000..73619eac --- /dev/null +++ b/lskat/lskat/KConnectEntry.h @@ -0,0 +1,56 @@ +/*************************************************************************** + KConnectEntry.h - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef _KCONNECTENTRY_H_ +#define _KCONNECTENTRY_H_ + +#include +#include "KRemoteConnect.h" +#include "KProcessConnect.h" +#include "KInteractiveConnect.h" +#include "KConnectTypes.h" +#include "KEMessage.h" + +union UConnect +{ + KRemoteConnect *r; + KProcessConnect *p; + KInteractiveConnect *i; +}; + +class KConnectEntry +{ + private: + KG_INPUTTYPE type; // remote,computer,interactive + UConnect connect; + protected: + int ID; + + public: + KG_INPUTTYPE QueryType(); + KRemoteConnect *QueryRemoteConnect(); + KProcessConnect *QueryProcessConnect(); + KInteractiveConnect *QueryInteractiveConnect(); + KConnectEntry(); + ~KConnectEntry(); + KConnectEntry(KConnectEntry &entry); + KConnectEntry &operator=(KConnectEntry &entry); + bool Exit(); + bool Init(KG_INPUTTYPE stype,int id=0,KEMessage *msg=0); + KR_STATUS QueryStatus(); + bool SendMsg(KEMessage *msg); +}; +#endif diff --git a/lskat/lskat/KConnectTypes.h b/lskat/lskat/KConnectTypes.h new file mode 100644 index 00000000..6a9822b3 --- /dev/null +++ b/lskat/lskat/KConnectTypes.h @@ -0,0 +1,38 @@ +/*************************************************************************** + KConnectTypes.h - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef _KCONNECTTYPES_H_ +#define _KCONNECTTYPES_H_ + +enum KGM_TYPE {KGM_TYPE_INVALID=0,KGM_TYPE_SHORT=1,KGM_TYPE_LONG=2, + KGM_TYPE_FLOAT=3,KGM_TYPE_DATA=4}; + +enum KG_INPUTTYPE { + KG_INPUTTYPE_INVALID=0, + KG_INPUTTYPE_INTERACTIVE=1, + KG_INPUTTYPE_PROCESS=2, + KG_INPUTTYPE_REMOTE=3}; + +enum KR_STATUS { + KR_NO_SOCKET=-2, + KR_WAIT_FOR_CLIENT=-1, + KR_INVALID=0, + // >0 OK + KR_OK=1, + KR_CLIENT=2, + KR_SERVER=3 + }; +#endif diff --git a/lskat/lskat/KEInput.cpp b/lskat/lskat/KEInput.cpp new file mode 100644 index 00000000..b5765d00 --- /dev/null +++ b/lskat/lskat/KEInput.cpp @@ -0,0 +1,296 @@ +/*************************************************************************** + KEInput.cpp - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include +#include "KEInput.h" +#include "KConnectEntry.h" + +#define K_INPUT_DELAY 25 // delay following non interactive moves + +#include "KEInput.moc" + +KEInput::KEInput(QObject * parent) + : QObject(parent,0) +{ + number_of_inputs=0; + locked=FALSE; + previous_input=-1; + next_input=-1; + cTimer=0; +// mMsg=(KEMessage *)0; +} + +KEInput::~KEInput() +{ + int i; + for (i=number_of_inputs-1;i>=0;i--) + { + RemoveInput(i); + } + delete cTimer; +} + +int KEInput::QueryNumberOfInputs() +{ + return number_of_inputs; +} + +int KEInput::QueryNext() +{ + return next_input; +} + +int KEInput::QueryPrevious() +{ + return previous_input; +} + +bool KEInput::IsInput(int no) +{ + if (no>=number_of_inputs || no<0) return FALSE; + if (QueryType(no)==0) return FALSE; + return TRUE; +} + +// Default is the type of the current player +bool KEInput::IsInteractive(int no) +{ + return QueryType(no)==KG_INPUTTYPE_INTERACTIVE; +} +bool KEInput::IsProcess(int no) +{ + return QueryType(no)==KG_INPUTTYPE_PROCESS; +} +bool KEInput::IsRemote(int no) +{ + return QueryType(no)==KG_INPUTTYPE_REMOTE; +} +KG_INPUTTYPE KEInput::QueryType(int no) +{ + if (no==-1) no=QueryNext(); + if (no>=number_of_inputs || no<0) return KG_INPUTTYPE_INVALID; + return playerArray[no].QueryType(); +} + +KR_STATUS KEInput::QueryStatus(int no) +{ + if (no==-1) no=QueryNext(); + if (no>=number_of_inputs || no<0) + { + return KR_INVALID; + } + return playerArray[no].QueryStatus(); +} +bool KEInput::SendMsg(KEMessage *msg,int no) +{ + if (no==-1) no=QueryNext(); + if (no>=number_of_inputs || no<0) + { + return false; + } + return playerArray[no].SendMsg(msg); +} + + +bool KEInput::SetInputDevice(int no, KG_INPUTTYPE type,KEMessage *msg) +{ + bool result; + // Grow if necessary + if (no<0) return false; + else if (no=number_of_inputs) + { + playerArray.resize(no+1); + number_of_inputs=no+1; + } + result=playerArray[no].Init(type,no,msg); + // if (result) + // Connect even if remote connection is not yet build + if (result || playerArray[no].QueryStatus()==KR_WAIT_FOR_CLIENT) + { + switch(QueryType(no)) + { + case KG_INPUTTYPE_INTERACTIVE: + connect(playerArray[no].QueryInteractiveConnect(),SIGNAL(signalReceiveMsg(KEMessage *,int )), + this,SLOT(slotSetInput(KEMessage *,int ))); + connect(playerArray[no].QueryInteractiveConnect(),SIGNAL(signalPrepareMove(KEMessage *,KG_INPUTTYPE)), + this,SLOT(slotPrepareMove(KEMessage *,KG_INPUTTYPE))); + break; + case KG_INPUTTYPE_REMOTE: + connect(playerArray[no].QueryRemoteConnect(),SIGNAL(signalReceiveMsg(KEMessage *,int )), + this,SLOT(slotSetInput(KEMessage *,int ))); + connect(playerArray[no].QueryRemoteConnect(),SIGNAL(signalPrepareMove(KEMessage *,KG_INPUTTYPE)), + this,SLOT(slotPrepareMove(KEMessage *,KG_INPUTTYPE))); + break; + case KG_INPUTTYPE_PROCESS: + connect(playerArray[no].QueryProcessConnect(),SIGNAL(signalReceiveMsg(KEMessage *,int )), + this,SLOT(slotSetInput(KEMessage *,int ))); + connect(playerArray[no].QueryProcessConnect(),SIGNAL(signalPrepareMove(KEMessage *,KG_INPUTTYPE)), + this,SLOT(slotPrepareMove(KEMessage *,KG_INPUTTYPE))); + break; + default: + break; + } + } + return result; +} + +bool KEInput::RemoveInput(int no) +{ + bool result; + if (no>=number_of_inputs || no<0) return FALSE; + result=playerArray[no].Exit(); + // shrink if last entry is removed + if (no==number_of_inputs-1) + { + playerArray.resize(no); + number_of_inputs=no; + } + return result; +} + + +// Sets a new player and sends it a message +bool KEInput::Next(int number, bool force) +{ + if (locked && !force) return FALSE; + if (!IsInput(number)) return FALSE; + locked=TRUE; + + // printf("KEInput::Next %d OK ... lock set!!\n",number); + + previous_input=next_input; + next_input=number; + + switch(QueryType(number)) + { + case KG_INPUTTYPE_INTERACTIVE: + playerArray[number].QueryInteractiveConnect()->Next(); + break; + case KG_INPUTTYPE_REMOTE: + if (QueryType(previous_input)!=0 && + QueryType(previous_input)!=KG_INPUTTYPE_INTERACTIVE) + { + // delay non interactive move to allow interactive inout + if (cTimer) delete cTimer; // Ouch... + cTimer=new QTimer(this); + connect(cTimer,SIGNAL(timeout()),this,SLOT(slotTimerNextRemote())); + cTimer->start(K_INPUT_DELAY,TRUE); + } + else + { + playerArray[number].QueryRemoteConnect()->Next(); + } + break; + case KG_INPUTTYPE_PROCESS: + if (QueryType(previous_input)!=0 && + QueryType(previous_input)!=KG_INPUTTYPE_INTERACTIVE) + { + // delay non interactive move to allow interactive inout + cTimer=new QTimer(this); + connect(cTimer,SIGNAL(timeout()),this,SLOT(slotTimerNextProcess())); + cTimer->start(K_INPUT_DELAY,TRUE); + } + else + playerArray[number].QueryProcessConnect()->Next(); + break; + default: return FALSE; + } + return TRUE; +} + +void KEInput::slotTimerNextRemote() +{ + delete cTimer; + cTimer=0; + if (next_input>=0 && next_inputNext(); +} + +void KEInput::slotTimerNextProcess() +{ + delete cTimer; + cTimer=0; + if (next_input>=0 && next_inputNext(); +} + +// called to prepare a move which is send to a remote/computer +// should fill in message data +void KEInput::slotPrepareMove(KEMessage *msg,KG_INPUTTYPE type) +{ + // just forward it + switch(type) + { + case KG_INPUTTYPE_INTERACTIVE: + emit signalPrepareInteractiveMove(msg); + break; + case KG_INPUTTYPE_PROCESS: + emit signalPrepareProcessMove(msg); + break; + case KG_INPUTTYPE_REMOTE: + emit signalPrepareRemoteMove(msg); + break; + default: // Do nothing + break; + } +} +// called by ReceiveMsg +void KEInput::slotSetInput(KEMessage *msg,int id) +{ + if (!msg) return ; + SetInput(msg,id); +} + +bool KEInput::SetInput(KEMessage *msg,int number) +{ + /* + if (!locked) + { + printf("KEINput:SetInput not locked(should be) returning FALSE\n"); + return FALSE; + } + */ + if (number<0 || number>=number_of_inputs) number=next_input; // automatically select player +// KEMessage *mMsg= new KEMessage; + // evt if (mMsg) delete mMsg; +// *mMsg=*msg; + // locked=FALSE; + // printf("**** KEInput: emitting signalReceiveInput 474\n"); + emit signalReceiveInput(msg,number); +// emit signalReceiveInput(mMsg); +// delete mMsg; + return TRUE; +} + +void KEInput::Lock() +{ + locked=true; +} + +void KEInput::Unlock() +{ + locked=false; +} + +bool KEInput::IsLocked() +{ + return locked; +} + diff --git a/lskat/lskat/KEInput.h b/lskat/lskat/KEInput.h new file mode 100644 index 00000000..e2f3ef8b --- /dev/null +++ b/lskat/lskat/KEInput.h @@ -0,0 +1,82 @@ +/*************************************************************************** + KEInput.h - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef _KEINPUT_H_ +#define _KEINPUT_H_ + +#include +#include +#include +#include +#include "KConnectEntry.h" +#include "KRemoteConnect.h" +#include "KProcessConnect.h" +#include "KInteractiveConnect.h" +#include "KEMessage.h" +#include "KConnectTypes.h" + + +class KEInput : public QObject +{ + Q_OBJECT + + private: + int number_of_inputs; + int previous_input,next_input; + bool locked; +// KEMessage *mMsg; + QTimer *cTimer; + + QPtrList remoteList; + QPtrList computerList; + QPtrList interactiveList; + QMemArray playerArray; + + public: + KEInput(QObject * parent=0); + ~KEInput(); + int QueryNumberOfInputs(); + int QueryNext(); + bool IsInteractive(int no=-1); + bool IsProcess(int no=-1); + bool IsRemote(int no=-1); + int QueryPrevious(); + KG_INPUTTYPE QueryType(int no=-1); + KR_STATUS QueryStatus(int no=-1); + bool IsInput(int no); + bool SetInputDevice(int no, KG_INPUTTYPE type, KEMessage *msg=0); + bool RemoveInput(int no); + bool Next(int number, bool force=false); + bool SetInput(KEMessage *msg,int number=-1); + bool IsLocked(); + bool SendMsg(KEMessage *msg,int no=-1); + void Unlock(); + void Lock(); + + public slots: + void slotTimerNextRemote(); + void slotTimerNextProcess(); + void slotSetInput(KEMessage *msg,int id); + void slotPrepareMove(KEMessage *msg,KG_INPUTTYPE type); + + signals: + void signalReceiveInput(KEMessage *msg,int id); + void signalPrepareRemoteMove(KEMessage *msg); + void signalPrepareProcessMove(KEMessage *msg); + void signalPrepareInteractiveMove(KEMessage *msg); + +}; +#endif diff --git a/lskat/lskat/KEMessage.cpp b/lskat/lskat/KEMessage.cpp new file mode 100644 index 00000000..9d3d3d3f --- /dev/null +++ b/lskat/lskat/KEMessage.cpp @@ -0,0 +1,326 @@ +/*************************************************************************** + KEMessage.cpp - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include +#include +#include +#include "KEMessage.h" + +void KEMessage::AddEntry(QString key,KMessageEntry *entry) +{ + // printf(" AddingEntry: %s with data field %p\n",(char *)key,entry->QueryData()); + if (!entry) return ; + dict.insert(key,entry); + keys.append(key.latin1()); +} + +void KEMessage::AddDataType(QString key,int size,const char *data,KGM_TYPE type) +{ +// printf("AddDataType for %s size=%d\n",(const char *)key,size); + if (size<=0) return ; + KMessageEntry *entry=new KMessageEntry; + entry->SetType(type); + entry->CopyData(size,data); + AddEntry(key,entry); +} + +void KEMessage::AddData(QString key,short data) +{ + AddDataType(key,sizeof(short),(char *)&data,KGM_TYPE_SHORT); +} + +void KEMessage::AddData(QString key,long data) +{ + AddDataType(key,sizeof(long),(char *)&data,KGM_TYPE_LONG); +} + +void KEMessage::AddData(QString key,float data) +{ + AddDataType(key,sizeof(float),(char *)&data,KGM_TYPE_FLOAT); +} + +void KEMessage::AddData(QString key, const char *data,int size) +{ + if (size<0) size=strlen(data)+1; // +1 for 0 Byte + AddDataType(key,size,data,KGM_TYPE_DATA); +} + +KGM_TYPE KEMessage::QueryType(QString key) +{ + KMessageEntry *entry; + entry=dict.find(key); + if (!entry) return (KGM_TYPE)0; + return entry->QueryType(); +} + +bool KEMessage::HasKey(QString key) +{ + KMessageEntry *entry; + entry=dict.find(key); + if (!entry) return false; + return true; +} + +bool KEMessage::GetData(QString key,short &s) +{ + short *result; + KMessageEntry *entry; + entry=dict.find(key); + if (!entry) return false; + if (entry->QueryType()!=KGM_TYPE_SHORT) return false; + // printf("GetShortData: %p for %s\n",entry->QueryData(),(char *)key); + result=(short *)entry->QueryData(); + s=*result; + return true; +} + +bool KEMessage::GetData(QString key,long &l) +{ + long *result; + KMessageEntry *entry; + entry=dict.find(key); + if (!entry) return false; + if (entry->QueryType()!=KGM_TYPE_LONG) return false; + result=(long *)entry->QueryData(); + l=*result; + return true; +} + +bool KEMessage::GetData(QString key,float &f) +{ + float *result; + KMessageEntry *entry; + entry=dict.find(key); + if (!entry) return false; + if (entry->QueryType()!=KGM_TYPE_FLOAT) return false; + // printf("GetFloatData: %p for %s\n",entry->QueryData(),(char *)key); + result=(float *)entry->QueryData(); + f=*result; + return true; +} + +bool KEMessage::GetData(QString key,char * &c,int &size) +{ + KMessageEntry *entry; + entry=dict.find(key); + if (!entry) return false; + if (entry->QueryType()!=KGM_TYPE_DATA) return false; + c=entry->QueryData(); + size=entry->QuerySize(); + return true; +} + +QString KEMessage::EntryToString(char *key,KMessageEntry *entry) +{ + QString s,tmp; + int size,i; + KGM_TYPE type; + char *data; + s=QCString(""); + if (!entry) return s; + size=entry->QuerySize(); + type=entry->QueryType(); + data=entry->QueryData(); + + // Key + /* + tmp.sprintf("%s%s%d%s%d%s", + key,KEMESSAGE_SEP, + size,KEMESSAGE_SEP, + (int)type,KEMESSAGE_SEP); + */ + tmp=QCString(key); + s+=tmp; + s+=KEMESSAGE_SEP; + tmp.sprintf("%d",size); + s+=tmp; + s+=KEMESSAGE_SEP; + tmp.sprintf("%d",(int)type); + s+=tmp; + s+=KEMESSAGE_SEP; + + + // We ignore the type of data and process them all as + // byte sequence + for (i=0;i>4)&15)); + s+=tmp; + } + s+=KEMESSAGE_CR; + + return s; +} + +QString KEMessage::StringToEntry(QString str,KMessageEntry *entry) +{ + int pos,oldpos,cnt,len; + QString key,size,type,data; + const char *p; + char *q; + char c; + + len=KEMESSAGE_SEP.length(); + + if (!entry) return QString(); + pos=str.find(KEMESSAGE_SEP,0); + if (pos<0) return QString(); // wrong format + key=str.left(pos); + + + oldpos=pos; + pos=str.find(KEMESSAGE_SEP,oldpos+len); + if (pos<0) return QString(); // wrong format + size=str.mid(oldpos+len,pos-oldpos-len); + + + oldpos=pos; + pos=str.find(KEMESSAGE_SEP,oldpos+len); + if (pos<0) return QString(); // wrong format + type=str.mid(oldpos+len,pos-oldpos-len); + + + data=str.right(str.length()-pos-len); + + + cnt=size.toInt(); + entry->SetType((KGM_TYPE)type.toInt()); + + // I hope this works with unicode strings as well + p=data.latin1(); + q=(char *)calloc(data.length()/2,sizeof(char)); + if (!q) return QString(); + for(pos=0;pos(int)data.length()) return QString(); // SEVERE ERROR + c=*(p+2*pos)-'a' | ((*(p+2*pos+1)-'a')<<4); + q[pos]=c; + } + entry->CopyData(cnt,q); + + free(q); + return key; +} + +QString KEMessage::ToString() +{ + QString s; + KMessageEntry *entry; + char *it; + s=KEMESSAGE_HEAD+KEMESSAGE_CR; + for (it=keys.first();it!=0;it=keys.next()) + { + entry=dict.find(QCString(it)); + s+=EntryToString(it,entry); + } + s+=KEMESSAGE_TAIL+KEMESSAGE_CR; + return s; +} + +bool KEMessage::AddString(QString s) +{ + // break s into key,size and data + QString key; + KMessageEntry *entry=new KMessageEntry; + key=StringToEntry(s,entry); + if (!key) return false; + AddEntry(key,entry); + return true; +} +bool KEMessage::AddStringMsg(QString str) +{ + bool result; + QString data; + int pos,oldpos,len; + + len=KEMESSAGE_CR.length(); + + pos=str.find(KEMESSAGE_CR); + if (pos<0) return false; // wrong format + if (str.left(pos)!=(KEMESSAGE_HEAD)) return false; // wrong message + + do + { + oldpos=pos; + pos=str.find(KEMESSAGE_CR,oldpos+len); + if (pos<0) return false; // wrong format + data=str.mid(oldpos+len,pos-oldpos-len); + if (data!=(KEMESSAGE_TAIL)) + { + result=AddString(data); + if (!result) return false; // wrong format + } + }while(data!=(KEMESSAGE_TAIL)); + + return result; +} + +void KEMessage::RemoveAll() +{ + keys.clear(); + dict.clear(); +} + +void KEMessage::Remove(QString key) +{ + keys.remove(key.latin1()); + dict.remove(key); +} + +uint KEMessage::QueryNumberOfKeys() +{ + return keys.count(); +} +QStrList *KEMessage::QueryKeys() +{ + return &keys; +} + +KEMessage::~KEMessage() +{ + // printf("Deleteing KEMessage %p\n",this); +} +KEMessage::KEMessage() +{ + // printf("KEMessage construct %p\n",this); + dict.setAutoDelete(true); +} +KEMessage::KEMessage(KEMessage &msg) +{ + // printf("KEMessage copy constructor from %p to %p\n",&msg,this); + *this=msg; +} +KEMessage &KEMessage::operator=(KEMessage &msg) +{ + // KEMessage *newmsg=new KEMessage; + KMessageEntry *entry; + KMessageEntry *newentry; + char *it; + // printf("Assigning = KEMessage from %p to %p\n",&msg,this); + for (it=msg.keys.first();it!=0;it=msg.keys.next()) + { + entry=msg.dict.find(QCString(it)); + newentry=new KMessageEntry; + *newentry=*entry; + AddEntry(QCString(it),newentry); + + } + // return *newmsg; + return *this; +} diff --git a/lskat/lskat/KEMessage.h b/lskat/lskat/KEMessage.h new file mode 100644 index 00000000..0a1913cc --- /dev/null +++ b/lskat/lskat/KEMessage.h @@ -0,0 +1,66 @@ +/*************************************************************************** + KEMessage.h - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef _KEMESSAGE_H_ +#define _KEMESSAGE_H_ + +#include +#include +#include +#include +#include "KMessageEntry.h" + +#define KEMESSAGE_HEAD QString(QCString("BEGIN_V1000")) +#define KEMESSAGE_TAIL QString(QCString("END_V1000")) +#define KEMESSAGE_CR QString(QCString("\n")) +#define KEMESSAGE_SEP QString(QCString(":::")) + +class KEMessage +{ + private: + QStrList keys; + QDict dict; + + protected: + void AddEntry(QString key,KMessageEntry *entry); + public: + QStrList *QueryKeys(); + uint QueryNumberOfKeys(); + void AddDataType(QString key,int size,const char *data,KGM_TYPE type); + void AddData(QString key,short data); + void AddData(QString key,long data); + void AddData(QString key,float data); + void AddData(QString key,const char *data,int size=-1); + bool GetData(QString key,short &s); + bool GetData(QString key,long &l); + bool GetData(QString key,float &f); + bool GetData(QString key,char * &c,int &size); + bool HasKey(QString key); + void Remove(QString key); + KGM_TYPE QueryType(QString key); + QString ToString(); + QString EntryToString(char *key,KMessageEntry *entry); + QString StringToEntry(QString str,KMessageEntry *entry); + bool AddString(QString s); + bool AddStringMsg(QString str); + void RemoveAll(); + ~KEMessage(); + KEMessage(); + KEMessage(KEMessage &msg); + KEMessage &operator=(KEMessage &msg); +}; + +#endif diff --git a/lskat/lskat/KInputChildProcess.cpp b/lskat/lskat/KInputChildProcess.cpp new file mode 100644 index 00000000..fb6e743a --- /dev/null +++ b/lskat/lskat/KInputChildProcess.cpp @@ -0,0 +1,106 @@ +/*************************************************************************** + KInputChildProcess.cpp - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ +#include +#include +#include +#include +#include + +#include "KInputChildProcess.h" +#include "KInputChildProcess.moc" + + +KInputChildProcess::~KInputChildProcess() +{ + delete buffer; + delete childConnect; +} +KInputChildProcess::KInputChildProcess(int size_buffer) + : QObject(0,0) +{ + buffersize=size_buffer; + if (buffersize<1) buffersize=1024; + buffer=new char[buffersize]; + inputbuffer=""; + terminateChild=false; +} +bool KInputChildProcess::exec() +{ + int pos; + QString s; + + childConnect=new KChildConnect; + if (!childConnect) return false; + connect(childConnect,SIGNAL(signalReceiveMsg(KEMessage *,int)), + this,SLOT(slotReceiveMsg(KEMessage *,int))); + do + { + // Wait for input + if (feof(stdin)) + { + sleep(1); + continue; + } + + if (!fgets(buffer,buffersize,stdin) ) + { + continue; + } + s=buffer; + s=inputbuffer+s; + // printf("ChildABC '%s'\n",(const char *)s); + // fflush(stdout); + pos=s.findRev(KEMESSAGE_CR); + if (pos<0) + { + inputbuffer=s; + } + else if (pos+KEMESSAGE_CR.length()==s.length()) + { + // CR at the end...calling receive + childConnect->Receive(s); + } + else + { + inputbuffer=s.right(s.length()-pos-KEMESSAGE_CR.length()); + s=s.left(pos+KEMESSAGE_CR.length()); + // printf("s='%s' in='%s'\n",(const char *)s,(const char *)inputbuffer); + childConnect->Receive(s); + } + }while(!terminateChild); + return true; +} + +void KInputChildProcess::Terminate() +{ + terminateChild=true; +} +bool KInputChildProcess::IsTerminated() +{ + return terminateChild; +} + +bool KInputChildProcess::ReceiveMsg(KEMessage *,int ) +{ + return false; +} +void KInputChildProcess::slotReceiveMsg(KEMessage *msg,int id) +{ + if (!ReceiveMsg(msg,id)) // made for virtual override + { + // otherwise emit signal + emit signalReceiveMsg(msg,id); + } +} +bool KInputChildProcess::SendMsg(KEMessage *msg) +{ + if (childConnect) return childConnect->SendMsg(msg); + return false; +} + + diff --git a/lskat/lskat/KInputChildProcess.h b/lskat/lskat/KInputChildProcess.h new file mode 100644 index 00000000..b4694df5 --- /dev/null +++ b/lskat/lskat/KInputChildProcess.h @@ -0,0 +1,57 @@ +/*************************************************************************** + KInputChildProcess.h - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef _KINPUTCHILDPROCESS_H_ +#define _KINPUTCHILDPROCESS_H_ + +#include +#include "KEMessage.h" +#include "KChildConnect.h" + + +class KInputChildProcess : public QObject +{ + Q_OBJECT + + private: + char *buffer; + QString inputbuffer; + int buffersize; + bool terminateChild; + protected: + KChildConnect *childConnect; + + public: + KInputChildProcess(int size_buffer=4096); + ~KInputChildProcess(); + bool exec(); + virtual bool ReceiveMsg(KEMessage *msg,int id); + // Forward calls to childconnect + bool SendMsg(KEMessage *msg); + // Immediately kills child's exec ! + void Terminate(); + bool IsTerminated(); + + + public slots: + void slotReceiveMsg(KEMessage *msg,int id); + + signals: + void signalReceiveMsg(KEMessage *msg,int id); +}; + + +#endif diff --git a/lskat/lskat/KInteractiveConnect.cpp b/lskat/lskat/KInteractiveConnect.cpp new file mode 100644 index 00000000..e95dcbe1 --- /dev/null +++ b/lskat/lskat/KInteractiveConnect.cpp @@ -0,0 +1,80 @@ +/*************************************************************************** + KInteractiveConnect.cpp - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +/*************************************************************************** + FILENAME| - description + ------------------- + begin : Tue Apr 4 2000 + copyright : (C) |1995-2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include +#include +#include "KInteractiveConnect.h" + +#include "KInteractiveConnect.moc" + +bool KInteractiveConnect::Next() +{ + // Dummy only for interactive + KEMessage *msg=new KEMessage; + emit signalPrepareMove(msg,KG_INPUTTYPE_INTERACTIVE); + delete msg; + return true; +} +bool KInteractiveConnect::Init(int id,KEMessage *) +{ + // emit signalReceiveMsg(msg); + SetID(id); + return true; +} +bool KInteractiveConnect::Exit() +{ + return true; +} + +KInteractiveConnect::~KInteractiveConnect() +{ +// printf("DESTRUCTING INTERACTIVE\n"); +} + +KInteractiveConnect::KInteractiveConnect() + : KChildConnect() +{ +} + +bool KInteractiveConnect::SendMsg(KEMessage *msg) +{ + printf("+- Interactive::SendMessage MESSAGE::Emmiting slotReceiveMsg\n"); + emit signalReceiveMsg(msg,QueryID()); + return true; +} +// Try not to use this..prodices just unnecessary string-msg +// conversion +bool KInteractiveConnect::Send(QString str) +{ + Receive(str); + return true; +} diff --git a/lskat/lskat/KInteractiveConnect.h b/lskat/lskat/KInteractiveConnect.h new file mode 100644 index 00000000..5a393c26 --- /dev/null +++ b/lskat/lskat/KInteractiveConnect.h @@ -0,0 +1,41 @@ +/*************************************************************************** + KInteractiveConnect.h - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef _KINTERACTIVECONNECT_H_ +#define _KINTERACTIVECONNECT_H_ + +#include "KEMessage.h" +#include "KChildConnect.h" + +class KInteractiveConnect:public KChildConnect +{ + Q_OBJECT + + public: + KInteractiveConnect(); + ~KInteractiveConnect(); + bool Next(); + bool Init(int id=0,KEMessage *msg=0); + bool Exit(); + bool SendMsg(KEMessage *msg); + bool Send(QString str); + + signals: + void signalPrepareMove(KEMessage *msg,KG_INPUTTYPE type); +// void signalReceiveMsg(KEMessage *msg,int id); +}; + +#endif diff --git a/lskat/lskat/KMessageEntry.cpp b/lskat/lskat/KMessageEntry.cpp new file mode 100644 index 00000000..cb9b2dc0 --- /dev/null +++ b/lskat/lskat/KMessageEntry.cpp @@ -0,0 +1,81 @@ +/*************************************************************************** + KMessageEntry.cpp - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include "KMessageEntry.h" + +void KMessageEntry::SetType(KGM_TYPE t) +{ + type=t; +} + +KGM_TYPE KMessageEntry::QueryType() +{ + return type; +} + +int KMessageEntry::QuerySize() +{ + return size; +} + +char * KMessageEntry::QueryData() +{ + return data; +} + +bool KMessageEntry::CopyData(int s,const char *c) +{ + if (s<1) return false; + data=(char *)calloc(s,1); + if (!data) return false; + // printf(" MessageEntry Copy Data to calloc %p\n",data); + memcpy(data,c,s); + size=s; + return true; +} + +KMessageEntry::KMessageEntry() +{ + // printf("KMessageEntry construct %p\n",this); + size=0; + type=(KGM_TYPE)0; + data=(char *)0; +} + +KMessageEntry::KMessageEntry(KMessageEntry &entry) +{ + // printf("KMessageEntry copy constructor from %p to %p\n",&entry,this); + *this=entry; +} +KMessageEntry &KMessageEntry::operator=(KMessageEntry &entry) +{ + // printf("KMessageEntry operator= from %p to %p\n",&entry,this); + SetType(entry.type); + CopyData(entry.size,entry.data); + return *this; +} + +KMessageEntry::~KMessageEntry() +{ + // printf("MessageEntry destructor %p\n",this); + // printf(" MessageEntry free %p\n",data); + if (data) free(data); + data=(char *)0; +} + diff --git a/lskat/lskat/KMessageEntry.h b/lskat/lskat/KMessageEntry.h new file mode 100644 index 00000000..3feb4fad --- /dev/null +++ b/lskat/lskat/KMessageEntry.h @@ -0,0 +1,42 @@ +/*************************************************************************** + KMessageEntry.h - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef _KMESSAGEENTRY_H_ +#define _KMESSAGEENTRY_H_ + +#include "KConnectTypes.h" + +class KMessageEntry +{ + private: + int size; + KGM_TYPE type; + char *data; + + public: + void SetType(KGM_TYPE t); + KGM_TYPE QueryType(); + int QuerySize(); + char *QueryData(); + bool CopyData(int s,const char *c); + KMessageEntry(); + KMessageEntry(KMessageEntry &entry); + KMessageEntry &operator=(KMessageEntry &entry); + ~KMessageEntry(); +}; + +#endif diff --git a/lskat/lskat/KProcessConnect.cpp b/lskat/lskat/KProcessConnect.cpp new file mode 100644 index 00000000..740486b3 --- /dev/null +++ b/lskat/lskat/KProcessConnect.cpp @@ -0,0 +1,192 @@ +/*************************************************************************** + KProcessConnect.cpp - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +/*************************************************************************** + FILENAME| - description + ------------------- + begin : Tue Apr 4 2000 + copyright : (C) |1995-2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include +#include "KProcessConnect.h" + +#include "KProcessConnect.moc" + +KProcessConnect::KProcessConnect() + : KChildConnect() +{ + running=false; + process=0; +} + +KProcessConnect::~KProcessConnect() +{ + Exit(); + delete process; +// printf("DESTRUCTRING KPROCESSCONNECT\n"); +} + +bool KProcessConnect::Init(int id,KEMessage *msg) +{ + int size; + char *p; + + SetID(id); + if (msg) + { + if (!msg->GetData(QCString("ProcessName"),p,size)) return false; // no process name + processname=p; + /* + printf("Found processname '%s' size %d size=%u\n", + p,size,msg->QueryNumberOfKeys()); + */ + msg->Remove(QCString("ProcessName")); + } + if (processname.length()<1) return false; + + inputbuffer=""; + + // Delete first on multiple init + if (running) Exit(); + + // create process + process=new KProcess; + *process << processname; + connect(process, SIGNAL(receivedStdout(KProcess *, char *, int )), + this, SLOT(slotReceivedStdout(KProcess *, char * , int ))); + connect(process, SIGNAL(processExited(KProcess *)), + this, SLOT(slotProcessExited(KProcess *))); + /* + connect(process, SIGNAL(wroteStdin(KProcess *)), + this, SLOT(slotWroteStdin(KProcess *))); + */ + + // TRUE if ok + running=process->start(KProcess::NotifyOnExit,KProcess::All); + + if (running && msg && msg->QueryNumberOfKeys()>0) + { + SendMsg(msg); + } + + return running; +} + +void KProcessConnect::slotReceivedStdout(KProcess *, char *buffer, int buflen) +{ + QString s; + char c; + int pos; + + if (buflen<1) return ; + if (buffer[buflen-1]!=0) // shit..we got a not null terminated string + { + c=buffer[buflen-1]; + buffer[buflen-1]=0; + s=buffer; + s+=c; + } + else + { + s=buffer; + } + // Append old unresolved input + s=inputbuffer+s; + pos=s.findRev(KEMESSAGE_CR); + // printf("String '%s' pos=%d len=%d\n",(const char *)s,pos,s.length()); + if (pos<0) + { + inputbuffer=s; + } + else if (pos+KEMESSAGE_CR.length()==s.length()) + { + // CR at the end...calling receive + Receive(s); + } + else + { + inputbuffer=s.right(s.length()-pos-KEMESSAGE_CR.length()); + s=s.left(pos+KEMESSAGE_CR.length()); + // printf("s='%s' in='%s'\n",(const char *)s,(const char *)inputbuffer); + Receive(s); + } +} +void KProcessConnect::slotProcessExited(KProcess *) +{ + running=false; + delete process; + process=0; + Init(QueryID()); +} +void KProcessConnect::slotWroteStdin(KProcess *) +{ + printf("slotWroteStdin:: IS NEVER CALLED\n"); +} + +bool KProcessConnect::Exit() +{ + // kill process if running + if (running) + { + running=false; + if (process) process->kill(); + delete process; + process=0; + } + return true; +} + +bool KProcessConnect::Next() +{ + bool result; + if (!running) return false; + // create and send message + // printf("+- KProcessConnect::ProcessNext\n"); + KEMessage *msg=new KEMessage; + // User fills message + emit signalPrepareMove(msg,KG_INPUTTYPE_PROCESS); + result=SendMsg(msg); + delete msg; + return result; +} + +// Send string to child +bool KProcessConnect::Send(QString str) +{ + bool result; + // printf("****** PROCESS:SEND\n"); + if (!running || !process) return false; + if (!str || str.length()<1) return true; // no need to send crap + // TODO ..why? + QString s; + s=KEMESSAGE_CR+KEMESSAGE_CR; + str=s+str; + // printf("+++ Sending to child '%s'!!!\n",(const char *)str); + result=process->writeStdin(str.latin1(),str.length()+1); + if (!result) printf("ERROR in PROCESS SEND\n"); + return result; +} + diff --git a/lskat/lskat/KProcessConnect.h b/lskat/lskat/KProcessConnect.h new file mode 100644 index 00000000..9229ddd4 --- /dev/null +++ b/lskat/lskat/KProcessConnect.h @@ -0,0 +1,57 @@ +/*************************************************************************** + KProcessConnect.h - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef _KPROCESSCONNECT_H_ +#define _KPROCESSCONNECT_H_ + +#include +//#include +// #include "KEInput.h" +#include "KEMessage.h" +#include "KChildConnect.h" + + +class KProcessConnect: public KChildConnect +{ + Q_OBJECT + + private: + KProcess *process; + QString processname; + bool running; + + public: + KProcessConnect(); + ~KProcessConnect(); + bool Init(int id=0,KEMessage *msg=0); + bool Exit(); + bool Next(); + // bool SendMsg(KEMessage *msg); + virtual bool Send(QString str); + // void Receive(QString input); + + public slots: + void slotReceivedStdout(KProcess *proc, char *buffer, int buflen); + void slotProcessExited(KProcess *p); + void slotWroteStdin(KProcess *p); + + + signals: + void signalPrepareMove(KEMessage *msg,KG_INPUTTYPE type); +// void signalReceiveMsg(KEMessage *msg); +}; + +#endif diff --git a/lskat/lskat/KRSocket.cpp b/lskat/lskat/KRSocket.cpp new file mode 100644 index 00000000..0a699600 --- /dev/null +++ b/lskat/lskat/KRSocket.cpp @@ -0,0 +1,374 @@ +/*************************************************************************** + KRSocket.cpp - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +/*************************************************************************** + FILENAME| - description + ------------------- + begin : Tue Apr 4 2000 + copyright : (C) |1995-2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +/* + * This file is part of the KDE libraries + * Copyright (C) 1997 Torben Weis (weis@kde.org) + * + * $Id$ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + **/ + +#include + +#include +#include +// on Linux/libc5, this includes linux/socket.h where SOMAXCONN is defined +#include +#include +#include +#include + +#include + +#include + +#include "KRSocket.h" + +#include +#include +#include +// defines MAXDNAME under Solaris +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYSENT_H +#include +#endif + +#ifdef TIME_WITH_SYS_TIME +#include +#endif + +// Play it safe, use a reasonable default, if SOMAXCONN was nowhere defined. +#ifndef SOMAXCONN +#warning Your header files do not seem to support SOMAXCONN +#define SOMAXCONN 5 +#endif + +#include + +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 // this is the value, I found under Linux +#endif + +#include + +KRServerSocket::KRServerSocket( const char *_path, int optname, int value, int level) : + notifier( 0L ), sock( -1 ) +{ + domain = PF_UNIX; + + if ( !init ( _path,optname,value,level ) ) + { + qFatal("Error constructing PF_UNIX domain server socket\n"); + return; + } + + notifier = new QSocketNotifier( sock, QSocketNotifier::Read ); + connect( notifier, SIGNAL( activated(int) ), this, SLOT( slotAccept(int) ) ); +} + +KRServerSocket::KRServerSocket( const char *_path ) : + notifier( 0L ), sock( -1 ) +{ + domain = PF_UNIX; + + if ( !init ( _path ) ) + { + qFatal("Error constructing PF_UNIX domain server socket\n"); + return; + } + + notifier = new QSocketNotifier( sock, QSocketNotifier::Read ); + connect( notifier, SIGNAL( activated(int) ), this, SLOT( slotAccept(int) ) ); +} + +KRServerSocket::KRServerSocket( unsigned short int _port ) : + notifier( 0L ), sock( -1 ) +{ + domain = PF_INET; + + if ( !init ( _port ) ) + { + // fatal("Error constructing\n"); + return; + } + + notifier = new QSocketNotifier( sock, QSocketNotifier::Read ); + connect( notifier, SIGNAL( activated(int) ), this, SLOT( slotAccept(int) ) ); +} + +KRServerSocket::KRServerSocket( unsigned short int _port,int optname,int value,int level ) : + notifier( 0L ), sock( -1 ) +{ + domain = PF_INET; + + if ( !init ( _port,optname,value,level ) ) + { + // fatal("Error constructing\n"); + return; + } + + notifier = new QSocketNotifier( sock, QSocketNotifier::Read ); + connect( notifier, SIGNAL( activated(int) ), this, SLOT( slotAccept(int) ) ); +} + +bool KRServerSocket::init( const char *_path ) +{ + return init(_path,0); +} + +bool KRServerSocket::init( const char *_path,int optname,int value,int level ) +{ + if ( domain != PF_UNIX ) + return false; + + int l = strlen( _path ); + if ( l > UNIX_PATH_MAX - 1 ) + { + kdWarning() << "Too long PF_UNIX domain name: " << _path << endl; + return false; + } + + sock = ::socket( PF_UNIX, SOCK_STREAM, 0 ); + if (sock < 0) + { + kdWarning() << "Could not create socket" << endl; + return false; + } + // Heni - 05042000 + if (optname>0) + { + kde_socklen_t len=sizeof(value); + if (-1==setsockopt(sock,level,optname,(char*)&value,len )) + { + kdWarning() << "Could not set socket options." << endl; + } + } + // end Heni + + unlink(_path ); + + struct sockaddr_un name; + name.sun_family = AF_UNIX; + strcpy( name.sun_path, _path ); + + if ( bind( sock, (struct sockaddr*) &name,sizeof( name ) ) < 0 ) + { + kdWarning() << "Could not bind to socket." << endl; + ::close( sock ); + sock = -1; + return false; + } + + if ( chmod( _path, 0600) < 0 ) + { + kdWarning() << "Could not setupt premissions for server socket." << endl; + ::close( sock ); + sock = -1; + return false; + } + + if ( listen( sock, SOMAXCONN ) < 0 ) + { + kdWarning() << "Error listening on socket." << endl; + ::close( sock ); + sock = -1; + return false; + } + + return true; +} + +bool KRServerSocket::init( unsigned short int _port ) +{ + return init(_port,0); +} +bool KRServerSocket::init( unsigned short int _port,int optname,int value,int level ) +{ + if ( +#ifdef INET6 + ( domain != PF_INET6 ) && +#endif + ( domain != PF_INET ) ) + return false; + + sock = ::socket( domain, SOCK_STREAM, 0 ); + if (sock < 0) + { + kdWarning() << "Could not create socket" << endl; + return false; + } + // Heni - 05042000 + if (optname>0) + { + kde_socklen_t len=sizeof(value); + if (-1==setsockopt(sock,level,optname,(char*)&value,len )) + { + kdWarning() << "Could not set socket options." << endl; + } + } + // end Heni + + if (domain == AF_INET) { + + sockaddr_in name; + + name.sin_family = domain; + name.sin_port = htons( _port ); + name.sin_addr.s_addr = htonl(INADDR_ANY); + + if ( bind( sock, (struct sockaddr*) &name,sizeof( name ) ) < 0 ) { + kdWarning() << "Could not bind to socket." << endl; + ::close( sock ); + sock = -1; + return false; + } + } +#ifdef INET6 + else if (domain == AF_INET6) { + sockaddr_in6 name; + + name.sin6_family = domain; + name.sin6_flowinfo = 0; + name.sin6_port = htons(_port); + memcpy(&name.sin6_addr, &in6addr_any, sizeof(in6addr_any)); + + if ( bind( sock, (struct sockaddr*) &name,sizeof( name ) ) < 0 ) { + kdWarning() << "Could not bind to socket!" << endl; + ::close( sock ); + sock = -1; + return false; + } + } +#endif + + if ( listen( sock, SOMAXCONN ) < 0 ) + { + kdWarning() << "Error listening on socket" << endl; + ::close( sock ); + sock = -1; + return false; + } + + return true; +} + +unsigned short int KRServerSocket::port() +{ + if ( domain != PF_INET ) + return false; + + ksockaddr_in name; kde_socklen_t len = sizeof(name); + getsockname(sock, (struct sockaddr *) &name, &len); + return ntohs(get_sin_port(name)); +} + +unsigned long KRServerSocket::ipv4_addr() +{ + if ( domain != PF_INET ) + return 0; + + sockaddr_in name; kde_socklen_t len = sizeof(name); + getsockname(sock, (struct sockaddr *) &name, &len); + if (name.sin_family == AF_INET) // It's IPv4 + return ntohl(name.sin_addr.s_addr); +#ifdef INET6 + else if (name.sin_family == AF_INET6) // It's IPv6 Ah. + return 0; +#endif + else // We dunno what it is + return 0; +} + +void KRServerSocket::slotAccept( int ) +{ + if ( domain == PF_INET ) + { + ksockaddr_in clientname; + int new_sock; + + kde_socklen_t size = sizeof(clientname); + + if ((new_sock = accept (sock, (struct sockaddr *) &clientname, &size)) < 0) + { + kdWarning() << "Error accepting" << endl; + return; + } + + emit accepted( new KSocket( new_sock ) ); + } + else if ( domain == PF_UNIX ) + { + struct sockaddr_un clientname; + int new_sock; + + kde_socklen_t size = sizeof(clientname); + + if ((new_sock = accept (sock, (struct sockaddr *) &clientname, &size)) < 0) + { + kdWarning() << "Error accepting" << endl; + return; + } + + emit accepted( new KSocket( new_sock ) ); + } +} + +KRServerSocket::~KRServerSocket() +{ + delete notifier; + + close( sock ); +} + +#include "KRSocket.moc" diff --git a/lskat/lskat/KRSocket.h b/lskat/lskat/KRSocket.h new file mode 100644 index 00000000..6c0b9974 --- /dev/null +++ b/lskat/lskat/KRSocket.h @@ -0,0 +1,169 @@ +/*************************************************************************** + KRSocket.h - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +/* + * Remark: This is a copy of the ksock library. It will be merged with + * the ksock file as soon as it supports the SOCK_REUSE_ADDR flag +*/ + + +#ifndef KRSOCK_H +#define KRSOCK_H + +#include +#include +// we define STRICT_ANSI to get rid of some warnings in glibc +#ifndef __STRICT_ANSI__ +#define __STRICT_ANSI__ +#define _WE_DEFINED_IT_ +#endif +#include +#ifdef _WE_DEFINED_IT_ +#undef __STRICT_ANSI__ +#undef _WE_DEFINED_IT_ +#endif + +#include + +#include +class QSocketNotifier; + +#ifdef INET6 +typedef sockaddr_in6 ksockaddr_in; +#define KSOCK_DEFAULT_DOMAIN PF_INET6 +#else +typedef sockaddr_in ksockaddr_in; +#define KSOCK_DEFAULT_DOMAIN PF_INET +#endif + +#include + +class KRServerSocketPrivate; + + +/** + * Monitor a port for incoming TCP/IP connections. + * + * You can use a KRServerSocket to listen on a port for incoming + * connections. When a connection arrived in the port, a KSocket + * is created and the signal accepted is raised. Make sure you + * always connect to this signal. If you dont the ServerSocket will + * create new KSocket's and no one will delete them! + * + * If socket() is -1 or less the socket was not created properly. + * + * @author Torben Weis + * @version $Id$ + * @short Monitor a port for incoming TCP/IP connections. +*/ +class KRServerSocket : public QObject +{ + Q_OBJECT +public: + /** + * Constructor. + * @param _port the port number to monitor for incoming connections. + */ + KRServerSocket( unsigned short int _port ); + KRServerSocket( unsigned short int _port, int optname, int value=1, int level=SOL_SOCKET); + + /** + * Creates a UNIX domain server socket. + */ + KRServerSocket( const char *_path ); + KRServerSocket( const char *_path, int optname, int value=1, int level=SOL_SOCKET); + + /** + * Destructor. Closes the socket if it was not already closed. + */ + ~KRServerSocket(); + + /** + * Get the file descriptor assoziated with the socket. + */ + int socket() const { return sock; } + + /** + * Returns the port number which is being monitored. + */ + unsigned short int port(); + + /** + * The address. + */ + unsigned long ipv4_addr(); + +public slots: + /** + * Called when someone connected to our port. + */ + virtual void slotAccept( int ); + +signals: + /** + * A connection has been accepted. + * It is your task to delete the KSocket if it is no longer needed. + */ + void accepted( KSocket* ); + +protected: + bool init( short unsigned int ); + bool init( const char *_path ); + bool init( const char *_path, int optname, int value=1, int level=SOL_SOCKET); + bool init( short unsigned int, int optname, int value=1, int level=SOL_SOCKET); + + /** + * Notifies us when there is something to read on the port. + */ + QSocketNotifier *notifier; + + /** + * The file descriptor for this socket. sock may be -1. + * This indicates that it is not connected. + */ + int sock; + + int domain; + +private: + KRServerSocket(const KRServerSocket&); + KRServerSocket& operator=(const KRServerSocket&); + + KRServerSocketPrivate *d; +}; + + +// Here are a whole bunch of hackish macros that allow one to +// get at the correct member of ksockaddr_in + +#ifdef INET6 +#define get_sin_addr(x) x.sin6_addr +#define get_sin_port(x) x.sin6_port +#define get_sin_family(x) x.sin6_family +#define get_sin_paddr(x) x->sin6_addr +#define get_sin_pport(x) x->sin6_port +#define get_sin_pfamily(x) x->sin6_family +#else +#define get_sin_addr(x) x.sin_addr +#define get_sin_port(x) x.sin_port +#define get_sin_family(x) x.sin_family +#define get_sin_paddr(x) x->sin_addr +#define get_sin_pport(x) x->sin_port +#define get_sin_pfamily(x) x->sin_family +#endif + +#endif diff --git a/lskat/lskat/KRemoteConnect.cpp b/lskat/lskat/KRemoteConnect.cpp new file mode 100644 index 00000000..f1bcb4f3 --- /dev/null +++ b/lskat/lskat/KRemoteConnect.cpp @@ -0,0 +1,334 @@ +/*************************************************************************** + KRemoteConnect.cpp - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +/*************************************************************************** + FILENAME| - description + ------------------- + begin : Tue Apr 4 2000 + copyright : (C) |1995-2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include +#include +#include +#include +#include +#include "KRemoteConnect.h" + +#include "KRemoteConnect.moc" + +const char* LSKAT_SERVICE = "_lskat._tcp"; + +KRemoteConnect::KRemoteConnect() + : KChildConnect() +{ + port=7687; + IP="localhost"; + socketStatus=KR_INVALID; + kSocket=0; + service=0; + kServerSocket=0; + bufferMsg=0; + buffer=new char[4097]; + inputbuffer=""; + input_pending=false; +} +KRemoteConnect::~KRemoteConnect() +{ + Exit(); + delete buffer; + delete service; + printf("DESTGRUCTING KRemoteConenct\n"); +} + +KR_STATUS KRemoteConnect::QueryStatus() +{ + return socketStatus; +} + +bool KRemoteConnect::Init(int id,KEMessage *msg) +{ +short prt; +char *p; +int size; +bool tryserver; + + SetID(id); + if (msg) + { + if (msg->GetData(QCString("Port"),prt)) + { + port=(unsigned int)prt; + msg->Remove(QCString("Port")); + } + if (msg->GetData(QCString("IP"),p,size)) + { + IP=QCString(p); + msg->Remove(QCString("IP")); + } + if (msg->GetData(QCString("Name"),p,size)) + { + Name=QString::fromUtf8(p); + msg->Remove(QCString("Name")); + } + } + /* + printf("Connecting to %s %u (remain=%d)\n", + (const char *)IP,port,msg->QueryNumberOfKeys()); + */ + + // First try to connect to given host:socket + // if no IP given only offer server + tryserver=false; + if (!IP.isEmpty()) + { + kSocket=new KSocket(IP.latin1(),port); + if (!kSocket) return false; + if (kSocket->socket()!=-1) // success + { + kSocket->enableRead(TRUE); + //kSocket->enableWrite(TRUE); + connect(kSocket,SIGNAL(closeEvent(KSocket *)), + this,SLOT(socketClosed(KSocket *))); + connect(kSocket,SIGNAL(readEvent(KSocket *)), + this,SLOT(socketRead(KSocket *))); + /* + connect(kSocket,SIGNAL(writeEvent(KSocket *)), + this,SLOT(socketWrite(KSocket *))); + */ + /* + printf("Socket(%d) %p connection built to a server\n", + kSocket->socket(),kSocket); + */ + socketStatus=KR_CLIENT; + + // Send msg if any + if (msg->QueryNumberOfKeys()>0) + { + msg->AddData(QCString("Server"),(short)QueryID()); + SendMsg(msg); + } + + } + else + { + tryserver=true; + } + } + else + { + printf("NO IP given..only server possible\n"); + tryserver=true; + } + + if (tryserver) // become a server + { + delete kSocket; + kSocket=0; + delete service; + service = 0; + // Store message + if (msg->QueryNumberOfKeys()>0) + { + bufferMsg=new KEMessage; + *bufferMsg=*msg; + } + else + { + bufferMsg=0; + } + socketStatus=KR_WAIT_FOR_CLIENT; + OfferServerSocket(); + return false; + } + + return true; +} + +bool KRemoteConnect::OfferServerSocket() +{ + if (kServerSocket) + { + return false; + } + kServerSocket=new KRServerSocket(port,SO_REUSEADDR); + if (!kServerSocket) + { + socketStatus=KR_INVALID; + return false; + } + if (kServerSocket->socket()==-1) + { + socketStatus=KR_NO_SOCKET; + return false; + } + printf("Offering socket and publishing stuff\n"); + service = new DNSSD::PublicService(Name,LSKAT_SERVICE,port); + service->publishAsync(); + connect(kServerSocket,SIGNAL(accepted(KSocket *)), + this,SLOT(socketRequest(KSocket *))); + + return true; +} +void KRemoteConnect::socketRequest(KSocket *sock) +{ + if (kSocket) // already connected + { + delete sock; + delete kServerSocket; + kServerSocket=0; + return ; + } + kSocket=sock; + if (kSocket->socket()!=-1) // success + { + kSocket->enableRead(TRUE); + //kSocket->enableWrite(TRUE); + connect(kSocket,SIGNAL(closeEvent(KSocket *)), + this,SLOT(socketClosed(KSocket *))); + connect(kSocket,SIGNAL(readEvent(KSocket *)), + this,SLOT(socketRead(KSocket *))); + /* + connect(kSocket,SIGNAL(writeEvent(KSocket *)), + this,SLOT(socketWrite(KSocket *))); + */ + socketStatus=KR_SERVER; + delete kServerSocket; // no more connections + kServerSocket=0; + if (bufferMsg) + { + // Delayed sending of init msg + bufferMsg->AddData(QCString("Client"),(short)QueryID()); + SendMsg(bufferMsg); + delete bufferMsg; + bufferMsg=0; + } + } +} + +void KRemoteConnect::socketClosed(KSocket *sock) +{ + delete sock; + kSocket=0; + socketStatus=KR_INVALID; + KEMessage *msg=new KEMessage; + msg->AddData(QCString("ConnectionLost"),(short)QueryID()); + emit signalReceiveMsg(msg,QueryID()); + delete msg; +} +void KRemoteConnect::socketRead(KSocket *sock) +{ + ssize_t buflen; + QString s; + char c; + int pos; + + // printf("++++++ Incoming socket read +++++++\n"); + if (-1==sock->socket()) return ; + // printf("Read input on socket %p\n",sock); + buflen=read(sock->socket(),buffer,4096); + buffer[buflen]=0; + // printf("Read %d byte <%s>\n",buflen,buffer); + + if (buflen<1) return ; + if (buffer[buflen-1]!=0) // shit..we got a not null terminated string + { + c=buffer[buflen-1]; + buffer[buflen-1]=0; + s=buffer; + s+=c; + } + else + { + s=buffer; + } + + // Append old unresolved input + s=inputbuffer+s; + pos=s.findRev(KEMESSAGE_CR); + // printf("String '%s' pos=%d len=%d\n",(const char *)s,pos,s.length()); + if (pos<0) + { + inputbuffer=s; + } + else if (pos+KEMESSAGE_CR.length()==s.length()) + { + // CR at the end...calling receive + Receive(s); + } + else + { + inputbuffer=s.right(s.length()-pos-KEMESSAGE_CR.length()); + s=s.left(pos+KEMESSAGE_CR.length()); + // printf("s='%s' in='%s'\n",(const char *)s,(const char *)inputbuffer); + Receive(s); + } +} + + +void KRemoteConnect::socketWrite(KSocket *) +{ + // printf("wrtie input on socket %p\n",sock); +} + +bool KRemoteConnect::Exit() +{ + delete kSocket; + delete kServerSocket; + delete bufferMsg; + kSocket=0; + kServerSocket=0; + bufferMsg=0; + socketStatus=KR_INVALID; + return true; +} +bool KRemoteConnect::Next() +{ + bool result; + // printf("+- KRemoteConnect::Next() status=%d\n",socketStatus); + if (socketStatus<=0) return false; + // create and send message + KEMessage *msg=new KEMessage; + // User fills message + emit signalPrepareMove(msg,KG_INPUTTYPE_REMOTE); + result=SendMsg(msg); + delete msg; + return result; +} + +// Send string to child +bool KRemoteConnect::Send(QString str) +{ + // connected? + if (!kSocket || kSocket->socket()==-1) return false; + if (socketStatus<=0) return false; + + if (-1==write(kSocket->socket(),str.latin1(),str.length()+1)) + { + printf("Warning: Problems writing to socket.\n"); + return false; + } + return true;; +} diff --git a/lskat/lskat/KRemoteConnect.h b/lskat/lskat/KRemoteConnect.h new file mode 100644 index 00000000..baa005d6 --- /dev/null +++ b/lskat/lskat/KRemoteConnect.h @@ -0,0 +1,74 @@ +/*************************************************************************** + KRemoteConnect.h - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef _KREMOTECONNECT_H_ +#define _KREMOTECONNECT_H_ + +//#include +#include "KEMessage.h" +#include "KRSocket.h" +//#include "ksock.h" +#include "KChildConnect.h" +#include + + + +class KRemoteConnect: public KChildConnect +{ + Q_OBJECT + + protected: + KRServerSocket *kServerSocket; + KSocket *kSocket; + QString IP; + QString Name; + ushort port; + KR_STATUS socketStatus; + char *buffer; + KEMessage *bufferMsg; + DNSSD::PublicService *service; +// QString inputbuffer; +// bool input_pending; +// QStrList inputcache; + + public: + KRemoteConnect(); + ~KRemoteConnect(); + bool Init(int id=0,KEMessage *msg=0); + bool Exit(); + bool Next(); +// bool SendMsg(KEMessage *msg); + virtual bool Send(QString str); +// void Receive(QString input); + virtual KR_STATUS QueryStatus(); + + protected: + bool OfferServerSocket(); + + protected slots: + void socketClosed(KSocket *sock); + void socketRead(KSocket *sock); + void socketWrite(KSocket *sock); + void socketRequest(KSocket *sock); + + + + signals: + void signalPrepareMove(KEMessage *msg,KG_INPUTTYPE type); +// void signalReceiveMsg(KEMessage *msg); +}; + +#endif diff --git a/lskat/lskat/Makefile.am b/lskat/lskat/Makefile.am new file mode 100644 index 00000000..ade32c96 --- /dev/null +++ b/lskat/lskat/Makefile.am @@ -0,0 +1,21 @@ + +####### kdevelop will overwrite this part!!! (begin)########## +bin_PROGRAMS = lskat +lskat_SOURCES = msgdlg.cpp networkdlg.cpp namedlg.cpp KRemoteConnect.cpp KRSocket.cpp KProcessConnect.cpp KMessageEntry.cpp KInteractiveConnect.cpp KInputChildProcess.cpp KEMessage.cpp KEInput.cpp KConnectEntry.cpp KChildConnect.cpp lskatview.cpp lskatdoc.cpp lskat.cpp main.cpp networkdlgbase.ui + +lskat_LDADD = $(LIB_KFILE) $(LIB_KDEGAMES) $(LIB_KDNSSD) +lskat_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes) + +METASOURCES = AUTO + +lskat_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +rcdir = $(kde_datadir)/lskat +rc_DATA = lskatui.rc + + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/lskat.pot + diff --git a/lskat/lskat/lskat.cpp b/lskat/lskat/lskat.cpp new file mode 100644 index 00000000..9d1ce7a6 --- /dev/null +++ b/lskat/lskat/lskat.cpp @@ -0,0 +1,1081 @@ +/* + lskat.cpp - description + ------------------- + begin : Tue May 2 15:47:11 CEST 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +// include files for QT +#include + +// include files for KDE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// application specific includes +#include "lskat.h" +#include "lskatview.h" +#include "lskatdoc.h" +#include "namedlg.h" +#include "networkdlg.h" +#include "msgdlg.h" +#include + +#include +#include + +#define ACTION(x) (actionCollection()->action(x)) +#define ID_STATUS_MSG 1003 +#define ID_STATUS_MOVER 1002 + +LSkatApp::LSkatApp() : KMainWindow(0) +{ + config=kapp->config(); + + // localise data file + QString file=QString::fromLatin1("lskat/grafix/t1.png"); + mGrafix=kapp->dirs()->findResourceDir("data", file); + if (mGrafix.isNull()) mGrafix = QCString("grafix/"); + else mGrafix+=QCString("lskat/grafix/"); + if (global_debug>3) printf("Localised datafile=%s\n",mGrafix.latin1()); + + + /////////////////////////////////////////////////////////////////// + // call inits to invoke all other construction parts + initGUI(); + initStatusBar(); + + setupGUI(KMainWindow::StatusBar | Save); + createGUI(QString::null, false); + + initDocument(); + initView(); + + doc->ReadConfig(config); + + // Needs to be after readOptions as we read in default paths + doc->LoadGrafix(mGrafix); + + mInput=new KEInput(this); + doc->SetInputHandler(mInput); + connect(mInput,SIGNAL(signalPrepareProcessMove(KEMessage *)), + this,SLOT(slotPrepareProcessMove(KEMessage *))); + connect(mInput,SIGNAL(signalPrepareRemoteMove(KEMessage *)), + this,SLOT(slotPrepareRemoteMove(KEMessage *))); + connect(mInput,SIGNAL(signalPrepareInteractiveMove(KEMessage *)), + this,SLOT(slotPrepareInteractiveMove(KEMessage *))); + connect(mInput,SIGNAL(signalReceiveInput(KEMessage *, int)), + this,SLOT(slotReceiveInput(KEMessage *,int ))); + + setMinimumSize(640,480); + setMaximumSize(800,600); + resize( 640, 480 ); + + // better be last in init + checkMenus(); +} + +LSkatApp::~LSkatApp() +{ + delete mInput; +} + +void LSkatApp::checkMenus(int menu) +{ + if (!menu || (menu&CheckFileMenu)) + { + if (doc->IsRunning()) disableAction("new_game"); + else enableAction("new_game"); + + if (!doc->IsRunning()) disableAction("end_game"); + else enableAction("end_game"); + + if (doc->GetPlayedBy(0)==KG_INPUTTYPE_REMOTE || + doc->GetPlayedBy(0)==KG_INPUTTYPE_REMOTE ) + { + enableAction("send_message"); + } + else + { + disableAction("send_message"); + } + } + + if (!menu || (menu&CheckOptionsMenu)) + { + ((KSelectAction *)ACTION("startplayer"))->setCurrentItem(doc->GetStartPlayer()); + + if (doc->IsRunning()) disableAction("startplayer"); + else enableAction("startplayer"); + + if (doc->GetPlayedBy(0)==KG_INPUTTYPE_INTERACTIVE) + ((KSelectAction *)ACTION("player1"))->setCurrentItem(0); + else if (doc->GetPlayedBy(0)==KG_INPUTTYPE_PROCESS) + ((KSelectAction *)ACTION("player1"))->setCurrentItem(1); + else + ((KSelectAction *)ACTION("player1"))->setCurrentItem(2); + + /* + if (doc->IsRunning()) disableAction("player1"); + else enableAction("player1"); + */ + + if (doc->GetPlayedBy(1)==KG_INPUTTYPE_INTERACTIVE) + ((KSelectAction *)ACTION("player2"))->setCurrentItem(0); + else if (doc->GetPlayedBy(1)==KG_INPUTTYPE_PROCESS) + ((KSelectAction *)ACTION("player2"))->setCurrentItem(1); + else + ((KSelectAction *)ACTION("player2"))->setCurrentItem(2); + + /* + if (doc->IsRunning()) disableAction("player2"); + else enableAction("player2"); + */ + + ((KSelectAction *)ACTION("choose_level"))->setCurrentItem(doc->GetComputerLevel()-1); + } + +} + +void LSkatApp::initGUI() +{ + QStringList list; + + (void)KStdAction::openNew(this, SLOT(slotFileNew()), actionCollection(), "new_game"); + ACTION("new_game")->setStatusText(i18n("Starting a new game...")); + ACTION("new_game")->setWhatsThis(i18n("Starting a new game...")); + (void)new KAction(i18n("&End Game"),"stop", 0, this, SLOT(slotFileEnd()), + actionCollection(), "end_game"); + ACTION("end_game")->setStatusText(i18n("Ending the current game...")); + ACTION("end_game")->setWhatsThis(i18n("Aborts a currently played game. No winner will be declared.")); + (void)new KAction(i18n("&Clear Statistics"),"flag", 0, this, SLOT(slotFileStatistics()), + actionCollection(), "clear_statistics"); + ACTION("clear_statistics")->setStatusText(i18n("Delete all time statistics...")); + ACTION("clear_statistics")->setWhatsThis(i18n("Clears the all time statistics which is kept in all sessions.")); + (void)new KAction(i18n("Send &Message..."), CTRL+Key_M, this, SLOT(slotFileMessage()), + actionCollection(), "send_message"); + ACTION("send_message")->setStatusText(i18n("Sending message to remote player...")); + ACTION("send_message")->setWhatsThis(i18n("Allows you to talk with a remote player.")); + (void)KStdAction::quit(this, SLOT(slotFileQuit()), actionCollection(), "game_exit"); + ACTION("game_exit")->setStatusText(i18n("Exiting...")); + ACTION("game_exit")->setWhatsThis(i18n("Quits the program.")); + + (void)new KSelectAction(i18n("Starting Player"),0,this,SLOT(slotStartplayer()), + actionCollection(), "startplayer"); + ACTION("startplayer")->setStatusText(i18n("Changing starting player...")); + ACTION("startplayer")->setWhatsThis(i18n("Chooses which player begins the next game.")); + list.clear(); + list.append(i18n("Player &1")); + list.append(i18n("Player &2")); + ((KSelectAction *)ACTION("startplayer"))->setItems(list); + + (void)new KSelectAction(i18n("Player &1 Played By"),0,this,SLOT(slotPlayer1By()), + actionCollection(), "player1"); + ACTION("player1")->setStatusText(i18n("Changing who plays player 1...")); + ACTION("player1")->setWhatsThis(i18n("Changing who plays player 1...")); + list.clear(); + list.append(i18n("&Player")); + list.append(i18n("&Computer")); + list.append(i18n("&Remote")); + ((KSelectAction *)ACTION("player1"))->setItems(list); + (void)new KSelectAction(i18n("Player &2 Played By"),0,this,SLOT(slotPlayer2By()), + actionCollection(), "player2"); + ACTION("player1")->setStatusText(i18n("Changing who plays player 2...")); + ACTION("player1")->setWhatsThis(i18n("Changing who plays player 2...")); + ((KSelectAction *)ACTION("player2"))->setItems(list); + + (void)new KSelectAction(i18n("&Level"),0,this,SLOT(slotLevel()), + actionCollection(), "choose_level"); + ACTION("choose_level")->setStatusText(i18n("Change level...")); + ACTION("choose_level")->setWhatsThis(i18n("Change the strength of the computer player.")); + list.clear(); + list.append(i18n("&Normal")); + list.append(i18n("&Advanced")); + list.append(i18n("&Hard")); + ((KSelectAction *)ACTION("choose_level"))->setItems(list); + + (void)new KAction(i18n("Select &Card Deck..."), 0, this, SLOT(slotOptionsCardDeck()), + actionCollection(), "select_carddeck"); + ACTION("select_carddeck")->setStatusText(i18n("Configure card decks...")); + ACTION("select_carddeck")->setWhatsThis(i18n("Choose how the cards should look.")); + + (void)new KAction(i18n("Change &Names..."), 0, this, SLOT(slotOptionsNames()), + actionCollection(), "change_names"); + ACTION("change_names")->setStatusText(i18n("Configure player names...")); + ACTION("change_names")->setWhatsThis(i18n("Configure player names...")); + + actionCollection()->setHighlightingEnabled(true); + connect(actionCollection(), SIGNAL(actionStatusText(const QString &)), SLOT(slotStatusMsg(const QString &))); + connect(actionCollection(), SIGNAL(clearStatusText()), SLOT(slotClearStatusMsg())); + + KStdAction::keyBindings(guiFactory(), SLOT(configureShortcuts()), +actionCollection()); +} + + +void LSkatApp::initStatusBar() +{ + /////////////////////////////////////////////////////////////////// + // STATUSBAR + // statusBar()->setInsertOrder(KStatusBar::RightToLeft); + statusBar()->insertItem(i18n("This leaves space for the mover"),ID_STATUS_MOVER,0,true); + statusBar()->insertItem(i18n("Ready"), ID_STATUS_MSG); + + slotStatusMover(i18n("(c) Martin Heni ")); + slotStatusMsg(i18n("Welcome to Lieutenant Skat")); + + // computer move timer + procTimer=new QTimer(this); + connect(procTimer,SIGNAL(timeout()),this,SLOT(slotProcTimer())); +} + +void LSkatApp::initDocument() +{ + doc = new LSkatDoc(this); + // TODO check for return false !!! + doc->newDocument(config,mGrafix); +} + +void LSkatApp::initView() +{ + //////////////////////////////////////////////////////////////////// + // create the main widget here that is managed by KMainWindow's view-region and + // connect the widget to your document to display document contents. + view = new LSkatView(this); + doc->addView(view); + setCentralWidget(view); + setCaption(i18n("Lieutenant Skat")); +} + +void LSkatApp::enableAction(const char *s) +{ + if (s) + { + KAction *act=actionCollection()->action(s); + if (act) act->setEnabled(true); + } + +} +void LSkatApp::disableAction(const char *s) +{ + if (s) + { + KAction *act=actionCollection()->action(s); + if (act) act->setEnabled(false); + } +} + +LSkatDoc *LSkatApp::getDocument() const +{ + return doc; +} + +void LSkatApp::saveProperties(KConfig *_cfg) +{ + if(doc->getTitle()!=i18n("Untitled") && !doc->isModified()) + { + // saving to tempfile not necessary + } + else + { + QString filename=doc->getAbsFilePath(); + _cfg->writePathEntry("filename", filename); + _cfg->writeEntry("modified", doc->isModified()); + + QString tempname = kapp->tempSaveName(filename); + doc->saveDocument(tempname); + } +} + + +void LSkatApp::readProperties(KConfig* _cfg) +{ + QString filename = _cfg->readPathEntry("filename"); + bool modified = _cfg->readBoolEntry("modified", false); + if(modified) + { + bool canRecover; + QString tempname = kapp->checkRecoverFile(filename, canRecover); + + if(canRecover) + { + doc->openDocument(tempname); + doc->setModified(); + QFileInfo info(filename); + doc->setAbsFilePath(info.absFilePath()); + doc->setTitle(info.fileName()); + QFile::remove(tempname); + } + } + else + { + if(!filename.isEmpty()) + { + doc->openDocument(filename); + } + } + + //QString caption=kapp->caption(); + setCaption(i18n("Lieutenant Skat")); +} + +bool LSkatApp::queryClose() +{ + return true; + return doc->saveModified(); +} + +bool LSkatApp::queryExit() +{ + doc->WriteConfig(config); + return true; +} + +///////////////////////////////////////////////////////////////////// +// SLOT IMPLEMENTATION +///////////////////////////////////////////////////////////////////// + +void LSkatApp::slotFileStatistics() +{ + QString message; + message=i18n("Do you really want to clear the all time " + "statistical data?"); + + if (KMessageBox::Yes==KMessageBox::questionYesNo(this,message,QString::null,KStdGuiItem::clear())) + { + doc->ClearStats(); + doc->slotUpdateAllViews(0); + } +} + +/** send message */ +void LSkatApp::slotFileMessage() +{ + int res; + + MsgDlg *dlg=new MsgDlg(this,QCString("Send message...")); + res=dlg->exec(); + if (res==QDialog::Accepted) + { + QString s; + s=dlg->GetMsg(); + if (!s || s.length()<1) s=QCString("..."); + KEMessage *msg=new KEMessage; + + // printf("Msg: %s\n",(char *)msg); + + msg->AddData(QCString("Message"),(char *)(s.latin1())); + if (mInput->QueryType(0)==KG_INPUTTYPE_REMOTE) + mInput->SendMsg(msg,0); + if (mInput->QueryType(1)==KG_INPUTTYPE_REMOTE) + mInput->SendMsg(msg,1); + delete msg; + } +} + +void LSkatApp::slotFileNew() +{ + NewGame(); + checkMenus(CheckFileMenu|CheckOptionsMenu); +} + +void LSkatApp::slotFileEnd() +{ + doc->EndGame(true); + doc->slotUpdateAllViews(0); + slotStatusMsg(i18n("Game ended...start a new one...")); + + KEMessage *msg=new KEMessage; + msg->AddData(QCString("EndGame"),(short)1); + if (mInput->QueryType(0)==KG_INPUTTYPE_REMOTE) + mInput->SendMsg(msg,0); + if (mInput->QueryType(1)==KG_INPUTTYPE_REMOTE) + mInput->SendMsg(msg,1); + + msg->RemoveAll(); + msg->AddData(QCString("Terminate"),(short)1); + if (mInput->QueryType(0)==KG_INPUTTYPE_PROCESS) + mInput->SendMsg(msg,0); + if (mInput->QueryType(1)==KG_INPUTTYPE_PROCESS) + mInput->SendMsg(msg,1); + delete msg; + checkMenus(CheckFileMenu|CheckOptionsMenu); + slotStatusNames(); +} + +void LSkatApp::slotFileQuit() +{ + doc->WriteConfig(config); + if (view) view->close(); + close(); + kdDebug() << "slotFileQuit done"<currentItem(); + doc->SetStartPlayer(i); + doc->UpdateViews(UPDATE_STATUS); +} + +void LSkatApp::slotPlayer1By() +{ + switch(((KSelectAction *)ACTION("player1"))->currentItem()) + { + case 0: + slotPlayer1(KG_INPUTTYPE_INTERACTIVE); + break; + case 1: + slotPlayer1(KG_INPUTTYPE_PROCESS); + break; + case 2: + slotPlayer1(KG_INPUTTYPE_REMOTE); + break; + } +} + +void LSkatApp::slotPlayer2By() +{ + switch(((KSelectAction *)ACTION("player2"))->currentItem()) + { + case 0: + slotPlayer2(KG_INPUTTYPE_INTERACTIVE); + break; + case 1: + slotPlayer2(KG_INPUTTYPE_PROCESS); + break; + case 2: + slotPlayer2(KG_INPUTTYPE_REMOTE); + break; + } +} + +void LSkatApp::slotPlayer1(KG_INPUTTYPE i) +{ + doc->SetPlayedBy(0,i); + if (doc->IsRunning()) + { + MakeInputDevice(0); + // New: Start computer when switched during game + if (mInput->QueryType(0)!=KG_INPUTTYPE_REMOTE && + doc->GetCurrentPlayer()==0 ) + { + mInput->Unlock(); + mInput->Next(doc->GetCurrentPlayer()); + } + } + doc->UpdateViews(UPDATE_STATUS); +} + +void LSkatApp::slotPlayer2(KG_INPUTTYPE i) +{ + doc->SetPlayedBy(1,i); + if (doc->IsRunning()) + { + MakeInputDevice(1); + // New: Start computer when switched during game + if (mInput->QueryType(0)!=KG_INPUTTYPE_REMOTE && + doc->GetCurrentPlayer()==1 ) + { + mInput->Unlock(); + mInput->Next(doc->GetCurrentPlayer()); + } + } + doc->UpdateViews(UPDATE_STATUS); +} + +void LSkatApp::slotOptionsNames() +{ + NameDlg *dlg=new NameDlg(this,QCString("Enter your name...")); + dlg->SetNames(doc->GetName(0),doc->GetName(1)); + if (dlg->exec()==QDialog::Accepted) + { + QString n1,n2; + dlg->GetNames(n1,n2); + doc->SetName(0,n1); + doc->SetName(1,n2); + doc->UpdateViews(UPDATE_STATUS); + slotStatusNames(); + } +} + +void LSkatApp::slotOptionsCardDeck() +{ + QString s1,s2; + int result; + s1=doc->GetDeckpath(); + s2=doc->GetCardpath(); + + result=KCardDialog::getCardDeck(s1,s2); + if (result==QDialog::Accepted) + { + doc->SetCardDeckPath(s1,s2); + doc->slotUpdateAllViews(0); + } +} + +void LSkatApp::slotLevel() +{ + int i=((KSelectAction *)ACTION("choose_level"))->currentItem(); + i++; // we start at 1 + doc->SetComputerLevel(i); + doc->UpdateViews(UPDATE_STATUS); + printf("Level set to %d\n",i); +} + +void LSkatApp::slotClearStatusMsg() +{ + slotStatusMsg(i18n("Ready")); +} + +void LSkatApp::slotStatusMsg(const QString &text) +{ + /////////////////////////////////////////////////////////////////// + // change status message permanently + statusBar()->clear(); + statusBar()->changeItem(text, ID_STATUS_MSG); +} + +void LSkatApp::slotStatusMover(const QString &text) +{ + /////////////////////////////////////////////////////////////////// + // change status mover permanently + statusBar()->clear(); + statusBar()->changeItem(text, ID_STATUS_MOVER); +} + + +void LSkatApp::slotStatusHelpMsg(const QString &text) +{ + /////////////////////////////////////////////////////////////////// + // change status message of whole statusbar temporary (text, msec) + statusBar()->message(text, 2000); +} + +/** Triggers the processmove timer */ +void LSkatApp::slotProcTimer(void) +{ + mInput->Unlock(); + mInput->Next(doc->GetCurrentPlayer()); + /* + printf("Delayed setting Next=%d ->%d\n", + doc->GetCurrentPlayer(),mInput->QueryNext()); + */ +} + +/** Set the names in the mover field */ +void LSkatApp::slotStatusNames(){ + + QString msg; + if (!doc->IsRunning()) msg=i18n("No game running"); + else + { + msg=i18n("%1 to move...").arg(doc->GetName(doc->GetCurrentPlayer())); + } + slotStatusMover(msg); +} + +void LSkatApp::NewGame() +{ + bool res; + // doc->SetIntro(0); + doc->NewGame(); + doc->slotUpdateAllViews(0); + res=MakeInputDevice(0); + if (!res) + { + KMessageBox::error(this, + i18n("Cannot start player 1. Maybe the network connection " + "failed or the computer player process file is not " + "found.")); + return ; + } + res=MakeInputDevice(1); + if (!res) + { + KMessageBox::error(this, + i18n("Cannot start player 2. Maybe the network connection " + "failed or the computer player process file is not " + "found.")); + return ; + } + // Remote game is started when receiving the start mesage .. not here! + if (mInput->QueryType(0)!=KG_INPUTTYPE_REMOTE && + mInput->QueryType(1)!=KG_INPUTTYPE_REMOTE ) + { + mInput->Unlock(); + mInput->Next(doc->GetStartPlayer()); + } + // Connected 0 and we are server + else if (mInput->QueryType(0)==KG_INPUTTYPE_REMOTE && + mInput->QueryStatus()>0 && doc->IsServer()) + { + mInput->Unlock(); + mInput->Next(doc->GetStartPlayer()); + } + // Connected 1 and we are server + else if (mInput->QueryType(1)==KG_INPUTTYPE_REMOTE && + mInput->QueryStatus()>1 && doc->IsServer()) + { + mInput->Unlock(); + mInput->Next(doc->GetStartPlayer()); + } + slotStatusNames(); +} + +bool LSkatApp::MakeInputDevice(int no) +{ + bool res=true; + KEMessage *msg; + disableAction("send_message"); + KG_INPUTTYPE type=doc->GetPlayedBy(no); + if (type==KG_INPUTTYPE_INTERACTIVE) + { + if (mInput->QueryType(no)!=type) + res=mInput->SetInputDevice(no,type); + } + else if (type==KG_INPUTTYPE_REMOTE) + { + QString host; + short port; + QString Name; + msg=new KEMessage; + PrepareGame(msg); + // Build new connection + if ( mInput->QueryType(no)!=type || mInput->QueryStatus(no)<=0) + { + OptionsNetwork(); + port=doc->QueryPort(); + host=doc->QueryHost(); + Name=doc->QueryName(); + msg->AddData(QCString("Port"),(short)port); + msg->AddData(QCString("IP"),(char *)(host.latin1())); + msg->AddData(QCString("Name"),(const char *)(Name.utf8())); + res=mInput->SetInputDevice(no,type,msg); + if (!res) + { + QProgressDialog *progress; + QString s; + int tim,j; + tim=10000; + if (!host.isEmpty()) + { + s=i18n("Remote connection to %1:%2...").arg(host).arg(port); + } + else + { + s=i18n("Offering remote connection on port %1...").arg(port); + } + progress=new QProgressDialog(s, i18n("Abort"), tim, this,0,true ); + progress->setCaption(i18n("Lieutenant Skat")); + for (j=0; jsetProgress( j ); + if ( progress->wasCancelled() ) break; + if (mInput->QueryStatus(no)>0) break; + usleep(100); + } + // progress.setProgress(tim); + delete progress; + } + } + else // we are already connected + { + // force the other one to be client + usleep(1000); + if (doc->IsServer()) + { + if (global_debug>10) + printf("We want theother one to be client\n"); + msg->AddData(QCString("Client"),(short)-1); // force client + mInput->SendMsg(msg,no); + } + // resp server + else + { + if (global_debug>10) + printf("We want theother one to be server\n"); + msg->AddData(QCString("Server"),(short)-1); // force server + mInput->SendMsg(msg,no); + } + } + delete msg; + if (mInput->QueryStatus(no)>0) + { + res=true; + } + else res=false; + enableAction("send_message"); + } + else if (type==KG_INPUTTYPE_PROCESS) + { + if (mInput->QueryType(no)!=type) + { + // QString path=kapp->dirs()->findExe(doc->QueryProcessName()); + QString path=doc->GetProcess(); + if (global_debug>5) + { + printf("Make Process %d\n",no); + printf("Exe file found: %s\n",path.latin1()); + } + if (path.isNull()) return false; + msg=new KEMessage; + msg->AddData(QCString("ProcessName"),(char *)(path.latin1())); + // msg->AddData("ProcessName",doc->QueryProcessName()); + res=mInput->SetInputDevice(no,KG_INPUTTYPE_PROCESS,msg); + delete msg; + } + + } + return res; +} + +void LSkatApp::OptionsNetwork() +{ + int res; + + NetworkDlg *dlg=new NetworkDlg(this,QCString("Configure a network game...")); + dlg->SetPort(doc->QueryPort()); + dlg->SetHost(doc->QueryHost()); + dlg->SetName(doc->QueryName()); + res=dlg->exec(); + doc->SetPort(dlg->QueryPort()); + doc->SetHost(dlg->QueryHost()); + doc->SetName(dlg->QueryName()); + delete dlg; +} + +void LSkatApp::slotPrepareProcessMove(KEMessage *msg) +{ + if (global_debug>3) + printf("+++ main should prepare process move\n"); + slotStatusMsg(i18n("Waiting for the computer to move...")); + + msg->AddData(QCString("Hint"),(short)0); + msg->AddData(QCString("Move"),(short)1); + + if (global_debug>3) + printf("PREPARE GAME in processmove\n"); + if (global_debug>1) + msg->AddData(QCString("KLogSendMsg"),"process.log"); + PrepareGame(msg); +} + +void LSkatApp::slotPrepareRemoteMove(KEMessage *) +{ + if (global_debug>3) + printf("+++ main should prepare remote move\n"); + slotStatusMsg(i18n("Waiting for remote player...")); +} + +void LSkatApp::slotPrepareInteractiveMove(KEMessage *) +{ + if (global_debug>3) + printf("+++ main should prepare interactive move\n"); + slotStatusMsg(i18n("Please make your move...")); +} + +void LSkatApp::slotReceiveInput(KEMessage *msg,int ) +{ + /* + if (global_debug>=0) + { + QStrList *keys=msg->QueryKeys(); + char *it; + printf(" MESSAGESIZE=%u\n",msg->QueryNumberOfKeys()); + for (it=keys->first();it!=0;it=keys->next()) + { + printf(" Key '%s' type=%d\n",it,msg->QueryType(it)); + } + } + */ + short move; + QString message; + short x,y,player; + bool remotesend; + int size; + + if (msg->HasKey(QCString("Debug"))) + { + char *debug; + msg->GetData(QCString("Debug"),debug,size); + printf("Received Debug: %d <%s>\n",size,debug); + } + if (msg->HasKey(QCString("ConnectionLost"))) + { + if (msg->GetData(QCString("ConnectionLost"),move)) + { + if (move==0) + { + message=i18n("Remote connection lost for player 1..."); + KMessageBox::information(this,message); + slotStatusMsg(message); + slotPlayer1(KG_INPUTTYPE_INTERACTIVE); + } + else + { + message=i18n("Remote connection lost for player 2..."); + KMessageBox::information(this,message); + slotStatusMsg(message); + slotPlayer2(KG_INPUTTYPE_INTERACTIVE); + } + } + } + if (msg->HasKey(QCString("Message"))) + { + char *p; + if (msg->GetData(QCString("Message"),p,size)) + { + message=i18n("Message from remote player:\n")+p; + KMessageBox::information(this,message); + if (global_debug>3) + printf("MESSAGE **** %s ****\n",message.latin1()); + } + } + if (msg->HasKey(QCString("EndGame"))) + { + KEMessage *msg2=new KEMessage; + + msg2->AddData(QCString("Terminate"),(short)1); + if (mInput->QueryType(0)==KG_INPUTTYPE_PROCESS) + mInput->SendMsg(msg2,0); + if (mInput->QueryType(1)==KG_INPUTTYPE_PROCESS) + mInput->SendMsg(msg2,1); + delete msg2; + + msg->GetData(QCString("EndGame"),move); + message=i18n("Remote player ended game..."); + KMessageBox::information(this,message); + slotStatusMsg(message); + + doc->EndGame(true); + doc->slotUpdateAllViews(0); + slotStatusNames(); + } + + if (msg->HasKey(QCString("Move"))) + { + slotStatusMsg(i18n("Ready")); + msg->GetData(QCString("Move"),player); + msg->GetData(QCString("MoveX"),x); + msg->GetData(QCString("MoveY"),y); + remotesend=msg->HasKey(QCString("RemoteMove")); + if (remotesend && doc->IsRemoteSwitch()) + player=1-player; + Move((int)x,(int)y,(int)player,remotesend); + } + // Client key is automatically added by message system !!! + if (msg->HasKey(QCString("Client"))) + { + if (global_debug>5) + printf("We are client and extracting game data now.\n"); + slotStatusMsg(i18n("You are network client...loading remote game...")); + doc->NewGame(); + ExtractGame(msg); + doc->SetServer(false); + doc->slotUpdateAllViews(0); + mInput->Unlock(); + mInput->Next(doc->GetStartPlayer()); + } + // Server key is automatically added by message system !!! + if (msg->HasKey(QCString("Server"))) + { + if (global_debug>5) + printf("We are server now.\n"); + slotStatusMsg(i18n("You are network server...")); + doc->SetServer(true); + mInput->Unlock(); + mInput->Next(doc->GetStartPlayer()); + } +} + +// Called after the move animation done +void LSkatApp::MoveFinished() +{ + int res=doc->MakeMove(); + QString ld,s; + if (res==2) // end game + { + doc->EvaluateGame(); + doc->slotUpdateAllViews(0); + // m->SetStatusBar(i18n("Game over"),3); + //doc->SwitchStartPlayer(); + doc->slotUpdateAllViews(0); + checkMenus(CheckFileMenu|CheckOptionsMenu); + } + else if (doc->IsRunning()) // continue game + { + // Delay fast playing of the computer ! + /* + printf("*** next=%d current=%d playedby=%d\n", + mInput->QueryNext(),doc->GetCurrentPlayer() , + doc->GetPlayedBy(doc->GetCurrentPlayer())); + */ + // Maybe we do not need to distinquish these two cases? + // Computer was last and is next + if (doc->GetPlayedBy(mInput->QueryNext())==KG_INPUTTYPE_PROCESS && + doc->GetPlayedBy(doc->GetCurrentPlayer())==KG_INPUTTYPE_PROCESS) + { + procTimer->start(1000,true); + } + // Computer is next + else if (doc->GetPlayedBy(doc->GetCurrentPlayer())==KG_INPUTTYPE_PROCESS) + { + procTimer->start(1000,true); + } + else + { + mInput->Unlock(); + mInput->Next(doc->GetCurrentPlayer()); + } + } + slotStatusNames(); +} + +void LSkatApp::Move(int x,int y,int player,bool remote) +{ + KEMessage *msg; + if (global_debug>1) + printf("Move of %d to x=%d y=%d\n",player,x,y); + if (x<0 || y<0 || x>7 || y>1 || player <0 || player>1) + { + KMessageBox::error(this, + i18n("Severe internal error. Move to illegal position.\n" + "Restart game and report bug to the developer.\n")); + return ; + } + int res=doc->PrepareMove(player,y*4+x); + if (res>0) + { + doc->InitMove(0,player,x,y); + if (mInput->QueryType()!=KG_INPUTTYPE_REMOTE && !remote ) + { + msg=new KEMessage; + if (doc->IsRemoteSwitch()) player=1-player; + msg->AddData(QCString("Move"),(short)player); + msg->AddData(QCString("MoveX"),(short)x); + msg->AddData(QCString("MoveY"),(short)y); + msg->AddData(QCString("RemoteMove"),(short)1); + if (mInput->QueryType(0)==KG_INPUTTYPE_REMOTE) + mInput->SendMsg(msg,0); + if (mInput->QueryType(1)==KG_INPUTTYPE_REMOTE) + mInput->SendMsg(msg,1); + delete msg; + } + } + else if (res==-3) + { + KMessageBox::information(this, + i18n("This move would not follow the rulebook.\n" + "Better think again!\n")); + return ; + } + else if (res==-2) + { + KMessageBox::information(this,i18n("It is not your turn.\n")); + return ; + } + else + { + KMessageBox::information(this, i18n("This move is not possible.\n")); + return ; + } +} + +void LSkatApp::PrepareGame(KEMessage *msg) +{ + if (!msg) + return; + + msg->AddData(QCString("Cards"),(char *)doc->GetCardP(),NO_OF_CARDS*sizeof(int)); + msg->AddData(QCString("Startplayer"),(short)doc->GetStartPlayer()); + msg->AddData(QCString("CurrentPlayer"),(short)doc->GetCurrentPlayer()); + if (doc->GetPlayedBy(0)==KG_INPUTTYPE_REMOTE) + msg->AddData(QCString("RemoteIs"),(short)0); + else if (doc->GetPlayedBy(1)==KG_INPUTTYPE_REMOTE) + msg->AddData(QCString("RemoteIs"),(short)1); + msg->AddData(QCString("Trump"),(short)doc->GetTrump()); + // For computer player + // -1 or the current played card + msg->AddData(QCString("CurrentMove"),(short)doc->GetMove(doc->GetStartPlayer())); + msg->AddData(QCString("Height"),(char *)doc->GetCardHeightP(),NO_OF_CARDS/2*sizeof(int)); + msg->AddData(QCString("No"),(short)doc->GetMoveNo()); + msg->AddData(QCString("Sc1"),(short)doc->GetScore(0)); + msg->AddData(QCString("Sc2"),(short)doc->GetScore(1)); + msg->AddData(QCString("Level"),(short)doc->GetComputerLevel()); +} + +void LSkatApp::ExtractGame(KEMessage *msg) +{ + if (!msg) + return; + // Do we have to switch players? + bool switchit; + short remote; + msg->GetData(QCString("RemoteIs"),remote); + if (doc->GetPlayedBy(remote)==KG_INPUTTYPE_REMOTE) switchit=true; + else switchit=false; + + short start; + int i,size; + int *cards; + char *p; + short trump; + msg->GetData(QCString("Startplayer"),start); + msg->GetData(QCString("Cards"),p,size); + cards=(int *)p; + msg->GetData(QCString("Trump"),trump); + if (size!=NO_OF_CARDS*sizeof(int)) + { + printf("Error: Transmission of cards failed..wrong sizes\n"); + } + + doc->SetRemoteSwitch(switchit); + if (switchit) + { + start=1-start; + for (i=0;i=NO_OF_CARDS/2) + doc->SetCard(i-NO_OF_CARDS/2,cards[i]); + else + doc->SetCard(i+NO_OF_CARDS/2,cards[i]); + } + } + else + { + for (i=0;iSetCard(i,cards[i]); + } + } + + doc->SetStartPlayer(start); + doc->SetCurrentPlayer(start); + doc->SetTrump((CCOLOUR)trump); +} + + +void LSkatApp::SetGrafix(QString s) +{ + mGrafix=s; +} + +#include "lskat.moc" diff --git a/lskat/lskat/lskat.h b/lskat/lskat/lskat.h new file mode 100644 index 00000000..b5686dde --- /dev/null +++ b/lskat/lskat/lskat.h @@ -0,0 +1,216 @@ +/*************************************************************************** + lskat.h - description + ------------------- + begin : Tue May 2 15:47:11 CEST 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef LSKAT_H +#define LSKAT_H + + +#ifdef HAVE_CONFIG_H +#include +#endif + +// include files for Qt +#include + +// include files for KDE +#include +#include +#include + +#include "KEInput.h" +#include "KEMessage.h" +#include "KConnectTypes.h" + +typedef enum {Club=0,Spade=1,Heart=2,Diamond=3,Grand=4} CCOLOUR; +typedef enum {Ace=0,King=1,Queen=2,Jack=3,Ten=4,Nine=5,Eight=6,Seven=7} CCARD; +#define NO_OF_CARDS 32 +#define NO_OF_TILES 16 +#define NO_OF_TRUMPS 5 +//#define NO_OF_DECKS 6 +#define NO_OF_ANIM 24 + +// Window update codes +#define UPDATE_STATUS 1 + +extern int global_debug; +// forward declaration of the LSkat classes +class LSkatDoc; +class LSkatView; + +/** + * The base class for LSkat application windows. It sets up the main + * window and reads the config file as well as providing a menubar, toolbar + * and statusbar. An instance of LSkatView creates your center view, which is connected + * to the window's Doc object. + * LSkatApp reimplements the methods that KMainWindow provides for main window handling and supports + * full session management as well as keyboard accelerator configuration by using KAccel. + * @see KMainWindow + * @see KApplication + * @see KConfig + * @see KAccel + * + * @author Source Framework Automatically Generated by KDevelop, (c) The KDevelop Team. + * @version KDevelop version 0.4 code generation + */ +class LSkatApp : public KMainWindow +{ + Q_OBJECT + + friend class LSkatView; + + public: + /** construtor of LSkatApp, calls all init functions to create the application. + */ + LSkatApp(); + ~LSkatApp(); + /** enables menuentries/toolbar items + */ + void enableAction(const char *); + /** disables menuentries/toolbar items + */ + void disableAction(const char *); + /** add a opened file to the recent file list and update recent_file_menu + */ + /** returns a pointer to the current document connected to the KMainWindow instance and is used by + * the View class to access the document object's methods + */ + LSkatDoc *getDocument() const; + void MoveFinished(); + void Move(int x,int y,int player,bool remote); + void SetGrafix(QString s); + + protected: + void NewGame(); + bool MakeInputDevice(int no); + void OptionsNetwork(); + // Puts all game data into a message + void PrepareGame(KEMessage *msg); + void ExtractGame(KEMessage *msg); + + /** initGUI creates the menubar and inserts the menupopups as well as creating the helpMenu. + * @see KApplication#getHelpMenu + */ + void initGUI(); + /** Checks all menus..usually done on init programm */ + void checkMenus(int menu=0); + + /** this creates the toolbars. + */ + /** sets up the statusbar for the main window by initialzing a statuslabel. + */ + void initStatusBar(); + /** initializes the document object of the main window that is connected to the view in initView(). + * @see initView(); + */ + void initDocument(); + /** creates the centerwidget of the KMainWindow instance and sets it as the view + */ + void initView(); + /** queryClose is called by KMainWindow on each closeEvent of a window. Against the + * default implementation (only returns true), this calles saveModified() on the document object to ask if the document shall + * be saved if Modified; on cancel the closeEvent is rejected. + * @see KMainWindow#queryClose + * @see KMainWindow#closeEvent + */ + virtual bool queryClose(); + /** queryExit is called by KMainWindow when the last window of the application is going to be closed during the closeEvent(). + * Against the default implementation that just returns true, this calls saveOptions() to save the settings of the last window's + * properties. + * @see KMainWindow#queryExit + * @see KMainWindow#closeEvent + */ + virtual bool queryExit(); + /** saves the window properties for each open window during session end to the session config file, including saving the currently + * opened file by a temporary filename provided by KApplication. + * @see KMainWindow#saveProperties + */ + virtual void saveProperties(KConfig *_cfg); + /** reads the session config file and restores the application's state including the last opened files and documents by reading the + * temporary files saved by saveProperties() + * @see KMainWindow#readProperties + */ + virtual void readProperties(KConfig *_cfg); + + public slots: + void slotPrepareProcessMove(KEMessage *msg); + void slotPrepareRemoteMove(KEMessage *msg); + void slotPrepareInteractiveMove(KEMessage *msg); + void slotReceiveInput(KEMessage *msg,int id); + + /** clears the document in the actual view to reuse it as the new document */ + void slotFileNew(); + /** asks for saving if the file is modified, then closes the actual file and window*/ + void slotFileEnd(); + /** closes all open windows by calling close() on each memberList item until the list is empty, then quits the application. + * If queryClose() returns false because the user canceled the saveModified() dialog, the closing breaks. + */ + void slotFileQuit(); + // clear all time stats + void slotFileStatistics(); + /** Msg to remote player */ + void slotFileMessage(); + /** changes the statusbar contents for the standard label permanently, used to indicate current actions. + * @param text the text that is displayed in the statusbar + */ + void slotStatusMsg(const QString &text); + void slotClearStatusMsg(); + /** changes the status message of the whole statusbar for two seconds, then restores the last status. This is used to display + * statusbar messages that give information about actions for toolbar icons and menuentries. + * @param text the text that is displayed in the statusbar + */ + void slotStatusHelpMsg(const QString &text); + /** Set the names in the mover field */ + void slotStatusNames(); + void slotStatusMover(const QString &text); + + void slotLevel(); + void slotStartplayer(); + void slotPlayer1(KG_INPUTTYPE i); + void slotPlayer2(KG_INPUTTYPE i); + void slotPlayer1By(); + void slotPlayer2By(); + void slotOptionsNames(); + void slotOptionsCardDeck(); + /** Triggers the process timer */ + void slotProcTimer(void); + +protected: // Protected attributes + + enum CheckFlags {All=0,CheckFileMenu=1,CheckOptionsMenu=2,CheckViewMenu=4}; + + /** */ + /** Counts the time in the status bar */ + QTimer * procTimer; + KEInput *mInput; + QString mGrafix; + + private: + /** contains the recently used filenames */ + QStrList recentFiles; + + /** the configuration object of the application */ + KConfig *config; + + LSkatView *view; + /** doc represents your actual document and is created only once. It keeps + * information such as filename and does the serialization of your files. + */ + LSkatDoc *doc; +}; + +#endif // LSKAT_H + diff --git a/lskat/lskat/lskatdoc.cpp b/lskat/lskat/lskatdoc.cpp new file mode 100644 index 00000000..8da62ea4 --- /dev/null +++ b/lskat/lskat/lskatdoc.cpp @@ -0,0 +1,817 @@ +/*************************************************************************** + lskatdoc.cpp - description + ------------------- + begin : Tue May 2 15:47:11 CEST 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +// include files for Qt +#include +#include +#include +#include +#include +#include +#include + + +// include files for KDE +#include + +// application specific includes +#include "lskatdoc.h" +#include "lskat.h" +#include "lskatview.h" +#include + +QPtrList *LSkatDoc::pViewList = 0L; + +LSkatDoc::LSkatDoc(QWidget *parent, const char *name) : QObject(parent, name) +{ + int i; + if(!pViewList) + { + pViewList = new QPtrList(); + } + + pViewList->setAutoDelete(true); + + isrunning=0; + wasgame=false; + initrandom(); + // Allow translation of playernames + /* + names[0]=i18n("Alice"); + names[1]=i18n("Bob"); + */ + + for (i=0;i<14;i++) cardvalues[i]=0; + cardvalues[(int)Ace]=11; + cardvalues[(int)Ten]=10; + cardvalues[(int)King]=4; + cardvalues[(int)Queen]=3; + cardvalues[(int)Jack]=2; + + /* + computerlevel=2; + playedby[1]=KG_INPUTTYPE_INTERACTIVE; + playedby[0]=KG_INPUTTYPE_PROCESS; + // playedby[1]=KG_INPUTTYPE_INTERACTIVE; + */ + + + isintro=1; + server=false; + port=7432; + host=""; + Name=""; + remoteswitch=false; + + + ClearStats(); +} + +LSkatDoc::~LSkatDoc() +{ +} + +void LSkatDoc::addView(LSkatView *view) +{ + pViewList->append(view); +} + +void LSkatDoc::removeView(LSkatView *view) +{ + pViewList->remove(view); +} + +void LSkatDoc::setAbsFilePath(const QString &filename) +{ + absFilePath=filename; +} + +const QString &LSkatDoc::getAbsFilePath() const +{ + return absFilePath; +} + +void LSkatDoc::setTitle(const QString &_t) +{ + title=_t; +} + +const QString &LSkatDoc::getTitle() const +{ + return title; +} + +void LSkatDoc::InitMove(LSkatView *sender,int player,int x,int y) +{ + LSkatView *w; + if(pViewList) + { + for(w=pViewList->first(); w!=0; w=pViewList->next()) + { + if(w!=sender) w->InitMove(player,x,y); + } + } + +} +void LSkatDoc::slotUpdateAllViews(LSkatView *sender) +{ + LSkatView *w; + if(pViewList) + { + for(w=pViewList->first(); w!=0; w=pViewList->next()) + { + if(w!=sender) + w->repaint(); + } + } +} +void LSkatDoc::UpdateViews(int mode) +{ + LSkatView *w; + if (IsIntro()) return ; + if(pViewList) + { + for(w=pViewList->first(); w!=0; w=pViewList->next()) + { + if (mode & UPDATE_STATUS) w->updateStatus(); + } + } +} + +bool LSkatDoc::saveModified() +{ + return true; +} + +void LSkatDoc::closeDocument() +{ + deleteContents(); +} + +bool LSkatDoc::newDocument(KConfig * /*config*/,QString path) +{ + int res; + modified=false; + absFilePath=QDir::homeDirPath(); + title=i18n("Untitled"); + if (global_debug>1) printf("path=%s\n",path.latin1()); + res=LoadBitmap(path); + if (res==0) return false; + return true; +} + +bool LSkatDoc::LoadGrafix(QString path) +{ + int res; + res=LoadCards(cardPath); + if (res==0) return false; + res=LoadDeck(deckPath); + if (res==0) return false; + return true; +} + +bool LSkatDoc::SetCardDeckPath(QString deck,QString card) +{ + bool update=false; + if (!deck.isNull() && deck!=deckPath) + { + update=true; + deckPath=deck; + LoadDeck(deckPath); + } + if (!card.isNull() && card!=cardPath) + { + update=true; + cardPath=card; + LoadCards(cardPath); + } + return update; +} + +bool LSkatDoc::openDocument(const QString &filename, const char * /*format*/ /*=0*/) +{ + QFileInfo fileInfo(filename); + title=fileInfo.fileName(); + absFilePath=fileInfo.absFilePath(); + ///////////////////////////////////////////////// + // TODO: Add your document opening code here + ///////////////////////////////////////////////// + + modified=false; + return true; +} + +bool LSkatDoc::saveDocument(const QString & /*filename*/, const char * /*format*/ /*=0*/) +{ + ///////////////////////////////////////////////// + // TODO: Add your document saving code here + ///////////////////////////////////////////////// + + modified=false; + return true; +} + +void LSkatDoc::deleteContents() +{ + ///////////////////////////////////////////////// + // TODO: Add implementation to delete the document contents + ///////////////////////////////////////////////// + +} + +// Called after game ends..give points to players +void LSkatDoc::EvaluateGame() +{ + if (score[0]+score[1]!=120) + { + printf("Warning: Score does not end up to 120\n"); + } + stat_games[0]++; + stat_games[1]++; + if (score[0]==score[1]) // drawn + { + stat_points[0]++; + stat_points[1]++; + } + else if (score[0]>score[1]) + { + stat_won[0]++; + stat_points[0]+=2; + if (score[0]>=90) stat_points[0]++; // Schneider + if (score[0]>=120) stat_points[0]++; // SChwarz + } + else + { + stat_won[1]++; + stat_points[1]+=2; + if (score[1]>=90) stat_points[1]++; // Schneider + if (score[1]>=120) stat_points[1]++; // SChwarz + } +} + +void LSkatDoc::EndGame(bool aborted) +{ + if (aborted) + { + stat_brk[0]++; + stat_brk[1]++; + } + else + { + startplayer=1-began_game; + } + isrunning=0; + wasgame=true; +} + +void LSkatDoc::NewGame() +{ + int i,r; + int num[NO_OF_CARDS]; + + isintro=0; + + began_game=startplayer; + for (i=0;i5) printf("Trump=%d\n",trump); + if (random(8)==0) trump=Grand; // grand + + // Fast drawing of random cards + for (i=0;i5) printf("Loading bitmaps\n"); + for (i=0;ipw_gecos ); + } + + config->setGroup("Parameter"); + host=config->readEntry("host"); + port=(unsigned short)config->readNumEntry("port",7432); + procfile=config->readEntry("process",QCString("lskatproc")); + Name=config->readEntry("gamename"); + names[0]=config->readEntry("Name1",i18n("Alice")); + // names[1]=config->readEntry("Name2",i18n("Bob")); + names[1]=config->readEntry("Name2", name.isEmpty() ? i18n("Bob") : name); + + + // This is for debug and testing as you can run it from the CVS without + // installing the carddecks ! + // For the release version you can remove the aruments to the following two + // functions !!!! + cardPath=config->readPathEntry("cardpath", KCardDialog::getDefaultCardDir()); + deckPath=config->readPathEntry("deckpath", KCardDialog::getDefaultDeck()); + + // Debug only + if (global_debug>3) + printf("cardPath=%s\ndeckPath=%s\n",cardPath.latin1(),deckPath.latin1()); + + startplayer=config->readNumEntry("Startplayer",0); + if (startplayer>1 || startplayer<0) startplayer=0; + began_game=startplayer; + computerlevel=config->readNumEntry("Level",2); + playedby[0]=(KG_INPUTTYPE)config->readNumEntry("Player1", + (int)KG_INPUTTYPE_PROCESS); + playedby[1]=(KG_INPUTTYPE)config->readNumEntry("Player2", + (int)KG_INPUTTYPE_INTERACTIVE); + + stat_won[0]=config->readNumEntry("Stat1W",0); + stat_won[1]=config->readNumEntry("Stat2W",0); + stat_brk[0]=config->readNumEntry("Stat1B",0); + stat_brk[1]=config->readNumEntry("Stat2B",0); + stat_points[0]=config->readNumEntry("Stat1P",0); + stat_points[1]=config->readNumEntry("Stat2P",0); + stat_games[0]=config->readNumEntry("Stat1G",0); + stat_games[1]=config->readNumEntry("Stat2G",0); +} + +/** write config file */ +void LSkatDoc::WriteConfig(KConfig *config) +{ + config->setGroup("Parameter"); + config->writeEntry("host",host); + config->writeEntry("port",port); + config->writeEntry("process",procfile); + config->writePathEntry("tmppath",picpath); + config->writeEntry("delpath",delpath); + config->writeEntry("Name1",names[0]); + config->writeEntry("Name2",names[1]); + config->writeEntry("gamename",Name); + + config->writeEntry("Startplayer",startplayer); + config->writeEntry("Level",computerlevel); + config->writeEntry("Player1",int(playedby[0])); + config->writeEntry("Player2",int(playedby[1])); + + config->writeEntry("Stat1W",stat_won[0]); + config->writeEntry("Stat2W",stat_won[1]); + config->writeEntry("Stat1B",stat_brk[0]); + config->writeEntry("Stat2B",stat_brk[1]); + config->writeEntry("Stat1P",stat_points[0]); + config->writeEntry("Stat2P",stat_points[1]); + config->writeEntry("Stat1G",stat_games[0]); + config->writeEntry("Stat2G",stat_games[1]); + + config->writePathEntry("cardpath",cardPath); + config->writePathEntry("deckpath",deckPath); + + config->sync(); +} + +#include "lskatdoc.moc" diff --git a/lskat/lskat/lskatdoc.h b/lskat/lskat/lskatdoc.h new file mode 100644 index 00000000..1b880360 --- /dev/null +++ b/lskat/lskat/lskatdoc.h @@ -0,0 +1,235 @@ +/*************************************************************************** + lskatdoc.h - description + ------------------- + begin : Tue May 2 15:47:11 CEST 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef LSKATDOC_H +#define LSKATDOC_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +// include files for QT +#include +#include +#include + +#include +#include "lskat.h" +#include "KEInput.h" + +// forward declaration of the LSkat classes +class LSkatView; + +/** LSkatDoc provides a document object for a document-view model. + * + * The LSkatDoc class provides a document object that can be used in conjunction with the classes LSkatApp and LSkatView + * to create a document-view model for standard KDE applications based on KApplication and KTMainWindow. Thereby, the document object + * is created by the LSkatApp instance and contains the document structure with the according methods for manipulation of the document + * data by LSkatView objects. Also, LSkatDoc contains the methods for serialization of the document data from and to files. + * + * @author Source Framework Automatically Generated by KDevelop, (c) The KDevelop Team. + * @version KDevelop version 0.4 code generation + */ +class LSkatDoc : public QObject +{ + Q_OBJECT + public: + /** Constructor for the fileclass of the application */ + LSkatDoc(QWidget *parent, const char *name=0); + /** Destructor for the fileclass of the application */ + ~LSkatDoc(); + + /** adds a view to the document which represents the document contents. Usually this is your main view. */ + void addView(LSkatView *view); + /** removes a view from the list of currently connected views */ + void removeView(LSkatView *view); + /** sets the modified flag for the document after a modifying action on the view connected to the document.*/ + void setModified(bool _m=true){ modified=_m; } + /** returns if the document is modified or not. Use this to determine if your document needs saving by the user on closing.*/ + bool isModified(){ return modified; } + /** "save modified" - asks the user for saving if the document is modified */ + bool saveModified(); + /** deletes the document's contents */ + void deleteContents(); + /** initializes the document generally */ + bool newDocument(KConfig *config,QString path); + /** closes the acutal document */ + void closeDocument(); + /** loads the document by filename and format and emits the updateViews() signal */ + bool openDocument(const QString &filename, const char *format=0); + /** saves the document under filename and format.*/ + bool saveDocument(const QString &filename, const char *format=0); + /** sets the path to the file connected with the document */ + void setAbsFilePath(const QString &filename); + /** returns the pathname of the current document file*/ + const QString &getAbsFilePath() const; + /** sets the filename of the document */ + void setTitle(const QString &_t); + /** returns the title of the document */ + const QString &getTitle() const; + + int random(int max); + void NewGame(); + void EndGame(bool aborted=false); + void ClearStats(); + void EvaluateGame(); + int GetCard(int player,int pos,int height=0); + int GetHeight(int player, int pos); + void SetHeight(int player, int pos,int h); + int GetMove(int i); + int PrepareMove(int player,int pos); + int MakeMove(); + bool IsRunning(); + CCOLOUR GetTrump(); + void SetTrump(CCOLOUR i); + int GetMoveStatus(); + void SetMoveStatus(int i); + int GetCurrentPlayer(); + void SetCurrentPlayer(int i); + int GetStartPlayer(); + void SetStartPlayer(int i); + void SetName(int no,QString n); + QString GetName(int no); + int GetScore(int no); + int GetMoveNo(); + bool LegalMove(int card1, int card2); + int WonMove(int card1,int card2); + int CardValue(int card); + /* + int GetDeckNo(); + void SetDeckNo(int no); + */ + int GetLastStartPlayer(); + KG_INPUTTYPE GetPlayedBy(int no); + void SetPlayedBy(int no,KG_INPUTTYPE type); + int GetComputerLevel(); + void SetComputerLevel(int lev); + void SetComputerScore(int sc); + int GetComputerScore(); + void SetLock(); + void ReleaseLock(); + bool IsLocked(); + bool IsIntro(); + bool WasRunning(); + void SetIntro(bool b); + + // Stats + int GetStatWon(int no); + int GetStatGames(int no); + int GetStatAborted(int no); + int GetStatPoints(int no); + + void WriteConfig(KConfig *config); + void ReadConfig(KConfig *config); + + void SetInputHandler(KEInput *i); + KEInput *QueryInputHandler(); + void InitMove(LSkatView *sender,int player,int x,int y); + int SwitchStartPlayer(); + void UpdateViews(int mode); + bool IsServer(); + void SetServer(bool b); + void SetHost(QString h); + void SetName(const QString& n); + void SetPort(short p); + QString QueryHost(); + short QueryPort(); + QString QueryName() const; + // Only for fast remote access + int *GetCardP(); + int *GetCardHeightP(); + void SetCard(int i,int c); + bool IsRemoteSwitch(); + void SetRemoteSwitch(bool b); + QString GetProcess(); + int LoadCards(QString path); + int LoadDeck(QString path); + bool SetCardDeckPath(QString deck,QString card); + QString GetDeckpath() {return deckPath;} + QString GetCardpath() {return cardPath;} + bool LoadGrafix(QString path); + + protected: + void initrandom(); + int LoadBitmap(QString path); + +public: + QPixmap mPixCard[NO_OF_CARDS]; + QPixmap mPixTrump[NO_OF_TRUMPS]; + QPixmap mPixDeck; + QPixmap mPixBackground; + QSize cardsize; + QPixmap mPixType[3]; + QPixmap mPixAnim[NO_OF_ANIM]; + + private: + QString procfile; + QString picpath; + int delpath; + bool remoteswitch; + KEInput *inputHandler; + short port; + QString host; + QString Name; + bool server; + bool lock; + int startplayer; + int cardheight[16]; + int card[NO_OF_CARDS]; + int curmove[2]; + int moveno; + int isrunning; + bool isintro; + bool wasgame; + int movestatus; + int currentplayer; + CCOLOUR trump; + QString names[2]; + int score[2]; + int laststartplayer; + int began_game; + + KG_INPUTTYPE playedby[2]; + int computerlevel; + int computerscore; + + int stat_won[2]; // how many wins + int stat_points[2]; // how many points + int stat_games[2]; // how many games + int stat_brk[2]; // how many games aborted + + int cardvalues[14]; + + public slots: + /** calls repaint() on all views connected to the document object and is called by the view by which the document has been changed. + * As this view normally repaints itself, it is excluded from the paintEvent. + */ + void slotUpdateAllViews(LSkatView *sender); + + public: + /** the list of the views currently connected to the document */ + static QPtrList *pViewList; + + private: + /** the modified flag of the current document */ + bool modified; + QString title; + QString absFilePath; + QString deckPath,cardPath; +}; + +#endif // LSKATDOC_H diff --git a/lskat/lskat/lskatui.rc b/lskat/lskat/lskatui.rc new file mode 100644 index 00000000..867f21c0 --- /dev/null +++ b/lskat/lskat/lskatui.rc @@ -0,0 +1,25 @@ + + + + &Game + + + + + + + + + + &Settings + + + + + + + + + + + diff --git a/lskat/lskat/lskatview.cpp b/lskat/lskat/lskatview.cpp new file mode 100644 index 00000000..c14d5cee --- /dev/null +++ b/lskat/lskat/lskatview.cpp @@ -0,0 +1,934 @@ +/*************************************************************************** + lskatview.cpp - description + ------------------- + begin : Tue May 2 15:47:11 CEST 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +// include files for Qt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// application specific includes +#include "lskatview.h" +#include "lskatdoc.h" +#include "lskat.h" +#include "KEInput.h" +#include "KEMessage.h" + +#define CARD_X_OFFSET 32 // offset fromleft side +#define CARD_Y_OFFSET 5 // offset from top +#define CARD_X_SPACE 10 // space between cards +#define CARD_Y_SPACE 5 // space between cards +#define CARD_Y_BOARD_OFFSET 20 // space between players + +#define CARD_X_DECK 5 // shift deck and card +#define CARD_Y_DECK 3 // shift deck and card + +#define CARD_X_MOVE1 430 // positon of first moved card +#define CARD_Y_MOVE1 140 +#define CARD_X_MOVE2 450 // psoiton of second moved card +#define CARD_Y_MOVE2 190 + +// Probably most unnecessary and unused now +#define STATUS_X0 15 // pos in status win +#define STATUS_X1 60 +#define STATUS_X2 100 +#define STATUS_Y0 26 +#define STATUS_Y1 44 +#define STATUS_Y2 62 +#define STATUS_Y3 80 +#define STATUS_XT 128 +#define STATUS_YT 47 +#define STATUS_XTYPE 135 +#define STATUS_YTYPE 12 + +#define FINAL_XT1 15 +#define FINAL_XT2 -55 +#define FINAL_YT 15 + +#define FINAL_X0 140 +#define FINAL_Y0 35 + +#define FINAL_X1 110 +#define FINAL_Y1 90 + +#define FINAL_X2 130 +#define FINAL_Y2 65 + +#define FINAL_X3 30 +#define FINAL_X4 90 +#define FINAL_X5 170 +#define FINAL_X6 230 + +#define FINAL_Y3 95 +#define FINAL_Y4 125 + +#define FINAL_X7 30 +#define FINAL_Y7 155 + +//#define COL_STATUSLIGHT white +#define COL_STATUSBORDER black +//#define COL_STATUSFIELD QColor(192,192,192) +//#define COL_STATUSDARK QColor(65,65,65) +#define COL_STATUSFIELD QColor(130,130,255) +#define COL_STATUSDARK QColor(0,0,65) +#define COL_STATUSLIGHT QColor(210,210,255) +#define COL_PLAYER QColor(255,255,0) + +#define DLGBOXTITLE TITLE + +#define MOVECOUNTER 20 // so many moves when playing card +#define MOVE_TIMER_DELAY 7 // timer in milllisec default 7 + +LSkatView::LSkatView(QWidget *parent, const char *name) : QWidget(parent, name) +{ + setBackgroundMode(PaletteBase); + // setBackgroundMode(NoBackground); + + status_rect1=QRect(412,CARD_Y_OFFSET+5,180,95+25); + status_rect2=QRect(412,310,180,95+25); + status_rect3=QRect(CARD_X_OFFSET+60,CARD_Y_OFFSET+5+100+15+20, + 400,320-100-CARD_Y_OFFSET-30); + + setBackgroundColor(QColor(0,0,128)); + setBackgroundPixmap( getDocument()->mPixBackground ); + + moveTimer=new QTimer(this); + moveTimer->stop(); + connect(moveTimer,SIGNAL(timeout()),this,SLOT(moveTimerReady())); + + introcnt=0; + introTimer=new QTimer(this); + introTimer->stop(); + connect(introTimer,SIGNAL(timeout()),this,SLOT(introTimerReady())); + introTimer->start(75,FALSE); + + for (int i=0;irandom(NO_OF_CARDS); + } +} + +// Access the document with the game data +LSkatDoc *LSkatView::getDocument() const +{ + LSkatApp *theApp=(LSkatApp *) parentWidget(); + return theApp->getDocument(); +} + +// draw a border around a rect +// p: Painter +// rect: The rect to draw a border +// offset: An offset to the rect to make it smaller +// width: The width of the border +// mode: 0: Light border, 1: dark border +void LSkatView::drawBorder(QPainter *p,QRect rect,int offset,int width,int mode) +{ +QPen graypen; +int i; +QPoint p1,p2; + + if (mode!=0 && mode!=1) return; + + p1=rect.topLeft(); + p2=rect.bottomRight(); + + if (mode==0) p->setPen(COL_STATUSLIGHT); + else if (mode==1) p->setPen(COL_STATUSDARK); + + for (i=0;imoveTo(p1.x()+offset+i,p2.y()-offset-i); + p->lineTo(p1.x()+offset+i,p1.y()+offset+i); + p->lineTo(p2.x()-offset-i,p1.y()+offset+i); + } + if (mode==1) p->setPen(COL_STATUSLIGHT); + else if (mode==0) p->setPen(COL_STATUSDARK); + for (i=0;imoveTo(p1.x()+offset+i,p2.y()-offset-i); + p->lineTo(p2.x()-offset-i,p2.y()-offset-i); + p->lineTo(p2.x()-offset-i,p1.y()+offset+i); + } +} + +// draw a move by animating the movment and turning of the +// card. This is called until the card reaches its final +// position +void LSkatView::drawMove(QPainter *p) +{ + int m1,m2; + int below; + QPoint point1,point2; + + m1=getDocument()->GetMove(0); + m2=getDocument()->GetMove(1); + point1=QPoint(CARD_X_MOVE1,CARD_Y_MOVE1); + point2=QPoint(CARD_X_MOVE2,CARD_Y_MOVE2); + + below=getDocument()->GetLastStartPlayer(); + if (below<0) below=getDocument()->GetStartPlayer(); + + if (below==0) + { + if (m1>=0) p->drawPixmap(point1,getDocument()->mPixCard[m1]); + if (m2>=0) p->drawPixmap(point2,getDocument()->mPixCard[m2]); + } + else + { + if (m2>=0) p->drawPixmap(point2,getDocument()->mPixCard[m2]); + if (m1>=0) p->drawPixmap(point1,getDocument()->mPixCard[m1]); + } + + int card; + QPoint point; + card=getDocument()->GetMoveStatus(); + if (card>=0) + { + + if (cardmoveunder>=0) + { + // turn new card + if ((double)cardmovecnt/(double)MOVECOUNTER>0.5) + { + QPixmap pix1(getDocument()->mPixCard[cardmoveunder]); + int wid=pix1.width(); + QWMatrix m; + m.scale(2.0*((double)cardmovecnt/(double)MOVECOUNTER-0.5),1.0); + pix1=pix1.xForm(m); + point=QPoint((wid-pix1.width())/2,0); + p->drawPixmap(cardorigin+point,pix1); + } + // turn deck + else + { + QPixmap pix1(getDocument()->mPixDeck); + int wid=pix1.width(); + QWMatrix m; + m.scale(1.0-2.0*((double)cardmovecnt/(double)MOVECOUNTER),1.0); + pix1=pix1.xForm(m); + point=QPoint((wid-pix1.width())/2,0); + p->drawPixmap(cardorigin+point,pix1); + } + } /* end turn card */ + + point=cardorigin+(cardend-cardorigin)*cardmovecnt/MOVECOUNTER; + p->drawPixmap(point,getDocument()->mPixCard[card]); + + } +} + +// Show the intro (Cards+Text) +// This is called repeatetly +void LSkatView::drawIntro(QPainter *p) +{ + int i,c1,c2,x,cnt,y,col,col2,col3,col4; + // The window width; + // int win_width = p->window().width(); + QPoint point,point1,p2; + QString s; + // Get a nice font + QFont font = KGlobalSettings::generalFont(); + font.setPointSize(48); + // Get the font info to determine text sizes + QFontMetrics fontMetrics(font); + + p->setFont(font); + + cnt=introcnt; + if (cnt>NO_OF_CARDS) cnt=NO_OF_CARDS; + + + i=0; + point=QPoint(20,20); + point1=QPoint(550,20); + + for (i=0;idrawPixmap(point+p2,getDocument()->mPixCard[c1]); + p2=QPoint(-i*10-x, i*10); + p->drawPixmap(point1+p2,getDocument()->mPixCard[c2]); + } + + // All text are positioned that they end up at x=310-textwidth/2 + // All texts are drawn twice with different colors to produce a shadow + col=255-8*i; + if (col<0) col=0; + col2=2*col/3; + col3=255-2*i; + col4=2*col3/3; + s=i18n("Lieutenant Skat"); + x=310-fontMetrics.width(s)/2; + y=-20+(int)(200.0*sin(0.5*M_PI/(float)NO_OF_CARDS*cnt)); + p->setPen(QColor(col4,col2,0)); + p->drawText(x-2,y+2,s); + + p->setPen(QColor(col3,col,0)); + p->drawText(x,y,s); + + s=i18n("for"); + y=270+(NO_OF_CARDS-cnt)*3; + x=-fontMetrics.width(s)/2+(int)(310.0*sin(0.5*M_PI/(float)NO_OF_CARDS*cnt)); + p->setPen(QColor(col4,col2,0)); + p->drawText(x-2,y+2,s); + + p->setPen(QColor(col3,col,0)); + p->drawText(x,y,s); + + s=i18n("K D E"); + y=350+(NO_OF_CARDS-cnt)*3; + // x=640-(int)(380.0*sin(0.5*M_PI/(float)NO_OF_CARDS*cnt)); + x=570-fontMetrics.width(s)/2-(int)(260.0*sin(0.5*M_PI/(float)NO_OF_CARDS*cnt)); + p->setPen(QColor(col4,col2,0)); + p->drawText(x-2,y+2,s); + + p->setPen(QColor(col3,col,0)); + p->drawText(x,y,s); +} + +// Draw all cards to create the game board +void LSkatView::drawDeck(QPainter *p) +{ + int x,y,card; + int player,pos,height; + QPoint point; + + for (y=0;y<4;y++) + { + if (y<2) player=0; + else player=1; + + for (x=0;x<4;x++) + { + pos=x+4*(y%2); + + point=calcCardPos(x,y); + if (getDocument()->IsRunning()) + { + height=getDocument()->GetHeight(player,pos); + card=getDocument()->GetCard(player,pos,height); + + + if (height==2) + p->drawPixmap(point-QPoint(CARD_X_DECK,CARD_Y_DECK),getDocument()->mPixDeck); + + if (cardmovecnt<1 || x!=cardmovex || y!=cardmovey) + { + if (height>0 ) p->drawPixmap(point,getDocument()->mPixCard[card]); + } + else + { + // moved to drawMove + // if (height>0 ) p->drawPixmap(point,getDocument()->mPixDeck); + } + } + else + { + p->drawPixmap(point,getDocument()->mPixDeck); + } + } + } +} +// Draw the winner field +void LSkatView::drawFinal(QPainter *p) +{ + int sc1,sc0,pt0,pt1; + //QPoint p1,p2; + int trump; + //QRect r; + QString ld; + int ts[10]; + + QFont font24 = KGlobalSettings::generalFont(); + font24.setPointSize(24); + QFont font14 = KGlobalSettings::generalFont(); + font14.setPointSize(13); + + //p1=status_rect3.topLeft(); + //p2=status_rect3.bottomRight(); + + sc0=getDocument()->GetScore(0); + sc1=getDocument()->GetScore(1); + + if (sc0>=120) pt0=4; + else if (sc0>=90) pt0=3; + else if (sc0>60) pt0=2; + else if (sc0==60) pt0=1; + else pt0=0; + if (sc1>=120) pt1=4; + else if (sc1>=90) pt1=3; + else if (sc1>60) pt1=2; + else if (sc1==60) pt1=1; + else pt1=0; + + + trump=getDocument()->GetTrump(); + + + + QString line1,line2,line3,line4,line5; + QString col1_3,col2_3,col3_3,col4_3; + QString col1_4,col2_4,col3_4,col4_4; + QRect sumrect; + QRect rect; + QRect brect1,brect2,brect3,brect4,brect5; + QRect brect1_3,brect2_3,brect3_3,brect4_3; + QRect brect1_4,brect2_4,brect3_4,brect4_4; + + // Calculate geometry + line1=i18n("Game over"); + rect=p->window(); + //rect1.moveBy(0,FINAL_Y0-24); + p->setFont(font24); + brect1=p->boundingRect(rect,Qt::AlignHCenter|Qt::SingleLine|Qt::AlignTop,line1); + //QRect wrect=p->window(); + sumrect=brect1; + + if (sc0+sc1!=120) // aborted + { + line2=i18n("Game was aborted - no winner"); + int hp=getDocument()->mPixTrump[trump].height()+5; + rect=QRect(0,hp>sumrect.height()?hp:sumrect.height(),p->window().width(),p->window().height()); + p->setFont(font14); + brect2=p->boundingRect(rect,Qt::AlignHCenter|Qt::SingleLine|Qt::AlignTop,line2); + sumrect|=brect2; + } + else + { + if (sc0==sc1) + { + line2=i18n(" Game is drawn"); + } + else if (sc0>sc1) + { + line2=i18n("Player 1 - %1 won ").arg(getDocument()->GetName(0)); + } + else + { + line2=i18n("Player 2 - %1 won ").arg(getDocument()->GetName(1)); + } + int hp=getDocument()->mPixTrump[trump].height()+5; + rect=QRect(0,hp>sumrect.height()?hp:sumrect.height(),p->window().width(),p->window().height()); + p->setFont(font14); + brect2=p->boundingRect(rect,Qt::AlignHCenter|Qt::SingleLine|Qt::AlignTop,line2); + sumrect|=brect2; + + p->setFont(font14); + col1_3=i18n("Score:"); + col1_4=QString(" "); + rect=QRect(0,0,p->window().width(),p->window().height()); + brect1_3=p->boundingRect(rect,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,col1_3); + ts[0]=brect1_3.width()+10; + + col2_3=getDocument()->GetName(0); + rect=QRect(0,0,p->window().width(),p->window().height()); + brect2_3=p->boundingRect(rect,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,col2_3); + + col2_4=getDocument()->GetName(1); + rect=QRect(0,0,p->window().width(),p->window().height()); + brect2_4=p->boundingRect(rect,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,col2_4); + rect=brect2_3|brect2_4; + ts[1]=ts[0]+rect.width()+10; + + + col3_3.sprintf("%d",sc0); + rect=QRect(0,0,p->window().width(),p->window().height()); + brect3_3=p->boundingRect(rect,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,col3_3); + + col3_4.sprintf("%d",sc1); + rect=QRect(0,0,p->window().width(),p->window().height()); + brect3_4=p->boundingRect(rect,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,col3_4); + rect=brect3_3|brect3_4; + ts[2]=ts[1]+rect.width()+30; + + col4_3=i18n("%1 points").arg(pt0); + rect=QRect(0,0,p->window().width(),p->window().height()); + brect4_3=p->boundingRect(rect,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,col4_3); + + col4_4=i18n("%1 points").arg(pt1); + rect=QRect(0,0,p->window().width(),p->window().height()); + brect4_4=p->boundingRect(rect,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,col4_4); + + rect=brect4_3|brect4_4; + ts[3]=ts[2]+rect.width()+10; + ts[4]=0; + + line3=col1_3+QString("\t")+col2_3+QString("\t")+col3_3+QString("\t")+col4_3; + line4=col1_4+QString("\t")+col2_4+QString("\t")+col3_4+QString("\t")+col4_4; + brect3=QRect(sumrect.left(),sumrect.bottom()+10,ts[3],brect4_3.height()); + brect4=QRect(sumrect.left(),sumrect.bottom()+10+brect4_3.height()+6,ts[3],brect4_4.height()); + sumrect|=brect3; + sumrect|=brect4; + + + p->setFont(font14); + if (sc0>=120) + { + line5=i18n("%1 won to nil. Congratulations!").arg(getDocument()->GetName(0)); + rect=QRect(0,sumrect.height()+10,p->window().width(),p->window().height()); + brect5=p->boundingRect(rect,Qt::AlignHCenter|Qt::SingleLine|Qt::AlignTop,line5); + sumrect|=brect5; + } + else if (sc0>=90) + { + if (sc0==90) + line5=i18n("%1 won with 90 points. Super!").arg(getDocument()->GetName(0)); + else + line5=i18n("%1 won over 90 points. Super!").arg(getDocument()->GetName(0)); + rect=QRect(0,sumrect.height()+10,p->window().width(),p->window().height()); + brect5=p->boundingRect(rect,Qt::AlignHCenter|Qt::SingleLine|Qt::AlignTop,line5); + sumrect|=brect5; + } + if (sc1>=120) + { + line5=i18n("%1 won to nil. Congratulations!").arg(getDocument()->GetName(1)); + rect=QRect(0,sumrect.height()+10,p->window().width(),p->window().height()); + brect5=p->boundingRect(rect,Qt::AlignHCenter|Qt::SingleLine|Qt::AlignTop,line5); + sumrect|=brect5; + } + else if (sc1>=90) + { + if (sc1==90) + line5=i18n("%1 won with 90 points. Super!").arg(getDocument()->GetName(1)); + else + line5=i18n("%1 won over 90 points. Super!").arg(getDocument()->GetName(1)); + rect=QRect(0,sumrect.height()+10,p->window().width(),p->window().height()); + brect5=p->boundingRect(rect,Qt::AlignHCenter|Qt::SingleLine|Qt::AlignTop,line5); + sumrect|=brect5; + } + } + + + QPoint offset=QPoint(status_rect3.left()-sumrect.left(),status_rect3.top()); + sumrect.moveBy(offset.x(),offset.y()); + + // draw actual strings and symbols + QRect borderrect=QRect(sumrect.left()-20,sumrect.top()-20,sumrect.width()+40,sumrect.height()+40); + p->drawRect(borderrect); + drawBorder(p,borderrect,0,4,0); + drawBorder(p,borderrect,10,1,1); + + + p->setPen(black); + p->setFont(font24); + rect=sumrect; + rect.setTop(brect1.top()+offset.y()); + //brect1.moveBy(offset.x(),offset.y()); + p->drawText(rect,Qt::AlignHCenter|Qt::SingleLine|Qt::AlignTop,line1); + + p->setFont(font14); + p->setPen(COL_PLAYER); + rect=sumrect; + rect.setTop(brect2.top()+offset.y()); + //brect2.moveBy(offset.x(),offset.y()); + p->drawText(rect,Qt::AlignHCenter|Qt::SingleLine|Qt::AlignTop,line2); + + p->drawPixmap(sumrect.topLeft()+QPoint(-5,-5), getDocument()->mPixTrump[trump]); + p->drawPixmap(sumrect.topLeft()+QPoint(5,-5)+QPoint(sumrect.width()-getDocument()->mPixTrump[trump].width(),0), + getDocument()->mPixTrump[trump]); + + + if (!col1_3.isNull()) + { + p->setTabArray(ts); + p->setFont(font14); + p->setPen(Qt::black); + rect=sumrect; + rect.setTop(brect3.top()+offset.y()); + + // Workaround for the next line where the ExpandTab crashes!!! + drawTabText(p,rect,line3,ts); + // p->drawText(rect,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop|Qt::ExpandTabs,line3); + rect=sumrect; + rect.setTop(brect4.top()+offset.y()); + // Workaround for the next line where the ExpandTab crashes!!! + drawTabText(p,rect,line4,ts); + // p->drawText(rect,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop|Qt::ExpandTabs,line4); + } + if (!line5.isNull()) + { + p->setFont(font14); + p->setPen(Qt::black); + rect=sumrect; + rect.setTop(brect5.top()+offset.y()); + p->drawText(rect,Qt::AlignHCenter|Qt::SingleLine|Qt::AlignTop,line5); + } +} + +// This function is just a workaround for the QT function drawText +// with Qt::EXpandTAbs eanbled. For some strange reasons this crashes... +void LSkatView::drawTabText(QPainter *p,QRect rect,QString s,int *ts) +{ + int lcnt=0; + + // p->setPen(Qt::black); + // p->drawRect(rect); + while(s.length()>0 && (lcnt==0 || ts[lcnt-1]) ) + { + int lpos=s.find("\t"); + int rpos=s.length()-lpos-1; + if (lpos<0) + { + lpos=s.length(); + rpos=0; + } + QString tmp=s.left(lpos); + s=s.right(rpos); + QRect rect2=rect; + if (lcnt>0) + rect2.setLeft(rect.left()+ts[lcnt-1]); + p->drawText(rect2,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,tmp); + lcnt++; + } +} + +// Draw the status field at the right side +void LSkatView::drawStatus(QPainter *p) +{ + QPoint p1,p2; + int trump; + QRect drawrect; + // For loop + QRect srect[2]; + srect[0]=status_rect1; + srect[1]=status_rect2; + + QFont font10 = KGlobalSettings::generalFont(); + font10.setPointSize(13); + p->setFont(font10); + + trump=getDocument()->GetTrump(); + + + // draw text + QString ld; + QString line1,line2,line3,line4,line2a,line2b,line2c; + QRect sumrect,rect,rect2,brect1,brect2,brect3,brect4; + QPoint pa; + + for (int pl=0;pl<2;pl++) + { + drawrect=QRect(srect[pl].left()+14,srect[pl].top()+14,srect[pl].width()-28,srect[pl].height()-28); + p1=drawrect.topLeft(); + p2=drawrect.bottomRight(); + + // Draw border and field + p->setPen(COL_STATUSBORDER); + p->setBrush(COL_STATUSFIELD); + p->drawRect(srect[pl]); + drawBorder(p,srect[pl],0,4,0); + drawBorder(p,srect[pl],10,1,1); + + + // Player pl ------------------- + // line1=QString(i18n("Player 1"))+QString(QCString(" - "))+getDocument()->GetName(0); + line1=getDocument()->GetName(pl); + brect1=p->boundingRect(drawrect,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,line1); + sumrect=brect1; + + if (getDocument()->IsRunning()) + { + // Geometry and strings + line2=i18n("Score:"); + rect=QRect(drawrect.left(),sumrect.bottom()+16,drawrect.width(),drawrect.height()-sumrect.height()); + brect2=p->boundingRect(rect,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,line2); + sumrect|=brect2; + line3=i18n("Move:"); + rect=QRect(drawrect.left(),sumrect.bottom()+10,drawrect.width(),drawrect.height()-sumrect.height()); + brect3=p->boundingRect(rect,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,line3); + sumrect|=brect3; + + rect=brect2|brect3; + rect.setWidth(rect.width()+10); + line2a.sprintf("%3d",getDocument()->GetScore(pl)); + line2b.sprintf("%3d",getDocument()->GetMoveNo()); + + // paint + if (getDocument()->GetStartPlayer()==pl) p->setPen(COL_PLAYER); + else p->setPen(black); + p->drawText(brect1,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,line1); + p->setPen(black); + p->drawText(brect2,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,line2); + p->drawText(brect3,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,line3); + rect2=QRect(brect2.left()+rect.width(),brect2.top(),drawrect.width()-brect2.width()-rect.width(),brect2.height()); + p->drawText(rect2,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,line2a); + rect2=QRect(brect3.left()+rect.width(),brect3.top(),drawrect.width()-brect3.width()-rect.width(),brect3.height()); + p->drawText(rect2,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,line2b); + + pa=QPoint(drawrect.width()-getDocument()->mPixTrump[trump].width(),drawrect.height()-getDocument()->mPixTrump[trump].height()); + if (getDocument()->GetStartPlayer()==pl) + p->drawPixmap(p1+pa+QPoint(3,3),getDocument()->mPixTrump[trump]); + + pa=QPoint(drawrect.width()-getDocument()->mPixType[getDocument()->GetPlayedBy(pl)-1].width(),0); + p->drawPixmap(p1+pa+QPoint(3,-3), getDocument()->mPixType[getDocument()->GetPlayedBy(pl)-1]); + } + else // draw all time score + { + // Geometry and strings + line2=i18n("Points:"); + rect=QRect(drawrect.left(),sumrect.bottom()+6,drawrect.width(),drawrect.height()-sumrect.height()); + brect2=p->boundingRect(rect,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,line2); + sumrect|=brect2; + + line3=i18n("Won:"); + rect=QRect(drawrect.left(),sumrect.bottom()+6,drawrect.width(),drawrect.height()-sumrect.height()); + brect3=p->boundingRect(rect,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,line3); + sumrect|=brect3; + + line4=i18n("Games:"); + rect=QRect(drawrect.left(),sumrect.bottom()+6,drawrect.width(),drawrect.height()-sumrect.height()); + brect4=p->boundingRect(rect,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,line4); + sumrect|=brect4; + + rect=brect2|brect3|brect4; + rect.setWidth(rect.width()+10); + line2a.sprintf("%d",getDocument()->GetStatPoints(pl)); + line2b.sprintf("%d",getDocument()->GetStatWon(pl)); + line2c.sprintf("%d",getDocument()->GetStatGames(pl)); + + // paint + p->setPen(COL_PLAYER); + p->drawText(brect1,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,line1); + p->setPen(black); + p->drawText(brect2,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,line2); + p->drawText(brect3,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,line3); + p->drawText(brect4,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,line4); + rect2=QRect(brect2.left()+rect.width(),brect2.top(),drawrect.width()-brect2.width()-rect.width()+5,brect2.height()); + p->drawText(rect2,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,line2a); + rect2=QRect(brect3.left()+rect.width(),brect3.top(),drawrect.width()-brect3.width()-rect.width()+5,brect3.height()); + p->drawText(rect2,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,line2b); + rect2=QRect(brect4.left()+rect.width(),brect4.top(),drawrect.width()-brect4.width()-rect.width()+5,brect4.height()); + p->drawText(rect2,Qt::AlignLeft|Qt::SingleLine|Qt::AlignTop,line2c); + + pa=QPoint(drawrect.width()-getDocument()->mPixType[getDocument()->GetPlayedBy(pl)-1].width(),0); + p->drawPixmap(p1+pa+QPoint(3,-3), getDocument()->mPixType[getDocument()->GetPlayedBy(pl)-1]); + + } + + }// end pl +} + +// paint function +void LSkatView::Paint(QPainter *p) +{ + // If game is running + drawStatus(p); + drawDeck(p); + if (getDocument()->IsRunning()) + { + drawMove(p); + } + else if (getDocument()->WasRunning() && !getDocument()->IsIntro()) + { + drawFinal(p); + } +} + +// paint event +void LSkatView::paintEvent( QPaintEvent * e) +{ + if (getDocument()->IsIntro()) + { + QPixmap pm(this->rect().size()); + QPainter p; + QBrush brush; + p.begin(&pm,this); + brush.setPixmap( getDocument()->mPixBackground ); + p.fillRect(0,0,this->rect().width(),this->rect().height(),brush); + drawIntro(&p); + p.end(); + bitBlt(this,0,0,&pm); + } + else + { + QPainter paint( this ); + paint.setClipRect(e->rect()); + Paint( &paint ); + } +} + + +// x=0..4, y=0..4 +QPoint LSkatView::calcCardPos(int x,int y) +{ + QPoint point; + point=QPoint(CARD_X_OFFSET+x*(CARD_X_SPACE+getDocument()->cardsize.width()), + CARD_Y_OFFSET+y*(CARD_Y_SPACE+getDocument()->cardsize.height())); + if (y>=2) point+=QPoint(0,CARD_Y_BOARD_OFFSET); + point+=QPoint(CARD_X_DECK,CARD_Y_DECK); + return point; +} + +// mouse click event +void LSkatView::mousePressEvent( QMouseEvent *mouse ) +{ + if (mouse->button()!=LeftButton) return ; + if (!getDocument()->IsRunning()) return ; + + if (getDocument()->GetMoveStatus()!=-1) return ; + + QPoint point; + int mx,my,player; + + point=mouse->pos(); + point-=QPoint(CARD_X_OFFSET,CARD_Y_OFFSET); + if (point.y()>2*(getDocument()->cardsize.height()+CARD_Y_SPACE)) + { + point-=QPoint(0,CARD_Y_BOARD_OFFSET+2*(getDocument()->cardsize.height()+CARD_Y_SPACE)); + player=1; + } + else + { + player=0; + } + if (point.x()<0 || point.y()<0) return ; + mx=point.x()/(getDocument()->cardsize.width()+CARD_X_SPACE); + my=point.y()/(getDocument()->cardsize.height()+CARD_Y_SPACE); + if (mx>3 || my>1) return ; + + if (global_debug>10) + printf("Type=%d Next=%d player=%d\n", + getDocument()->QueryInputHandler()->QueryType(player), + getDocument()->QueryInputHandler()->QueryNext(),player); + if (getDocument()->QueryInputHandler()->IsInteractive(player) && + getDocument()->QueryInputHandler()->QueryNext()==player && + !getDocument()->IsLocked()) + { + KEMessage *msg=new KEMessage; + msg->AddData(QCString("Move"),(short)player); + msg->AddData(QCString("MoveX"),(short)mx); + msg->AddData(QCString("MoveY"),(short)my); + getDocument()->QueryInputHandler()->SetInput(msg); + delete msg; + } + else + { + QString m; + switch(getDocument()->random(4)) + { + case 0: + m=i18n("Hold on... the other player hasn't been yet..."); + break; + case 1: + m=i18n("Hold your horses..."); + break; + case 2: + m=i18n("Ah ah ah... only one go at a time..."); + break; + default: + m=i18n("Please wait... it is not your turn."); + } + KMessageBox::information(this,m); + } +} + + +void LSkatView::InitMove(int player,int mx,int my) +{ + cardmovex=mx; + cardmovey=my+2*player; + cardmoveunder=getDocument()->GetCard(player,mx+4*my,getDocument()->GetHeight(player,mx+4*my)); + introTimer->stop(); + moveTimer->start(MOVE_TIMER_DELAY,FALSE); + cardmovecnt=0; + cardorigin=calcCardPos(mx,my+2*player); + if (getDocument()->GetCurrentPlayer()==0) + cardend=QPoint(CARD_X_MOVE1,CARD_Y_MOVE1); + else cardend=QPoint(CARD_X_MOVE2,CARD_Y_MOVE2); + update(QRect( + cardorigin-QPoint(CARD_X_DECK,CARD_Y_DECK), + getDocument()->cardsize+QSize(CARD_X_DECK,CARD_Y_DECK))); + + QPoint point1=QPoint(CARD_X_MOVE1,CARD_Y_MOVE1); + QPoint point2=QPoint(CARD_X_MOVE2,CARD_Y_MOVE2); + update(QRect(point1,getDocument()->cardsize)); + update(QRect(point2,getDocument()->cardsize)); +} +void LSkatView::introTimerReady() +{ + if (!getDocument()->IsIntro()) + { + introTimer->stop(); + return ; + } + introcnt++; + if (introcnt>NO_OF_CARDS*4) + { + introcnt=0; + for (int i=0;irandom(NO_OF_CARDS); + } + } + else if (introcnt=MOVECOUNTER) + { + LSkatApp *m=(LSkatApp *) parentWidget(); + moveTimer->stop(); + cardmovecnt=0; + update(QRect(cardend,getDocument()->cardsize)); + update(QRect(cardorigin,getDocument()->cardsize)); + update(status_rect1); + update(status_rect2); + m->MoveFinished(); + + } + else + { + pos=cardorigin+(cardend-cardorigin)*cardmovecnt/MOVECOUNTER; + update(QRect(pos,getDocument()->cardsize)); + cardmovecnt++; + pos=cardorigin+(cardend-cardorigin)*cardmovecnt/MOVECOUNTER; + update(QRect(pos,getDocument()->cardsize)); + // Turning of the card + if ( cardmoveunder>=0) + { + update(QRect(cardorigin,getDocument()->cardsize)); + } + } +} + +void LSkatView::updateStatus() +{ + update(status_rect1); + update(status_rect2); +} + +#include "lskatview.moc" diff --git a/lskat/lskat/lskatview.h b/lskat/lskat/lskatview.h new file mode 100644 index 00000000..05b31356 --- /dev/null +++ b/lskat/lskat/lskatview.h @@ -0,0 +1,91 @@ +/*************************************************************************** + lskatview.h - description + ------------------- + begin : Tue May 2 15:47:11 CEST 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef LSKATVIEW_H +#define LSKATVIEW_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include "lskat.h" + +class LSkatDoc; + +/** The LSkatView class provides the view widget for the LSkatApp instance. + * The View instance inherits QWidget as a base class and represents the view object of a KTMainWindow. As LSkatView is part of the + * docuement-view model, it needs a reference to the document object connected with it by the LSkatApp class to manipulate and display + * the document structure provided by the LSkatDoc class. + * + * @author Source Framework Automatically Generated by KDevelop, (c) The KDevelop Team. + * @version KDevelop version 0.4 code generation + */ +class LSkatView : public QWidget +{ + Q_OBJECT + public: + /** Constructor for the main view */ + LSkatView(QWidget *parent = 0, const char *name=0); + + /** returns a pointer to the document connected to the view instance. Mind that this method requires a LSkatApp instance as a parent + * widget to get to the window document pointer by calling the LSkatApp::getDocument() method. + * + * @see LSkatApp#getDocument + */ + LSkatDoc *getDocument() const; + + void paintEvent( QPaintEvent * p); + void Paint(QPainter *p); + void InitMove(int player,int x,int y); + void updateStatus(); + + protected: + void drawDeck(QPainter *p); + void drawIntro(QPainter *p); + void drawMove(QPainter *p); + void drawStatus(QPainter *p); + void drawFinal(QPainter *p); + void drawBorder(QPainter *p,QRect rect,int offset,int width,int mode); + QPoint calcCardPos(int x,int y); + + void mousePressEvent ( QMouseEvent *m ); + + protected slots: + void moveTimerReady(); + void introTimerReady(); + void drawTabText(QPainter *p,QRect rect,QString s,int *ts); + + + private: + QRect status_rect1; + QRect status_rect2; + QRect status_rect3; + QTimer *moveTimer; + QTimer *introTimer; + int introcnt; + int cardmovecnt; + int cardmovex,cardmovey; + int cardmoveunder; + QPoint cardorigin; + QPoint cardend; + int introCards[NO_OF_CARDS]; + +}; + +#endif // LSKATVIEW_H diff --git a/lskat/lskat/main.cpp b/lskat/lskat/main.cpp new file mode 100644 index 00000000..50910540 --- /dev/null +++ b/lskat/lskat/main.cpp @@ -0,0 +1,67 @@ +/*************************************************************************** + begin : Tue May 2 15:47:11 CEST 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include +#include +#include +#include "lskat.h" + +static KCmdLineOptions options[] = +{ + { "d", 0, 0}, + { "debug ", I18N_NOOP("Enter debug level"), 0 }, + KCmdLineLastOption +}; + +int global_debug; + +int main(int argc, char *argv[]) +{ + + global_debug=0; + KAboutData aboutData( "lskat", I18N_NOOP("Lieutenant Skat"), + "1.0", + I18N_NOOP("Card Game"), + KAboutData::License_GPL, + "(c) 2000, Martin Heni"); + aboutData.addAuthor("Martin Heni",0, "martin@heni-online.de"); + aboutData.addCredit("Laura", I18N_NOOP("Beta testing"), 0); + KCmdLineArgs::init( argc, argv, &aboutData ); + KCmdLineArgs::addCmdLineOptions( options ); // Add our own options. + + /* command line handling */ + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + if (args->isSet("debug")) + { + QString s = args->getOption("debug"); + global_debug = s.toInt(); + qDebug("Debug level set to %d\n", global_debug); + } + args->clear(); + + KApplication app; + + if (app.isRestored()) + { + RESTORE(LSkatApp); + } + else + { + LSkatApp *lskat = new LSkatApp(); + lskat->show(); + } + + return app.exec(); +} diff --git a/lskat/lskat/msgdlg.cpp b/lskat/lskat/msgdlg.cpp new file mode 100644 index 00000000..daea1dac --- /dev/null +++ b/lskat/lskat/msgdlg.cpp @@ -0,0 +1,74 @@ +/*************************************************************************** + Msgdlg - Send message to remote + ------------------- + begin : Thu Mar 30 2000 + copyright : (C) |1995-2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +/* + Msgdlg.cpp + + $Id$ + + Msg dialog for player names + + (c) Martin Heni, martin@heni-online.de + June 1999 + + License: GPL + +*/ + +#include +#include +#include +#include +#include "msgdlg.h" + + + +// Create the dialog for changing the player names +MsgDlg::MsgDlg( QWidget *parent, const char *name,const char * /*sufi */ ) + : QDialog( parent, name,TRUE ) +{ + setCaption(i18n("Send Message to Remote Player")); + setMinimumSize(400,160); + setMaximumSize(600,360); + resize( 400, 160 ); + + QGroupBox* grp; + grp = new QGroupBox(i18n("Enter Message"), this); + grp->resize(380,100); + grp->move(10,10); + + MultiLine = new QMultiLineEdit( grp, "MLineEdit" ); + MultiLine->setGeometry( 10, 20, 360, 70 ); + MultiLine->setText(QCString("") ); + + QPushButton *PushButton; + PushButton = new QPushButton( i18n("Send" ), this, "PushButton_1" ); + PushButton->setGeometry( 20, 120, 65, 30 ); + connect( PushButton, SIGNAL(clicked()), SLOT(accept()) ); + PushButton->setAutoRepeat( FALSE ); + + PushButton = new KPushButton( KStdGuiItem::cancel(), this, "PushButton_2" ); + PushButton->setGeometry( 305, 120, 65, 30 ); + connect( PushButton, SIGNAL(clicked()), SLOT(reject()) ); + PushButton->setAutoRepeat( FALSE ); +} + +QString MsgDlg::GetMsg() +{ + return MultiLine->text(); +} + +#include "msgdlg.moc" diff --git a/lskat/lskat/msgdlg.h b/lskat/lskat/msgdlg.h new file mode 100644 index 00000000..614fa332 --- /dev/null +++ b/lskat/lskat/msgdlg.h @@ -0,0 +1,39 @@ +/*************************************************************************** + LSKat for KDE + ------------------- + begin : March 2000 + copyright : (C) 1995-2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef __MSGDLG_H_ +#define __MSGDLG_H_ +#include +#include +#include + +class MsgDlg : public QDialog +{ + Q_OBJECT + + public: + MsgDlg (QWidget* parent = NULL,const char* name = NULL,const char *sufi=NULL); + QString GetMsg(); + +protected slots: + +protected: + QMultiLineEdit *MultiLine; + + + +}; +#endif diff --git a/lskat/lskat/namedlg.cpp b/lskat/lskat/namedlg.cpp new file mode 100644 index 00000000..478f5ea5 --- /dev/null +++ b/lskat/lskat/namedlg.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** Form implementation generated from reading ui file 'namedlg.ui' +** +** Created: Thu Nov 23 11:10:17 2000 +** by: The User Interface Compiler (uic) +** +** WARNING! All changes made in this file will be lost! +****************************************************************************/ +#include "namedlg.h" + +#include +#include +#include +#include +#include + +#include + +#define NAME_MAX_LEN 12 + +/* + * Constructs a NameDlg which is a child of 'parent', with the + * name 'name' and widget flags set to 'f' + * + * The dialog will by default be modeless, unless you set 'modal' to + * TRUE to construct a modal dialog. + */ +NameDlg::NameDlg( QWidget *parent, const char *name,bool /* modal */, WFlags /* fl */ ) + : KDialogBase( Plain, i18n("Configure Names"), Ok|Cancel, Ok, + parent, name, true,true ) + +{ + QWidget *page = plainPage(); + if ( !name ) setName( "NameDlg" ); + resize( 252, 186 ); +// setCaption( i18n( "Configure Names" ) ); + vbox = new QVBoxLayout( page,spacingHint() ); + vbox->setSpacing( 6 ); + vbox->setMargin( 11 ); + + hbox = new QHBoxLayout; + hbox->setSpacing( 6 ); + hbox->setMargin( 0 ); + + player_names = new QGroupBox( page, "player_names" ); + player_names->setTitle(i18n("Player Names") ); + player_names->setColumnLayout(0, Qt::Vertical ); + player_names->layout()->setSpacing( 0 ); + player_names->layout()->setMargin( 0 ); + vbox_2 = new QVBoxLayout( player_names->layout() ); + vbox_2->setAlignment( Qt::AlignTop ); + vbox_2->setSpacing( 6 ); + vbox_2->setMargin( 11 ); + + vbox_3 = new QVBoxLayout; + vbox_3->setSpacing( 6 ); + vbox_3->setMargin( 0 ); + + hbox_2 = new QHBoxLayout; + hbox_2->setSpacing( 6 ); + hbox_2->setMargin( 0 ); + + text_player1 = new QLabel( player_names, "text_player1" ); + text_player1->setText( i18n("Player 1:" ) ); + hbox_2->addWidget( text_player1 ); + + edit_player1 = new QLineEdit( player_names, "edit_player1" ); + edit_player1->setMaxLength( NAME_MAX_LEN ); + QWhatsThis::add( edit_player1, i18n( "Enter a player's name" ) ); + + hbox_2->addWidget( edit_player1 ); + vbox_3->addLayout( hbox_2 ); + + hbox_3 = new QHBoxLayout; + hbox_3->setSpacing( 6 ); + hbox_3->setMargin( 0 ); + + text_player2 = new QLabel( player_names, "text_player2" ); + text_player2->setText( i18n("Player 2:" ) ); + hbox_3->addWidget( text_player2 ); + + edit_player2 = new QLineEdit( player_names, "edit_player2" ); + edit_player2->setMaxLength( NAME_MAX_LEN ); + QWhatsThis::add( edit_player2, i18n( "Enter a player's name" ) ); + hbox_3->addWidget( edit_player2 ); + vbox_3->addLayout( hbox_3 ); + vbox_2->addLayout( vbox_3 ); + + // left + QSpacerItem* spacer_3 = new QSpacerItem( 0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum ); + hbox->addItem( spacer_3 ); + + hbox->addWidget( player_names ); + QSpacerItem* spacer = new QSpacerItem( 20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); + hbox->addItem( spacer ); + + // top + QSpacerItem* spacer_4 = new QSpacerItem( 0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding ); + vbox->addItem( spacer_4 ); + + vbox->addLayout( hbox ); + QSpacerItem* spacer_2 = new QSpacerItem( 20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding ); + vbox->addItem( spacer_2 ); + +} + +/* + * Destroys the object and frees any allocated resources + */ +NameDlg::~NameDlg() +{ + // no need to delete child widgets, Qt does it all for us +} + +// In and output the name strings +void NameDlg::SetNames(QString n1, QString n2) +{ + edit_player1->setText( n1 ); + edit_player2->setText( n2 ); +} +void NameDlg::GetNames(QString &n1, QString &n2) +{ + n1=edit_player1->text( ); + n1.truncate(NAME_MAX_LEN); + n2=edit_player2->text( ); + n2.truncate(NAME_MAX_LEN); +} + +#include "namedlg.moc" diff --git a/lskat/lskat/namedlg.h b/lskat/lskat/namedlg.h new file mode 100644 index 00000000..c5d3391a --- /dev/null +++ b/lskat/lskat/namedlg.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** Form interface generated from reading ui file 'namedlg.ui' +** +** Created: Thu Nov 23 11:10:15 2000 +** by: The User Interface Compiler (uic) +** +** WARNING! All changes made in this file will be lost! +****************************************************************************/ +#ifndef NAMEDLG_H +#define NAMEDLG_H + +#include +class QVBoxLayout; +class QHBoxLayout; +class QGridLayout; +class QGroupBox; +class QLabel; +class QLineEdit; +class QPushButton; + +class NameDlg : public KDialogBase +{ + Q_OBJECT + +public: + NameDlg( QWidget* parent = 0, const char* name = 0, bool modal = TRUE, WFlags fl = 0 ); + ~NameDlg(); + void SetNames(QString n1,QString n2); + void GetNames(QString &n1,QString &n2); + + QGroupBox* player_names; + QLabel* text_player1; + QLineEdit* edit_player1; + QLabel* text_player2; + QLineEdit* edit_player2; + QPushButton* PushButton1; + QPushButton* PushButton2; + +protected: + QHBoxLayout* hbox; + QHBoxLayout* hbox_2; + QHBoxLayout* hbox_3; + QHBoxLayout* hbox_4; + QVBoxLayout* vbox; + QVBoxLayout* vbox_2; + QVBoxLayout* vbox_3; +}; + +#endif // NAMEDLG_H diff --git a/lskat/lskat/networkdlg.cpp b/lskat/lskat/networkdlg.cpp new file mode 100644 index 00000000..f43a1fd9 --- /dev/null +++ b/lskat/lskat/networkdlg.cpp @@ -0,0 +1,105 @@ +/*************************************************************************** + networkdlg.cpp - description + ------------------- + copyright : (C) 2004 by Jakub Stachowski + email : qbast@go2.pl + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + + +#include "networkdlg.h" +#include +#include +#include +#include +#include + +extern const char* LSKAT_SERVICE; + +// Create the dialog +NetworkDlg::NetworkDlg( QWidget *parent, const char *name ) + : NetworkDlgBase( parent, name, TRUE ) +{ + browser = new DNSSD::ServiceBrowser(QString::fromLatin1(LSKAT_SERVICE)); + connect(browser,SIGNAL(finished()),SLOT(gamesFound())); + browser->startBrowse(); +} + +NetworkDlg::~NetworkDlg() +{ + delete browser; +} + +void NetworkDlg::SetHost(const QString& host) +{ + hostname->setText(host); +} + +void NetworkDlg::SetName(const QString& name) +{ + serverName->setText(name); +} +QString NetworkDlg::QueryName() const +{ + return serverName->text(); +} + +void NetworkDlg::SetPort(unsigned short port) +{ + this->port->setValue(port); +} + +void NetworkDlg::gamesFound() +{ + bool autoselect=false; + if (!clientName->count() && group->selectedId()==1) autoselect=true; + clientName->clear(); + QStringList names; + QValueList::ConstIterator itEnd = browser->services().end(); + for (QValueList::ConstIterator it = browser->services().begin(); + it!=itEnd; ++it) names << (*it)->serviceName(); + clientName->insertStringList(names); + if (autoselect && clientName->count()) gameSelected(0); +} + +void NetworkDlg::gameSelected(int nr) +{ + if (nr>=browser->services().count() || nr<0) return; + DNSSD::RemoteService::Ptr srv = browser->services()[nr]; + if (!srv->isResolved() && !srv->resolve()) return; + hostname->setText(srv->hostName()); + port->setValue(srv->port()); +} + +unsigned short NetworkDlg::QueryPort() const +{ + return port->value(); +} + +QString NetworkDlg::QueryHost() const +{ + return hostname->text(); +} + +void NetworkDlg::toggleServerClient() +{ + stack->raiseWidget(group->selectedId()); + if (group->selectedId()==1) { + gameSelected(clientName->currentItem()); + hostname->setEnabled(true); + } + else { + hostname->setText(QString::null); + hostname->setEnabled(false); + } +} + +#include "networkdlg.moc" diff --git a/lskat/lskat/networkdlg.h b/lskat/lskat/networkdlg.h new file mode 100644 index 00000000..fb180eeb --- /dev/null +++ b/lskat/lskat/networkdlg.h @@ -0,0 +1,48 @@ +/*************************************************************************** + networkdlg.h - description + ------------------- + copyright : (C) 2004 by Jakub Stachowski + email : qbast@go2.pl + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef NETWORKDLG_H +#define NETWORKDLG_H + +#include +#include +#include "networkdlgbase.h" + + +class NetworkDlg : public NetworkDlgBase +{ + Q_OBJECT + + public: + NetworkDlg(QWidget* parent=NULL, const char* name=NULL); + ~NetworkDlg(); + void SetName(const QString& name); + void SetHost(const QString& host); + void SetPort(unsigned short port); + QString QueryName() const; + unsigned short QueryPort() const; + QString QueryHost() const; + protected: + virtual void toggleServerClient(); + virtual void gameSelected(int nr); + private slots: + void gamesFound(); + private: + DNSSD::ServiceBrowser* browser; +}; + +#endif // NETWORKDLG_H + diff --git a/lskat/lskat/networkdlgbase.ui b/lskat/lskat/networkdlgbase.ui new file mode 100644 index 00000000..861c77f3 --- /dev/null +++ b/lskat/lskat/networkdlgbase.ui @@ -0,0 +1,306 @@ + +NetworkDlgBase + + + NetworkDlgBase + + + + 0 + 0 + 320 + 218 + + + + Network Options + + + + unnamed + + + + group + + + + 5 + 0 + 0 + 0 + + + + Play As + + + + unnamed + + + + serverBtn + + + Server + + + true + + + + + clientBtn + + + Client + + + + + + + stack + + + + serverPage + + + 0 + + + + unnamed + + + 0 + + + 0 + + + + serverLabel + + + Game name: + + + serverName + + + + + serverName + + + + 1 + 0 + 0 + 0 + + + + + + + + clientPage + + + 1 + + + + unnamed + + + 0 + + + 0 + + + + clientLabel + + + Network games: + + + clientName + + + + + clientName + + + + + + + + layout9 + + + + unnamed + + + + hostLabel + + + + 5 + 0 + 0 + 0 + + + + Host: + + + AlignVCenter|AlignLeft + + + + + + + hostname + + + false + + + + + portLabel + + + + 0 + 0 + 0 + 0 + + + + Port: + + + AlignVCenter|AlignLeft + + + + + + + + + port + + + 65000 + + + 7432 + + + Choose a port to connect to + + + + + + + layout11 + + + + unnamed + + + + spacer5 + + + Horizontal + + + Expanding + + + + 51 + 20 + + + + + + pushButton1 + + + &OK + + + + + spacer6 + + + Horizontal + + + Expanding + + + + 61 + 20 + + + + + + + + + + pushButton1 + clicked() + NetworkDlgBase + accept() + + + clientName + activated(int) + NetworkDlgBase + gameSelected(int) + + + serverBtn + toggled(bool) + NetworkDlgBase + toggleServerClient() + + + clientBtn + toggled(bool) + NetworkDlgBase + toggleServerClient() + + + + toggleServerClient() + gameSelected(int nr) + + + diff --git a/lskat/lskat/templates/cpp_template b/lskat/lskat/templates/cpp_template new file mode 100644 index 00000000..6afef5d4 --- /dev/null +++ b/lskat/lskat/templates/cpp_template @@ -0,0 +1,16 @@ +/*************************************************************************** + |FILENAME| - description + ------------------- + begin : |DATE| + copyright : (C) |YEAR| by |AUTHOR| + email : |EMAIL| + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ diff --git a/lskat/lskat/templates/header_template b/lskat/lskat/templates/header_template new file mode 100644 index 00000000..6afef5d4 --- /dev/null +++ b/lskat/lskat/templates/header_template @@ -0,0 +1,16 @@ +/*************************************************************************** + |FILENAME| - description + ------------------- + begin : |DATE| + copyright : (C) |YEAR| by |AUTHOR| + email : |EMAIL| + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ diff --git a/lskat/lskatproc/KChildConnect.cpp b/lskat/lskatproc/KChildConnect.cpp new file mode 100644 index 00000000..0308a3f0 --- /dev/null +++ b/lskat/lskatproc/KChildConnect.cpp @@ -0,0 +1,124 @@ +/*************************************************************************** + KChildConnect.cpp - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include +#include "KChildConnect.h" + +#include "KChildConnect.moc" + +KChildConnect::KChildConnect() + : QObject(0,0) +{ + input_pending=false; + inputbuffer=""; +} + +KChildConnect::~KChildConnect() +{ +} + +KR_STATUS KChildConnect::QueryStatus() +{ + return KR_OK; +} + +// Communication with process +bool KChildConnect::SendMsg(KEMessage *msg) +{ + QString sendstring=msg->ToString(); + // Debug only + if (msg->HasKey(QCString("KLogSendMsg"))) + { + char *p; + int size; + FILE *fp; + msg->GetData(QCString("KLogSendMsg"),p,size); + if (p && (fp=fopen(p,"a")) ) + { + fprintf(fp,"------------------------------------\n"); + fprintf(fp,"%s", sendstring.utf8().data()); + fclose(fp); + } + } + // end debug only + return Send(sendstring); +} + +// Send string to parent +bool KChildConnect::Send(QString str) +{ + if (!str || str.length()<1) return true; // no need to send crap + printf("%s",str.latin1()); + fflush(stdout); + return true; +} + +void KChildConnect::Receive(QString input) +{ + // Cut out CR + int len,pos; + QString tmp; + + + // Call us recursive until there are no CR left + len=KEMESSAGE_CR.length(); + pos=input.find(KEMESSAGE_CR); + if (pos>0) + { + tmp=input.left(pos); + if (tmp.length()>0) Receive(tmp); // CR free + input=input.right(input.length()-pos-len); + if (input.length()>0) Receive(input); + return ; + } + +// printf(" ---> KChildConnect::Receive: '%s'\n",(const char *)input); + if (input==QString(KEMESSAGE_HEAD) && !input_pending) + { + input_pending=true; + inputcache.clear(); + return ; + } + if (!input_pending) return ; // ignore + if (input!=QString(KEMESSAGE_TAIL)) + { + inputcache.append(input.latin1()); + return; + } + input_pending=0; + + KEMessage *msg=new KEMessage; + char *it; + for (it=inputcache.first();it!=0;it=inputcache.next()) + { + msg->AddString(QCString(it)); + } + +// printf("+- CHILDprocess:: GOT MESSAGE::Emmiting slotReceiveMsg\n"); + emit signalReceiveMsg(msg,ID); + + delete msg; +} + +void KChildConnect::SetID(int id) +{ + ID=id; +} +int KChildConnect::QueryID() +{ + return ID; +} + diff --git a/lskat/lskatproc/KChildConnect.h b/lskat/lskatproc/KChildConnect.h new file mode 100644 index 00000000..9cad1a5d --- /dev/null +++ b/lskat/lskatproc/KChildConnect.h @@ -0,0 +1,45 @@ +/*************************************************************************** + KChildConnect.h - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +#ifndef _KCHILDCONNECT_H_ +#define _KCHILDCONNECT_H_ + +#include +#include +#include "KEMessage.h" + + +class KChildConnect: public QObject +{ + Q_OBJECT + + protected: + QStrList inputcache; + bool input_pending; + QString inputbuffer; + int ID; + + public: + KChildConnect(); + ~KChildConnect(); + void Receive(QString input); + int QueryID(); + void SetID(int id); + + virtual bool SendMsg(KEMessage *msg); + virtual bool Send(QString str); + virtual KR_STATUS QueryStatus(); + + public slots: + + + signals: + void signalReceiveMsg(KEMessage *msg,int id); +}; + +#endif diff --git a/lskat/lskatproc/KConnectTypes.h b/lskat/lskatproc/KConnectTypes.h new file mode 100644 index 00000000..12e9561a --- /dev/null +++ b/lskat/lskatproc/KConnectTypes.h @@ -0,0 +1,38 @@ +/*************************************************************************** + KConnectTypes.h - description + ------------------- + begin : Sun Apr 9 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef _KCONNECTTYPES_H_ +#define _KCONNECTTYPES_H_ + +enum KGM_TYPE {KGM_TYPE_INVALID=0,KGM_TYPE_SHORT=1,KGM_TYPE_LONG=2, + KGM_TYPE_FLOAT=3,KGM_TYPE_DATA=4}; + +enum KG_INPUTTYPE { + KG_INPUTTYPE_INVALID=0, + KG_INPUTTYPE_INTERACTIVE=1, + KG_INPUTTYPE_PROCESS=2, + KG_INPUTTYPE_REMOTE=3}; + +enum KR_STATUS { + KR_NO_SOCKET=-2, + KR_WAIT_FOR_CLIENT=-1, + KR_INVALID=0, + // >0 OK + KR_OK=1, + KR_CLIENT=2, + KR_SERVER=3 + }; +#endif diff --git a/lskat/lskatproc/KEMessage.cpp b/lskat/lskatproc/KEMessage.cpp new file mode 100644 index 00000000..db598573 --- /dev/null +++ b/lskat/lskatproc/KEMessage.cpp @@ -0,0 +1,326 @@ +/*************************************************************************** + KEMessage.cpp - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include +#include +#include +#include "KEMessage.h" + +void KEMessage::AddEntry(QString key,KMessageEntry *entry) +{ + // printf(" AddingEntry: %s with data field %p\n",(char *)key,entry->QueryData()); + if (!entry) return ; + dict.insert(key,entry); + keys.append(key.latin1()); +} + +void KEMessage::AddDataType(QString key,int size,const char *data,KGM_TYPE type) +{ +// printf("AddDataType for %s size=%d\n",(const char *)key,size); + if (size<=0) return ; + KMessageEntry *entry=new KMessageEntry; + entry->SetType(type); + entry->CopyData(size,data); + AddEntry(key,entry); +} + +void KEMessage::AddData(QString key,short data) +{ + AddDataType(key,sizeof(short),(char *)&data,KGM_TYPE_SHORT); +} + +void KEMessage::AddData(QString key,long data) +{ + AddDataType(key,sizeof(long),(char *)&data,KGM_TYPE_LONG); +} + +void KEMessage::AddData(QString key,float data) +{ + AddDataType(key,sizeof(float),(char *)&data,KGM_TYPE_FLOAT); +} + +void KEMessage::AddData(QString key,const char *data,int size) +{ + if (size<0) size=strlen(data)+1; // +1 for 0 Byte + AddDataType(key,size,data,KGM_TYPE_DATA); +} + +KGM_TYPE KEMessage::QueryType(QString key) +{ + KMessageEntry *entry; + entry=dict.find(key); + if (!entry) return (KGM_TYPE)0; + return entry->QueryType(); +} + +bool KEMessage::HasKey(QString key) +{ + KMessageEntry *entry; + entry=dict.find(key); + if (!entry) return false; + return true; +} + +bool KEMessage::GetData(QString key,short &s) +{ + short *result; + KMessageEntry *entry; + entry=dict.find(key); + if (!entry) return false; + if (entry->QueryType()!=KGM_TYPE_SHORT) return false; + // printf("GetShortData: %p for %s\n",entry->QueryData(),(char *)key); + result=(short *)entry->QueryData(); + s=*result; + return true; +} + +bool KEMessage::GetData(QString key,long &l) +{ + long *result; + KMessageEntry *entry; + entry=dict.find(key); + if (!entry) return false; + if (entry->QueryType()!=KGM_TYPE_LONG) return false; + result=(long *)entry->QueryData(); + l=*result; + return true; +} + +bool KEMessage::GetData(QString key,float &f) +{ + float *result; + KMessageEntry *entry; + entry=dict.find(key); + if (!entry) return false; + if (entry->QueryType()!=KGM_TYPE_FLOAT) return false; + // printf("GetFloatData: %p for %s\n",entry->QueryData(),(char *)key); + result=(float *)entry->QueryData(); + f=*result; + return true; +} + +bool KEMessage::GetData(QString key,char * &c,int &size) +{ + KMessageEntry *entry; + entry=dict.find(key); + if (!entry) return false; + if (entry->QueryType()!=KGM_TYPE_DATA) return false; + c=entry->QueryData(); + size=entry->QuerySize(); + return true; +} + +QString KEMessage::EntryToString(char *key,KMessageEntry *entry) +{ + QString s,tmp; + int size,i; + KGM_TYPE type; + char *data; + s=QCString(""); + if (!entry) return s; + size=entry->QuerySize(); + type=entry->QueryType(); + data=entry->QueryData(); + + // Key + /* + tmp.sprintf("%s%s%d%s%d%s", + key,KEMESSAGE_SEP, + size,KEMESSAGE_SEP, + (int)type,KEMESSAGE_SEP); + */ + tmp=QCString(key); + s+=tmp; + s+=KEMESSAGE_SEP; + tmp.sprintf("%d",size); + s+=tmp; + s+=KEMESSAGE_SEP; + tmp.sprintf("%d",(int)type); + s+=tmp; + s+=KEMESSAGE_SEP; + + + // We ignore the type of data and process them all as + // byte sequence + for (i=0;i>4)&15)); + s+=tmp; + } + s+=KEMESSAGE_CR; + + return s; +} + +QString KEMessage::StringToEntry(QString str,KMessageEntry *entry) +{ + int pos,oldpos,cnt,len; + QString key,size,type,data; + const char *p; + char *q; + char c; + + len=KEMESSAGE_SEP.length(); + + if (!entry) return QString(); + pos=str.find(KEMESSAGE_SEP,0); + if (pos<0) return QString(); // wrong format + key=str.left(pos); + + + oldpos=pos; + pos=str.find(KEMESSAGE_SEP,oldpos+len); + if (pos<0) return QString(); // wrong format + size=str.mid(oldpos+len,pos-oldpos-len); + + + oldpos=pos; + pos=str.find(KEMESSAGE_SEP,oldpos+len); + if (pos<0) return QString(); // wrong format + type=str.mid(oldpos+len,pos-oldpos-len); + + + data=str.right(str.length()-pos-len); + + + cnt=size.toInt(); + entry->SetType((KGM_TYPE)type.toInt()); + + // I hope this works with unicode strings as well + p=data.latin1(); + q=(char *)calloc(data.length()/2,sizeof(char)); + if (!q) return QString(); + for(pos=0;pos(int)data.length()) return QString(); // SEVERE ERROR + c=*(p+2*pos)-'a' | ((*(p+2*pos+1)-'a')<<4); + q[pos]=c; + } + entry->CopyData(cnt,q); + + free(q); + return key; +} + +QString KEMessage::ToString() +{ + QString s; + KMessageEntry *entry; + char *it; + s=KEMESSAGE_HEAD+KEMESSAGE_CR; + for (it=keys.first();it!=0;it=keys.next()) + { + entry=dict.find(QCString(it)); + s+=EntryToString(it,entry); + } + s+=KEMESSAGE_TAIL+KEMESSAGE_CR; + return s; +} + +bool KEMessage::AddString(QString s) +{ + // break s into key,size and data + QString key; + KMessageEntry *entry=new KMessageEntry; + key=StringToEntry(s,entry); + if (!key) return false; + AddEntry(key,entry); + return true; +} +bool KEMessage::AddStringMsg(QString str) +{ + bool result; + QString data; + int pos,oldpos,len; + + len=KEMESSAGE_CR.length(); + + pos=str.find(KEMESSAGE_CR); + if (pos<0) return false; // wrong format + if (str.left(pos)!=(KEMESSAGE_HEAD)) return false; // wrong message + + do + { + oldpos=pos; + pos=str.find(KEMESSAGE_CR,oldpos+len); + if (pos<0) return false; // wrong format + data=str.mid(oldpos+len,pos-oldpos-len); + if (data!=(KEMESSAGE_TAIL)) + { + result=AddString(data); + if (!result) return false; // wrong format + } + }while(data!=(KEMESSAGE_TAIL)); + + return result; +} + +void KEMessage::RemoveAll() +{ + keys.clear(); + dict.clear(); +} + +void KEMessage::Remove(QString key) +{ + keys.remove(key.latin1()); + dict.remove(key); +} + +uint KEMessage::QueryNumberOfKeys() +{ + return keys.count(); +} +QStrList *KEMessage::QueryKeys() +{ + return &keys; +} + +KEMessage::~KEMessage() +{ + // printf("Deleteing KEMessage %p\n",this); +} +KEMessage::KEMessage() +{ + // printf("KEMessage construct %p\n",this); + dict.setAutoDelete(true); +} +KEMessage::KEMessage(KEMessage &msg) +{ + // printf("KEMessage copy constructor from %p to %p\n",&msg,this); + *this=msg; +} +KEMessage &KEMessage::operator=(KEMessage &msg) +{ + // KEMessage *newmsg=new KEMessage; + KMessageEntry *entry; + KMessageEntry *newentry; + char *it; + // printf("Assigning = KEMessage from %p to %p\n",&msg,this); + for (it=msg.keys.first();it!=0;it=msg.keys.next()) + { + entry=msg.dict.find(QCString(it)); + newentry=new KMessageEntry; + *newentry=*entry; + AddEntry(QCString(it),newentry); + + } + // return *newmsg; + return *this; +} diff --git a/lskat/lskatproc/KEMessage.h b/lskat/lskatproc/KEMessage.h new file mode 100644 index 00000000..0a1913cc --- /dev/null +++ b/lskat/lskatproc/KEMessage.h @@ -0,0 +1,66 @@ +/*************************************************************************** + KEMessage.h - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef _KEMESSAGE_H_ +#define _KEMESSAGE_H_ + +#include +#include +#include +#include +#include "KMessageEntry.h" + +#define KEMESSAGE_HEAD QString(QCString("BEGIN_V1000")) +#define KEMESSAGE_TAIL QString(QCString("END_V1000")) +#define KEMESSAGE_CR QString(QCString("\n")) +#define KEMESSAGE_SEP QString(QCString(":::")) + +class KEMessage +{ + private: + QStrList keys; + QDict dict; + + protected: + void AddEntry(QString key,KMessageEntry *entry); + public: + QStrList *QueryKeys(); + uint QueryNumberOfKeys(); + void AddDataType(QString key,int size,const char *data,KGM_TYPE type); + void AddData(QString key,short data); + void AddData(QString key,long data); + void AddData(QString key,float data); + void AddData(QString key,const char *data,int size=-1); + bool GetData(QString key,short &s); + bool GetData(QString key,long &l); + bool GetData(QString key,float &f); + bool GetData(QString key,char * &c,int &size); + bool HasKey(QString key); + void Remove(QString key); + KGM_TYPE QueryType(QString key); + QString ToString(); + QString EntryToString(char *key,KMessageEntry *entry); + QString StringToEntry(QString str,KMessageEntry *entry); + bool AddString(QString s); + bool AddStringMsg(QString str); + void RemoveAll(); + ~KEMessage(); + KEMessage(); + KEMessage(KEMessage &msg); + KEMessage &operator=(KEMessage &msg); +}; + +#endif diff --git a/lskat/lskatproc/KInputChildProcess.cpp b/lskat/lskatproc/KInputChildProcess.cpp new file mode 100644 index 00000000..a821aa7a --- /dev/null +++ b/lskat/lskatproc/KInputChildProcess.cpp @@ -0,0 +1,106 @@ +/*************************************************************************** + KInputChildProcess.cpp - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ +#include +#include +#include +#include +#include + +#include "KInputChildProcess.h" +#include "KInputChildProcess.moc" + + +KInputChildProcess::~KInputChildProcess() +{ + delete buffer; + delete childConnect; +} +KInputChildProcess::KInputChildProcess(int size_buffer) + : QObject(0,0) +{ + buffersize=size_buffer; + if (buffersize<1) buffersize=1024; + buffer=new char[buffersize]; + inputbuffer=""; + terminateChild=false; +} +bool KInputChildProcess::exec() +{ + int pos; + QString s; + + childConnect=new KChildConnect; + if (!childConnect) return false; + connect(childConnect,SIGNAL(signalReceiveMsg(KEMessage *,int)), + this,SLOT(slotReceiveMsg(KEMessage *,int))); + do + { + // Wait for input + if (feof(stdin)) + { + sleep(1); + continue; + } + + if (!fgets(buffer,buffersize,stdin) ) + { + continue; + } + s=buffer; + s=inputbuffer+s; + // printf("ChildABC '%s'\n",(const char *)s); + // fflush(stdout); + pos=s.findRev(KEMESSAGE_CR); + if (pos<0) + { + inputbuffer=s; + } + else if (pos+KEMESSAGE_CR.length()==s.length()) + { + // CR at the end...calling receive + childConnect->Receive(s); + } + else + { + inputbuffer=s.right(s.length()-pos-KEMESSAGE_CR.length()); + s=s.left(pos+KEMESSAGE_CR.length()); + // printf("s='%s' in='%s'\n",(const char *)s,(const char *)inputbuffer); + childConnect->Receive(s); + } + }while(!terminateChild); + return true; +} + +void KInputChildProcess::Terminate() +{ + terminateChild=true; +} +bool KInputChildProcess::IsTerminated() +{ + return terminateChild; +} + +bool KInputChildProcess::ReceiveMsg(KEMessage *msg,int id) +{ + return false; +} +void KInputChildProcess::slotReceiveMsg(KEMessage *msg,int id) +{ + if (!ReceiveMsg(msg,id)) // made for virtual override + { + // otherwise emit signal + emit signalReceiveMsg(msg,id); + } +} +bool KInputChildProcess::SendMsg(KEMessage *msg) +{ + if (childConnect) return childConnect->SendMsg(msg); + return false; +} + + diff --git a/lskat/lskatproc/KInputChildProcess.h b/lskat/lskatproc/KInputChildProcess.h new file mode 100644 index 00000000..b4694df5 --- /dev/null +++ b/lskat/lskatproc/KInputChildProcess.h @@ -0,0 +1,57 @@ +/*************************************************************************** + KInputChildProcess.h - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef _KINPUTCHILDPROCESS_H_ +#define _KINPUTCHILDPROCESS_H_ + +#include +#include "KEMessage.h" +#include "KChildConnect.h" + + +class KInputChildProcess : public QObject +{ + Q_OBJECT + + private: + char *buffer; + QString inputbuffer; + int buffersize; + bool terminateChild; + protected: + KChildConnect *childConnect; + + public: + KInputChildProcess(int size_buffer=4096); + ~KInputChildProcess(); + bool exec(); + virtual bool ReceiveMsg(KEMessage *msg,int id); + // Forward calls to childconnect + bool SendMsg(KEMessage *msg); + // Immediately kills child's exec ! + void Terminate(); + bool IsTerminated(); + + + public slots: + void slotReceiveMsg(KEMessage *msg,int id); + + signals: + void signalReceiveMsg(KEMessage *msg,int id); +}; + + +#endif diff --git a/lskat/lskatproc/KMessageEntry.cpp b/lskat/lskatproc/KMessageEntry.cpp new file mode 100644 index 00000000..e91f645a --- /dev/null +++ b/lskat/lskatproc/KMessageEntry.cpp @@ -0,0 +1,98 @@ +/*************************************************************************** + KMessageEntry.cpp - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +/*************************************************************************** + FILENAME| - description + ------------------- + begin : Tue Apr 4 2000 + copyright : (C) |1995-2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include +#include +#include +#include "KMessageEntry.h" + + +void KMessageEntry::SetType(KGM_TYPE t) +{ + type=t; +} + +KGM_TYPE KMessageEntry::QueryType() +{ + return type; +} + +int KMessageEntry::QuerySize() +{ + return size; +} + +char * KMessageEntry::QueryData() +{ + return data; +} + +bool KMessageEntry::CopyData(int s,const char *c) +{ + if (s<1) return false; + data=(char *)calloc(s,1); + if (!data) return false; + // printf(" MessageEntry Copy Data to calloc %p\n",data); + memcpy(data,c,s); + size=s; + return true; +} + +KMessageEntry::KMessageEntry() +{ + // printf("KMessageEntry construct %p\n",this); + size=0; + type=(KGM_TYPE)0; + data=(char *)0; +} + +KMessageEntry::KMessageEntry(KMessageEntry &entry) +{ + // printf("KMessageEntry copy constructor from %p to %p\n",&entry,this); + *this=entry; +} +KMessageEntry &KMessageEntry::operator=(KMessageEntry &entry) +{ + // printf("KMessageEntry operator= from %p to %p\n",&entry,this); + SetType(entry.type); + CopyData(entry.size,entry.data); + return *this; +} + +KMessageEntry::~KMessageEntry() +{ + // printf("MessageEntry destructor %p\n",this); + // printf(" MessageEntry free %p\n",data); + if (data) free(data); + data=(char *)0; +} + diff --git a/lskat/lskatproc/KMessageEntry.h b/lskat/lskatproc/KMessageEntry.h new file mode 100644 index 00000000..947ce1ff --- /dev/null +++ b/lskat/lskatproc/KMessageEntry.h @@ -0,0 +1,44 @@ +/*************************************************************************** + KMessageEntry.h - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef _KMESSAGEENTRY_H_ +#define _KMESSAGEENTRY_H_ + +#include +#include "KConnectTypes.h" + + + +class KMessageEntry +{ + private: + int size; + KGM_TYPE type; + char *data; + + public: + void SetType(KGM_TYPE t); + KGM_TYPE QueryType(); + int QuerySize(); + char *QueryData(); + bool CopyData(int s,const char *c); + KMessageEntry(); + KMessageEntry(KMessageEntry &entry); + KMessageEntry &operator=(KMessageEntry &entry); + ~KMessageEntry(); +}; + +#endif diff --git a/lskat/lskatproc/Makefile.am b/lskat/lskatproc/Makefile.am new file mode 100644 index 00000000..4f99375b --- /dev/null +++ b/lskat/lskatproc/Makefile.am @@ -0,0 +1,13 @@ +bin_PROGRAMS = lskatproc + +lskatproc_SOURCES = lskatproc.cpp KChildConnect.cpp KInputChildProcess.cpp KEMessage.cpp KMessageEntry.cpp main.cpp + +lskatproc_LDADD = $(LIB_KFILE) + +# set the include path for X, qt and KDE +INCLUDES= $(all_includes) + +METASOURCES = AUTO + +# the library search path. +lskatproc_LDFLAGS = $(all_libraries) $(KDE_RPATH) diff --git a/lskat/lskatproc/docs/Makefile.am b/lskat/lskatproc/docs/Makefile.am new file mode 100644 index 00000000..271bd418 --- /dev/null +++ b/lskat/lskatproc/docs/Makefile.am @@ -0,0 +1,4 @@ +####### kdevelop will overwrite this part!!! (begin)########## + + +####### kdevelop will overwrite this part!!! (end)############ diff --git a/lskat/lskatproc/docs/en/Makefile.am b/lskat/lskatproc/docs/en/Makefile.am new file mode 100644 index 00000000..271bd418 --- /dev/null +++ b/lskat/lskatproc/docs/en/Makefile.am @@ -0,0 +1,4 @@ +####### kdevelop will overwrite this part!!! (begin)########## + + +####### kdevelop will overwrite this part!!! (end)############ diff --git a/lskat/lskatproc/lskatproc.cpp b/lskat/lskatproc/lskatproc.cpp new file mode 100644 index 00000000..c1fdcfba --- /dev/null +++ b/lskat/lskatproc/lskatproc.cpp @@ -0,0 +1,596 @@ +/*************************************************************************** + lskatproc.cpp - description + ------------------- + begin : Sun Apr 9 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include "lskatproc.h" + +#define MIN_TIME 1000 // usec + +// ------------ class game --------------------------------- +lgame::lgame() +{ + int i; + for (i=0;i<14;i++) cardvalues[i]=0; + cardvalues[(int)Ace]=11; + cardvalues[(int)Ten]=10; + cardvalues[(int)King]=4; + cardvalues[(int)Queen]=3; + cardvalues[(int)Jack]=2; + curmove[0]=-1; + curmove[1]=-1; + score[0]=0; + score[1]=0; + level=0; + endgame=false; + for (i=0;i=0) played[card]=1; + } + } + } +} + +// Add value of both cards to the score of startplayer +void lgame::AddScore(int c1,int c2) +{ + score[startplayer]+=CardValue(c1); + score[startplayer]+=CardValue(c2); +} +// Switch the startplayer +void lgame::SwitchStartplayer() +{ + startplayer=1-startplayer; +} + +// pos=0..7, player=0..1 +void lgame::SetHeight(int player, int pos,int h) +{ + int i; + i=8*player+pos; + cardheight[i]=h; +} +bool lgame::LegalMove(int p1, int p2) +{ + CCOLOUR col1,col2,col3; + CCARD card1,card2,card3; + card1=(CCARD)((p1)/4); + col1=(CCOLOUR)((p1)%4); + card2=(CCARD)((p2)/4); + col2=(CCOLOUR)((p2)%4); + + // force trump colour + if (card1==Jack) col1=trump; + if (card2==Jack) col2=trump; + + // same colour always ok + if (col1==col2) return true; + + // Search for same colour + bool flag=true; + for (int i=0;i<8;i++) + { + int h,c; + h=GetHeight(1-startplayer,i); + if (h==0) continue; + c=GetCard(1-startplayer,i,h); + card3=(CCARD)((c)/4); + col3=(CCOLOUR)((c)%4); + if (card3==Jack) col3=trump; + + if (col3==col1) + { + flag=false; + break; + } + } + if (flag) return true; + + + return false; +} +int lgame::CardValue(int card) +{ + int card1; + + card1=card/4; + return cardvalues[card1]; +} +int lgame::WonMove(int c1,int c2) +{ + CCOLOUR col1,col2; + CCARD card1,card2; + + card1=(CCARD)((c1)/4); + col1=(CCOLOUR)((c1)%4); + card2=(CCARD)((c2)/4); + col2=(CCOLOUR)((c2)%4); + + // Two jacks + if (card1==Jack && card2==Jack) + { + if (col15) sc+=800; + + if (haveten[i]&&havecol[i]<2) + { + card=8*i+Ace; + if (!played[card] && !haveace[i]) sc-=2500; // free ten + } + if (haveace[i]) sc+=1500; // ace + if (havejack[i]) + { + if (trump==Grand) sc+=4000+300*(4-i); + else sc+=2700+100*(4-i); + } + } + // evaluate + sc+=trum1*2500; + if (trum1==0) sc-=7000; + else if (trum1==1) sc-=5000; + return sc; +} + +int lgame::Value(int player) +{ + int sc; + sc=0; + + // Someone won? + if (score[0]>90) sc+=90000; + else if (score[0]>60) sc+=70000; + else if (score[0]==60) sc+=40000; + + if (score[1]>90) sc-=90000; + else if (score[1]>60) sc-=70000; + else if (score[1]==60) sc-=40000; + + // Reward points + sc+=(score[0]-score[1])*650; + + // Calulate cards + sc+=Subvalue(0); + sc-=Subvalue(1); + + // random + sc+=random(500)-250; + + if (player==1) return -sc; + return sc; +} + + +// -------------class lskatproc ---------------------------- +lskatproc::lskatproc() + : KInputChildProcess(4096) +{ + + initrandom(); +} + +lskatproc::~lskatproc(){ +} + + + +bool lskatproc::ReceiveMsg(KEMessage* msg,int id) +{ +// time_t timee,timea; +short x,y; + + SendDebug("Receiv Msg"); + // end of process + if (msg->HasKey(QCString("Terminate"))) + { + Terminate(); + } + // Init of process + if (msg->HasKey(QCString("Init"))) + { + // No init necessary + } + // Make a move + if (msg->HasKey(QCString("Cards"))) + { + SendDebug("Process HasKey(Cards)"); + // new game object + lgame game; + // extract data from message + game.ExtractGame(msg); + game.Init(); // must be AFTER ExtractGame + + // Debug stuff only + sprintf(buf,"Trump=%d move=%d sc1=%d sc2=%d", + game.trump,game.curmove[1-game.currentplayer],game.score[0],game.score[1]); + SendDebug(buf); + + if (game.currentplayer==0 && game.startplayer==0) + sprintf(buf,"+++ Computer ACTS as player ONE\n"); + else if (game.currentplayer==0 && game.startplayer==1) + sprintf(buf,"+++ Computer REACTS as player ONE\n"); + else if (game.currentplayer==1 && game.startplayer==1) + sprintf(buf,"+++ Computer ACTS as player TWO\n"); + else + sprintf(buf,"+++ Computer REACTS as player TWO\n"); + SendDebug(buf); + + // fills data + x=0;y=0; + GetComputerMove(game,x,y,0); + sprintf(buf,"Computer move player=%d x=%d y=%d",game.currentplayer,x,y); + SendDebug(buf); + + + // report move + msg->RemoveAll(); + msg->AddData(QCString("Move"),game.currentplayer); + msg->AddData(QCString("MoveX"),x); + msg->AddData(QCString("MoveY"),y); + + //timee=time(0); + // Sleep a minimum amount to slow down moves + //if ( 1000*(timee-timea) < MIN_TIME) usleep((MIN_TIME-1000*(timee-timea))); + SendDebug("Sending move back to main"); + + if (!IsTerminated()) SendMsg(msg); + fflush(stdout); // I do not know why it is needed..send does it too? + } + + return true; +} + + +/* --------------------------------------------------------------------------- */ +/* Computer Routinen */ +/* --------------------------------------------------------------------------- */ + +// extract game from msg +int lgame::ExtractGame(KEMessage *msg) +{ + int i; + short tmp; + char *p; + int size; + + msg->GetData(QCString("Startplayer"),startplayer); + msg->GetData(QCString("CurrentPlayer"),currentplayer); + msg->GetData(QCString("Cards"),p,size); + msg->GetData(QCString("Level"),level); + level--; // start with level 0 + for (i=0;iGetData(QCString("Height"),p,size); + for (i=0;iGetData(QCString("Trump"),tmp); + trump=(CCOLOUR)tmp; + short mm; + msg->GetData(QCString("CurrentMove"),mm); + curmove[1-currentplayer]=(int)mm; + curmove[currentplayer]=-1; + msg->GetData(QCString("No"),movenumber); + msg->GetData(QCString("Sc1"),score[0]); + msg->GetData(QCString("Sc2"),score[1]); + return 1; +} + +long lgame::random(long max) +{ +double value; +int r; + r=rand(); + value=(double)((double)r*(double)max)/(double)RAND_MAX; + return (long)value; +} + +void lskatproc::initrandom() +{ + srand( (unsigned)time( NULL ) ); // randomize +} + + +int lskatproc::GetComputerMove(lgame game,short &x,short &y,int rek) +{ + int i,maxvalue,maxmove,h,c; + //short oldscore; + bool startflag; + int startplayer; + int value; + lgame cgame; + char sbuf[100]; + short mx,my; + + + for (i=0;i<2*rek;i++) sbuf[i]=' '; + sbuf[2*rek]=0; + + x=0; + y=0; + if (game.currentplayer==game.startplayer) startflag=true; + else startflag=false; + + startplayer=game.startplayer; + + maxmove=0; + maxvalue=LOWERT; + + sprintf(buf,"%s:Prepareing computer move (cur=%d) startflag=%d", + sbuf,game.currentplayer,startflag); + //SendDebug(buf); + for (i=0;i<8;i++) + { + sprintf(buf,"%s:Checking for card %d of player %d\n",sbuf,i,game.currentplayer); + // SendDebug(buf); + cgame=game; + h=cgame.GetHeight(cgame.currentplayer,i); + if (h<1) + { + sprintf(buf,"%s:i=%d:: no cards left",sbuf,i); + // SendDebug(buf); + continue; // no cards left + } + c=cgame.GetCard(cgame.currentplayer,i,h); + if (cgame.MakeMove(c,i)<0) + { + sprintf(buf,"%s:i=%d:: no legal move c1=%d c2=%d", + sbuf,i,cgame.curmove[cgame.startplayer],c); + // SendDebug(buf); + continue; // wrong card + } + if (!startflag) // we are second + { + sprintf(buf,"LEVEL %d %d",cgame.level,rek); + SendDebug(buf); + // Still recursion necessary and game not yet ended? + if (rek<2*cgame.level && !cgame.endgame) + { + // If we have the same startplayer the movesequence + // is not switched and we can take the negative value + // otherwise we play again, and have to take the poitiv value + if (cgame.startplayer==startplayer) + { + value=-GetComputerMove(cgame,mx,my,rek+1); + // if (value==-LOWERT) value=LOWERT; // no move possible + } + else + value=GetComputerMove(cgame,mx,my,rek+1); + } + else // evaluate position + { + value=cgame.Value(1-startplayer); + } + } + else // we are first player + { + // Alwayss the other player moves now + value=-GetComputerMove(cgame,mx,my,rek+1); + } + + sprintf(buf,"%s:i=%d:: Value=%d",sbuf,i,value); + SendDebug(buf); + + if (value>maxvalue) + { + maxvalue=value; + maxmove=i; + } + } + x=maxmove%4; + y=maxmove/4; + return maxvalue; +} + +void lskatproc::SendDebug(const char *s) +{ + KEMessage *msg=new KEMessage; + msg->AddData(QCString("Debug"),s); +// msg->AddData("KLogSendMsg","debug.log"); +// DEBUG +// SendMsg(msg); +// printf("%s\n",s); + + delete msg; +} diff --git a/lskat/lskatproc/lskatproc.h b/lskat/lskatproc/lskatproc.h new file mode 100644 index 00000000..b5840461 --- /dev/null +++ b/lskat/lskatproc/lskatproc.h @@ -0,0 +1,104 @@ +/*************************************************************************** + lskatproc.h - description + ------------------- + begin : Sun Apr 9 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef LSKATPROC_H +#define LSKATPROC_H +#include +#include +#include +#include +#include + +#include "KInputChildProcess.h" + +/** + *@author Martin Heni + */ + + + +#define LOWERT -999999999L +#define SIEG_WERT 9999999L + + +#define START_REK 1 // (0) 1:Nur Stellungsbewertung bei Level 1 + // 0:Level 1 schon eine Rekursion + +typedef enum {Club=0,Spade=1,Heart=2,Diamond=3,Grand=4} CCOLOUR; +typedef enum {Ace=0,King=1,Queen=2,Jack=3,Ten=4,Nine=5,Eight=6,Seven=7} CCARD; +#define NO_OF_CARDS 32 +#define NO_OF_TILES 16 +#define NO_OF_TRUMPS 5 + +class lgame +{ + public: + lgame(); + lgame(lgame &game); + lgame &operator=(lgame &game); + + int WonMove(int c1,int c2); + int CardValue(int card); + bool LegalMove(int p1, int p2); + void SetHeight(int player, int pos,int h); + int GetHeight(int player, int pos); + int GetCard(int player, int pos,int height); + int Value(int player); + void AddScore(int c1,int c2); + void SwitchStartplayer(); + int MakeMove(int c,int pos); + void Init(); + long random(long max); + int Subvalue(int side); + int ExtractGame(KEMessage *msg); + + short currentplayer; + short startplayer; + int card[NO_OF_CARDS]; + int cardheight[16]; + int cardvalues[14]; + short score[2]; + CCOLOUR trump; + short movenumber; + int curmove[2]; + bool endgame; + int played[NO_OF_CARDS]; // cards already played + short level; +}; + + +class lskatproc : public KInputChildProcess +{ + +private: + +public: + lskatproc(); + ~lskatproc(); + + virtual bool ReceiveMsg(KEMessage *msg,int id); + + + void initrandom(); + int GetComputerMove(lgame game,short &x,short &y,int rek); + void SendDebug(const char *s); + + private: + char buf[1024]; +}; + +#endif diff --git a/lskat/lskatproc/main.cpp b/lskat/lskatproc/main.cpp new file mode 100644 index 00000000..d25cd8c9 --- /dev/null +++ b/lskat/lskatproc/main.cpp @@ -0,0 +1,28 @@ +/*************************************************************************** + main.cpp - description + ------------------- + begin : Sun Apr 9 22:56:15 CEST 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include "lskatproc.h" + + + + +int main(int argc, char *argv[]) +{ + lskatproc mComm; + return mComm.exec() ? 0 : 1; +} diff --git a/lskat/lskatproc/templates/cpp_template b/lskat/lskatproc/templates/cpp_template new file mode 100644 index 00000000..6afef5d4 --- /dev/null +++ b/lskat/lskatproc/templates/cpp_template @@ -0,0 +1,16 @@ +/*************************************************************************** + |FILENAME| - description + ------------------- + begin : |DATE| + copyright : (C) |YEAR| by |AUTHOR| + email : |EMAIL| + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ diff --git a/lskat/lskatproc/templates/header_template b/lskat/lskatproc/templates/header_template new file mode 100644 index 00000000..6afef5d4 --- /dev/null +++ b/lskat/lskatproc/templates/header_template @@ -0,0 +1,16 @@ +/*************************************************************************** + |FILENAME| - description + ------------------- + begin : |DATE| + copyright : (C) |YEAR| by |AUTHOR| + email : |EMAIL| + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/